This ERC defines a permissionless and deterministic deployment mechanism across all EVM-compatible chains. It uses the EIP-7702 Set Code for EOAs (0x4)
transaction type to deploy a universal CREATE2 factory contract to a fixed address (0xC0DE207acb0888c5409E51F27390Dad75e4ECbe7
) with known bytecode. The factory can then create any new contract to a deterministic address using the EIP-1014 CREATE2 (0xf5)
opcode. It does not require preinstalls, secret keys, or chain-specific infrastructure.
Ensuring that contracts share the same address and code on multiple chains is a hard problem. It is typically done by having a known CREATE2 factory contract at a specific address that can further deterministically deploy new contracts using the CREATE2 (0xf5)
opcode.
However, there is a bootstrapping problem: how do you get a CREATE2 factory contract with a specific address and code?
There are currently three main approaches to this problem:
Use Nick's method to randomly generate a signature for a transaction without EIP-155 replay protection that deploys the CREATE2 factory. Nick's method ensures that there is no known private key for the account that deploys the CREATE2 factory, meaning that the resulting contract will have a deterministic address and code on all chains. This strategy is used by the Deterministic Deployment Proxy (deployed to 0x4e59b44847b379578588920ca78fbf26c0b4956c
, including on Ethereum Mainnet), one of the most widely used CREATE2 factory contracts.
Downsides:
Keep a carefully guarded secret key and use it to sign transactions to deploy CREATE2 factory contracts. The resulting contract will have a deterministic address and code on all chains where the transaction at a given nonce of the deployer account is a CREATE2 factory deployment, which can be verified post-deployment to ensure trustlessness. Additionally, this method does not have the same gas sensitivity downsides as Nick's method, as the private key can sign a creation transaction with appropriate gas parameters at the time of execution. This is the strategy used by the Safe Singleton Factory and CreateX (deployed to 0x914d7Fec6aaC8cd542e72Bca78B30650d45643d7
and 0xba5Ed099633D3B313e4D5F7bdc1305d3c28ba5Ed
respectively, including on Ethereum Mainnet).
Downsides:
Have popular CREATE2 deployment factories deployed on new chains by default. This is, for example, what OP Stack and ZKsync do as part of their preinstalls, including the CREATE2 factory contracts mentioned above. This ensures that the CREATE2 factory contracts have known addresses and codes.
Downsides:
0x4
TransactionsThis ERC proposes a permissionless alternative fourth mechanism to the existing ones described above with none of their downsides. Additionally, it standardizes a set of deployment parameters for a universal CREATE2 factory deployment. This ensures a common CREATE2 factory for the community instead of multiple competing copies with slightly different codes at different addresses. This single CREATE2 factory copy can bootstrap additional deterministic deployment infrastructure (such as the comprehensive CreateX universal contract deployer).
Benefits
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174.
Parameter | Value |
---|---|
DEPLOYER_PRIVATE_KEY |
0x942ba639ec667bdded6d727ad2e483648a34b584f916e6b826fdb7b512633731 |
CREATE2_FACTORY_INIT_CODE |
0x7760203d3d3582360380843d373d34f580601457fd5b3d52f33d5260186008f3 |
CREATE2_FACTORY_SALT |
0x000000000000000000000000000000000000000000000000000000000019e078 |
Derived Parameter | Value |
---|---|
DEPLOYER_ADDRESS |
0x962560A0333190D57009A0aAAB7Bfa088f58461C |
CREATE2_FACTORY_ADDRESS |
0xC0DE945918F144DcdF063469823a4C51152Df05D |
CREATE2_FACTORY_RUNTIME_CODE |
0x60203d3d3582360380843d373d34f580601457fd5b3d52f3 |
CREATE2_FACTORY_CODE_HASH |
0xeac13dde1a2c9b8dc8a7aded29ad0af5d57c811b746f7909ea841cbfc6ef3adc |
DEPLOYER_PRIVATE_KEY
with address DEPLOYER_ADDRESS
.CREATE2 (0xf5)
opcode, allowing smart contracts to be deployed to deterministic addresses.CREATE2 (0xf5)
deployment of the CREATE2 factory contract.The bootstrap contract MUST execute the following or equivalent bootstrapping code as an EIP-7702 delegation target for the deployer account:
bytes memory initCode = CREATE2_FACTORY_INIT_CODE;
bytes32 salt = CREATE2_FACTORY_SALT;
assembly ("memory-safe") {
create2(0, add(initCode, 32), mload(initCode), salt)
}
The bootstrap contract MAY implement additional features such as:
DEPLOYER_PRIVATE_KEY
delegating to the bootstrap contract.0x4
transaction with the authorization from setup 2; the transaction MUST call DEPLOYER_ADDRESS
(either directly or indirectly) which delegates to the bootstrap contract and MUST perform the CREATE2 (0xf5)
of the CREATE2_FACTORY_INIT_CODE
with CREATE2_FACTORY_SALT
in the bootstrapping code.Assuming successful execution of the bootstrapping code without reverting in the context of the deployer, the CREATE2 factory contract will be deployed to CREATE2_FACTORY_ADDRESS
with code CREATE2_FACTORY_RUNTIME_CODE
and code hash CREATE2_FACTORY_CODE_HASH
.
The deployment mechanism was chosen such that it is uniquely parameterized by the DEPLOYER_ADDRESS
(which itself is derived from the DEPLOYER_PRIVATE_KEY
and is therefore deterministic), the CREATE2_FACTORY_INIT_CODE
and the CREATE2_FACTORY_SALT
which are both fixed and deterministic. Additionally, since the DEPLOYER_ADDRESS
will deploy the CREATE2 factory contract with the CREATE2 (0xf5)
opcode, this guarantees that the address and code of the contract are deterministic.
The use of a publicly known private key enables this mechanism, as anyone can permissionlessly generate a delegation signature to any bootstrap contract that would cause the DEPLOYER_ADDRESS
to execute the specified CREATE2 (0xf5)
operation and deploy the factory contract to a completely deterministic address. Because of the use of CREATE2 (0xf5)
, the CREATE2 factory will be deployed to CREATE2_FACTORY_ADDRESS
if and only if it is deployed with CREATE2_FACTORY_INIT_CODE
, thus guaranteeing a deployed code hash of CREATE2_FACTORY_CODE_HASH
. Additionally, the semantics of CREATE2 (0xf5)
make it so no transaction executed by DEPLOYER_ADDRESS
can permanently block the deployment of the CREATE2 factory contract.
One issue with this method is that because the DEPLOYER_PRIVATE_KEY
is public, anyone can sign alternative delegations or transactions and front-run a legitimate CREATE2 factory deployment. The front-running would increase the nonce of the DEPLOYER_ADDRESS
account and render the EIP-7702 authorization in the legitimate CREATE2 factory deployment transaction invalid, causing the deployment to potentially fail. We consider this to not be a serious issue, however, as:
Another known issue with this method is that a future network upgrade may introduce new mechanisms (such as a new opcode or transaction type) to permanently set an account's code. If this were to happen, and since the DEPLOYER_PRIVATE_KEY
is publicly known, the DEPLOYER_ADDRESS
account's code could be mistakenly or maliciously set to something that does not execute the necessary bootstrapping code, permanently preventing any future deployment of the CREATE2 factory contract. If this ERC were to gain sufficient adoption, then we believe this will not be an issue as:
DEPLOYER_PRIVATE_KEY
would no longer have any value on Ethereum Mainnet.CREATE2_FACTORY_ADDRESS
has code CREATE2_FACTORY_RUNTIME_CODE
.Instead of using a publicly known DEPLOYER_PRIVATE_KEY
, Nick's method can be used to generate a random EIP-7702 authorization signature. This would prevent the front-running and forward compatibility issues described above.
However, in order for Nick's method to work, the EIP-7702 authorization message that is signed, defined as keccak(MAGIC || rlp([chain_id, address, nonce]))
, must be constant. MAGIC
is already a constant; chain_id
can be trivially fixed to 0
to specify a chain-agnostic EIP-7702 authorization; nonce
can also be trivially fixed to 0
because Nick's method ensures the authority has no known private key, meaning it cannot sign another message that would increment the nonce. However, fixing address
is problematic. Doing so would require a contract with specific code to be deployed to the same address on all chains, which is the original bootstrapping problem the proposed permissionless CREATE2 factory aims to solve. Therefore, this creates a "chicken and egg problem", making it not a viable way to generate an EIP-7702 authorization signature.
This mechanism allows the DEPLOYER_ADDRESS
to do any CREATE2 (0xf5)
deployment, so it would be possible to forgo the intermediary CREATE2 factory contract and use the deployer technique for all deployments. There are multiple downsides to this, however:
Unfortunately, EIP-7702 type 0x4
transactions are restricted to to
values that are not null
, meaning that you cannot simultaneously deploy the bootstrap contract and delegate to it in a single transaction.
The DEPLOYER_PRIVATE_KEY
was chosen as the private key at derivation path m/44'/60'/0'/0/0
for the mnemonic make code code code code code code code code code code coconut
.
The CREATE2_FACTORY_SALT
was chosen as the first salt value starting from 0
such that the CREATE2 factory's ERC-55 checksum address starts with the case sensitive 0xC0DE...
prefix. A verifiable method for mining a vanity address for the CREATE2 factory contract was chosen in order to ensure that the ERC authors did not find a CREATE2 hash collision on the CREATE2_FACTORY_ADDRESS
that they can exploit at some point in the future.
The CREATE2 factory has a similar interface to existing implementations. Namely, it accepts salt || init_code
as input, which is a 32-byte salt
value concatenated with the init_code
of the contract to deploy. It will execute a CREATE2
with the specified salt
and init_code
, deploying a contract with init_code
to keccak256(0xff || CREATE2_FACTORY_ADDRESS || salt || keccak256(init_code))[12:]
.
Note that this contract returns the address of the created contract padded to 32 bytes. This differs from some existing implementations, but was done to maintain consistency with the 32-byte word size on the EVM (same encoding as ecrecover
precompile for example). A product of this is that the return data from CREATE2 factory is compatible with the Solidity ABI.
Throughout both the CREATE2 factory contract initialization and runtime code, we use RETURNDATASIZE (0x3d)
to push 0
onto the stack instead of the dedicated PUSH0 (0x5f)
opcode. This is done to increase compatibility with chains that support EIP-7702 but not EIP-3855, while remaining a 1-byte and 2-gas opcode.
The CREATE2_FACTORY_INIT_CODE
corresponds to the following assembly:
### Constructor Code ###
0x0000: PUSH24 0x60203d3d3582360380843d373d34f580601457fd5b3d52f3
# Stack: [runcode] | Push the CREATE2 factory runtime code
0x0019: RETURNDATASIZE # Stack: [0; runcode] | Push the offset in memory to store the code
0x001a: MSTORE # Stack: [] | The runtime code is now in `memory[8:32]`
0x001b: PUSH1 25 # Stack: [24] | Push the code length
0x001d: PUSH1 7 # Stack: [8; 24] | Push the memory offset of the start of code
0x001f: RETURN # Stack: [] | Return the runtime code
The CREATE2_FACTORY_RUNTIME_CODE
corresponds to the following assembly:
### Runtime Code ###
# Prepare our stack, push 32, a value we will use a lot and can summon with
# `DUP*` to save on one byte of code (over `PUSH1 32`), and a 0 which will
# be used by either the `RETURN` or `REVERT` branches at the end.
0x0000: PUSH1 32 # Stack: [32]
0x0002: RETURNDATASIZE # Stack: [0; 32]
# First, load the salt value and compute the actual code size for the CREATE2
# call, this is the calldata length minus 32 for the salt prefix.
# Stack: [0; 32]
0x0003: RETURNDATASIZE # Stack: [0; 0; 32] | Push the calldata offset 0 of the `salt` parameter
0x0004: CALLDATALOAD # Stack: [salt; 0; 32] | Load the `salt` from calldata
0x0005: DUP3 # Stack: [32; salt; 0; 32] | Push 32 to the stack
0x0006: CALLDATASIZE # Stack: [msg.data.len; 32; salt; ...] | Followed by the calldata length
0x0007: SUB # Stack: [code.len; salt; 0; 32] | Compute `msg.data.length - 32`, which is the length of
# the init `code`
# Copy the init code to memory offset 0.
# Stack: [code.len; salt; 0; 32]
0x0008: DUP1 # Stack: [code.len; .; salt; 0; 32] | Duplicate the length of the init code
0x0009: DUP5 # Stack: [32; code.len; ...] | Push the offset in calldata of the code, which is 32
# as it comes immediately after the 32-byte `salt`; use
# the 32 value at the bottom of the stack
0x000a: RETURNDATASIZE # Stack: [0; 32; code.len; ...] | Push the offset (0) in memory to copy the code to
0x000b: CALLDATACOPY # Stack: [code.len; salt; 0; 32] | Copy the init code, `memory[0:code.len]` contains the
# init `code`
# Deploy the contract.
# Stack: [code.len; salt; 0; 32]
0x000c: RETURNDATASIZE # Stack: [0; code.len; salt; 0; 32] | Push the offset in memory starting of the start of
# init `code`, which is 0
0x000d: CALLVALUE # Stack: [v; 0; code.len; salt; 0; 32] | Forward the call value to the contract constructor
0x000e: CREATE2 # Stack: [address; 0; 32] | Do `create2(v, code, salt)`, which leaves the address
# of the contract on the stack, or 0 if the contract
# creation reverted
# Verify the deployment was successful and return the address.
# Stack: [address; 0; 32]
0x000f: DUP1 # Stack: [address; .; 0; 32] | Duplicate the address value
0x0010: PUSH1 0x14 # Stack: [0x0014; address; .; 0; 32] | Push the jump destination offset for the code which
# handles successful deployments
0x0012: JUMPI # Stack: [address; 0; 32] | Jump if `address != 0`, i.e. `CREATE2` succeeded
# CREATE2 reverted.
# Stack: [address = 0; 0; 32]
0x0013: REVERT # Stack: [] | Revert with empty data `memory[0:0]`
# CREATE2 succeeded.
0x0014: JUMPDEST # Stack: [address; 0; 32]
0x0015: RETURNDATASIZE # Stack: [0; address; 0; 32] | Push the memory offset (0) to store return data at,
# we use `RETURNDATASIZE` because contract creation was
# successful, and therefore the return data has size 0
0x0016: MSTORE # Stack: [0; 32] | Store the address in memory, `memory[0:32]` contains
# the `address` left padded to 32-bytes
0x0017: RETURN # Stack: [] | Return `memory[0:32]`, i.e. the address
There are a few backwards compatibility considerations with the new proposal:
We include a reference implementation of a bootstrap contract to which the deployer account can delegate. The reference implementation expects a call to Bootstrap
to the function deploy()
in an EIP-7702 type 0x4
transaction including the EIP-7702 authorization delegating DEPLOYER_ADDRESS
to Bootstrap
(NOTE: the Bootstrap
is called as an entry point, instead of calling DEPLOYER_ADDRESS
directly which allows the contract to do some up-front checks to minimize gas griefing risk):
// SPDX-License-Identifier: CC0
pragma solidity ^0.8.29;
contract Bootstrap {
address private constant _DEPLOYER_ADDRESS = 0x962560A0333190D57009A0aAAB7Bfa088f58461C;
address private constant _CREATE2_FACTORY_ADDRESS = 0xC0DE945918F144DcdF063469823a4C51152Df05D;
bytes32 private constant _CREATE2_FACTORY_CODE_HASH = hex"eac13dde1a2c9b8dc8a7aded29ad0af5d57c811b746f7909ea841cbfc6ef3adc";
bytes private constant _CREATE2_FACTORY_INIT_CODE = hex"7760203d3d3582360380843d373d34f580601457fd5b3d52f33d5260186008f3";
bytes32 private constant _CREATE2_FACTORY_SALT = hex"000000000000000000000000000000000000000000000000000000000019e078";
error InvalidDelegation();
error CreationFailed();
function deploy() external {
if (_CREATE2_FACTORY_ADDRESS.codehash == _CREATE2_FACTORY_CODE_HASH) {
return;
}
bytes32 delegation = keccak256(abi.encodePacked(hex"ef0100", this));
require(_DEPLOYER_ADDRESS.codehash == delegation, InvalidDelegation());
Bootstrap(_DEPLOYER_ADDRESS).bootstrap();
}
function bootstrap() external {
bytes memory initCode = _CREATE2_FACTORY_INIT_CODE;
bytes32 salt = _CREATE2_FACTORY_SALT;
address factory;
assembly ("memory-safe") {
factory := create2(0, add(initCode, 32), mload(initCode), salt)
}
require(factory == _CREATE2_FACTORY_ADDRESS, CreationFailed());
}
}
A minimal bootstrap contract implementation is also possible (although this has a higher potential for gas griefing). The minimal bootstrap contract expects a call directly to the DEPLOYER_ADDRESS
in an EIP-7702 type 0x4
transaction including the EIP-7702 authorization delegating DEPLOYER_ADDRESS
to MiniBootstrap
:
// SPDX-License-Identifier: CC0
pragma solidity ^0.8.29;
contract MiniBootstrap {
bytes32 private constant _CREATE2_FACTORY_INIT_CODE = hex"7760203d3d3582360380843d373d34f580601457fd5b3d52f33d5260186008f3";
bytes32 private constant _CREATE2_FACTORY_SALT = hex"000000000000000000000000000000000000000000000000000000000019e078";
fallback() external {
assembly ("memory-safe") {
mstore(0, _CREATE2_FACTORY_INIT_CODE)
pop(create2(0, 0, 32, _CREATE2_FACTORY_SALT))
}
}
}
It is possible to front-run transactions that invalidate the deployer's EIP-7702 delegation and cause the deployment to fail. This, however, comes at a gas cost to the attacker, with limited benefit beyond delaying the deployment of the CREATE2 factory. Additionally, persistent attackers can be circumvented by either using private transaction queues or working with block builders directly to ensure that the EIP-7702 bootstrapping transaction is not front-run.
If new EVM opcodes or transaction types are introduced in future network upgrades that allow an account to permanently set its code, then this method is no longer guaranteed to work. The deployer account can permanently change its code to a contract that does not have the required bootstrapping code. This can trivially be done by a malicious actor using the publicly known DEPLOYER_PRIVATE_KEY
and would prevent any future deployments of CREATE2 factory.
Copyright and related rights waived via CC0.