Skip to content

Instantly share code, notes, and snippets.

@0age
Last active September 20, 2023 08:53
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 0age/cd7097672cd311b1f363daca5ff5c4ba to your computer and use it in GitHub Desktop.
Save 0age/cd7097672cd311b1f363daca5ff5c4ba to your computer and use it in GitHub Desktop.
Deploy (and redeploy) contracts to known addresses
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