Last active
September 20, 2023 08:53
-
-
Save 0age/cd7097672cd311b1f363daca5ff5c4ba to your computer and use it in GitHub Desktop.
Deploy (and redeploy) contracts to known addresses
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pragma solidity 0.5.8; | |
/** | |
* @title Metamorphic Delegator Factory | |
* @author 0age | |
* @notice This contract creates metamorphic contracts, or contracts that can be | |
* redeployed with new code to the same address. First, it deploys a contract | |
* with the initialization code of the contract you want to deploy stored in its | |
* RUNTIME code. Then, it deploys the contract, which retrieves the address of | |
* the init-in-runtime contract and DELEGATECALLs into it to execute the init | |
* code and retrieve the runtime code (or revert), which it then returns. | |
* 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 This contract has not yet been fully tested or audited - proceed with | |
* caution and please share any exploits or optimizations you discover. | |
*/ | |
contract MetamorphicDelegatorFactory { | |
// fires when a metamorphic contract is deployed by cloning another contract. | |
event Metamorphosed(address metamorphicContract); | |
// store the address of the current initialization-in-runtime | |
address private _initializationImplementation; | |
bytes32 private constant _METAMORPHIC_INIT_CODE = bytes32( | |
0x5859385958601c335a585952fa1582838382515af43d3d93833e601e57fd5bf3 | |
); | |
bytes32 private constant _METAMORPHIC_INIT_CODE_HASH = bytes32( | |
0x7816562e7f85866cae07183593075f3b5ec32aeff914a0693e20aaf39672babc | |
); | |
bytes11 private constant _INIT_CODE_IN_RUNTIME_CODE_PRELUDE = bytes11( | |
0x600b5981380380925939f3 | |
); | |
constructor() public { | |
require( | |
keccak256(abi.encode(_METAMORPHIC_INIT_CODE)) == _METAMORPHIC_INIT_CODE_HASH, | |
"Incorrect hash for metamorphic contract initialization code." | |
); | |
} | |
// supply the initialization code in order to deploy the contract | |
function deploy( | |
bytes calldata initializationCode | |
) external payable returns (address metamorphic) { | |
/* To take the provided initialization code and deploy a contract with that | |
initialization code as its runtime code, use the following prelude: | |
0x600b5981380380925939f3... | |
00 60 push1 0b [11 -> offset] | |
02 59 msize [offset, 0] | |
03 81 dup2 [offset, 0, offset] | |
04 38 codesize [offset, 0, offset, codesize] | |
05 03 sub [offset, 0, codesize - offset] | |
06 80 dup1 [offset, 0, codesize - offset, codesize - offset] | |
07 92 swap3 [codesize - offset, 0, codesize - offset, offset] | |
08 59 msize [codesize - offset, 0, codesize - offset, offset, 0] | |
09 39 codecopy [codesize - offset, 0] <init_code_in_runtime> | |
10 f3 return [] *init_code_in_runtime* | |
... init_code | |
*/ | |
bytes memory initializationInRuntimeInitCode = abi.encodePacked( | |
_INIT_CODE_IN_RUNTIME_CODE_PRELUDE, | |
initializationCode | |
); | |
// set up variable for the address of the initialization implementation. | |
address initializationImplementation; | |
// deploy the initialization-code-in-runtime-code contract. | |
assembly { | |
let encoded_data := add(0x20, initializationInRuntimeInitCode) | |
let encoded_size := mload(initializationInRuntimeInitCode) | |
initializationImplementation := create2(0, encoded_data, encoded_size, 0) | |
} | |
// ensure that it successfully deployed (could check if it already exists) | |
require(initializationImplementation != address(0), 'deployment failed.'); | |
// store the address of the init-code-in-runtime-code contract | |
// (note that this can be manipulated by passing an incorrect length param) | |
_initializationImplementation = initializationImplementation; | |
/* To deploy the metamorphic contract, retrieve the address of the contract | |
that contains the initialization code, then delegatecall into it, which will | |
execute the initialization code and return the runtime code (or revert). | |
Then, the runtime code returned from the delegatecall is returned, and since | |
we are still in the initialization context, it will be set as the runtime | |
code of the metamorphic contract. The initialization code is as follows: | |
0x5859385958601c335a585952fa1582838382515af43d3d93833e601e57fd5bf3 | |
00 58 PC [0] | |
01 59 MSIZE [0, 0] | |
02 38 CODESIZE [0, 0, codesize -> 32] | |
03 59 MSIZE [0, 0, 32, 0] | |
04 58 PC [0, 0, 32, 0, 4] | |
05 60 PUSH1 0x1c [0, 0, 32, 0, 4, 28] | |
07 33 CALLER [0, 0, 32, 0, 4, 28, caller] | |
08 5a GAS [0, 0, 32, 0, 4, 28, caller, gas] | |
09 58 PC [0, 0, 32, 0, 4, 28, caller, gas, 9 -> selector] | |
10 59 MSIZE [0, 0, 32, 0, 4, 28, caller, gas, selector, 0] | |
11 52 MSTORE [0, 0, 32, 0, 4, 28, caller, gas] <selector> | |
12 fa STATICCALL [0, 0, 1 => success] <init_in_runtime_address> | |
13 15 ISZERO [0, 0, 0] | |
14 82 DUP3 [0, 0, 0, 0] | |
15 83 DUP4 [0, 0, 0, 0, 0] | |
16 83 DUP4 [0, 0, 0, 0, 0, 0] | |
17 82 DUP3 [0, 0, 0, 0, 0, 0, 0] | |
18 51 MLOAD [0, 0, 0, 0, 0, 0, init_in_runtime_address] | |
19 5a GAS [0, 0, 0, 0, 0, 0, init_in_runtime_address, gas] | |
20 f4 DELEGATECALL [0, 0, 1 => success] {runtime_code} | |
21 3d RETURNDATASIZE [0, 0, 1 => success, size] | |
22 3d RETURNDATASIZE [0, 0, 1 => success, size, size] | |
23 93 SWAP4 [size, 0, 1 => success, size, 0] | |
24 83 DUP4 [size, 0, 1 => success, size, 0, 0] | |
25 3e RETURNDATACOPY [size, 0, 1 => success] <runtime_code> | |
26 60 PUSH1 0x1e [size, 0, 1 => success, 30] | |
28 57 JUMPI [size, 0] | |
29 fd REVERT [] *runtime_code* | |
30 5b JUMPDEST [size, 0] | |
31 f3 RETURN [] | |
*/ | |
assembly { | |
// put the metamorphic delegator init code in memory inside scratch space | |
mstore(0, 0x5859385958601c335a585952fa1582838382515af43d3d93833e601e57fd5bf3) | |
// deploy the contract via create2 - no salt provided... yet (⌐■_■) | |
metamorphic := create2(callvalue, 0, 0x20, 0) | |
// if the deployment reverted, grab return buffer and provide it to revert | |
if eq(metamorphic, 0) { | |
returndatacopy(0, 0, returndatasize) | |
revert(0, returndatasize) | |
} | |
} | |
// clean up storage | |
delete _initializationImplementation; | |
// emit the address of the deployed contract as an event | |
emit Metamorphosed(metamorphic); | |
} | |
// called by metamorphic contract during initialization, selector 0x00000009 | |
function getInitializationCodeFromContractRuntime_6CLUNS() external view returns (address) { | |
return _initializationImplementation; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment