Contract Name:
UniversalFactory
Contract Source Code:
// SPDX-License-Identifier: MIT
/*
* Universal Factory Contract
* This standard defines an universal factory smart contract where any address (contract or regular account) can
* deploy and reserve deterministic contract addresses in any network.
*
* Written in 2024 by Lohann Paterno Coutinho Ferreira <[email protected]>.
*
* Universal Factory is derived from EIP-2470 and EIP-3171, with an additional feature that allows the contract
* constructor to read arguments without including it in the bytecode, this way custom arguments can be provided
* and immutables can be set without influencing the resulting `CREATE2` address.
* - EIP-2470: https://eips.ethereum.org/EIPS/eip-2470
* - EIP-3171: https://github.com/ethereum/EIPs/pull/3171
*
* This contract is intented to be deployed at the same address on all networks using keyless deployment method.
* - Keyless Deployment Method: https://weka.medium.com/how-to-send-ether-to-11-440-people-187e332566b7
*
* ██╗ ██╗███╗ ██╗██╗██╗ ██╗███████╗██████╗ ███████╗ █████╗ ██╗
* ██║ ██║████╗ ██║██║██║ ██║██╔════╝██╔══██╗██╔════╝██╔══██╗██║
* ██║ ██║██╔██╗ ██║██║██║ ██║█████╗ ██████╔╝███████╗███████║██║
* ██║ ██║██║╚██╗██║██║╚██╗ ██╔╝██╔══╝ ██╔══██╗╚════██║██╔══██║██║
* ╚██████╔╝██║ ╚████║██║ ╚████╔╝ ███████╗██║ ██║███████║██║ ██║███████╗
* ╚═════╝ ╚═╝ ╚═══╝╚═╝ ╚═══╝ ╚══════╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚══════╝
* ███████╗ █████╗ ██████╗████████╗ ██████╗ ██████╗ ██╗ ██╗
* ██╔════╝██╔══██╗██╔════╝╚══██╔══╝██╔═══██╗██╔══██╗╚██╗ ██╔╝
* █████╗ ███████║██║ ██║ ██║ ██║██████╔╝ ╚████╔╝
* ██╔══╝ ██╔══██║██║ ██║ ██║ ██║██╔══██╗ ╚██╔╝
* ██║ ██║ ██║╚██████╗ ██║ ╚██████╔╝██║ ██║ ██║
* ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝
*
*/
pragma solidity ^0.8.0;
/**
* @dev The type of create operation being used by the current context.
*/
enum CreateKind {
CREATE2,
CREATE3
}
/**
* @dev Contract Creation Context, this struct is used to provide useful information
* to the contract constructor, such as the sender, value, call depth, etc. Without affecting
* the final address of the contract.
*/
struct Context {
address contractAddress;
address sender;
uint8 callDepth;
CreateKind kind;
bool hasCallback;
bytes4 callbackSelector;
uint256 value;
bytes32 salt;
bytes data;
}
/**
* @dev The interface exposed by the Universal Factory Contract.
*/
interface IUniversalFactory {
/**
* @dev The create2 `creationCode` reverted.
*/
error Create2Failed();
/**
* @dev The create3 `creationCode` reverted.
* obs: Called by `Create3Proxy` using `CREATE` OPCODE.
*/
error Create3Failed();
/**
* @dev The `callback` reverted, this error wraps the revert reason returned by the callback.
*/
error CallbackFailed(bytes);
/**
* @dev The deterministic address already exists.
*/
error ContractAlreadyExists(address);
/**
* @dev The provided `initCode` is reserved for internal use only, try to use `create3` instead.
*/
error ReservedInitCode();
/**
* @dev Maximum call stack of 127 exceeded.
* OBS: probably impossible to reach this limit, due EIP-150 `all but one 64th`.
*/
error CallStackOverflow();
/**
* @dev Emitted when a contract is succesfully created, this is the only event emitted by the
* universal factory.
*/
event ContractCreated(
address indexed contractAddress,
bytes32 indexed creationCodeHash,
bytes32 indexed salt,
address indexed sender,
bytes32 argumentsHash,
bytes32 codeHash,
bytes32 callbackHash,
uint8 depth,
uint256 value
) anonymous;
/**
* @dev Creates an contract at a deterministic address, the final address is derived from the
* `salt` and `creationCode`, and is computed as follow:
* ```solidity
* return address(uint160(uint256(keccak256(abi.encodePacked(uint8(0xff), address(factory), uint256(salt), keccak256(creationCode))))));
* ```
* The contract constructor can access the actual sender and other information by calling `context()`.
*
* @param salt Salt of the contract creation, this value affect the resulting address.
* @param creationCode Creation code (constructor) of the contract to be deployed, this value affect the resulting address.
* @return address of the created contract.
*/
function create2(bytes32 salt, bytes calldata creationCode) external payable returns (address);
/**
* @dev Same as `create2(uint256,bytes)`, but also includes `arguments` which will be available at `context.data`.
*
* @param salt Salt of the contract creation, this value affect the resulting address.
* @param creationCode Creation code (constructor) of the contract to be deployed, this value affect the resulting address.
* @param arguments data that will be available at `Context.data`, this field doesn't affect the resulting address.
* @return address of the created contract.
*/
function create2(bytes32 salt, bytes calldata creationCode, bytes calldata arguments)
external
payable
returns (address);
/**
* @dev Same as `create2(uint256,bytes,bytes)`, but also includes a callback used to call the contract after it is created.
* @notice The `context.hasCallback` is always set to `true`, this method ALWAYS calls the callback, even if it is empty.
* IMPORTANT 1: Throws an `CallbackFailed` error if the callback reverts.
* IMPORTANT 2: Funds sent to this method will be forwarded to the `callback`, not the contract constructor.
*
* @param salt Salt of the contract creation, this value affect the resulting address.
* @param creationCode Creation code (constructor) of the contract to be deployed, this value affect the resulting address.
* @param arguments data that will be available at `Context.data`, this field doesn't affect the resulting address.
* @param callback callback called after create the contract, this field doesn't affect the resulting address.
* @return address of the created contract.
*/
function create2(bytes32 salt, bytes calldata creationCode, bytes calldata arguments, bytes calldata callback)
external
payable
returns (address);
/**
* Creates an contract at a deterministic address, the final address is derived exclusively from the `salt` field:
* ```solidity
* salt = keccak256(abi.encodePacked(msg.sender, salt));
* bytes32 proxyHash = 0x0281a97663cf81306691f0800b13a91c4d335e1d772539f127389adae654ffc6;
* address proxy = address(uint160(uint256(keccak256(abi.encodePacked(uint8(0xff), address(factory), uint256(salt), proxyHash)))));
* return address(uint160(uint256(keccak256(abi.encodePacked(uint16(0xd694), proxy, uint8(1))))));
* ```
* The contract constructor can access the actual sender and other informations by calling `context()`.
*
* @param salt Salt of the contract creation, resulting address will be derivated from this value only.
* @param creationCode Creation code (constructor) of the contract to be deployed, this value doesn't affect the resulting address.
* @return address of the created contract.
*/
function create3(bytes32 salt, bytes calldata creationCode) external payable returns (address);
/**
* @dev Same as `create3(uint256,bytes)`, but also includes `arguments` which will be available at `context.data`.
*
* @param salt Salt of the contract creation, this value affect the resulting address.
* @param creationCode Creation code (constructor) of the contract to be deployed, this value doesn't affect the resulting address.
* @param arguments data that will be available at `Context.data`, this field doesn't affect the resulting address.
* @return address of the created contract.
*/
function create3(bytes32 salt, bytes calldata creationCode, bytes calldata arguments)
external
payable
returns (address);
/**
* @dev Same as `create3(uint256,bytes,bytes)`, but also includes a callback used to call the contract after it is created.
* @notice The `context.hasCallback` is always set to `true`, this method ALWAYS calls the callback, even if it is empty.
* IMPORTANT 1: Throws an `CallbackFailed` error if the callback reverts.
* IMPORTANT 2: Funds sent to this method will be forwarded to the `callback`, not the contract constructor.
*
* @param salt Salt of the contract creation, this value affect the resulting address.
* @param creationCode Creation code (constructor) of the contract to be deployed, this value doesn't affect the resulting address.
* @param arguments data that will be available at `Context.data`, this field doesn't affect the resulting address.
* @param callback callback called after create the contract, this field doesn't affect the resulting address.
* @return address of the created contract.
*/
function create3(bytes32 salt, bytes calldata creationCode, bytes calldata arguments, bytes calldata callback)
external
payable
returns (address);
/**
* @dev Current call context, returns zero for all fields if there's no context.
* This function provides useful information that can be accessed inside the contract constructor.
* Examples:
* - `Context.contractAddress` the address of the contract being created.
* - `Context.sender` actual `msg.sender` who called the UniversalFactory.
* - `Context.data` to send arguments to the contract constructor without change the resulting address.
* - `Context.hasCallback` whether a callback was provided or not.
* - `Context.callbackSelector` first 4 bytes of the callback calldata (zero if no callback is provided).
* - `Context.callDepth` current call depth, incremented when nested contracts are created.
* - `Context.salt` the salt used to derive this contract address.
* - `Context.kind` whether `CREATE2` or `CREATE3` is used.
* - `Context.value` the value provided, this minimal value between `msg.value` and `address(this).balance` due EVM compatibility.
* @return Context current call text, or zero for all values if there's no context.
*/
function context() external view returns (Context memory);
}
/**
* @title Universal Factory Contract
* @author Lohann Paterno Coutinho Ferreira
* @notice This contract is a factory that allows you to deploy contracts at deterministic addresses in any network.
*
* # How it works
* To pass arbitrary arguments to the contract without influecing resulting address, this contract caches the arguments
* locally, and provide it to the contract constructor when it calls the `context()` function.
* - For `cancun` it caches the arguments using the EIP-1153 Transient Storage (~100 gas per word + overhead).
* - For `shanghai` it caches the arguments using the regular storage (~2900 gas per word + overhead).
*
* # Predictable Gas Cost
* This contract uses many different Branchless Code techniques (most of them develop by the author), so this contract have an very
* predictable gas overhead in any network (the actual overhead may change depending on the EVM implementation).
* - For `shanghai` evms, to guarantee an predictable gas cost, it make sure all values stored are different than zero.
* This is accomplished by hashing the arguments, and use the resulting hash to XOR the bytes before store and after read it.
* - For `cancun` there's no diffence between storing zero or non-zero values, so the `XOR` step is skipped.
*/
contract UniversalFactory {
/**
* @notice The Constructor is payable due Frontier EVM compatibility, once that EVM have the concept
* of existential deposit (ED), in this evm if you send a balance to a contract without ED, then the
* ED will be discounted from the contract balance, as result `address(this).balance < msg.value`,
* which is impossible in standard EVM's clients.
* - https://github.com/polkadot-evm/frontier/blob/polkadot-v1.11.0/ts-tests/tests/test-balance.ts#L41
*
* This contract works correctly in Frontier and standard EVM's, because it forwards the minimum value
* between `address(this).balance` and `msg.value` to the created contract.
*/
constructor() payable {
assembly {
// Context Storage Layout
// | 32-bit | 22-bit | 32-bit | 160-bit | 7-bit | 3-bit |
// +-+-+-+-+-+-+-+-+-+-+-+--+-+-+-+-+-+-+-+-+-+-+-++-+-+-+-+-+-+-+-+
// | args | args.len | selector | contract | depth | flags | offset: 0
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | args[..12] (96-bit) | sender (160-bit) | offset: 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | salt | offset: 2
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | value | offset: 3
// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
// | keccak256(args)* | offset: 2**64 * depth
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | args[16..] | offset: 2**64 * depth + 1
// | ... | length: (args.length + 15) / 32
// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
//
// - For `cancun`, the context is stored in `transient storage` using the `TSTORE` and `TLOAD` opcodes.
// - For `shanghai`, the context is stored in `regular storage` using the `SSTORE` and `SLOAD` opcodes.
//
// If the EVM doesn't support EIP-1153 TRANSIENT STORAGE, then it initialize the storage,
// because the transition from empty to non-empty cost more gas than the transition
// from non-empty to any, example:
// - zero to non-zero 20000 gas
// - non-zero to non-zero: 2900 gas
// - non-zero to zero: 100 gas
//
// The original values are restored at the end of the execution, except for `args`, which don't need to be restored
// once they are stored in an unique offset per call depth.
//
// # Motivation behind `keccak256(args)`.
// Different than `EIP-1153` transient storage, the regular storage is persisted between transactions, so to
// guarantee an predictable gas cost, and prevent the previous calls to increase the gas cost of future calls, is
// necessary to guarantee that all values stored are different than zero. The way we enforce this is by computing
// the keccak256 of `args`, and use it to XOR the bytes before store and after read it, so is infeseable
// to create an `argument` that can have 256 consecutive zeros when stored.
let placeholder := not(0)
sstore(0, placeholder)
sstore(1, placeholder)
sstore(2, placeholder)
sstore(3, placeholder)
// Notice is possible to send funds to this contract address before it is deployed. To guarantee this contract
// doesn't hold any funds, we sent the whole balance to an `GiveAway` contract, which is deployed below.
// GiveAway contract bytecode
// 0x34600c573d3d3d3d47335af15b00:
// 0x00 0x34 CALLVALUE val
// 0x01 0x600c PUSH1 0x0c 12 val
// ,=<0x03 0x57 JUMPI
// | 0x04 0x3d RETURNDATASIZE 0
// | 0x05 0x3d RETURNDATASIZE 0 0
// | 0x06 0x3d RETURNDATASIZE 0 0 0
// | 0x07 0x3d RETURNDATASIZE 0 0 0 0
// | 0x08 0x47 SELFBALANCE balance 0 0 0 0
// | 0x09 0x33 CALLER addr balance 0 0 0 0
// | 0x0a 0x5a GAS gas addr balance 0 0 0 0
// | 0x0b 0xf1 CALL suc
// `=>0x0c 0x5b JUMPDEST suc
// 0x0d 0x00 STOP
mstore(0x00, 0x6d34600c573d3d3d3d47335af15b003d52600e6012f3)
pop(create(selfbalance(), 0x0a, 22))
}
}
/**
* @dev This contract must be compatible with `shanghai` and `cancun` hardforks, the `EIP-1153` opcodes must
* only be used when the EVM supports `cancun`, to accomplish that, this contract calls itself and check the
* `TLOAD` opcode support, this check is done dynamically in case the EVM supports it in the future.
*
* @notice inline assembly is used for 3 main reason:
* 1. Guarantee the opcode `MCOPY` isn't called, once it is only supported by `cancun`.
* 2. Guarantee an predictable gas cost, this contract uses various branchless code techniques develop by the author.
* 3. Optimized code, to reduce the gas cost of the contract.
*
* `Solidity` was chosen over pure `Yul` due portability and developer experience reasons, once currently you can't
* import Yul code in Solidity, also in most block explorers you can't verify/publish YUL code.
*/
fallback() external payable {
// ---------------- Static Memory Layout ---------------
// offset | description
// -------|---------------------------------------------
// 0x0000 | final contract address
// 0x0020 | `Create3Proxy` proxy contract address.
// 0x0040 | keccak256(creationCode)
// 0x0060 | keccak256(arguments)
// 0x0080 | keccak256(callback)
// 0x00a0 | current depth
// 0x00c0 | creationCode.offset
// 0x00e0 | creationCode.length
// 0x0100 | arguments.offset
// 0x0120 | arguments.length
// 0x0140 | callback.offset
// 0x0160 | callback.length
// 0x0180 | previous slot 0
// 0x01a0 | previous slot 1
// 0x01c0 | previous salt
// 0x01e0 | previous value
// 0x0200 | dynamic memory, ex: creationCode, arguments and callback.
//
// Obs: the memory layout above is ignored for revert messages.
assembly {
// If the contracts calls itself, then is for check whether EIP-1153
// transient storage is supported or not.
// This check is done dynamically for each call, because even if the EVM
// doesn't support this opcode right now, it may support it in the future.
if eq(caller(), address()) {
mstore(0x00, tload(0))
mstore(0x20, tload(1))
mstore(0x40, tload(2))
mstore(0x60, tload(3))
// obs: for debugging purposes, you can change this to `revert(0,0)`
// to disable EIP-1153 support.
return(0, 0x80)
}
// ------- BITFLAGS -------
// HAS_ARGUMENTS = 0x01
// HAS_CALLBACK = 0x02
// IS_CREATE3 = 0x04
// SUPPORT_EIP1153 = 0x08
// ------------------------
let bitflags
{
{
let selector := shr(0xe0, calldataload(0))
///////////////////////////////////////////////////////////////
// function context() external view returns (Context memory) //
///////////////////////////////////////////////////////////////
// This selector is checked first to reduce the gas overhead, once this is expected to
// be called more frequently.
if eq(selector, 0xd0496d6a) {
// No value can be sent once it is a view function. It also make sure the call has
// sufficient gas, to prevent false negatives when checking for EIP-1153 support.
if or(callvalue(), lt(gas(), 3000)) { revert(0, 0) }
//////////////////////////
// Load current context //
//////////////////////////
// First try to retrieve the context using EIP-1153 TRANSIENT STORAGE, the result
// is automatically stored in corresponding static memory.
// Obs: This call use all 2000 gas if the EVM doesn't support EIP-1153, and 472 gas
// if it supports. We provide an extra gas margin in case those opcodes change their
// gas cost in the future.
let support_eip1153 := staticcall(2000, address(), 0, 0, 0x0180, 0x80)
// if it doesn't support EIP-1153, then load the context from storage.
if iszero(support_eip1153) {
let slot0 := sload(0)
if xor(slot0, not(0)) {
mstore(0x0180, slot0)
mstore(0x01a0, sload(1))
// Once the `salt` zero is very common, we XOR it with the slot0 to reduce the likelihood
// of storing a zero value, otherwise using the salt zero ended up using more gas than
// using a non-zero salt, which is inconvenient but not an issue at all.
// Notice the previous salt is always restored at the end of the execution. So this value
// cannot influence any subsequent contract creation gas cost.
mstore(0x01c0, xor(sload(2), slot0))
// Only load the value if the `HAS_VALUE` flag is set.
// This flag is used to avoid storing a zero value in the storage, it also saves one storage
// write/read when no value is provided (saves ~2900 gas).
let has_value := and(slot0, 0x01)
if has_value { mstore(0x01e0, sload(3)) }
}
}
// Load context from static memory
let slot0 := mload(0x0180)
let slot1 := mload(0x01a0)
let salt := mload(0x01c0)
let value := mload(0x01e0)
// Decode `call_flags`.
// IS_CREATE3 := 0x04
// HAS_CALLBACK := 0x02
// HAS_VALUE := 0x01
let call_flags := and(slot0, 0x07)
let depth := and(shr(3, slot0), 0x7f)
let contract_addr := and(shr(10, slot0), 0xffffffffffffffffffffffffffffffffffffffff)
let callback_selector := shl(224, shr(170, slot0))
let args_len := and(shr(202, slot0), 0x3fffff)
let data := or(shl(160, shr(160, slot1)), shl(128, shr(224, slot0)))
let sender := and(slot1, 0xffffffffffffffffffffffffffffffffffffffff)
// discard `HAS_VALUE` flag
call_flags := shr(1, call_flags)
// Store `Context` in memory following Solidity ABI encoding
mstore(0x0000, 0x20) // offset
mstore(0x0020, contract_addr) // contract_address
mstore(0x0040, sender) // sender
mstore(0x0060, depth) // call depth
mstore(0x0080, shr(1, call_flags)) // kind (0 for CREATE2, 1 for CREATE3)
mstore(0x00a0, and(1, call_flags)) // hasCallback
mstore(0x00c0, callback_selector) // callbackSelector
mstore(0x00e0, value) // value
mstore(0x0100, salt) // salt
mstore(0x0120, 0x0120) // offset
mstore(0x0140, args_len) // data_len
mstore(0x0160, data) // arguments[..16]
// If the args.length > 16, then copy the rest of the arguments to memory.
switch support_eip1153
case 0 {
if gt(args_len, 16) {
// Copy `data[16..]` from storage to memory
for {
let end := add(0x0160, args_len)
let ptr := 0x170
let offset := shl(64, depth)
let args_hash := sload(offset)
offset := add(offset, 0x01)
} lt(ptr, end) {
ptr := add(ptr, 0x20)
offset := add(offset, 0x01)
} { mstore(ptr, xor(sload(offset), args_hash)) }
}
}
default {
// Copy `data[16..]` from transient storage to memory
for {
let end := add(0x0160, args_len)
let ptr := 0x170
let offset := shl(64, depth)
} lt(ptr, end) {
ptr := add(ptr, 0x20)
offset := add(offset, 0x01)
} { mstore(ptr, tload(offset)) }
}
// Remove any non-zero from padding bytes
mstore(add(0x0160, args_len), 0)
// Return an 32-byte aligned result
return(0x00, add(0x0160, and(add(args_len, 0x1f), 0xffffffffffffffe0)))
}
///////////////////////////
// Validate the selector //
///////////////////////////
// The 5 least significant bits of the selectors are unique, this allow an efficient selector
// verification using less than 100 gas.
// | FUNCTION | SELECTOR | suffix (5-bit) | index | bitflags |
// | create2(bytes32,bytes,bytes,bytes) | 0x8778391e | 0x8778391e & 0x1f == 30 | 26 | 011 |
// | create3(bytes32,bytes,bytes,bytes) | 0xac049de2 | 0xac049de2 & 0x1f == 2 | 27 | 111 |
// | create2(bytes32,bytes,bytes) | 0xce40d339 | 0xce40d339 & 0x1f == 25 | 28 | 001 |
// | create3(bytes32,bytes,bytes) | 0xd2a8169a | 0xd2a8169a & 0x1f == 26 | 29 | 101 |
// | create2(bytes32,bytes) | 0xb9aaf526 | 0xb9aaf526 & 0x1f == 6 | 30 | 000 |
// | create3(bytes32,bytes) | 0x2af25238 | 0x2af25238 & 0x1f == 24 | 31 | 100 |
// Convert the 5-bit suffix into an index using byte lookup.
let index
{
let suffix := and(selector, 0x1f)
index := byte(suffix, 0x00001b0000001e00000000000000000000000000000000001f1c1d0000001a00)
}
// Extract the selector at the expected index, where 0x2af25238b9aaf526d2a8169ace40d339ac049de28778391e
// is simply the selectors concatenated.
let expected_selector
{
let shift := byte(index, 0x20406080a0)
expected_selector := shr(shift, 0x2af25238b9aaf526d2a8169ace40d339ac049de28778391e)
expected_selector := and(expected_selector, 0xffffffff)
}
// Check if the `selector` matches the `expected_selector`
let valid := eq(selector, expected_selector)
// Validate the calldatasize against the minimal size
{
let min_calldata_size := byte(index, 0x838363634343)
valid := and(valid, gt(calldatasize(), min_calldata_size))
mstore(0x00, sub(min_calldata_size, 4))
}
// Revert if the selector is invalid
if iszero(valid) { revert(0, 0) }
///////////////////////////////
// Load the previous context //
///////////////////////////////
// First try to retrieve the context using EIP-1153 TRANSIENT STORAGE, the result is automatically
// stored in corresponding static memory.
// Obs: This call use all 2000 gas if the EVM doesn't support EIP-1153, and 472 gas if it supports.
// We provide an extra gas margin in case those opcodes change their gas cost in the future.
let support_eip1153 := staticcall(2000, address(), 0, 0, 0x0180, 0x80)
// if it doesn't support EIP-1153, then load the previous context from storage.
if iszero(support_eip1153) {
let slot0 := sload(0)
if xor(slot0, not(0)) {
mstore(0x0180, slot0)
mstore(0x01a0, sload(1))
mstore(0x01c0, sload(2))
// Only load the value if the `HAS_VALUE` flag is set.
// This flag is used to avoid storing a non-zero value in the storage.
let has_value := and(slot0, 0x01)
if has_value { mstore(0x01e0, sload(3)) }
}
}
// Set the bitflags using byte lookup.
// HAS_ARGUMENTS = 0x01
// HAS_CALLBACK = 0x02
// IS_CREATE3 = 0x04
// SUPPORT_EIP1153 = 0x08
bitflags := or(shl(3, support_eip1153), byte(index, 0x030701050004))
}
let valid
/////////////////////////////
// Validate `creationCode` //
/////////////////////////////
let min_calldata_size := mload(0x00)
{
// creationcode_ptr <= 0xffffffffffffffff
let creationcode_ptr := calldataload(0x24)
valid := lt(creationcode_ptr, 0x010000000000000000)
// creationcode_ptr > min_calldata_size
valid := and(valid, gt(creationcode_ptr, min_calldata_size))
// calldatasize > (creationcode_ptr + 0x1f)
creationcode_ptr := add(creationcode_ptr, 0x04)
valid := and(valid, slt(add(creationcode_ptr, 0x1f), calldatasize()))
// creationcode_len <= 0xffffffffffffffff
let creationcode_len := calldataload(creationcode_ptr)
valid := and(valid, lt(creationcode_len, 0x010000000000000000))
creationcode_ptr := add(creationcode_ptr, 0x20)
// (creationcode_ptr + creationcode_len + 0x20) <= calldatasize
valid := and(valid, iszero(gt(add(creationcode_ptr, creationcode_len), calldatasize())))
// creationcode_len > 0
valid := and(valid, gt(creationcode_len, 0))
// store the `creationcode_ptr` and `creationcode_len` at static memory 0xc0-0xe0
mstore(0xc0, creationcode_ptr)
mstore(0xe0, creationcode_len)
}
//////////////////////////
// Validate `arguments` //
//////////////////////////
{
// args_ptr <= 0xffffffffffffffff
let args_ptr := calldataload(0x44)
let has_args := and(bitflags, 0x01)
let valid_args := and(has_args, lt(args_ptr, 0x010000000000000000))
// args_ptr > min_calldata_size
valid_args := and(valid_args, gt(args_ptr, min_calldata_size))
// calldatasize > (args_ptr + 0x1f)
args_ptr := add(args_ptr, 0x04)
valid_args := and(valid_args, slt(add(args_ptr, 0x1f), calldatasize()))
// args_len <= 0x3fffff
let args_len := calldataload(args_ptr)
valid_args := and(valid_args, lt(args_len, 0x400000))
args_ptr := add(args_ptr, 0x20)
// (args_ptr + args_len + 0x20) >= calldatasize
valid_args := and(valid_args, iszero(gt(add(args_ptr, args_len), calldatasize())))
// Set args_ptr and args_len to zero if there's no arguments
valid_args := and(valid_args, has_args)
args_ptr := mul(args_ptr, valid_args)
args_len := mul(args_len, valid_args)
// store the `args_ptr` and `args_len` at static memory 0x0100-0x0120
mstore(0x0100, args_ptr)
mstore(0x0120, args_len)
// If has no arguments, it is always valid.
valid := and(valid, or(valid_args, iszero(has_args)))
}
/////////////////////////
// Validate `callback` //
/////////////////////////
{
// callback_ptr <= 0xffffffffffffffff
let callback_ptr := calldataload(0x64)
let valid_callback := lt(callback_ptr, 0x010000000000000000)
// callback_ptr > 0x7f
valid_callback := and(valid_callback, gt(callback_ptr, 0x7f))
// calldatasize > (callback_ptr + 0x1f)
callback_ptr := add(callback_ptr, 0x04)
valid_callback := and(valid_callback, slt(add(callback_ptr, 0x1f), calldatasize()))
// callback_len <= 0xffffffffffffffff
let callback_len := calldataload(callback_ptr)
valid_callback := and(valid_callback, lt(callback_len, 0x010000000000000000))
callback_ptr := add(callback_ptr, 0x20)
// (callback_ptr + callback_len + 0x20) >= calldatasize
valid_callback := and(valid_callback, iszero(gt(add(callback_ptr, callback_len), calldatasize())))
// Set callback_ptr and callback_len to zero if there's no callback
let has_callback := and(shr(1, bitflags), 1)
valid_callback := and(valid_callback, has_callback)
callback_ptr := mul(callback_ptr, valid_callback)
callback_len := mul(callback_len, valid_callback)
// store the `callback_ptr` and `callback_len` at static memory 0x0140-0x0160
mstore(0x0140, callback_ptr)
mstore(0x0160, callback_len)
// If the call has no callback, it is always valid.
valid_callback := or(valid_callback, iszero(has_callback))
valid := and(valid, valid_callback)
}
if iszero(valid) { revert(0, 0) }
}
///////////////////////////////////////////////////
// Check if the maximum depth of 127 was reached //
///////////////////////////////////////////////////
{
// Decode the previous `depth`.
let slot0 := mload(0x0180)
let depth := and(shr(3, slot0), 0x7f)
// The `depth` must be less than 127, which is the maximum number of nested calls before overflow.
// obs: probably impossible to reach this limit, due EIP-150 `all but one 64th`.
// - reference: https://eips.ethereum.org/EIPS/eip-150
if gt(depth, 0x7e) {
// revert CallStackOverflow()
mstore(0x00, 0x41f739de)
revert(0x1c, 0x04)
}
mstore(0xa0, add(depth, 1))
}
// Workaround for Frontier EVM chains, where `address(this).balance` can be less than `msg.value` if
// this contract has no previous existential deposit.
// - https://github.com/polkadot-evm/frontier/blob/polkadot-v1.11.0/ts-tests/tests/test-balance.ts#L41
//
// The following code is a branchless version of the ternary operator, equivalent to:
// ```solidity
// uint256 value = address(this).balance < msg.value ? address(this).balance : msg.value;
// ```
// also see: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/4976
let value := xor(callvalue(), mul(xor(selfbalance(), callvalue()), lt(selfbalance(), callvalue())))
////////////////////////////
// Compute Arguments Hash //
////////////////////////////
{
let arguments_ptr := mload(0x0100)
let arguments_len := mload(0x0120)
// Copy `arguments` to memory
calldatacopy(0x0200, arguments_ptr, arguments_len)
// Compute the keccak256 hash of the `arguments`
let arguments_hash := keccak256(0x0200, arguments_len)
// Set zero if there's no `arguments`.
arguments_hash := mul(arguments_hash, gt(arguments_ptr, 0))
// Save the `arguments_hash` in the static memory
mstore(0x60, arguments_hash)
}
{
let addr
{
// Copy `creationCode` to memory
let creationcode_len := mload(0xe0)
{
let creationcode_ptr := mload(0xc0)
calldatacopy(0x0200, creationcode_ptr, creationcode_len)
}
////////////////////////////////////////////////////////////////////
// Compute CREATE2 address //
////////////////////////////////////////////////////////////////////
addr := or(address(), 0xff0000000000000000000000000000000000000000)
mstore(0x00, addr)
mstore(0x20, calldataload(0x04))
let creationcode_hash := keccak256(0x0200, creationcode_len)
mstore(0x40, creationcode_hash)
let create2_addr := and(keccak256(11, 85), 0xffffffffffffffffffffffffffffffffffffffff)
////////////////////////////////////////////////////////////////////
// Compute CREATE3 address //
////////////////////////////////////////////////////////////////////
// Compute `CREATE3` salt, which is `keccak25(abi.encodePacked(msg.sender, salt))`
mstore(0x00, caller())
mstore(0x20, calldataload(0x04))
mstore(0x20, keccak256(12, 52))
// Compute `CREATE3` proxy address, which is `keccak256(abi.encodePacked(0xff, address(this), create3salt, proxyHash))`
mstore(0x00, addr)
let proxy_hash := 0x0281a97663cf81306691f0800b13a91c4d335e1d772539f127389adae654ffc6
mstore(0x40, proxy_hash)
let proxy_addr := and(keccak256(11, 85), 0xffffffffffffffffffffffffffffffffffffffff)
// Compute `CREATE3` contract address, which is `keccak256(abi.encodePacked(hex"d694", proxyAddr, hex"01"))`
mstore(0x00, or(0xd694000000000000000000000000000000000000000001, shl(8, proxy_addr)))
let create3_addr := and(keccak256(0x09, 23), 0xffffffffffffffffffffffffffffffffffffffff)
//////////////////////
// Validate //
//////////////////////
// Validate if the contract exists and if the `creationCode` is not the `Create3Proxy` contract.
//
// IMPORTANT: The `Create3Proxy` creationCode CANNOT be used in `create2(...)` functions, otherwise
// anyone can deploy an contract in a address that belongs to another `msg.sender`.
// If someone attempt it, this contract reverts with `ReservedInitCode` error.
{
let is_create3 := and(shr(2, bitflags), 1)
addr := xor(create2_addr, mul(xor(create3_addr, create2_addr), is_create3))
// The proxy creation code is reserved only for `create3` methods
let invalid_init_code := and(eq(creationcode_hash, proxy_hash), iszero(is_create3))
// The contract must not exist
let contract_exists := extcodesize(addr)
if or(contract_exists, invalid_init_code) {
// 0xb8bcb0c9 == bytes4(keccak256("ReservedInitCode()"))
// 0xc5644373 == bytes4(keccak256("ContractAlreadyExists(address)"))
// 0x7dd8f3ba == 0xb8bcb0c9 ^ 0xc5644373
let sig := xor(0xb8bcb0c9, mul(0x7dd8f3ba, invalid_init_code))
let len := add(0x04, shl(5, iszero(invalid_init_code)))
mstore(0x00, sig)
mstore(0x20, addr)
revert(0x1c, len)
}
}
// Store final contract address, proxy address and creationCode in
// their respective static memory slots.
mstore(0x00, addr)
mstore(0x20, proxy_addr)
mstore(0x40, creationcode_hash)
}
////////////////////////////////////////////////////////////////////
// UPDATE CONTEXT //
////////////////////////////////////////////////////////////////////
// Context Storage Layout
// | 32-bit | 22-bit | 32-bit | 160-bit | 7-bit | 3-bit |
// +-+-+-+-+-+-+-+-+-+-+-+--+-+-+-+-+-+-+-+-+-+-+-++-+-+-+-+-+-+-+-+
// | args | args len | selector | contract | depth | flags | offset: 0
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | args[..12] (96-bit) | sender (160-bit) | offset: 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | salt | offset: 2
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | value | offset: 3
// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
// | keccak256(args)* | offset: 2**64 * depth
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | args[16..] | offset: 2**64 * depth + 1
// | ... | length: (args.length + 15) / 32
// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
let arguments_ptr := mload(0x0100)
let arguments_len := mload(0x0120)
let arguments_data := mul(calldataload(arguments_ptr), gt(arguments_ptr, 0))
let slot0
// Encode `args[12..16]` (32 bits)
{
let has_args := and(bitflags, 0x01)
let args := shr(224, shl(96, arguments_data))
args := mul(args, has_args)
slot0 := args
}
// Encode args_len (22 bits)
// Obs: validated previously, so is always less than 2**22
slot0 := or(shl(22, slot0), arguments_len)
// Encode selector (32 bits)
{
let callback_ptr := mload(0x0140)
let callback_len := mload(0x0160)
let callback_selector := shr(224, calldataload(callback_ptr))
callback_selector := mul(callback_selector, gt(callback_len, 0))
slot0 := or(shl(32, slot0), callback_selector)
}
// Encode contractAddress (160 bits)
slot0 := or(shl(160, slot0), addr)
// Encode depth (7 bits)
let depth := mload(0xa0)
slot0 := or(shl(7, slot0), depth)
// Encode bitflags (3 bits)
slot0 := or(shl(3, slot0), or(and(bitflags, 0x06), gt(value, 0)))
// Encode `args[..12]` (96 bit) + sender (160 bit)
let slot1 := or(shl(160, shr(160, arguments_data)), caller())
// Encode salt (256 bits)
let salt := calldataload(0x04)
// Store the new Context in the transient storage or storage.
let support_eip1153 := and(bitflags, 0x08)
switch support_eip1153
case 0 {
// Store the context in the storage, skip `value` if zero.
sstore(0, slot0)
sstore(1, slot1)
// Once the `salt` zero is very common, we XOR it with the slot0 to reduce the likelihood
// of storing a zero value, otherwise using the salt zero ended up using more gas than
// using a non-zero salt, which is inconvenient but not an issue at all.
// Notice the previous salt is always restored at the end of the execution. So this value
// cannot influence any subsequent contract creation gas cost.
sstore(2, xor(salt, slot0))
// When `msg.value > 0`, then the first bit of `flags` is set, so no need to store this value (saves ~2900 gas).
if value { sstore(3, value) }
if gt(arguments_len, 16) {
// When `arguments.length > 16`, we also store the argument hash in the context.
let arguments_hash := mload(0x60)
/// If `args.length > 16`, then store the remaining bytes in the transient storage,
// starting at index `2**64 * depth`.
for {
let end := add(arguments_ptr, arguments_len)
let ptr := add(arguments_ptr, 16)
let offset := shl(64, depth)
sstore(offset, arguments_hash)
offset := add(offset, 0x01)
} lt(ptr, end) {
ptr := add(ptr, 0x20)
offset := add(offset, 0x01)
} { sstore(offset, xor(calldataload(ptr), arguments_hash)) }
}
}
default {
// Store the context in the EIP-1153 Transient Storage.
// obs: don't need to skip value once the gas cost is negligible (~100 gas).
tstore(0, slot0)
tstore(1, slot1)
tstore(2, salt)
tstore(3, value)
// If `data.length > 16`, then store the remaining bytes in the transient storage,
// starting at index `2**64 * depth`.
for {
let end := add(arguments_ptr, arguments_len)
let ptr := add(arguments_ptr, 16)
let offset := shl(64, depth)
} lt(ptr, end) {
ptr := add(ptr, 0x20)
offset := add(offset, 0x01)
} { tstore(offset, calldataload(ptr)) }
}
}
// Create contract using `create2` or `create3`
switch and(bitflags, 0x04)
case 0 {
/////////////////
// CREATE2 //
/////////////////
// Deploy contract or Proxy, depending if `is_create3` is enabled.
let creationcode_len := mload(0xe0)
let contract_addr :=
create2(mul(value, iszero(and(bitflags, 0x06))), 0x0200, creationcode_len, calldataload(0x04))
// Computed address and actual address must match
if or(iszero(contract_addr), xor(contract_addr, mload(0))) {
// 0x04a5b3ee -> Create2Failed()
mstore(0x00, 0x04a5b3ee)
revert(0x1c, 0x04)
}
}
default {
/////////////////
// CREATE3 //
/////////////////
// Create3Proxy creation code
// 0x763318602e57363d3d37363d47f080915215602e57f35bfd602b52336014526460203d3d733d526030601bf3:
// 0x00 0x763318602e.. PUSH23 0x3318.. 0x3318602e57363d3d37363d47f080915215602e57f35bfd
// 0x18 0x602b PUSH1 0x2b 43 0x3318602e57363d3d37363d47f080915215602e57f35bfd
// 0x1a 0x52 MSTORE
// 0x1b 0x33 CALLER addr
// 0x1c 0x6014 PUSH1 20 20 addr
// 0x1f 0x52 MSTORE
// 0x25 0x6460203d3d73 PUSH5 0x6020.. 0x60203d3d73
// 0x26 0x3d RETURNDATASIZE 0 0x60203d3d73
// 0x27 0x52 MSTORE
// 0x29 0x6030 PUSH1 48 48
// 0x2a 0x601b PUSH1 27 27 48
// 0x2b 0xf3 RETURN
// Create3Proxy runtime code, where `XXXX..` is the Universal Factory contract address.
// 0x60203d3d73XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX3318602e57363d3d37363d47f080915215602e57f35bfd
// 0x00 0x6020 PUSH1 32 32
// 0x02 0x3d RETURNDATASIZE 0 32
// 0x03 0x3d RETURNDATASIZE 0 0 32
// 0x04 0x74XXXXXX.. PUSH20 XXXXXXX factory 0 0 32
// 0x19 0x33 CALLER caller factory 0 0 32
// 0x1a 0x18 XOR invalid 0 0 32
// 0x1b 0x602e PUSH1 0x2e 46 invalid 0 0 32
// ,=< 0x1d 0x57 JUMPI 0 0 32
// | 0x1e 0x36 CALLDATASIZE cls 0 0 32
// | 0x1f 0x3d RETURNDATASIZE 0 cls 0 0 32
// | 0x20 0x3d RETURNDATASIZE 0 0 cls 0 0 32
// | 0x21 0x37 CALLDATACOPY 0 0 32
// | 0x22 0x36 CALLDATASIZE cls 0 0 32
// | 0x23 0x3d RETURNDATASIZE 0 cls 0 0 32
// | 0x24 0x47 SELFBALANCE val 0 cls 0 0 32
// | 0x25 0xf0 CREATE addr 0 0 32
// | 0x26 0x80 DUP1 addr addr 0 0 32
// | 0x27 0x91 SWAP2 0 addr addr 0 32
// | 0x28 0x52 MSTORE addr 0 32
// | 0x29 0x16 ISZERO fail 0 32
// | 0x2a 0x602e PUSH1 0x2e 46 fail 0 32
// |=< 0x2c 0x57 JUMPI 0 32
// | 0x2d 0xf3 RETURN
// `=> 0x2e 0x5b JUMPDEST
// 0x2f 0xfd REVERT
// Deploy the `Create3Proxy`
let proxy_addr
{
// Save the current memory state, to restore it after the proxy deployment.
let mem00 := mload(0x00)
let mem20 := mload(0x20)
{
// Compute `CREATE3` salt, which is `keccak25(abi.encodePacked(msg.sender, salt))`
mstore(0x00, caller())
mstore(0x20, calldataload(0x04))
let salt := keccak256(12, 52)
// Store `Create3Proxy` initcode in memory.
mstore(0x0c, 0x60203d3d733d526030601bf3)
mstore(0x00, 0x763318602e57363d3d37363d47f080915215602e57f35bfd602b523360145264)
proxy_addr := create2(0, 0x00, 44, salt)
}
// Restore the memory state
mstore(0x00, mem00)
mstore(0x20, mem20)
}
// return an error if failed to create `Create3Proxy`.
if iszero(and(eq(proxy_addr, mload(0x20)), eq(extcodesize(proxy_addr), 48))) {
// revert Create3Failed()
mstore(0x00, 0x08fde50a)
revert(0x1c, 0x04)
}
// Save the computed address in the stack
// once the `Create3Proxy` will override the 0x00 memory location.
let computed_addr := mload(0)
// Call the `Create3Proxy` to deploy the desired contract at deterministic address.
let creationcode_len := mload(0xe0)
// Only send funds if there's no callback
let no_callback := iszero(and(bitflags, 0x02))
// Deploy the contract using `Create3Proxy`
let success := call(gas(), proxy_addr, mul(value, no_callback), 0x0200, creationcode_len, 0x00, 0x20)
// Compare the computed address and actual address
if or(iszero(success), xor(computed_addr, mload(0))) {
// 0x08fde50a -> Create3Failed()
mstore(0x00, 0x08fde50a)
revert(0x1c, 0x04)
}
}
// Emit `ContractCreated` event and call `callback` if provided
{
let callback_ptr := mload(0x140)
let callback_len := mload(0x160)
{
// Compute `keccak256(callback)`
let callback_hash
// Copy `callback` to memory
calldatacopy(0x0200, callback_ptr, callback_len)
// Compute callback hash
callback_hash := keccak256(0x0200, callback_len)
// Set zero if there's no callback
callback_hash := mul(callback_hash, gt(callback_ptr, 0))
// emit ContractCreated(contractAddress, creationCodeHash, salt, dataHash, codeHash, callbackHash, depth, value)
let creation_code_hash := mload(0x40)
let contract_addr := mload(0)
let args_hash := mload(0x60)
let depth := mload(0xa0)
mstore(0x20, args_hash)
mstore(0x40, extcodehash(contract_addr))
mstore(0x60, callback_hash)
mstore(0x80, depth)
mstore(0xa0, value)
log4(0x20, 0xa0, contract_addr, creation_code_hash, calldataload(0x04), caller())
// Restore contract addr
mstore(0, contract_addr)
}
// Call `callback` if provided
if callback_ptr {
if iszero(call(gas(), mload(0), value, 0x0200, callback_len, 0, 0)) {
mstore(0x00, 0x30b9b6dd)
// error offset
mstore(0x20, 0x20)
// error length
mstore(0x40, returndatasize())
// cleanup padding bytes, in case it has non-zero bytes
mstore(add(0x60, returndatasize()), 0x00)
// Copy revert data to memory
returndatacopy(0x60, 0, returndatasize())
// revert(data + padding)
revert(0x1c, add(and(add(returndatasize(), 31), 0xffffffffffffffe0), 0x44))
}
}
}
// Restore previous ctx and salt
// Obs: the logic for restore the state is different for storage and transient storage,
// because for storage, the `zero to non-zero` transition use more gas than the `non-zero to non-zero`.
let prev_slot0 := mload(0x0180)
let prev_slot1 := mload(0x01a0)
let prev_salt := mload(0x01c0)
let prev_value := mload(0x01e0)
let support_eip1153 := and(bitflags, 0x08)
switch support_eip1153
case 0 {
let is_empty := iszero(prev_slot0)
let mask := sub(0, is_empty)
sstore(0, or(prev_slot0, mask))
sstore(1, or(prev_slot1, mask))
sstore(2, or(prev_salt, mask))
if value { sstore(3, or(prev_value, mask)) }
}
default {
tstore(0, prev_slot0)
tstore(1, prev_slot1)
tstore(2, prev_salt)
tstore(3, prev_value)
}
// return the created contract address
return(0, 0x20)
}
}
}