Created
June 21, 2023 07:15
-
-
Save CodeSandwich/3022c63e3660a8f1b2dee2a235687a88 to your computer and use it in GitHub Desktop.
CREATE3 EOA
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// @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