CreateX
โ A Trustless, Universal Contract Deployer
Factory smart contract to make easier and safer usage of the CREATE
and CREATE2
EVM opcodes as well as of CREATE3
-based (i.e. without an initcode factor) contract creations.
Note
The CreateX
contract should be considered as maximally extensible. Be encouraged to build on top of it! The Solidity-based interface can be found here.
CreateX
โ A Trustless, Universal Contract Deployer
So What on Earth Is a Contract Factory?
It is important to understand that Ethereum Virtual Machine (EVM) opcodes can only be called via a smart contract. A contract factory in the context of the EVM refers to a special smart contract that is used to create and deploy other smart contracts on EVM-compatible blockchains using contract creation opcodes (i.e. CREATE
or CREATE2
). Using a contract factory provides a flexible and efficient way to deploy and manage smart contracts that share similar functionalities but may have different configurations or settings.
Different approaches can be used to create contracts using a factory contract, and this is exactly what CreateX
offers: a comprehensive range of contract creation functions that are triggered by a smart contract itself. It is worth emphasising the two differences in the address calculation of the opcodes CREATE
and CREATE2
(||
stands for byte-wise concatenation, [12:]
refers to the last 20 bytes of a 32-byte expression, and rlp
is an abbreviation for Ethereum's "Recursive Length Prefix" serialisation scheme):
CREATE
:address computedAddress = keccak256(rlpEncode([deployerAddress, deployerNonce]))[12:]
,CREATE2
:address computedAddress = keccak256(0xff||deployerAddress||salt||keccak256(initCode))[12:]
.
Available Versatile Functions
CreateX
โโโ CREATE
โ โโโ Read-Only Functions
โ โ โโโ "function computeCreateAddress(uint256) view returns (address)"
โ โ โโโ "function computeCreateAddress(address,uint256) view returns (address)"
โ โโโ Write Functions
โ โโโ "function deployCreate(bytes) payable returns (address)"
โ โโโ "function deployCreateAndInit(bytes,bytes,tuple(uint256,uint256)) payable returns (address)"
โ โโโ "function deployCreateAndInit(bytes,bytes,tuple(uint256,uint256),address) payable returns (address)"
โ โโโ "function deployCreateClone(address,bytes) payable returns (address)"
โโโ CREATE2
โ โโโ Read-Only Functions
โ โ โโโ "function computeCreate2Address(bytes32,bytes32) view returns (address)"
โ โ โโโ "function computeCreate2Address(bytes32,bytes32,address) pure returns (address)"
โ โโโ Write Functions
โ โโโ "function deployCreate2(bytes) payable returns (address)"
โ โโโ "function deployCreate2(bytes32,bytes) payable returns (address)"
โ โโโ "function deployCreate2AndInit(bytes,bytes,tuple(uint256,uint256)) payable returns (address)"
โ โโโ "function deployCreate2AndInit(bytes32,bytes,bytes,tuple(uint256,uint256)) payable returns (address)"
โ โโโ "function deployCreate2AndInit(bytes,bytes,tuple(uint256,uint256),address) payable returns (address)"
โ โโโ "function deployCreate2AndInit(bytes32,bytes,bytes,tuple(uint256,uint256),address) payable returns (address)"
โ โโโ "function deployCreate2Clone(address,bytes) payable returns (address)"
โ โโโ "function deployCreate2Clone(bytes32,address,bytes) payable returns (address)"
โโโ CREATE3
โโโ Read-Only Functions
โ โโโ "function computeCreate3Address(bytes32) view returns (address)"
โ โโโ "function computeCreate3Address(bytes32,address) pure returns (address)"
โโโ Write Functions
โโโ "function deployCreate3(bytes) payable returns (address)"
โโโ "function deployCreate3(bytes32,bytes) payable returns (address)"
โโโ "function deployCreate3AndInit(bytes,bytes,tuple(uint256,uint256)) payable returns (address)"
โโโ "function deployCreate3AndInit(bytes32,bytes,bytes,tuple(uint256,uint256)) payable returns (address)"
โโโ "function deployCreate3AndInit(bytes,bytes,tuple(uint256,uint256),address) payable returns (address)"
โโโ "function deployCreate3AndInit(bytes32,bytes,bytes,tuple(uint256,uint256),address) payable returns (address)"
computeCreateAddress(uint256)
Returns the address where a contract will be stored if deployed via this contract (i.e. CreateX
) using the CREATE
opcode. For the specification of the Recursive Length Prefix (RLP) encoding scheme, please refer to p. 19 of the Ethereum Yellow Paper and the Ethereum Wiki. Based on the EIP-161 specification, all contract accounts on the Ethereum mainnet are initiated with nonce = 1
. Thus, the first contract address created by another contract is calculated with a non-zero nonce.
# /*:ยฐโข Function Argument โขยฐ:*/ #
- name: nonce
type: uint256
description: The next 32-byte nonce of this contract.
# /*:ยฐโข Return Value โขยฐ:*/ #
- name: computedAddress
type: address
description: The 20-byte address where a contract will be stored.
computeCreateAddress(address,uint256)
Returns the address where a contract will be stored if deployed via deployer
using the CREATE
opcode. For the specification of the Recursive Length Prefix (RLP) encoding scheme, please refer to p. 19 of the Ethereum Yellow Paper and the Ethereum Wiki. Based on the EIP-161 specification, all contract accounts on the Ethereum mainnet are initiated with nonce = 1
. Thus, the first contract address created by another contract is calculated with a non-zero nonce.
# /*:ยฐโข Function Arguments โขยฐ:*/ #
- name: deployer
type: address
description: The 20-byte deployer address.
- name: nonce
type: uint256
description: The next 32-byte nonce of the deployer address.
# /*:ยฐโข Return Value โขยฐ:*/ #
- name: computedAddress
type: address
description: The 20-byte address where a contract will be stored.
deployCreate(bytes)
Deploys a new contract via calling the CREATE
opcode and using the creation bytecode initCode
and msg.value
as inputs. In order to save deployment costs, we do not sanity check the initCode
length. Note that if msg.value
is non-zero, initCode
must have a payable
constructor.
# /*:ยฐโข Function Argument โขยฐ:*/ #
- name: initCode
type: bytes
description: The creation bytecode.
# /*:ยฐโข Return Value โขยฐ:*/ #
- name: newContract
type: address
description: The 20-byte address where the contract was deployed.
deployCreateAndInit(bytes,bytes,tuple(uint256,uint256))
Deploys and initialises a new contract via calling the CREATE
opcode and using the creation bytecode initCode
, the initialisation code data
, the struct for the payable
amounts values
, and msg.value
as inputs. In order to save deployment costs, we do not sanity check the initCode
length. Note that if values.constructorAmount
is non-zero, initCode
must have a payable
constructor, and any excess ether is returned to msg.sender
.
# /*:ยฐโข Function Arguments โขยฐ:*/ #
- name: initCode
type: bytes
description: The creation bytecode.
- name: data
type: bytes
description: The initialisation code that is passed to the deployed contract.
- name: values
type: tuple(uint256,uint256)
description: The specific `payable` amounts for the deployment and initialisation call.
# /*:ยฐโข Return Value โขยฐ:*/ #
- name: newContract
type: address
description: The 20-byte address where the contract was deployed.
โน๏ธ Note
This function allows for reentrancy, however we refrain from adding a mutex lock to keep it as use-case agnostic as possible. Please ensure at the protocol level that potentially malicious reentrant calls do not affect your smart contract system.
deployCreateAndInit(bytes,bytes,tuple(uint256,uint256),address)
Deploys and initialises a new contract via calling the CREATE
opcode and using the creation bytecode initCode
, the initialisation code data
, the struct for the payable
amounts values
, the refund address refundAddress
, and msg.value
as inputs. In order to save deployment costs, we do not sanity check the initCode
length. Note that if values.constructorAmount
is non-zero, initCode
must have a payable
constructor.
# /*:ยฐโข Function Arguments โขยฐ:*/ #
- name: initCode
type: bytes
description: The creation bytecode.
- name: data
type: bytes
description: The initialisation code that is passed to the deployed contract.
- name: values
type: tuple(uint256,uint256)
description: The specific `payable` amounts for the deployment and initialisation call.
- name: refundAddress
type: address
description: The 20-byte address where any excess ether is returned to.
# /*:ยฐโข Return Value โขยฐ:*/ #
- name: newContract
type: address
description: The 20-byte address where the contract was deployed.
โน๏ธ Note
This function allows for reentrancy, however we refrain from adding a mutex lock to keep it as use-case agnostic as possible. Please ensure at the protocol level that potentially malicious reentrant calls do not affect your smart contract system.
deployCreateClone(address,bytes)
Deploys a new EIP-1167 minimal proxy contract using the CREATE
opcode, and initialises the implementation contract using the implementation address implementation
, the initialisation code data
, and msg.value
as inputs. Note that if msg.value
is non-zero, the initialiser function called via data
must be payable
.
# /*:ยฐโข Function Arguments โขยฐ:*/ #
- name: implementation
type: address
description: The 20-byte implementation contract address.
- name: data
type: bytes
description: The initialisation code that is passed to the deployed proxy contract.
# /*:ยฐโข Return Value โขยฐ:*/ #
- name: newContract
type: address
description: The 20-byte address where the clone was deployed.
โน๏ธ Note
This function allows for reentrancy, however we refrain from adding a mutex lock to keep it as use-case agnostic as possible. Please ensure at the protocol level that potentially malicious reentrant calls do not affect your smart contract system.
computeCreate2Address(bytes32,bytes32)
Returns the address where a contract will be stored if deployed via this contract (i.e. CreateX
) using the CREATE2
opcode. Any change in the initCodeHash
or salt
values will result in a new destination address.
# /*:ยฐโข Function Arguments โขยฐ:*/ #
- name: salt
type: bytes32
description: The 32-byte random value used to create the contract address.
- name: initCodeHash
type: bytes32
description: The 32-byte bytecode digest of the contract creation bytecode.
# /*:ยฐโข Return Value โขยฐ:*/ #
- name: computedAddress
type: address
description: The 20-byte address where a contract will be stored.
computeCreate2Address(bytes32,bytes32,address)
Returns the address where a contract will be stored if deployed via deployer
using the CREATE2
opcode. Any change in the initCodeHash
or salt
values will result in a new destination address.
# /*:ยฐโข Function Arguments โขยฐ:*/ #
- name: salt
type: bytes32
description: The 32-byte random value used to create the contract address.
- name: initCodeHash
type: bytes32
description: The 32-byte bytecode digest of the contract creation bytecode.
- name: deployer
type: address
description: The 20-byte deployer address.
# /*:ยฐโข Return Value โขยฐ:*/ #
- name: computedAddress
type: address
description: The 20-byte address where a contract will be stored.
deployCreate2(bytes)
Deploys a new contract via calling the CREATE2
opcode and using the creation bytecode initCode
and msg.value
as inputs. The salt value is calculated pseudo-randomly using a diverse selection of block and transaction properties. This approach does not guarantee true randomness! In order to save deployment costs, we do not sanity check the initCode
length. Note that if msg.value
is non-zero, initCode
must have a payable
constructor.
# /*:ยฐโข Function Argument โขยฐ:*/ #
- name: initCode
type: bytes
description: The creation bytecode.
# /*:ยฐโข Return Value โขยฐ:*/ #
- name: newContract
type: address
description: The 20-byte address where the contract was deployed.
deployCreate2(bytes32,bytes)
Deploys a new contract via calling the CREATE2
opcode and using the salt value salt
, the creation bytecode initCode
, and msg.value
as inputs. In order to save deployment costs, we do not sanity check the initCode
length. Note that if msg.value
is non-zero, initCode
must have a payable
constructor.
# /*:ยฐโข Function Arguments โขยฐ:*/ #
- name: salt
type: bytes32
description: The 32-byte random value used to create the contract address.
- name: initCode
type: bytes
description: The creation bytecode.
# /*:ยฐโข Return Value โขยฐ:*/ #
- name: newContract
type: address
description: The 20-byte address where the contract was deployed.
deployCreate2AndInit(bytes,bytes,tuple(uint256,uint256))
Deploys and initialises a new contract via calling the CREATE2
opcode and using the creation bytecode initCode
, the initialisation code data
, the struct for the payable
amounts values
, and msg.value
as inputs. The salt value is calculated pseudo-randomly using a diverse selection of block and transaction properties. This approach does not guarantee true randomness! In order to save deployment costs, we do not sanity check the initCode
length. Note that if values.constructorAmount
is non-zero, initCode
must have a payable
constructor, and any excess ether is returned to msg.sender
.
# /*:ยฐโข Function Arguments โขยฐ:*/ #
- name: initCode
type: bytes
description: The creation bytecode.
- name: data
type: bytes
description: The initialisation code that is passed to the deployed contract.
- name: values
type: tuple(uint256,uint256)
description: The specific `payable` amounts for the deployment and initialisation call.
# /*:ยฐโข Return Value โขยฐ:*/ #
- name: newContract
type: address
description: The 20-byte address where the contract was deployed.
โน๏ธ Note
This function allows for reentrancy, however we refrain from adding a mutex lock to keep it as use-case agnostic as possible. Please ensure at the protocol level that potentially malicious reentrant calls do not affect your smart contract system.
deployCreate2AndInit(bytes32,bytes,bytes,tuple(uint256,uint256))
Deploys and initialises a new contract via calling the CREATE2
opcode and using the salt value salt
, creation bytecode initCode
, the initialisation code data
, the struct for the payable
amounts values
, and msg.value
as inputs. In order to save deployment costs, we do not sanity check the initCode
length. Note that if values.constructorAmount
is non-zero, initCode
must have a payable
constructor, and any excess ether is returned to msg.sender
.
# /*:ยฐโข Function Arguments โขยฐ:*/ #
- name: salt
type: bytes32
description: The 32-byte random value used to create the contract address.
- name: initCode
type: bytes
description: The creation bytecode.
- name: data
type: bytes
description: The initialisation code that is passed to the deployed contract.
- name: values
type: tuple(uint256,uint256)
description: The specific `payable` amounts for the deployment and initialisation call.
# /*:ยฐโข Return Value โขยฐ:*/ #
- name: newContract
type: address
description: The 20-byte address where the contract was deployed.
โน๏ธ Note
This function allows for reentrancy, however we refrain from adding a mutex lock to keep it as use-case agnostic as possible. Please ensure at the protocol level that potentially malicious reentrant calls do not affect your smart contract system.
deployCreate2AndInit(bytes,bytes,tuple(uint256,uint256),address)
Deploys and initialises a new contract via calling the CREATE2
opcode and using the creation bytecode initCode
, the initialisation code data
, the struct for the payable
amounts values
, the refund address refundAddress
, and msg.value
as inputs. The salt value is calculated pseudo-randomly using a diverse selection of block and transaction properties. This approach does not guarantee true randomness! In order to save deployment costs, we do not sanity check the initCode
length. Note that if values.constructorAmount
is non-zero, initCode
must have a payable
constructor.
# /*:ยฐโข Function Arguments โขยฐ:*/ #
- name: initCode
type: bytes
description: The creation bytecode.
- name: data
type: bytes
description: The initialisation code that is passed to the deployed contract.
- name: values
type: tuple(uint256,uint256)
description: The specific `payable` amounts for the deployment and initialisation call.
- name: refundAddress
type: address
description: The 20-byte address where any excess ether is returned to.
# /*:ยฐโข Return Value โขยฐ:*/ #
- name: newContract
type: address
description: The 20-byte address where the contract was deployed.
โน๏ธ Note
This function allows for reentrancy, however we refrain from adding a mutex lock to keep it as use-case agnostic as possible. Please ensure at the protocol level that potentially malicious reentrant calls do not affect your smart contract system.
deployCreate2AndInit(bytes32,bytes,bytes,tuple(uint256,uint256),address)
Deploys and initialises a new contract via calling the CREATE2
opcode and using the salt value salt
, the creation bytecode initCode
, the initialisation code data
, the struct for the payable
amounts values
, the refund address refundAddress
, and msg.value
as inputs. In order to save deployment costs, we do not sanity check the initCode
length. Note that if values.constructorAmount
is non-zero, initCode
must have a payable
constructor.
# /*:ยฐโข Function Arguments โขยฐ:*/ #
- name: salt
type: bytes32
description: The 32-byte random value used to create the contract address.
- name: initCode
type: bytes
description: The creation bytecode.
- name: data
type: bytes
description: The initialisation code that is passed to the deployed contract.
- name: values
type: tuple(uint256,uint256)
description: The specific `payable` amounts for the deployment and initialisation call.
- name: refundAddress
type: address
description: The 20-byte address where any excess ether is returned to.
# /*:ยฐโข Return Value โขยฐ:*/ #
- name: newContract
type: address
description: The 20-byte address where the contract was deployed.
โน๏ธ Note
This function allows for reentrancy, however we refrain from adding a mutex lock to keep it as use-case agnostic as possible. Please ensure at the protocol level that potentially malicious reentrant calls do not affect your smart contract system.
deployCreate2Clone(address,bytes)
Deploys a new EIP-1167 minimal proxy contract using the CREATE2
opcode and the salt value salt
, and initialises the implementation contract using the implementation address implementation
, the initialisation code data
, and msg.value
as inputs. The salt value is calculated pseudo-randomly using a diverse selection of block and transaction properties. This approach does not guarantee true randomness! Note that if msg.value
is non-zero, the initialiser function called via data
must be payable
.
# /*:ยฐโข Function Arguments โขยฐ:*/ #
- name: implementation
type: address
description: The 20-byte implementation contract address.
- name: data
type: bytes
description: The initialisation code that is passed to the deployed proxy contract.
# /*:ยฐโข Return Value โขยฐ:*/ #
- name: newContract
type: address
description: The 20-byte address where the clone was deployed.
โน๏ธ Note
This function allows for reentrancy, however we refrain from adding a mutex lock to keep it as use-case agnostic as possible. Please ensure at the protocol level that potentially malicious reentrant calls do not affect your smart contract system.
deployCreate2Clone(bytes32,address,bytes)
Deploys a new EIP-1167 minimal proxy contract using the CREATE2
opcode and the salt value salt
, and initialises the implementation contract using the implementation address implementation
, the initialisation code data
, and msg.value
as inputs. Note that if msg.value
is non-zero, the initialiser function called via data
must be payable
.
# /*:ยฐโข Function Arguments โขยฐ:*/ #
- name: salt
type: bytes32
description: The 32-byte random value used to create the proxy contract address.
- name: implementation
type: address
description: The 20-byte implementation contract address.
- name: data
type: bytes
description: The initialisation code that is passed to the deployed proxy contract.
# /*:ยฐโข Return Value โขยฐ:*/ #
- name: newContract
type: address
description: The 20-byte address where the clone was deployed.
โน๏ธ Note
This function allows for reentrancy, however we refrain from adding a mutex lock to keep it as use-case agnostic as possible. Please ensure at the protocol level that potentially malicious reentrant calls do not affect your smart contract system.
computeCreate3Address(bytes32)
Returns the address where a contract will be stored if deployed via this contract (i.e. CreateX
) using the CREATE3
pattern (i.e. without an initcode factor). Any change in the salt
value will result in a new destination address.
# /*:ยฐโข Function Argument โขยฐ:*/ #
- name: salt
type: bytes32
description: The 32-byte random value used to create the proxy contract address.
# /*:ยฐโข Return Value โขยฐ:*/ #
- name: computedAddress
type: address
description: The 20-byte address where a contract will be stored.
computeCreate3Address(bytes32,address)
Returns the address where a contract will be stored if deployed via deployer
using the CREATE3
pattern (i.e. without an initcode factor). Any change in the salt
value will result in a new destination address.
# /*:ยฐโข Function Arguments โขยฐ:*/ #
- name: salt
type: bytes32
description: The 32-byte random value used to create the proxy contract address.
- name: deployer
type: address
description: The 20-byte deployer address.
# /*:ยฐโข Return Value โขยฐ:*/ #
- name: computedAddress
type: address
description: The 20-byte address where a contract will be stored.
deployCreate3(bytes)
Deploys a new contract via employing the CREATE3
pattern (i.e. without an initcode factor) and using the salt value salt
, the creation bytecode initCode
, and msg.value
as inputs. The salt value is calculated pseudo-randomly using a diverse selection of block and transaction properties. This approach does not guarantee true randomness! In order to save deployment costs, we do not sanity check the initCode
length. Note that if msg.value
is non-zero, initCode
must have a payable
constructor.
# /*:ยฐโข Function Argument โขยฐ:*/ #
- name: initCode
type: bytes
description: The creation bytecode.
# /*:ยฐโข Return Value โขยฐ:*/ #
- name: newContract
type: address
description: The 20-byte address where the contract was deployed.
deployCreate3(bytes32,bytes)
Deploys a new contract via employing the CREATE3
pattern (i.e. without an initcode factor) and using the salt value salt
, the creation bytecode initCode
, and msg.value
as inputs. In order to save deployment costs, we do not sanity check the initCode
length. Note that if msg.value
is non-zero, initCode
must have a payable
constructor.
# /*:ยฐโข Function Arguments โขยฐ:*/ #
- name: salt
type: bytes32
description: The 32-byte random value used to create the proxy contract address.
- name: initCode
type: bytes
description: The creation bytecode.
# /*:ยฐโข Return Value โขยฐ:*/ #
- name: newContract
type: address
description: The 20-byte address where the contract was deployed.
โน๏ธ Note
We strongly recommend implementing a permissioned deploy protection by setting the first 20 bytes equal tomsg.sender
in thesalt
to prevent maliciously intended frontrun proxy deployments on other chains.
deployCreate3AndInit(bytes,bytes,tuple(uint256,uint256))
Deploys and initialises a new contract via employing the CREATE3
pattern (i.e. without an initcode factor) and using the creation bytecode initCode
, the initialisation code data
, the struct for the payable
amounts values
, msg.value
as inputs. The salt value is calculated pseudo-randomly using a diverse selection of block and transaction properties. This approach does not guarantee true randomness! In order to save deployment costs, we do not sanity check the initCode
length. Note that if values.constructorAmount
is non-zero, initCode
must have a payable
constructor, and any excess ether is returned to msg.sender
.
# /*:ยฐโข Function Arguments โขยฐ:*/ #
- name: initCode
type: bytes
description: The creation bytecode.
- name: data
type: bytes
description: The initialisation code that is passed to the deployed contract.
- name: values
type: tuple(uint256,uint256)
description: The specific `payable` amounts for the deployment and initialisation call.
# /*:ยฐโข Return Value โขยฐ:*/ #
- name: newContract
type: address
description: The 20-byte address where the contract was deployed.
โน๏ธ Note
This function allows for reentrancy, however we refrain from adding a mutex lock to keep it as use-case agnostic as possible. Please ensure at the protocol level that potentially malicious reentrant calls do not affect your smart contract system.
deployCreate3AndInit(bytes32,bytes,bytes,tuple(uint256,uint256))
Deploys and initialises a new contract via employing the CREATE3
pattern (i.e. without an initcode factor) and using the salt value salt
, the creation bytecode initCode
, the initialisation code data
, the struct for the payable
amounts values
, and msg.value
as inputs. In order to save deployment costs, we do not sanity check the initCode
length. Note that if values.constructorAmount
is non-zero, initCode
must have a payable
constructor, and any excess ether is returned to msg.sender
.
# /*:ยฐโข Function Arguments โขยฐ:*/ #
- name: salt
type: bytes32
description: The 32-byte random value used to create the proxy contract address.
- name: initCode
type: bytes
description: The creation bytecode.
- name: data
type: bytes
description: The initialisation code that is passed to the deployed contract.
- name: values
type: tuple(uint256,uint256)
description: The specific `payable` amounts for the deployment and initialisation call.
# /*:ยฐโข Return Value โขยฐ:*/ #
- name: newContract
type: address
description: The 20-byte address where the contract was deployed.
โน๏ธ Note
This function allows for reentrancy, however we refrain from adding a mutex lock to keep it as use-case agnostic as possible. Please ensure at the protocol level that potentially malicious reentrant calls do not affect your smart contract system. Furthermore, we strongly recommend implementing a permissioned deploy protection by setting the first 20 bytes equal tomsg.sender
in thesalt
to prevent maliciously intended frontrun proxy deployments on other chains.
deployCreate3AndInit(bytes,bytes,tuple(uint256,uint256),address)
Deploys and initialises a new contract via employing the CREATE3
pattern (i.e. without an initcode factor) and using the creation bytecode initCode
, the initialisation code data
, the struct for the payable
amounts values
, the refund address refundAddress
, and msg.value
as inputs. The salt value is calculated pseudo-randomly using a diverse selection of block and transaction properties. This approach does not guarantee true randomness! In order to save deployment costs, we do not sanity check the initCode
length. Note that if values.constructorAmount
is non-zero, initCode
must have a payable
constructor.
# /*:ยฐโข Function Arguments โขยฐ:*/ #
- name: initCode
type: bytes
description: The creation bytecode.
- name: data
type: bytes
description: The initialisation code that is passed to the deployed contract.
- name: values
type: tuple(uint256,uint256)
description: The specific `payable` amounts for the deployment and initialisation call.
- name: refundAddress
type: address
description: The 20-byte address where any excess ether is returned to.
# /*:ยฐโข Return Value โขยฐ:*/ #
- name: newContract
type: address
description: The 20-byte address where the contract was deployed.
โน๏ธ Note
This function allows for reentrancy, however we refrain from adding a mutex lock to keep it as use-case agnostic as possible. Please ensure at the protocol level that potentially malicious reentrant calls do not affect your smart contract system.
deployCreate3AndInit(bytes32,bytes,bytes,tuple(uint256,uint256),address)
Deploys and initialises a new contract via employing the CREATE3
pattern (i.e. without an initcode factor) and using the salt value salt
, the creation bytecode initCode
, the initialisation code data
, the struct for the payable
amounts values
, the refund address refundAddress
, and msg.value
as inputs. In order to save deployment costs, we do not sanity check the initCode
length. Note that if values.constructorAmount
is non-zero, initCode
must have a payable
constructor.
# /*:ยฐโข Function Arguments โขยฐ:*/ #
- name: salt
type: bytes32
description: The 32-byte random value used to create the proxy contract address.
- name: initCode
type: bytes
description: The creation bytecode.
- name: data
type: bytes
description: The initialisation code that is passed to the deployed contract.
- name: values
type: tuple(uint256,uint256)
description: The specific `payable` amounts for the deployment and initialisation call.
- name: refundAddress
type: address
description: The 20-byte address where any excess ether is returned to.
# /*:ยฐโข Return Value โขยฐ:*/ #
- name: newContract
type: address
description: The 20-byte address where the contract was deployed.
โน๏ธ Note
This function allows for reentrancy, however we refrain from adding a mutex lock to keep it as use-case agnostic as possible. Please ensure at the protocol level that potentially malicious reentrant calls do not affect your smart contract system. Furthermore, we strongly recommend implementing a permissioned deploy protection by setting the first 20 bytes equal tomsg.sender
in thesalt
to prevent maliciously intended frontrun proxy deployments on other chains.
Special Features
Permissioned Deploy Protection and Cross-Chain Redeploy Protection
The salt
value implements different safeguarding mechanisms depending on the encoded values in the salt (||
stands for byte-wise concatenation):
=> salt (32 bytes) = 0xbebebebebebebebebebebebebebebebebebebebe||ff||1212121212121212121212
- The first 20 bytes (i.e.
bebebebebebebebebebebebebebebebebebebebe
) may be used to implement a permissioned deploy protection by setting them equal tomsg.sender
, - The 21st byte (i.e.
ff
) may be used to implement a cross-chain redeploy protection by setting it equal to0x01
, - The last random 11 bytes (i.e.
1212121212121212121212
) allow for$2^{88}$ bits of entropy for mining a salt.
The full logic is implemented in the internal
_guard
function:
function _guard(bytes32 salt) internal view returns (bytes32 guardedSalt) {
(
SenderBytes senderBytes,
RedeployProtectionFlag redeployProtectionFlag
) = _parseSalt({ salt: salt });
if (
senderBytes == SenderBytes.MsgSender &&
redeployProtectionFlag == RedeployProtectionFlag.True
) {
// Configures a permissioned deploy protection as well as a cross-chain redeploy protection.
guardedSalt = keccak256(abi.encode(msg.sender, block.chainid, salt));
} else if (
senderBytes == SenderBytes.MsgSender &&
redeployProtectionFlag == RedeployProtectionFlag.False
) {
// Configures solely a permissioned deploy protection.
guardedSalt = _efficientHash({
a: bytes32(uint256(uint160(msg.sender))),
b: salt
});
} else if (senderBytes == SenderBytes.MsgSender) {
// Reverts if the 21st byte is greater than `0x01` in order to enforce developer explicitness.
revert InvalidSalt({ emitter: _SELF });
} else if (
senderBytes == SenderBytes.ZeroAddress &&
redeployProtectionFlag == RedeployProtectionFlag.True
) {
// Configures solely a cross-chain redeploy protection. In order to prevent a pseudo-randomly
// generated cross-chain redeploy protection, we enforce the zero address check for the first 20 bytes.
guardedSalt = _efficientHash({ a: bytes32(block.chainid), b: salt });
} else if (
senderBytes == SenderBytes.ZeroAddress &&
redeployProtectionFlag == RedeployProtectionFlag.Unspecified
) {
// Reverts if the 21st byte is greater than `0x01` in order to enforce developer explicitness.
revert InvalidSalt({ emitter: _SELF });
} else {
// For the non-pseudo-random cases, the salt value `salt` is hashed to prevent the safeguard mechanisms
// from being bypassed. Otherwise, the salt value `salt` is not modified.
guardedSalt = (salt != _generateSalt())
? keccak256(abi.encode(salt))
: salt;
}
}
Please note that when you configure a permissioned deploy protection, you must specify whether you want cross-chain redeploy protection (i.e. 21st byte equals 0x01
) or not (i.e. the 21st byte equals 0x00
). The underlying reason for this logic is to enforce developer explicitness. If you don't specify a cross-chain redeploy protection decision (i.e. the 21st byte is greater than 0x01
) the function reverts.
Furthermore, you can configure only cross-chain redeploy protection by setting the first 20 bytes equal to the zero address 0x0000000000000000000000000000000000000000
. The rationale behind this logic is to prevent a pseudo-randomly generated 32 byte salt from inadvertently activating cross-chain redeploy protection. Also in this case, if you don't specify a cross-chain redeploy protection, i.e. the 21st byte is greater than 0x01
, the function reverts. The underlying reason for this logic is as well to enforce developer explicitness.
Pseudo-Random Salt Value
For developer convenience, the CreateX
contract offers several overloaded functions that generate the salt value pseudo-randomly using a diverse selection of block and transaction properties. Please note that this approach does not guarantee true randomness!
The full logic is implemented in the internal
_generateSalt
function:
function _generateSalt() internal view returns (bytes32 salt) {
unchecked {
salt = keccak256(
abi.encode(
// We don't use `block.number - 256` (the maximum value on the EVM) to accommodate
// any chains that may try to reduce the amount of available historical block hashes.
// We also don't subtract 1 to mitigate any risks arising from consecutive block
// producers on a PoS chain. Therefore, we use `block.number - 32` as a reasonable
// compromise, one we expect should work on most chains, which is 1 epoch on Ethereum
// mainnet. Please note that if you use this function between the genesis block and block
// number 31, the block property `blockhash` will return zero, but the returned salt value
// `salt` will still have a non-zero value due to the hashing characteristic and the other
// remaining properties.
blockhash(block.number - 32),
block.coinbase,
block.number,
block.timestamp,
block.prevrandao,
block.chainid,
msg.sender
)
);
}
}
Design Principles
CreateX
should cover most but not all contract creation use cases.CreateX
should be human-readable and should be simple to understand for readers with low prior experience.CreateX
should be maximally secure, i.e. no hidden footguns.CreateX
should be trustless.CreateX
should be stateless.CreateX
should be extensible (i.e. it can be used to deploy protocols, within protocols, or to deploy other types of deterministic deployer factories).
The following consequences result from these principles:
- We only use inline assembly if it is required or if the code section itself is based on short and/or audited code.
- We document the contract to the smallest detail.
- We extensively fuzz test all functions.
- We deliberately do not implement special functions for clones with immutable arguments, as there is neither a finalised standard nor a properly audited contract version.
- We do not implement any special functions for EIP-5202 (a.k.a. blueprint contracts), as all existing functions in
CreateX
are already cost-effective alternatives in our opinion.
Security Considerations
Warning
This contract is unaudited! Special thanks go to Oleksii Matiiasevych for his thorough review and feedback ๐๐ฝ.
Generally, for security issues, see our Security Policy. Furthermore, you must be aware of the following aspects:
-
Several functions allow for reentrancy, however we refrain from adding a mutex lock to keep it as use-case agnostic as possible. Please ensure at the protocol level that potentially malicious reentrant calls do not affect your smart contract system.
-
In the functions:
deployCreate3(bytes32,bytes)
,deployCreate3AndInit(bytes32,bytes,bytes,tuple(uint256,uint256))
, anddeployCreate3AndInit(bytes32,bytes,bytes,tuple(uint256,uint256),address)
we strongly recommend implementing a permissioned deploy protection by setting the first 20 bytes equal to
msg.sender
in thesalt
to prevent maliciously intended frontrun proxy deployments on other chains. -
The target EVM version for compilation is set to
paris
, i.e. neither the contract creation bytecode ofCreateX
nor the returned runtime bytecode contains aPUSH0
instruction.
Tests
For all (fuzz) tests available in the test
directory, we have consistently applied the Branching Tree Technique with bulloak
. This means that each test file is accompanied by a .tree
file that defines all the necessary branches to be tested.
Example (CreateX._guard.tree
):
CreateX_Guard_Internal_Test
โโโ When the first 20 bytes of the salt equals the caller
โ โโโ When the 21st byte of the salt equals 0x01
โ โ โโโ It should return the keccak256 hash of the ABI-encoded values msg.sender, block.chainid, and the salt.
โ โโโ When the 21st byte of the salt equals 0x00
โ โ โโโ It should return the keccak256 hash of the ABI-encoded values msg.sender and the salt.
โ โโโ When the 21st byte of the salt is greater than 0x01
โ โโโ It should revert.
โโโ When the first 20 bytes of the salt equals the zero address
โ โโโ When the 21st byte of the salt equals 0x01
โ โ โโโ It should return the keccak256 hash of the ABI-encoded values block.chainid and the salt.
โ โโโ When the 21st byte of the salt equals 0x00
โ โ โโโ It should return the keccak256 hash of the ABI-encoded value salt.
โ โโโ When the 21st byte of the salt is greater than 0x01
โ โโโ It should revert.
โโโ When the first 20 bytes of the salt do not equal the caller or the zero address
โโโ It should return the keccak256 hash of the ABI-encoded value salt.
โโโ When the salt value is generated pseudo-randomly
โโโ It should return the unmodified salt value.
Test Coverage
This project repository uses forge coverage
. Simply run:
forge coverage
In order to generate an HTML
file with the coverage data, you can invoke:
pnpm coverage:report
The written tests available in the directory test
achieve a test coverage of 100% for the CreateX
contract:
| File | % Lines | % Statements | % Branches | % Funcs |
|-----------------|-------------------|-------------------|-----------------|-----------------|
| src/CreateX.sol | 100.00% (149/149) | 100.00% (210/210) | 100.00% (78/78) | 100.00% (31/31) |
Important
A test coverage of 100% does not mean that there are no vulnerabilities. What really counts is the quality and spectrum of the tests themselves!
ABI (Application Binary Interface)
Tip
If you forge install
this repository, the Solidity-based interface can also be found here.
Solidity
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.4;
/**
* @title CreateX Factory Interface Definition
* @author pcaversaccio (https://web.archive.org/web/20230921103111/https://pcaversaccio.com/)
* @custom:coauthor Matt Solomon (https://web.archive.org/web/20230921103335/https://mattsolomon.dev/)
*/
interface ICreateX {
/*ยด:ยฐโข.ยฐ+.*โขยด.*:ห.ยฐ*.หโขยด.ยฐ:ยฐโข.ยฐโข.*โขยด.*:ห.ยฐ*.หโขยด.ยฐ:ยฐโข.ยฐ+.*โขยด.*:*/
/* TYPES */
/*.โขยฐ:ยฐ.ยด+ห.*ยฐ.ห:*.ยดโข*.+ยฐ.โขยฐ:ยด*.ยดโข*.โขยฐ.โขยฐ:ยฐ.ยด:โขหยฐ.*ยฐ.ห:*.ยด+ยฐ.โข*/
struct Values {
uint256 constructorAmount;
uint256 initCallAmount;
}
/*ยด:ยฐโข.ยฐ+.*โขยด.*:ห.ยฐ*.หโขยด.ยฐ:ยฐโข.ยฐโข.*โขยด.*:ห.ยฐ*.หโขยด.ยฐ:ยฐโข.ยฐ+.*โขยด.*:*/
/* EVENTS */
/*.โขยฐ:ยฐ.ยด+ห.*ยฐ.ห:*.ยดโข*.+ยฐ.โขยฐ:ยด*.ยดโข*.โขยฐ.โขยฐ:ยฐ.ยด:โขหยฐ.*ยฐ.ห:*.ยด+ยฐ.โข*/
event ContractCreation(address indexed newContract, bytes32 indexed salt);
event ContractCreation(address indexed newContract);
event Create3ProxyContractCreation(
address indexed newContract,
bytes32 indexed salt
);
/*ยด:ยฐโข.ยฐ+.*โขยด.*:ห.ยฐ*.หโขยด.ยฐ:ยฐโข.ยฐโข.*โขยด.*:ห.ยฐ*.หโขยด.ยฐ:ยฐโข.ยฐ+.*โขยด.*:*/
/* CUSTOM ERRORS */
/*.โขยฐ:ยฐ.ยด+ห.*ยฐ.ห:*.ยดโข*.+ยฐ.โขยฐ:ยด*.ยดโข*.โขยฐ.โขยฐ:ยฐ.ยด:โขหยฐ.*ยฐ.ห:*.ยด+ยฐ.โข*/
error FailedContractCreation(address emitter);
error FailedContractInitialisation(address emitter, bytes revertData);
error InvalidSalt(address emitter);
error InvalidNonceValue(address emitter);
error FailedEtherTransfer(address emitter, bytes revertData);
/*ยด:ยฐโข.ยฐ+.*โขยด.*:ห.ยฐ*.หโขยด.ยฐ:ยฐโข.ยฐโข.*โขยด.*:ห.ยฐ*.หโขยด.ยฐ:ยฐโข.ยฐ+.*โขยด.*:*/
/* CREATE */
/*.โขยฐ:ยฐ.ยด+ห.*ยฐ.ห:*.ยดโข*.+ยฐ.โขยฐ:ยด*.ยดโข*.โขยฐ.โขยฐ:ยฐ.ยด:โขหยฐ.*ยฐ.ห:*.ยด+ยฐ.โข*/
function deployCreate(
bytes memory initCode
) external payable returns (address newContract);
function deployCreateAndInit(
bytes memory initCode,
bytes memory data,
Values memory values,
address refundAddress
) external payable returns (address newContract);
function deployCreateAndInit(
bytes memory initCode,
bytes memory data,
Values memory values
) external payable returns (address newContract);
function deployCreateClone(
address implementation,
bytes memory data
) external payable returns (address proxy);
function computeCreateAddress(
address deployer,
uint256 nonce
) external view returns (address computedAddress);
function computeCreateAddress(
uint256 nonce
) external view returns (address computedAddress);
/*ยด:ยฐโข.ยฐ+.*โขยด.*:ห.ยฐ*.หโขยด.ยฐ:ยฐโข.ยฐโข.*โขยด.*:ห.ยฐ*.หโขยด.ยฐ:ยฐโข.ยฐ+.*โขยด.*:*/
/* CREATE2 */
/*.โขยฐ:ยฐ.ยด+ห.*ยฐ.ห:*.ยดโข*.+ยฐ.โขยฐ:ยด*.ยดโข*.โขยฐ.โขยฐ:ยฐ.ยด:โขหยฐ.*ยฐ.ห:*.ยด+ยฐ.โข*/
function deployCreate2(
bytes32 salt,
bytes memory initCode
) external payable returns (address newContract);
function deployCreate2(
bytes memory initCode
) external payable returns (address newContract);
function deployCreate2AndInit(
bytes32 salt,
bytes memory initCode,
bytes memory data,
Values memory values,
address refundAddress
) external payable returns (address newContract);
function deployCreate2AndInit(
bytes32 salt,
bytes memory initCode,
bytes memory data,
Values memory values
) external payable returns (address newContract);
function deployCreate2AndInit(
bytes memory initCode,
bytes memory data,
Values memory values,
address refundAddress
) external payable returns (address newContract);
function deployCreate2AndInit(
bytes memory initCode,
bytes memory data,
Values memory values
) external payable returns (address newContract);
function deployCreate2Clone(
bytes32 salt,
address implementation,
bytes memory data
) external payable returns (address proxy);
function deployCreate2Clone(
address implementation,
bytes memory data
) external payable returns (address proxy);
function computeCreate2Address(
bytes32 salt,
bytes32 initCodeHash,
address deployer
) external pure returns (address computedAddress);
function computeCreate2Address(
bytes32 salt,
bytes32 initCodeHash
) external view returns (address computedAddress);
/*ยด:ยฐโข.ยฐ+.*โขยด.*:ห.ยฐ*.หโขยด.ยฐ:ยฐโข.ยฐโข.*โขยด.*:ห.ยฐ*.หโขยด.ยฐ:ยฐโข.ยฐ+.*โขยด.*:*/
/* CREATE3 */
/*.โขยฐ:ยฐ.ยด+ห.*ยฐ.ห:*.ยดโข*.+ยฐ.โขยฐ:ยด*.ยดโข*.โขยฐ.โขยฐ:ยฐ.ยด:โขหยฐ.*ยฐ.ห:*.ยด+ยฐ.โข*/
function deployCreate3(
bytes32 salt,
bytes memory initCode
) external payable returns (address newContract);
function deployCreate3(
bytes memory initCode
) external payable returns (address newContract);
function deployCreate3AndInit(
bytes32 salt,
bytes memory initCode,
bytes memory data,
Values memory values,
address refundAddress
) external payable returns (address newContract);
function deployCreate3AndInit(
bytes32 salt,
bytes memory initCode,
bytes memory data,
Values memory values
) external payable returns (address newContract);
function deployCreate3AndInit(
bytes memory initCode,
bytes memory data,
Values memory values,
address refundAddress
) external payable returns (address newContract);
function deployCreate3AndInit(
bytes memory initCode,
bytes memory data,
Values memory values
) external payable returns (address newContract);
function computeCreate3Address(
bytes32 salt,
address deployer
) external pure returns (address computedAddress);
function computeCreate3Address(
bytes32 salt
) external view returns (address computedAddress);
}
ethers.js
[
"error FailedContractCreation(address)",
"error FailedContractInitialisation(address,bytes)",
"error FailedEtherTransfer(address,bytes)",
"error InvalidNonceValue(address)",
"error InvalidSalt(address)",
"event ContractCreation(address indexed,bytes32 indexed)",
"event ContractCreation(address indexed)",
"event Create3ProxyContractCreation(address indexed,bytes32 indexed)",
"function computeCreate2Address(bytes32,bytes32) view returns (address)",
"function computeCreate2Address(bytes32,bytes32,address) pure returns (address)",
"function computeCreate3Address(bytes32,address) pure returns (address)",
"function computeCreate3Address(bytes32) view returns (address)",
"function computeCreateAddress(uint256) view returns (address)",
"function computeCreateAddress(address,uint256) view returns (address)",
"function deployCreate(bytes) payable returns (address)",
"function deployCreate2(bytes32,bytes) payable returns (address)",
"function deployCreate2(bytes) payable returns (address)",
"function deployCreate2AndInit(bytes32,bytes,bytes,tuple(uint256,uint256),address) payable returns (address)",
"function deployCreate2AndInit(bytes,bytes,tuple(uint256,uint256)) payable returns (address)",
"function deployCreate2AndInit(bytes,bytes,tuple(uint256,uint256),address) payable returns (address)",
"function deployCreate2AndInit(bytes32,bytes,bytes,tuple(uint256,uint256)) payable returns (address)",
"function deployCreate2Clone(bytes32,address,bytes) payable returns (address)",
"function deployCreate2Clone(address,bytes) payable returns (address)",
"function deployCreate3(bytes) payable returns (address)",
"function deployCreate3(bytes32,bytes) payable returns (address)",
"function deployCreate3AndInit(bytes32,bytes,bytes,tuple(uint256,uint256)) payable returns (address)",
"function deployCreate3AndInit(bytes,bytes,tuple(uint256,uint256)) payable returns (address)",
"function deployCreate3AndInit(bytes32,bytes,bytes,tuple(uint256,uint256),address) payable returns (address)",
"function deployCreate3AndInit(bytes,bytes,tuple(uint256,uint256),address) payable returns (address)",
"function deployCreateAndInit(bytes,bytes,tuple(uint256,uint256)) payable returns (address)",
"function deployCreateAndInit(bytes,bytes,tuple(uint256,uint256),address) payable returns (address)",
"function deployCreateClone(address,bytes) payable returns (address)"
]
viem
[
"error FailedContractCreation(address emitter)",
"error FailedContractInitialisation(address emitter, bytes revertData)",
"error FailedEtherTransfer(address emitter, bytes revertData)",
"error InvalidNonceValue(address emitter)",
"error InvalidSalt(address emitter)",
"event ContractCreation(address indexed newContract, bytes32 indexed salt)",
"event ContractCreation(address indexed newContract)",
"event Create3ProxyContractCreation(address indexed newContract, bytes32 indexed salt)",
"function computeCreate2Address(bytes32 salt, bytes32 initCodeHash) view returns (address computedAddress)",
"function computeCreate2Address(bytes32 salt, bytes32 initCodeHash, address deployer) pure returns (address computedAddress)",
"function computeCreate3Address(bytes32 salt, address deployer) pure returns (address computedAddress)",
"function computeCreate3Address(bytes32 salt) view returns (address computedAddress)",
"function computeCreateAddress(uint256 nonce) view returns (address computedAddress)",
"function computeCreateAddress(address deployer, uint256 nonce) view returns (address computedAddress)",
"function deployCreate(bytes initCode) payable returns (address newContract)",
"function deployCreate2(bytes32 salt, bytes initCode) payable returns (address newContract)",
"function deployCreate2(bytes initCode) payable returns (address newContract)",
"function deployCreate2AndInit(bytes32 salt, bytes initCode, bytes data, (uint256 constructorAmount, uint256 initCallAmount) values, address refundAddress) payable returns (address newContract)",
"function deployCreate2AndInit(bytes initCode, bytes data, (uint256 constructorAmount, uint256 initCallAmount) values) payable returns (address newContract)",
"function deployCreate2AndInit(bytes initCode, bytes data, (uint256 constructorAmount, uint256 initCallAmount) values, address refundAddress) payable returns (address newContract)",
"function deployCreate2AndInit(bytes32 salt, bytes initCode, bytes data, (uint256 constructorAmount, uint256 initCallAmount) values) payable returns (address newContract)",
"function deployCreate2Clone(bytes32 salt, address implementation, bytes data) payable returns (address proxy)",
"function deployCreate2Clone(address implementation, bytes data) payable returns (address proxy)",
"function deployCreate3(bytes initCode) payable returns (address newContract)",
"function deployCreate3(bytes32 salt, bytes initCode) payable returns (address newContract)",
"function deployCreate3AndInit(bytes32 salt, bytes initCode, bytes data, (uint256 constructorAmount, uint256 initCallAmount) values) payable returns (address newContract)",
"function deployCreate3AndInit(bytes initCode, bytes data, (uint256 constructorAmount, uint256 initCallAmount) values) payable returns (address newContract)",
"function deployCreate3AndInit(bytes32 salt, bytes initCode, bytes data, (uint256 constructorAmount, uint256 initCallAmount) values, address refundAddress) payable returns (address newContract)",
"function deployCreate3AndInit(bytes initCode, bytes data, (uint256 constructorAmount, uint256 initCallAmount) values, address refundAddress) payable returns (address newContract)",
"function deployCreateAndInit(bytes initCode, bytes data, (uint256 constructorAmount, uint256 initCallAmount) values) payable returns (address newContract)",
"function deployCreateAndInit(bytes initCode, bytes data, (uint256 constructorAmount, uint256 initCallAmount) values, address refundAddress) payable returns (address newContract)",
"function deployCreateClone(address implementation, bytes data) payable returns (address proxy)",
] as const;
JSON
[
{
"inputs": [
{
"internalType": "address",
"name": "emitter",
"type": "address"
}
],
"name": "FailedContractCreation",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "emitter",
"type": "address"
},
{
"internalType": "bytes",
"name": "revertData",
"type": "bytes"
}
],
"name": "FailedContractInitialisation",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "emitter",
"type": "address"
},
{
"internalType": "bytes",
"name": "revertData",
"type": "bytes"
}
],
"name": "FailedEtherTransfer",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "emitter",
"type": "address"
}
],
"name": "InvalidNonceValue",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "emitter",
"type": "address"
}
],
"name": "InvalidSalt",
"type": "error"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "newContract",
"type": "address"
},
{
"indexed": true,
"internalType": "bytes32",
"name": "salt",
"type": "bytes32"
}
],
"name": "ContractCreation",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "newContract",
"type": "address"
}
],
"name": "ContractCreation",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "newContract",
"type": "address"
},
{
"indexed": true,
"internalType": "bytes32",
"name": "salt",
"type": "bytes32"
}
],
"name": "Create3ProxyContractCreation",
"type": "event"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "salt",
"type": "bytes32"
},
{
"internalType": "bytes32",
"name": "initCodeHash",
"type": "bytes32"
}
],
"name": "computeCreate2Address",
"outputs": [
{
"internalType": "address",
"name": "computedAddress",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "salt",
"type": "bytes32"
},
{
"internalType": "bytes32",
"name": "initCodeHash",
"type": "bytes32"
},
{
"internalType": "address",
"name": "deployer",
"type": "address"
}
],
"name": "computeCreate2Address",
"outputs": [
{
"internalType": "address",
"name": "computedAddress",
"type": "address"
}
],
"stateMutability": "pure",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "salt",
"type": "bytes32"
},
{
"internalType": "address",
"name": "deployer",
"type": "address"
}
],
"name": "computeCreate3Address",
"outputs": [
{
"internalType": "address",
"name": "computedAddress",
"type": "address"
}
],
"stateMutability": "pure",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "salt",
"type": "bytes32"
}
],
"name": "computeCreate3Address",
"outputs": [
{
"internalType": "address",
"name": "computedAddress",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "nonce",
"type": "uint256"
}
],
"name": "computeCreateAddress",
"outputs": [
{
"internalType": "address",
"name": "computedAddress",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "deployer",
"type": "address"
},
{
"internalType": "uint256",
"name": "nonce",
"type": "uint256"
}
],
"name": "computeCreateAddress",
"outputs": [
{
"internalType": "address",
"name": "computedAddress",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes",
"name": "initCode",
"type": "bytes"
}
],
"name": "deployCreate",
"outputs": [
{
"internalType": "address",
"name": "newContract",
"type": "address"
}
],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "salt",
"type": "bytes32"
},
{
"internalType": "bytes",
"name": "initCode",
"type": "bytes"
}
],
"name": "deployCreate2",
"outputs": [
{
"internalType": "address",
"name": "newContract",
"type": "address"
}
],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes",
"name": "initCode",
"type": "bytes"
}
],
"name": "deployCreate2",
"outputs": [
{
"internalType": "address",
"name": "newContract",
"type": "address"
}
],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "salt",
"type": "bytes32"
},
{
"internalType": "bytes",
"name": "initCode",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "data",
"type": "bytes"
},
{
"components": [
{
"internalType": "uint256",
"name": "constructorAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "initCallAmount",
"type": "uint256"
}
],
"internalType": "struct CreateX.Values",
"name": "values",
"type": "tuple"
},
{
"internalType": "address",
"name": "refundAddress",
"type": "address"
}
],
"name": "deployCreate2AndInit",
"outputs": [
{
"internalType": "address",
"name": "newContract",
"type": "address"
}
],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes",
"name": "initCode",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "data",
"type": "bytes"
},
{
"components": [
{
"internalType": "uint256",
"name": "constructorAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "initCallAmount",
"type": "uint256"
}
],
"internalType": "struct CreateX.Values",
"name": "values",
"type": "tuple"
}
],
"name": "deployCreate2AndInit",
"outputs": [
{
"internalType": "address",
"name": "newContract",
"type": "address"
}
],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes",
"name": "initCode",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "data",
"type": "bytes"
},
{
"components": [
{
"internalType": "uint256",
"name": "constructorAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "initCallAmount",
"type": "uint256"
}
],
"internalType": "struct CreateX.Values",
"name": "values",
"type": "tuple"
},
{
"internalType": "address",
"name": "refundAddress",
"type": "address"
}
],
"name": "deployCreate2AndInit",
"outputs": [
{
"internalType": "address",
"name": "newContract",
"type": "address"
}
],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "salt",
"type": "bytes32"
},
{
"internalType": "bytes",
"name": "initCode",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "data",
"type": "bytes"
},
{
"components": [
{
"internalType": "uint256",
"name": "constructorAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "initCallAmount",
"type": "uint256"
}
],
"internalType": "struct CreateX.Values",
"name": "values",
"type": "tuple"
}
],
"name": "deployCreate2AndInit",
"outputs": [
{
"internalType": "address",
"name": "newContract",
"type": "address"
}
],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "salt",
"type": "bytes32"
},
{
"internalType": "address",
"name": "implementation",
"type": "address"
},
{
"internalType": "bytes",
"name": "data",
"type": "bytes"
}
],
"name": "deployCreate2Clone",
"outputs": [
{
"internalType": "address",
"name": "proxy",
"type": "address"
}
],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "implementation",
"type": "address"
},
{
"internalType": "bytes",
"name": "data",
"type": "bytes"
}
],
"name": "deployCreate2Clone",
"outputs": [
{
"internalType": "address",
"name": "proxy",
"type": "address"
}
],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes",
"name": "initCode",
"type": "bytes"
}
],
"name": "deployCreate3",
"outputs": [
{
"internalType": "address",
"name": "newContract",
"type": "address"
}
],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "salt",
"type": "bytes32"
},
{
"internalType": "bytes",
"name": "initCode",
"type": "bytes"
}
],
"name": "deployCreate3",
"outputs": [
{
"internalType": "address",
"name": "newContract",
"type": "address"
}
],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "salt",
"type": "bytes32"
},
{
"internalType": "bytes",
"name": "initCode",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "data",
"type": "bytes"
},
{
"components": [
{
"internalType": "uint256",
"name": "constructorAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "initCallAmount",
"type": "uint256"
}
],
"internalType": "struct CreateX.Values",
"name": "values",
"type": "tuple"
}
],
"name": "deployCreate3AndInit",
"outputs": [
{
"internalType": "address",
"name": "newContract",
"type": "address"
}
],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes",
"name": "initCode",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "data",
"type": "bytes"
},
{
"components": [
{
"internalType": "uint256",
"name": "constructorAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "initCallAmount",
"type": "uint256"
}
],
"internalType": "struct CreateX.Values",
"name": "values",
"type": "tuple"
}
],
"name": "deployCreate3AndInit",
"outputs": [
{
"internalType": "address",
"name": "newContract",
"type": "address"
}
],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "salt",
"type": "bytes32"
},
{
"internalType": "bytes",
"name": "initCode",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "data",
"type": "bytes"
},
{
"components": [
{
"internalType": "uint256",
"name": "constructorAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "initCallAmount",
"type": "uint256"
}
],
"internalType": "struct CreateX.Values",
"name": "values",
"type": "tuple"
},
{
"internalType": "address",
"name": "refundAddress",
"type": "address"
}
],
"name": "deployCreate3AndInit",
"outputs": [
{
"internalType": "address",
"name": "newContract",
"type": "address"
}
],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes",
"name": "initCode",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "data",
"type": "bytes"
},
{
"components": [
{
"internalType": "uint256",
"name": "constructorAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "initCallAmount",
"type": "uint256"
}
],
"internalType": "struct CreateX.Values",
"name": "values",
"type": "tuple"
},
{
"internalType": "address",
"name": "refundAddress",
"type": "address"
}
],
"name": "deployCreate3AndInit",
"outputs": [
{
"internalType": "address",
"name": "newContract",
"type": "address"
}
],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes",
"name": "initCode",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "data",
"type": "bytes"
},
{
"components": [
{
"internalType": "uint256",
"name": "constructorAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "initCallAmount",
"type": "uint256"
}
],
"internalType": "struct CreateX.Values",
"name": "values",
"type": "tuple"
}
],
"name": "deployCreateAndInit",
"outputs": [
{
"internalType": "address",
"name": "newContract",
"type": "address"
}
],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes",
"name": "initCode",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "data",
"type": "bytes"
},
{
"components": [
{
"internalType": "uint256",
"name": "constructorAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "initCallAmount",
"type": "uint256"
}
],
"internalType": "struct CreateX.Values",
"name": "values",
"type": "tuple"
},
{
"internalType": "address",
"name": "refundAddress",
"type": "address"
}
],
"name": "deployCreateAndInit",
"outputs": [
{
"internalType": "address",
"name": "newContract",
"type": "address"
}
],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "implementation",
"type": "address"
},
{
"internalType": "bytes",
"name": "data",
"type": "bytes"
}
],
"name": "deployCreateClone",
"outputs": [
{
"internalType": "address",
"name": "proxy",
"type": "address"
}
],
"stateMutability": "payable",
"type": "function"
}
]
New Deployment(s)
Caution
The address 0x0000000000000000000000000000000000000000
is a simple placeholder for now. Do not send any funds there!
We offer two options for deploying CreateX
to your desired chain:
- Deploy it yourself by using one of the pre-signed transactions. Details can be found in the subsequent paragraph.
- Request a deployment by opening an issue. You can significantly reduce the time to deployment by sending funds to cover the deployment cost to the deployer account:
0x0000000000000000000000000000000000000000
.
TBD (section on pre-signed transactions; we will offer multiple pre-signed transactions with different gasLimit
levels; will be added after the feedback phase)
Contract Verification
To verify a deployed CreateX
contract on a block explorer, use the following parameters:
- TBD (will be added after the feedback phase)
CreateX
Deployments
Caution
The address 0x0000000000000000000000000000000000000000
is a simple placeholder for now. Do not send any funds there!
- EVM-Based Production Networks:
- Ethereum Test Networks:
- Additional EVM-Based Test Networks:
- Optimism Testnet (Goerli):
0x0000000000000000000000000000000000000000
- Optimism Testnet (Goerli):
๐๐ผ Acknowledgement
All the work has been done jointly by myself and Matt Solomon as a public good for our ecosystem. Public good software is not just code; it's the embodiment of collective progress, a testament to collaboration's power, and a canvas where innovation meets the needs of the many. I hope we can live up to these principles! ๐ซก