Skip to content

Instantly share code, notes, and snippets.

@CodeSandwich
Created June 21, 2023 07:15
Show Gist options
  • Save CodeSandwich/3022c63e3660a8f1b2dee2a235687a88 to your computer and use it in GitHub Desktop.
Save CodeSandwich/3022c63e3660a8f1b2dee2a235687a88 to your computer and use it in GitHub Desktop.
CREATE3 EOA
/// @notice Creates a contract under a deterministic addresses
/// derived only from the deployer's address and the salt.
/// The deployment is a two-step process, first, a proxy is deployed using CREATE2 with
/// the given salt, and then it's called with the bytecode of the deployed contract,
/// the proxy uses it to deploy the contract using regular CREATE.
/// If the deployed contract's constructor reverts, the proxy also reverts and bubbles the error.
/// If the proxy call has a non-zero value, it's passed to the deployed contract's constructor.
/// A deployment can be made either by a contract or an EOA, and over multiple transactions,
/// because the proxy only accepts being called by its deployer, so there's no risk of frontrunning.
/// Each proxy can be used only once, so it's guaranteed to only deploy the contract using nonce 1.
/// This is an extended version of https://github.com/0xsequence/create3.
library Create3 {
using Address for address;
////////////////////////////// PROXY BYTECODE //////////////////////////////
// Opcode | Opcode name | Stack values after executing
// // Push some zeros onto the stack to use later
// 0x3d | RETURNDATASIZE | 0
// 0x3d | RETURNDATASIZE | 0 0
// 0x3d | RETURNDATASIZE | 0 0 0
// // Load the deployer from the state
// 0x54 | SLOAD | deployer 0 0
// // Revert if the caller is not the proxy deployer
// 0x33 | CALLER | caller deployer 0 0
// 0x14 | EQ | isDeployer 0 0
// 0x600a | PUSH1 10 | 10 isDeployer 0 0
// 0x57 | JUMPI | 0 0
// 0xfd | REVERT |
// 0x5b | JUMPDEST | 0 0
// // Clear the deployer from the state to prevent future usage
// 0x55 | SSTORE |
// // Copy the calldata to memory
// 0x36 | CALLDATASIZE | size
// 0x3d | RETURNDATASIZE | 0 size
// 0x3d | RETURNDATASIZE | 0 0 size
// 0x37 | CALLDATACOPY |
// // Create the contract
// 0x3d | RETURNDATASIZE | 0
// 0x36 | CALLDATASIZE | size 0
// 0x3d | RETURNDATASIZE | 0 size 0
// 0x34 | CALLVALUE | value 0 size 0
// 0xf0 | CREATE | newContract 0
// // Revert if the creation failed
// 0x601f | PUSH1 31 | 31 newContract 0
// 0x57 | JUMPI | 0
// // Copy the returned error to memory
// 0x3d | RETURNDATASIZE | size 0
// 0x81 | DUP2 | 0 size 0
// 0x80 | DUP1 | 0 0 size 0
// 0x3e | RETURNDATACOPY | 0
// // Revert with the returned error
// 0x3d | RETURNDATASIZE | size 0
// 0x90 | SWAP1 | 0 size
// 0xfd | REVERT |
// // Stop the execution successfully
// 0x5b | JUMPDEST | 0
//////////////////////////// PROXY CREATION CODE ///////////////////////////
// Opcode | Opcode name | Stack values after executing
// // Store the deployer's address in the state
// 0x33 | CALLER | caller
// 0x3d | RETURNDATASIZE | 0 caller
// 0x55 | SSTORE |
// // Store the proxy bytecode in memory
// 0x7fXX..XX | PUSH32 bytecode | bytecode
// 0x3d | RETURNDATASIZE | 0 bytecode
// 0x52 | MSTORE |
// // Return the proxy bytecode
// 0x6020 | PUSH1 32 | 32 0
// 0x3d | RETURNDATASIZE | 0
// 0xf3 | RETURN |
bytes private constant PROXY_CREATION_CODE =
// Proxy creation code up to `PUSH32`
hex"333d557f"
// Proxy bytecode
hex"3d3d3d543314600a57fd5b55363d3d373d363d34f0601f573d81803e3d90fd5b"
// Proxy creation code after `PUSH32`
hex"3d5260203df3";
/// @notice Deploys a contract under a deterministic address.
/// @param amount The amount to pass into the deployed contract's constructor.
/// @param salt The salt to use. It must have never been used by this contract.
/// @param bytecode The creation code of the deployed contract.
/// @return addr The deployed contract's address.
function deploy(uint256 amount, bytes32 salt, bytes memory bytecode)
internal
returns (address addr)
{
address proxy;
(proxy, addr) = _computeAddress(salt, address(this));
require(!proxy.isContract(), "Salt already used");
Create2.deploy(0, salt, PROXY_CREATION_CODE);
proxy.functionCallWithValue(bytecode, amount, "Deployment failed");
}
/// @notice Computes the deterministic address of a contract deployed by this contract.
/// The deployed contract doesn't need to be deployed yet, it's a hypothetical address.
/// @param salt The salt used for deployment.
/// @return addr The deployed contract's address.
function computeAddress(bytes32 salt) internal view returns (address addr) {
return computeAddress(salt, address(this));
}
/// @notice Computes the deterministic address of a contract deployed by a deployer.
/// The contract doesn't need to be deployed yet, it's a hypothetical address.
/// @param salt The salt used for deployment.
/// @param deployer The address of the deployer of the proxy and the contract.
/// @return addr The deployed contract's address.
function computeAddress(bytes32 salt, address deployer) internal pure returns (address addr) {
(, addr) = _computeAddress(salt, deployer);
}
/// @notice Computes the deterministic address of a proxy and a contract deployed by a deployer.
/// The proxy and the contract don't need to be deployed yet, these are hypothetical addresses.
/// @param salt The salt used for deployment.
/// @param deployer The address of the deployer of the proxy and the contract.
/// @return proxy The proxy's address.
/// @return addr The deployed contract's address.
function _computeAddress(bytes32 salt, address deployer)
private
pure
returns (address proxy, address addr)
{
proxy = Create2.computeAddress(salt, keccak256(PROXY_CREATION_CODE), deployer);
addr = address(uint160(uint256(keccak256(abi.encodePacked(hex"d694", proxy, hex"01")))));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment