tornado-governance/contracts/v4-patch/metamorphic/MetamorphicContractFactory.sol
AlienTornadosaurusHex f14e8d988b manual deployment, optimization, formatting, need to update deploy script
Signed-off-by: AlienTornadosaurusHex <>
2023-05-24 23:59:14 +00:00

608 lines
26 KiB
Solidity

// 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] | <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.
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."
);
_;
}
}