-
Notifications
You must be signed in to change notification settings - Fork 588
Expand file tree
/
Copy pathTWFactory.sol
More file actions
144 lines (109 loc) · 6.28 KB
/
TWFactory.sol
File metadata and controls
144 lines (109 loc) · 6.28 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.11;
/// @author thirdweb
// $$\ $$\ $$\ $$\ $$\
// $$ | $$ | \__| $$ | $$ |
// $$$$$$\ $$$$$$$\ $$\ $$$$$$\ $$$$$$$ |$$\ $$\ $$\ $$$$$$\ $$$$$$$\
// \_$$ _| $$ __$$\ $$ |$$ __$$\ $$ __$$ |$$ | $$ | $$ |$$ __$$\ $$ __$$\
// $$ | $$ | $$ |$$ |$$ | \__|$$ / $$ |$$ | $$ | $$ |$$$$$$$$ |$$ | $$ |
// $$ |$$\ $$ | $$ |$$ |$$ | $$ | $$ |$$ | $$ | $$ |$$ ____|$$ | $$ |
// \$$$$ |$$ | $$ |$$ |$$ | \$$$$$$$ |\$$$$$\$$$$ |\$$$$$$$\ $$$$$$$ |
// \____/ \__| \__|\__|\__| \_______| \_____\____/ \_______|\_______/
import { TWRegistry } from "./TWRegistry.sol";
import "./interface/IThirdwebContract.sol";
import "../extension/interface/IContractFactory.sol";
import { AccessControlEnumerable, Context } from "@openzeppelin/contracts/access/AccessControlEnumerable.sol";
import { ERC2771Context } from "../external-deps/openzeppelin/metatx/ERC2771Context.sol";
import { Create2 } from "@openzeppelin/contracts/utils/Create2.sol";
import { Multicall } from "../extension/Multicall.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import { Clones } from "@openzeppelin/contracts/proxy/Clones.sol";
contract TWFactory is Multicall, ERC2771Context, AccessControlEnumerable, IContractFactory {
/// @dev Only FACTORY_ROLE holders can approve/unapprove implementations for proxies to point to.
bytes32 public constant FACTORY_ROLE = keccak256("FACTORY_ROLE");
TWRegistry public immutable registry;
/// @dev Emitted when a proxy is deployed.
event ProxyDeployed(address indexed implementation, address proxy, address indexed deployer);
event ImplementationAdded(address implementation, bytes32 indexed contractType, uint256 version);
event ImplementationApproved(address implementation, bool isApproved);
/// @dev mapping of implementation address to deployment approval
mapping(address => bool) public approval;
/// @dev mapping of implementation address to implementation added version
mapping(bytes32 => uint256) public currentVersion;
/// @dev mapping of contract type to module version to implementation address
mapping(bytes32 => mapping(uint256 => address)) public implementation;
/// @dev mapping of proxy address to deployer address
mapping(address => address) public deployer;
constructor(address[] memory _trustedForwarders, address _registry) ERC2771Context(_trustedForwarders) {
_setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
_setupRole(FACTORY_ROLE, _msgSender());
registry = TWRegistry(_registry);
}
/// @dev Deploys a proxy that points to the latest version of the given contract type.
function deployProxy(bytes32 _type, bytes memory _data) external returns (address) {
bytes32 salt = bytes32(registry.count(_msgSender()));
return deployProxyDeterministic(_type, _data, salt);
}
/**
* @dev Deploys a proxy at a deterministic address by taking in `salt` as a parameter.
* Proxy points to the latest version of the given contract type.
*/
function deployProxyDeterministic(bytes32 _type, bytes memory _data, bytes32 _salt) public returns (address) {
address _implementation = implementation[_type][currentVersion[_type]];
return deployProxyByImplementation(_implementation, _data, _salt);
}
/// @dev Deploys a proxy that points to the given implementation.
function deployProxyByImplementation(
address _implementation,
bytes memory _data,
bytes32 _salt
) public override returns (address deployedProxy) {
require(approval[_implementation], "implementation not approved");
bytes32 salthash = keccak256(abi.encodePacked(_msgSender(), _salt));
deployedProxy = Clones.cloneDeterministic(_implementation, salthash);
deployer[deployedProxy] = _msgSender();
emit ProxyDeployed(_implementation, deployedProxy, _msgSender());
registry.add(_msgSender(), deployedProxy);
if (_data.length > 0) {
// slither-disable-next-line unused-return
Address.functionCall(deployedProxy, _data);
}
}
/// @dev Lets a contract admin set the address of a contract type x version.
function addImplementation(address _implementation) external {
require(hasRole(FACTORY_ROLE, _msgSender()), "not admin.");
IThirdwebContract module = IThirdwebContract(_implementation);
bytes32 ctype = module.contractType();
require(ctype.length > 0, "invalid module");
uint8 version = module.contractVersion();
uint8 currentVersionOfType = uint8(currentVersion[ctype]);
require(version >= currentVersionOfType, "wrong module version");
currentVersion[ctype] = version;
implementation[ctype][version] = _implementation;
approval[_implementation] = true;
emit ImplementationAdded(_implementation, ctype, version);
}
/// @dev Lets a contract admin approve a specific contract for deployment.
function approveImplementation(address _implementation, bool _toApprove) external {
require(hasRole(FACTORY_ROLE, _msgSender()), "not admin.");
approval[_implementation] = _toApprove;
emit ImplementationApproved(_implementation, _toApprove);
}
/// @dev Returns the implementation given a contract type and version.
function getImplementation(bytes32 _type, uint256 _version) external view returns (address) {
return implementation[_type][_version];
}
/// @dev Returns the latest implementation given a contract type.
function getLatestImplementation(bytes32 _type) external view returns (address) {
return implementation[_type][currentVersion[_type]];
}
function _msgSender() internal view virtual override(Context, ERC2771Context, Multicall) returns (address sender) {
return ERC2771Context._msgSender();
}
function _msgData() internal view virtual override(Context, ERC2771Context) returns (bytes calldata) {
return ERC2771Context._msgData();
}
function _contextSuffixLength() internal view virtual override(Context, ERC2771Context) returns (uint256) {
return ERC2771Context._contextSuffixLength();
}
}