-
Notifications
You must be signed in to change notification settings - Fork 94
/
Copy pathMetamorphicContractFactory.sol
604 lines (545 loc) · 26.3 KB
/
MetamorphicContractFactory.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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
pragma solidity 0.5.6;
/**
* @title Metamorphic Contract Factory
* @author 0age
* @notice This contract creates metamorphic contracts, or contracts that can be
* redeployed with new code to the same address. It does so by deploying a
* contract with fixed, non-deterministic initialization code via the CREATE2
* opcode. This contract clones the implementation contract in its constructor.
* Once a contract undergoes metamorphosis, all existing storage will be deleted
* and any existing contract code will be replaced with the deployed contract
* code of the new implementation contract.
* @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 MetamorphicContractFactory {
// fires when a metamorphic contract is deployed by cloning another contract.
event Metamorphosed(address metamorphicContract, address newImplementation);
// fires when a metamorphic contract is deployed through a transient contract.
event MetamorphosedWithConstructor(
address metamorphicContract,
address transientContract
);
// store the initialization code for metamorphic contracts.
bytes private _metamorphicContractInitializationCode;
// store hash of the initialization code for metamorphic contracts as well.
bytes32 private _metamorphicContractInitializationCodeHash;
// store init code for transient contracts that deploy metamorphic contracts.
bytes private _transientContractInitializationCode;
// store the hash of the initialization code for transient contracts as well.
bytes32 private _transientContractInitializationCodeHash;
// maintain a mapping of metamorphic contracts to metamorphic implementations.
mapping(address => address) private _implementations;
// maintain a mapping of transient contracts to metamorphic init codes.
mapping(address => bytes) private _initCodes;
/**
* @dev In the constructor, set up the initialization code for metamorphic
* contracts as well as the keccak256 hash of the given initialization code.
* @param transientContractInitializationCode bytes The initialization code
* that will be used to deploy any transient contracts, which will deploy any
* metamorphic contracts that require the use of a constructor.
*
* Metamorphic contract initialization code (29 bytes):
*
* 0x5860208158601c335a63aaf10f428752fa158151803b80938091923cf3
*
* Description:
*
* pc|op|name | [stack] | <memory>
*
* ** set the first stack item to zero - used later **
* 00 58 getpc [0] <>
*
* ** set second stack item to 32, length of word returned from staticcall **
* 01 60 push1
* 02 20 outsize [0, 32] <>
*
* ** set third stack item to 0, position of word returned from staticcall **
* 03 81 dup2 [0, 32, 0] <>
*
* ** set fourth stack item to 4, length of selector given to staticcall **
* 04 58 getpc [0, 32, 0, 4] <>
*
* ** set fifth stack item to 28, position of selector given to staticcall **
* 05 60 push1
* 06 1c inpos [0, 32, 0, 4, 28] <>
*
* ** set the sixth stack item to msg.sender, target address for staticcall **
* 07 33 caller [0, 32, 0, 4, 28, caller] <>
*
* ** set the seventh stack item to msg.gas, gas to forward for staticcall **
* 08 5a gas [0, 32, 0, 4, 28, caller, gas] <>
*
* ** set the eighth stack item to selector, "what" to store via mstore **
* 09 63 push4
* 10 aaf10f42 selector [0, 32, 0, 4, 28, caller, gas, 0xaaf10f42] <>
*
* ** set the ninth stack item to 0, "where" to store via mstore ***
* 11 87 dup8 [0, 32, 0, 4, 28, caller, gas, 0xaaf10f42, 0] <>
*
* ** call mstore, consume 8 and 9 from the stack, place selector in memory **
* 12 52 mstore [0, 32, 0, 4, 0, caller, gas] <0xaaf10f42>
*
* ** call staticcall, consume items 2 through 7, place address in memory **
* 13 fa staticcall [0, 1 (if successful)] <address>
*
* ** flip success bit in second stack item to set to 0 **
* 14 15 iszero [0, 0] <address>
*
* ** push a third 0 to the stack, position of address in memory **
* 15 81 dup2 [0, 0, 0] <address>
*
* ** place address from position in memory onto third stack item **
* 16 51 mload [0, 0, address] <>
*
* ** place address to fourth stack item for extcodesize to consume **
* 17 80 dup1 [0, 0, address, address] <>
*
* ** get extcodesize on fourth stack item for extcodecopy **
* 18 3b extcodesize [0, 0, address, size] <>
*
* ** dup and swap size for use by return at end of init code **
* 19 80 dup1 [0, 0, address, size, size] <>
* 20 93 swap4 [size, 0, address, size, 0] <>
*
* ** push code position 0 to stack and reorder stack items for extcodecopy **
* 21 80 dup1 [size, 0, address, size, 0, 0] <>
* 22 91 swap2 [size, 0, address, 0, 0, size] <>
* 23 92 swap3 [size, 0, size, 0, 0, address] <>
*
* ** call extcodecopy, consume four items, clone runtime code to memory **
* 24 3c extcodecopy [size, 0] <code>
*
* ** return to deploy final code in memory **
* 25 f3 return [] *deployed!*
*
*
* Transient contract initialization code derived from TransientContract.sol.
*/
constructor(bytes memory transientContractInitializationCode) public {
// assign the initialization code for the metamorphic contract.
_metamorphicContractInitializationCode = (
hex"5860208158601c335a63aaf10f428752fa158151803b80938091923cf3"
);
// calculate and assign keccak256 hash of metamorphic initialization code.
_metamorphicContractInitializationCodeHash = keccak256(
abi.encodePacked(
_metamorphicContractInitializationCode
)
);
// store the initialization code for the transient contract.
_transientContractInitializationCode = transientContractInitializationCode;
// calculate and assign keccak256 hash of transient initialization code.
_transientContractInitializationCodeHash = keccak256(
abi.encodePacked(
_transientContractInitializationCode
)
);
}
/* solhint-disable function-max-lines */
/**
* @dev Deploy a metamorphic contract by submitting a given salt or nonce
* along with the initialization code for the metamorphic contract, and
* optionally provide calldata for initializing the new metamorphic contract.
* To replace the contract, first selfdestruct the current contract, then call
* with the same salt value and new initialization code (be aware that all
* existing state will be wiped from the existing contract). Also note that
* the first 20 bytes of the salt must match the calling address, which
* prevents contracts from being created by unintended parties.
* @param salt bytes32 The nonce that will be passed into the CREATE2 call and
* thus will determine the resulant address of the metamorphic contract.
* @param implementationContractInitializationCode bytes The initialization
* code for the implementation contract for the metamorphic contract. It will
* be used to deploy a new contract that the metamorphic contract will then
* clone in its constructor.
* @param metamorphicContractInitializationCalldata bytes An optional data
* parameter that can be used to atomically initialize the metamorphic
* contract.
* @return Address of the metamorphic contract that will be created.
*/
function deployMetamorphicContract(
bytes32 salt,
bytes calldata implementationContractInitializationCode,
bytes calldata metamorphicContractInitializationCalldata
) external payable containsCaller(salt) returns (
address metamorphicContractAddress
) {
// move implementation init code and initialization calldata to memory.
bytes memory implInitCode = implementationContractInitializationCode;
bytes memory data = metamorphicContractInitializationCalldata;
// move the initialization code from storage to memory.
bytes memory initCode = _metamorphicContractInitializationCode;
// declare variable to verify successful metamorphic contract deployment.
address deployedMetamorphicContract;
// determine the address of the metamorphic contract.
metamorphicContractAddress = _getMetamorphicContractAddress(salt);
// declare a variable for the address of the implementation contract.
address implementationContract;
// load implementation init code and length, then deploy via CREATE.
/* solhint-disable no-inline-assembly */
assembly {
let encoded_data := add(0x20, implInitCode) // load initialization code.
let encoded_size := mload(implInitCode) // load init code's length.
implementationContract := create( // call CREATE with 3 arguments.
0, // do not forward any endowment.
encoded_data, // pass in initialization code.
encoded_size // pass in init code's length.
)
} /* solhint-enable no-inline-assembly */
require(
implementationContract != address(0),
"Could not deploy implementation."
);
// store the implementation to be retrieved by the metamorphic contract.
_implementations[metamorphicContractAddress] = implementationContract;
// load metamorphic contract data and length of data and deploy via CREATE2.
/* solhint-disable no-inline-assembly */
assembly {
let encoded_data := add(0x20, initCode) // load initialization code.
let encoded_size := mload(initCode) // load the init code's length.
deployedMetamorphicContract := create2( // call CREATE2 with 4 arguments.
0, // do not forward any endowment.
encoded_data, // pass in initialization code.
encoded_size, // pass in init code's length.
salt // pass in the salt value.
)
} /* solhint-enable no-inline-assembly */
// ensure that the contracts were successfully deployed.
require(
deployedMetamorphicContract == metamorphicContractAddress,
"Failed to deploy the new metamorphic contract."
);
// initialize the new metamorphic contract if any data or value is provided.
if (data.length > 0 || msg.value > 0) {
/* solhint-disable avoid-call-value */
(bool success,) = deployedMetamorphicContract.call.value(msg.value)(data);
/* solhint-enable avoid-call-value */
require(success, "Failed to initialize the new metamorphic contract.");
}
emit Metamorphosed(deployedMetamorphicContract, implementationContract);
} /* solhint-enable function-max-lines */
/**
* @dev Deploy a metamorphic contract by submitting a given salt or nonce
* along with the address of an existing implementation contract to clone, and
* optionally provide calldata for initializing the new metamorphic contract.
* To replace the contract, first selfdestruct the current contract, then call
* with the same salt value and a new implementation address (be aware that
* all existing state will be wiped from the existing contract). Also note
* that the first 20 bytes of the salt must match the calling address, which
* prevents contracts from being created by unintended parties.
* @param salt bytes32 The nonce that will be passed into the CREATE2 call and
* thus will determine the resulant address of the metamorphic contract.
* @param implementationContract address The address of the existing
* implementation contract to clone.
* @param metamorphicContractInitializationCalldata bytes An optional data
* parameter that can be used to atomically initialize the metamorphic
* contract.
* @return Address of the metamorphic contract that will be created.
*/
function deployMetamorphicContractFromExistingImplementation(
bytes32 salt,
address implementationContract,
bytes calldata metamorphicContractInitializationCalldata
) external payable containsCaller(salt) returns (
address metamorphicContractAddress
) {
// move initialization calldata to memory.
bytes memory data = metamorphicContractInitializationCalldata;
// move the initialization code from storage to memory.
bytes memory initCode = _metamorphicContractInitializationCode;
// declare variable to verify successful metamorphic contract deployment.
address deployedMetamorphicContract;
// determine the address of the metamorphic contract.
metamorphicContractAddress = _getMetamorphicContractAddress(salt);
// store the implementation to be retrieved by the metamorphic contract.
_implementations[metamorphicContractAddress] = implementationContract;
// using inline assembly: load data and length of data, then call CREATE2.
/* solhint-disable no-inline-assembly */
assembly {
let encoded_data := add(0x20, initCode) // load initialization code.
let encoded_size := mload(initCode) // load the init code's length.
deployedMetamorphicContract := create2( // call CREATE2 with 4 arguments.
0, // do not forward any endowment.
encoded_data, // pass in initialization code.
encoded_size, // pass in init code's length.
salt // pass in the salt value.
)
} /* solhint-enable no-inline-assembly */
// ensure that the contracts were successfully deployed.
require(
deployedMetamorphicContract == metamorphicContractAddress,
"Failed to deploy the new metamorphic contract."
);
// initialize the new metamorphic contract if any data or value is provided.
if (data.length > 0 || msg.value > 0) {
/* solhint-disable avoid-call-value */
(bool success,) = metamorphicContractAddress.call.value(msg.value)(data);
/* solhint-enable avoid-call-value */
require(success, "Failed to initialize the new metamorphic contract.");
}
emit Metamorphosed(deployedMetamorphicContract, implementationContract);
}
/* solhint-disable function-max-lines */
/**
* @dev Deploy a metamorphic contract by submitting a given salt or nonce
* along with the initialization code to a transient contract which will then
* deploy the metamorphic contract before immediately selfdestructing. To
* replace the metamorphic contract, first selfdestruct the current contract,
* then call with the same salt value and new initialization code (be aware
* that all existing state will be wiped from the existing contract). Also
* note that the first 20 bytes of the salt must match the calling address,
* which prevents contracts from being created by unintended parties.
* @param salt bytes32 The nonce that will be passed into the CREATE2 call and
* thus will determine the resulant address of the metamorphic contract.
* @param initializationCode bytes The initialization code for the metamorphic
* contract that will be deployed by the transient contract.
* @return Address of the metamorphic contract that will be created.
*/
function deployMetamorphicContractWithConstructor(
bytes32 salt,
bytes calldata initializationCode
) external payable containsCaller(salt) returns (
address metamorphicContractAddress
) {
// move transient contract initialization code from storage to memory.
bytes memory initCode = _transientContractInitializationCode;
// declare variable to verify successful transient contract deployment.
address deployedTransientContract;
// determine the address of the transient contract.
address transientContractAddress = _getTransientContractAddress(salt);
// store the initialization code to be retrieved by the transient contract.
_initCodes[transientContractAddress] = initializationCode;
// load transient contract data and length of data, then deploy via CREATE2.
/* solhint-disable no-inline-assembly */
assembly {
let encoded_data := add(0x20, initCode) // load initialization code.
let encoded_size := mload(initCode) // load the init code's length.
deployedTransientContract := create2( // call CREATE2 with 4 arguments.
callvalue, // forward any supplied endowment.
encoded_data, // pass in initialization code.
encoded_size, // pass in init code's length.
salt // pass in the salt value.
)
} /* solhint-enable no-inline-assembly */
// ensure that the contracts were successfully deployed.
require(
deployedTransientContract == transientContractAddress,
"Failed to deploy metamorphic contract using given salt and init code."
);
metamorphicContractAddress = _getMetamorphicContractAddressWithConstructor(
transientContractAddress
);
emit MetamorphosedWithConstructor(
metamorphicContractAddress,
transientContractAddress
);
} /* solhint-enable function-max-lines */
/**
* @dev View function for retrieving the address of the implementation
* contract to clone. Called by the constructor of each metamorphic contract.
*/
function getImplementation() external view returns (address implementation) {
return _implementations[msg.sender];
}
/**
* @dev View function for retrieving the initialization code for a given
* metamorphic contract to deploy via a transient contract. Called by the
* constructor of each transient contract.
* @return The initialization code to use to deploy the metamorphic contract.
*/
function getInitializationCode() external view returns (
bytes memory initializationCode
) {
return _initCodes[msg.sender];
}
/**
* @dev View function for retrieving the address of the current implementation
* contract of a given metamorphic contract, where the address of the contract
* is supplied as an argument. Be aware that the implementation contract has
* an independent state and may have been altered or selfdestructed from when
* it was last cloned by the metamorphic contract.
* @param metamorphicContractAddress address The address of the metamorphic
* contract.
* @return Address of the corresponding implementation contract.
*/
function getImplementationContractAddress(
address metamorphicContractAddress
) external view returns (address implementationContractAddress) {
return _implementations[metamorphicContractAddress];
}
/**
* @dev View function for retrieving the initialization code for a given
* metamorphic contract instance deployed via a transient contract, where the address
* of the transient contract is supplied as an argument.
* @param transientContractAddress address The address of the transient
* contract that deployed the metamorphic contract.
* @return The initialization code used to deploy the metamorphic contract.
*/
function getMetamorphicContractInstanceInitializationCode(
address transientContractAddress
) external view returns (bytes memory initializationCode) {
return _initCodes[transientContractAddress];
}
/**
* @dev Compute the address of the metamorphic contract that will be created
* upon submitting a given salt to the contract.
* @param salt bytes32 The nonce passed into CREATE2 by metamorphic contract.
* @return Address of the corresponding metamorphic contract.
*/
function findMetamorphicContractAddress(
bytes32 salt
) external view returns (address metamorphicContractAddress) {
// determine the address where the metamorphic contract will be deployed.
metamorphicContractAddress = _getMetamorphicContractAddress(salt);
}
/**
* @dev Compute the address of the transient contract that will be created
* upon submitting a given salt to the contract.
* @param salt bytes32 The nonce passed into CREATE2 when deploying the
* transient contract.
* @return Address of the corresponding transient contract.
*/
function findTransientContractAddress(
bytes32 salt
) external view returns (address transientContractAddress) {
// determine the address where the transient contract will be deployed.
transientContractAddress = _getTransientContractAddress(salt);
}
/**
* @dev Compute the address of the metamorphic contract that will be created
* by the transient contract upon submitting a given salt to the contract.
* @param salt bytes32 The nonce passed into CREATE2 when deploying the
* transient contract.
* @return Address of the corresponding metamorphic contract.
*/
function findMetamorphicContractAddressWithConstructor(
bytes32 salt
) external view returns (address metamorphicContractAddress) {
// determine the address of the metamorphic contract.
metamorphicContractAddress = _getMetamorphicContractAddressWithConstructor(
_getTransientContractAddress(salt)
);
}
/**
* @dev View function for retrieving the initialization code of metamorphic
* contracts for purposes of verification.
*/
function getMetamorphicContractInitializationCode() external view returns (
bytes memory metamorphicContractInitializationCode
) {
return _metamorphicContractInitializationCode;
}
/**
* @dev View function for retrieving the keccak256 hash of the initialization
* code of metamorphic contracts for purposes of verification.
*/
function getMetamorphicContractInitializationCodeHash() external view returns (
bytes32 metamorphicContractInitializationCodeHash
) {
return _metamorphicContractInitializationCodeHash;
}
/**
* @dev View function for retrieving the initialization code of transient
* contracts for purposes of verification.
*/
function getTransientContractInitializationCode() external view returns (
bytes memory transientContractInitializationCode
) {
return _transientContractInitializationCode;
}
/**
* @dev View function for retrieving the keccak256 hash of the initialization
* code of transient contracts for purposes of verification.
*/
function getTransientContractInitializationCodeHash() external view returns (
bytes32 transientContractInitializationCodeHash
) {
return _transientContractInitializationCodeHash;
}
/**
* @dev Internal view function for calculating a metamorphic contract address
* given a particular salt.
*/
function _getMetamorphicContractAddress(
bytes32 salt
) internal view returns (address) {
// determine the address of the metamorphic contract.
return 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.
_metamorphicContractInitializationCodeHash // the init code hash.
)
)
)
)
);
}
/**
* @dev Internal view function for calculating a transient contract address
* given a particular salt.
*/
function _getTransientContractAddress(
bytes32 salt
) internal view returns (address) {
// determine the address of the transient contract.
return 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.
_transientContractInitializationCodeHash // supply init code hash.
)
)
)
)
);
}
/**
* @dev Internal view function for calculating a metamorphic contract address
* that has been deployed via a transient contract given the address of the
* transient contract.
*/
function _getMetamorphicContractAddressWithConstructor(
address transientContractAddress
) internal pure returns (address) {
// determine the address of the metamorphic contract.
return address(
uint160( // downcast to match the address type.
uint256( // set to uint to truncate upper digits.
keccak256( // compute CREATE hash via RLP encoding.
abi.encodePacked( // pack all inputs to the hash together.
byte(0xd6), // first RLP byte.
byte(0x94), // second RLP byte.
transientContractAddress, // called by the transient contract.
byte(0x01) // nonce begins at 1 for contracts.
)
)
)
)
);
}
/**
* @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.
* @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,
"Invalid salt - first 20 bytes of the salt must match calling address."
);
_;
}
}