// SPDX-License-Identifier: MIT 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] | * * ** 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)]
* * ** flip success bit in second stack item to set to 0 ** * 14 15 iszero [0, 0]
* * ** push a third 0 to the stack, position of address in memory ** * 15 81 dup2 [0, 0, 0]
* * ** 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] * * ** 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. bytes1(0xd6), // first RLP byte. bytes1(0x94), // second RLP byte. transientContractAddress, // called by the transient contract. bytes1(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."); _; } }