3
3
4
4
pragma solidity ^ 0.8.20 ;
5
5
6
+ import {Create2} from "../utils/Create2.sol " ;
6
7
import {Errors} from "../utils/Errors.sol " ;
7
8
8
9
/**
@@ -17,6 +18,8 @@ import {Errors} from "../utils/Errors.sol";
17
18
* deterministic method.
18
19
*/
19
20
library Clones {
21
+ error CloneArgumentsTooLong ();
22
+
20
23
/**
21
24
* @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
22
25
*
@@ -118,4 +121,142 @@ library Clones {
118
121
) internal view returns (address predicted ) {
119
122
return predictDeterministicAddress (implementation, salt, address (this ));
120
123
}
124
+
125
+ /**
126
+ * @dev Deploys and returns the address of a clone that mimics the behavior of `implementation` with custom
127
+ * immutable arguments. These are provided through `args` and cannot be changed after deployment. To
128
+ * access the arguments within the implementation, use {fetchCloneArgs}.
129
+ *
130
+ * This function uses the create opcode, which should never revert.
131
+ */
132
+ function cloneWithImmutableArgs (address implementation , bytes memory args ) internal returns (address instance ) {
133
+ return cloneWithImmutableArgs (implementation, args, 0 );
134
+ }
135
+
136
+ /**
137
+ * @dev Same as {xref-Clones-cloneWithImmutableArgs-address-bytes-}[cloneWithImmutableArgs], but with a `value`
138
+ * parameter to send native currency to the new contract.
139
+ *
140
+ * NOTE: Using a non-zero value at creation will require the contract using this function (e.g. a factory)
141
+ * to always have enough balance for new deployments. Consider exposing this function under a payable method.
142
+ */
143
+ function cloneWithImmutableArgs (
144
+ address implementation ,
145
+ bytes memory args ,
146
+ uint256 value
147
+ ) internal returns (address instance ) {
148
+ if (address (this ).balance < value) {
149
+ revert Errors.InsufficientBalance (address (this ).balance, value);
150
+ }
151
+ bytes memory bytecode = _cloneCodeWithImmutableArgs (implementation, args);
152
+ assembly ("memory-safe" ) {
153
+ instance := create (value, add (bytecode, 0x20 ), mload (bytecode))
154
+ }
155
+ if (instance == address (0 )) {
156
+ revert Errors.FailedDeployment ();
157
+ }
158
+ }
159
+
160
+ /**
161
+ * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation` with custom
162
+ * immutable arguments. These are provided through `args` and cannot be changed after deployment. To
163
+ * access the arguments within the implementation, use {fetchCloneArgs}.
164
+ *
165
+ * This function uses the create2 opcode and a `salt` to deterministically deploy the clone. Using the same
166
+ * `implementation` and `salt` multiple time will revert, since the clones cannot be deployed twice at the same
167
+ * address.
168
+ */
169
+ function cloneDeterministicWithImmutableArgs (
170
+ address implementation ,
171
+ bytes memory args ,
172
+ bytes32 salt
173
+ ) internal returns (address instance ) {
174
+ return cloneDeterministicWithImmutableArgs (implementation, args, salt, 0 );
175
+ }
176
+
177
+ /**
178
+ * @dev Same as {xref-Clones-cloneDeterministicWithImmutableArgs-address-bytes-bytes32-}[cloneDeterministicWithImmutableArgs],
179
+ * but with a `value` parameter to send native currency to the new contract.
180
+ *
181
+ * NOTE: Using a non-zero value at creation will require the contract using this function (e.g. a factory)
182
+ * to always have enough balance for new deployments. Consider exposing this function under a payable method.
183
+ */
184
+ function cloneDeterministicWithImmutableArgs (
185
+ address implementation ,
186
+ bytes memory args ,
187
+ bytes32 salt ,
188
+ uint256 value
189
+ ) internal returns (address instance ) {
190
+ bytes memory bytecode = _cloneCodeWithImmutableArgs (implementation, args);
191
+ return Create2.deploy (value, salt, bytecode);
192
+ }
193
+
194
+ /**
195
+ * @dev Computes the address of a clone deployed using {Clones-cloneDeterministicWithImmutableArgs}.
196
+ */
197
+ function predictDeterministicAddressWithImmutableArgs (
198
+ address implementation ,
199
+ bytes memory args ,
200
+ bytes32 salt ,
201
+ address deployer
202
+ ) internal pure returns (address predicted ) {
203
+ bytes memory bytecode = _cloneCodeWithImmutableArgs (implementation, args);
204
+ return Create2.computeAddress (salt, keccak256 (bytecode), deployer);
205
+ }
206
+
207
+ /**
208
+ * @dev Computes the address of a clone deployed using {Clones-cloneDeterministicWithImmutableArgs}.
209
+ */
210
+ function predictDeterministicAddressWithImmutableArgs (
211
+ address implementation ,
212
+ bytes memory args ,
213
+ bytes32 salt
214
+ ) internal view returns (address predicted ) {
215
+ return predictDeterministicAddressWithImmutableArgs (implementation, args, salt, address (this ));
216
+ }
217
+
218
+ /**
219
+ * @dev Get the immutable args attached to a clone.
220
+ *
221
+ * - If `instance` is a clone that was deployed using `clone` or `cloneDeterministic`, this
222
+ * function will return an empty array.
223
+ * - If `instance` is a clone that was deployed using `cloneWithImmutableArgs` or
224
+ * `cloneDeterministicWithImmutableArgs`, this function will return the args array used at
225
+ * creation.
226
+ * - If `instance` is NOT a clone deployed using this library, the behavior is undefined. This
227
+ * function should only be used to check addresses that are known to be clones.
228
+ */
229
+ function fetchCloneArgs (address instance ) internal view returns (bytes memory ) {
230
+ bytes memory result = new bytes (instance.code.length - 0x2d ); // revert if length is too short
231
+ assembly ("memory-safe" ) {
232
+ extcodecopy (instance, add (result, 0x20 ), 0x2d , mload (result))
233
+ }
234
+ return result;
235
+ }
236
+
237
+ /**
238
+ * @dev Helper that prepares the initcode of the proxy with immutable args.
239
+ *
240
+ * An assembly variant of this function requires copying the `args` array, which can be efficiently done using
241
+ * `mcopy`. Unfortunately, that opcode is not available before cancun. A pure solidity implementation using
242
+ * abi.encodePacked is more expensive but also more portable and easier to review.
243
+ *
244
+ * NOTE: https://eips.ethereum.org/EIPS/eip-170[EIP-170] limits the length of the contract code to 24576 bytes.
245
+ * With the proxy code taking 45 bytes, that limits the length of the immutable args to 24531 bytes.
246
+ */
247
+ function _cloneCodeWithImmutableArgs (
248
+ address implementation ,
249
+ bytes memory args
250
+ ) private pure returns (bytes memory ) {
251
+ if (args.length > 0x5fd3 ) revert CloneArgumentsTooLong ();
252
+ return
253
+ abi.encodePacked (
254
+ hex "61 " ,
255
+ uint16 (args.length + 0x2d ),
256
+ hex "3d81600a3d39f3363d3d373d3d3d363d73 " ,
257
+ implementation,
258
+ hex "5af43d82803e903d91602b57fd5bf3 " ,
259
+ args
260
+ );
261
+ }
121
262
}
0 commit comments