generated from zeroknots/femplate
-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathCreate2Factory.sol
150 lines (137 loc) · 6.97 KB
/
Create2Factory.sol
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
145
146
147
148
149
150
pragma solidity 0.8.24;
/**
* @title Immutable Create2 Contract Factory
* @author 0age
* @notice This contract provides a safeCreate2 function that takes a salt value
* and a block of initialization code as arguments and passes them into inline
* assembly. The contract prevents redeploys by maintaining a mapping of all
* contracts that have already been deployed, and prevents frontrunning or other
* collisions by requiring that the first 20 bytes of the salt are equal to the
* address of the caller (this can be bypassed by setting the first 20 bytes to
* the null address). There is also a view function that computes the address of
* the contract that will be created when submitting a given salt or nonce along
* with a given block of initialization code.
* @dev CREATE2 will not be available on mainnet until (at least) block
* 7,280,000. This contract has not yet been fully tested or audited - proceed
* with caution and please share any exploits or optimizations you discover.
*/
contract ImmutableCreate2Factory {
// mapping to track which addresses have already been deployed.
mapping(address => bool) private _deployed;
function safeCreate2(
bytes32 salt,
bytes calldata initializationCode
)
external
payable
containsCaller(salt)
returns (address deploymentAddress)
{
// move the initialization code from calldata to memory.
bytes memory initCode = initializationCode;
// determine the target address for contract deployment.
address targetDeploymentAddress = address(
uint160( // downcast to match the address type.
uint256( // convert to uint to truncate upper digits.
keccak256( // compute the CREATE2 hash using 4 inputs.
abi.encodePacked( // pack all inputs to the hash together.
hex"ff", // start with 0xff to distinguish from RLP.
address(this), // this contract will be the caller.
salt, // pass in the supplied salt value.
keccak256( // pass in the hash of initialization code.
abi.encodePacked(initCode))
)
)
)
)
);
// ensure that a contract hasn't been previously deployed to target address.
require(!_deployed[targetDeploymentAddress], "Invalid contract creation - contract has already been deployed.");
// using inline assembly: load data and length of data, then call CREATE2.
assembly {
// solhint-disable-line
let encoded_data := add(0x20, initCode) // load initialization code.
let encoded_size := mload(initCode) // load the init code's length.
deploymentAddress :=
create2( // call CREATE2 with 4 arguments.
callvalue(), // forward any attached value.
encoded_data, // pass in initialization code.
encoded_size, // pass in init code's length.
salt // pass in the salt value.
)
}
// check address against target to ensure that deployment was successful.
require(deploymentAddress == targetDeploymentAddress, "Failed to deploy contract using provided salt and initialization code.");
// record the deployment of the contract to prevent redeploys.
_deployed[deploymentAddress] = true;
}
function findCreate2Address(bytes32 salt, bytes calldata initCode) external view returns (address deploymentAddress) {
// determine the address where the contract will be deployed.
deploymentAddress = address(
uint160( // downcast to match the address type.
uint256( // convert to uint to truncate upper digits.
keccak256( // compute the CREATE2 hash using 4 inputs.
abi.encodePacked( // pack all inputs to the hash together.
hex"ff", // start with 0xff to distinguish from RLP.
address(this), // this contract will be the caller.
salt, // pass in the supplied salt value.
keccak256( // pass in the hash of initialization code.
abi.encodePacked(initCode))
)
)
)
)
);
// return null address to signify failure if contract has been deployed.
if (_deployed[deploymentAddress]) {
return address(0);
}
}
function findCreate2AddressViaHash(bytes32 salt, bytes32 initCodeHash) external view returns (address deploymentAddress) {
// determine the address where the contract will be deployed.
deploymentAddress = address(
uint160( // downcast to match the address type.
uint256( // convert to uint to truncate upper digits.
keccak256( // compute the CREATE2 hash using 4 inputs.
abi.encodePacked( // pack all inputs to the hash together.
hex"ff", // start with 0xff to distinguish from RLP.
address(this), // this contract will be the caller.
salt, // pass in the supplied salt value.
initCodeHash // pass in the hash of initialization code.
)
)
)
)
);
// return null address to signify failure if contract has been deployed.
if (_deployed[deploymentAddress]) {
return address(0);
}
}
/**
* @dev Determine if a contract has already been deployed by the factory to a
* given address.
* @param deploymentAddress address The contract address to check.
* @return True if the contract has been deployed, false otherwise.
*/
function hasBeenDeployed(address deploymentAddress) external view returns (bool) {
// determine if a contract has been deployed to the provided address.
return _deployed[deploymentAddress];
}
/**
* @dev Modifier to ensure that the first 20 bytes of a submitted salt match
* those of the calling account. This provides protection against the salt
* being stolen by frontrunners or other attackers. The protection can also be
* bypassed if desired by setting each of the first 20 bytes to zero.
* @param salt bytes32 The salt value to check against the calling address.
*/
modifier containsCaller(bytes32 salt) {
// prevent contract submissions from being stolen from tx.pool by requiring
// that the first 20 bytes of the submitted salt match msg.sender.
require(
(address(bytes20(salt)) == msg.sender) || (bytes20(salt) == bytes20(0)),
"Invalid salt - first 20 bytes of the salt must match calling address."
);
_;
}
}