Source Code
| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
Latest 10 internal transactions
Advanced mode:
| Parent Transaction Hash | Block | From | To | |||
|---|---|---|---|---|---|---|
| 132745365 | 336 days ago | Contract Creation | 0 ETH | |||
| 132270793 | 347 days ago | Contract Creation | 0 ETH | |||
| 132270502 | 347 days ago | Contract Creation | 0 ETH | |||
| 132236544 | 348 days ago | Contract Creation | 0 ETH | |||
| 132193368 | 349 days ago | Contract Creation | 0 ETH | |||
| 132192050 | 349 days ago | Contract Creation | 0 ETH | |||
| 132181443 | 349 days ago | Contract Creation | 0 ETH | |||
| 129549378 | 410 days ago | Contract Creation | 0 ETH | |||
| 129513799 | 411 days ago | Contract Creation | 0 ETH | |||
| 129073578 | 421 days ago | Contract Creation | 0 ETH |
Cross-Chain Transactions
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
K1ValidatorFactory
Compiler Version
v0.8.27+commit.40a35a09
Optimization Enabled:
Yes with 999 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; // ────────────────────────────────────────────────────────────────────────────── // _ __ _ __ // / | / /__ | |/ /_ _______ // / |/ / _ \| / / / / ___/ // / /| / __/ / /_/ (__ ) // /_/ |_/\___/_/|_\__,_/____/ // // ────────────────────────────────────────────────────────────────────────────── // Nexus: A suite of contracts for Modular Smart Accounts compliant with ERC-7579 and ERC-4337, developed by Biconomy. // Learn more at https://biconomy.io. For security issues, contact: [email protected] import { LibClone } from "solady/utils/LibClone.sol"; import { INexus } from "../interfaces/INexus.sol"; import { BootstrapLib } from "../lib/BootstrapLib.sol"; import { NexusBootstrap, BootstrapConfig } from "../utils/NexusBootstrap.sol"; import { Stakeable } from "../common/Stakeable.sol"; import { IERC7484 } from "../interfaces/IERC7484.sol"; /// @title K1ValidatorFactory for Nexus Account /// @notice Manages the creation of Modular Smart Accounts compliant with ERC-7579 and ERC-4337 using a K1 validator. /// @author @livingrockrises | Biconomy | [email protected] /// @author @aboudjem | Biconomy | [email protected] /// @author @filmakarov | Biconomy | [email protected] /// @author @zeroknots | Rhinestone.wtf | zeroknots.eth /// Special thanks to the Solady team for foundational contributions: https://github.com/Vectorized/solady contract K1ValidatorFactory is Stakeable { /// @notice Stores the implementation contract address used to create new Nexus instances. /// @dev This address is set once upon deployment and cannot be changed afterwards. address public immutable ACCOUNT_IMPLEMENTATION; /// @notice Stores the K1 Validator module address. /// @dev This address is set once upon deployment and cannot be changed afterwards. address public immutable K1_VALIDATOR; /// @notice Stores the Bootstrapper module address. /// @dev This address is set once upon deployment and cannot be changed afterwards. NexusBootstrap public immutable BOOTSTRAPPER; IERC7484 public immutable REGISTRY; /// @notice Emitted when a new Smart Account is created, capturing the account details and associated module configurations. event AccountCreated(address indexed account, address indexed owner, uint256 indexed index); /// @notice Error thrown when a zero address is provided for the implementation, K1 validator, or bootstrapper. error ZeroAddressNotAllowed(); /// @notice Error thrown when an inner call fails. error InnerCallFailed(); /// @notice Constructor to set the immutable variables. /// @param implementation The address of the Nexus implementation to be used for all deployments. /// @param factoryOwner The address of the factory owner. /// @param k1Validator The address of the K1 Validator module to be used for all deployments. /// @param bootstrapper The address of the Bootstrapper module to be used for all deployments. constructor( address implementation, address factoryOwner, address k1Validator, NexusBootstrap bootstrapper, IERC7484 registry ) Stakeable(factoryOwner) { require( !(implementation == address(0) || k1Validator == address(0) || address(bootstrapper) == address(0) || factoryOwner == address(0)), ZeroAddressNotAllowed() ); ACCOUNT_IMPLEMENTATION = implementation; K1_VALIDATOR = k1Validator; BOOTSTRAPPER = bootstrapper; REGISTRY = registry; } /// @notice Creates a new Nexus with a specific validator and initialization data. /// @param eoaOwner The address of the EOA owner of the Nexus. /// @param index The index of the Nexus. /// @param attesters The list of attesters for the Nexus. /// @param threshold The threshold for the Nexus. /// @return The address of the newly created Nexus. function createAccount( address eoaOwner, uint256 index, address[] calldata attesters, uint8 threshold ) external payable returns (address payable) { // Compute the actual salt for deterministic deployment bytes32 actualSalt = keccak256(abi.encodePacked(eoaOwner, index, attesters, threshold)); // Deploy the Nexus contract using the computed salt (bool alreadyDeployed, address account) = LibClone.createDeterministicERC1967(msg.value, ACCOUNT_IMPLEMENTATION, actualSalt); // Create the validator configuration using the NexusBootstrap library BootstrapConfig memory validator = BootstrapLib.createSingleConfig(K1_VALIDATOR, abi.encodePacked(eoaOwner)); bytes memory initData = BOOTSTRAPPER.getInitNexusWithSingleValidatorCalldata(validator, REGISTRY, attesters, threshold); // Initialize the account if it was not already deployed if (!alreadyDeployed) { INexus(account).initializeAccount(initData); emit AccountCreated(account, eoaOwner, index); } return payable(account); } /// @notice Computes the expected address of a Nexus contract using the factory's deterministic deployment algorithm. /// @param eoaOwner The address of the EOA owner of the Nexus. /// @param index The index of the Nexus. /// @param attesters The list of attesters for the Nexus. /// @param threshold The threshold for the Nexus. /// @return expectedAddress The expected address at which the Nexus contract will be deployed if the provided parameters are used. function computeAccountAddress( address eoaOwner, uint256 index, address[] calldata attesters, uint8 threshold ) external view returns (address payable expectedAddress) { // Compute the actual salt for deterministic deployment bytes32 actualSalt = keccak256(abi.encodePacked(eoaOwner, index, attesters, threshold)); // Predict the deterministic address using the LibClone library expectedAddress = payable(LibClone.predictDeterministicAddressERC1967(ACCOUNT_IMPLEMENTATION, actualSalt, address(this))); } }
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Minimal proxy library.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibClone.sol)
/// @author Minimal proxy by 0age (https://github.com/0age)
/// @author Clones with immutable args by wighawag, zefram.eth, Saw-mon & Natalie
/// (https://github.com/Saw-mon-and-Natalie/clones-with-immutable-args)
/// @author Minimal ERC1967 proxy by jtriley-eth (https://github.com/jtriley-eth/minimum-viable-proxy)
///
/// @dev Minimal proxy:
/// Although the sw0nt pattern saves 5 gas over the ERC1167 pattern during runtime,
/// it is not supported out-of-the-box on Etherscan. Hence, we choose to use the 0age pattern,
/// which saves 4 gas over the ERC1167 pattern during runtime, and has the smallest bytecode.
/// - Automatically verified on Etherscan.
///
/// @dev Minimal proxy (PUSH0 variant):
/// This is a new minimal proxy that uses the PUSH0 opcode introduced during Shanghai.
/// It is optimized first for minimal runtime gas, then for minimal bytecode.
/// The PUSH0 clone functions are intentionally postfixed with a jarring "_PUSH0" as
/// many EVM chains may not support the PUSH0 opcode in the early months after Shanghai.
/// Please use with caution.
/// - Automatically verified on Etherscan.
///
/// @dev Clones with immutable args (CWIA):
/// The implementation of CWIA here is does NOT append the immutable args into the calldata
/// passed into delegatecall. It is simply an ERC1167 minimal proxy with the immutable arguments
/// appended to the back of the runtime bytecode.
/// - Uses the identity precompile (0x4) to copy args during deployment.
///
/// @dev Minimal ERC1967 proxy:
/// An minimal ERC1967 proxy, intended to be upgraded with UUPS.
/// This is NOT the same as ERC1967Factory's transparent proxy, which includes admin logic.
/// - Automatically verified on Etherscan.
///
/// @dev Minimal ERC1967 proxy with immutable args:
/// - Uses the identity precompile (0x4) to copy args during deployment.
/// - Automatically verified on Etherscan.
///
/// @dev ERC1967I proxy:
/// An variant of the minimal ERC1967 proxy, with a special code path that activates
/// if `calldatasize() == 1`. This code path skips the delegatecall and directly returns the
/// `implementation` address. The returned implementation is guaranteed to be valid if the
/// keccak256 of the proxy's code is equal to `ERC1967I_CODE_HASH`.
///
/// @dev ERC1967I proxy with immutable args:
/// An variant of the minimal ERC1967 proxy, with a special code path that activates
/// if `calldatasize() == 1`. This code path skips the delegatecall and directly returns the
/// - Uses the identity precompile (0x4) to copy args during deployment.
///
/// @dev Minimal ERC1967 beacon proxy:
/// A minimal beacon proxy, intended to be upgraded with an upgradable beacon.
/// - Automatically verified on Etherscan.
///
/// @dev Minimal ERC1967 beacon proxy with immutable args:
/// - Uses the identity precompile (0x4) to copy args during deployment.
/// - Automatically verified on Etherscan.
///
/// @dev ERC1967I beacon proxy:
/// An variant of the minimal ERC1967 beacon proxy, with a special code path that activates
/// if `calldatasize() == 1`. This code path skips the delegatecall and directly returns the
/// `implementation` address. The returned implementation is guaranteed to be valid if the
/// keccak256 of the proxy's code is equal to `ERC1967I_CODE_HASH`.
///
/// @dev ERC1967I proxy with immutable args:
/// An variant of the minimal ERC1967 beacon proxy, with a special code path that activates
/// if `calldatasize() == 1`. This code path skips the delegatecall and directly returns the
/// - Uses the identity precompile (0x4) to copy args during deployment.
library LibClone {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The keccak256 of deployed code for the clone proxy,
/// with the implementation set to `address(0)`.
bytes32 internal constant CLONE_CODE_HASH =
0x48db2cfdb2853fce0b464f1f93a1996469459df3ab6c812106074c4106a1eb1f;
/// @dev The keccak256 of deployed code for the PUSH0 proxy,
/// with the implementation set to `address(0)`.
bytes32 internal constant PUSH0_CLONE_CODE_HASH =
0x67bc6bde1b84d66e267c718ba44cf3928a615d29885537955cb43d44b3e789dc;
/// @dev The keccak256 of deployed code for the ERC-1167 CWIA proxy,
/// with the implementation set to `address(0)`.
bytes32 internal constant CWIA_CODE_HASH =
0x3cf92464268225a4513da40a34d967354684c32cd0edd67b5f668dfe3550e940;
/// @dev The keccak256 of the deployed code for the ERC1967 proxy.
bytes32 internal constant ERC1967_CODE_HASH =
0xaaa52c8cc8a0e3fd27ce756cc6b4e70c51423e9b597b11f32d3e49f8b1fc890d;
/// @dev The keccak256 of the deployed code for the ERC1967I proxy.
bytes32 internal constant ERC1967I_CODE_HASH =
0xce700223c0d4cea4583409accfc45adac4a093b3519998a9cbbe1504dadba6f7;
/// @dev The keccak256 of the deployed code for the ERC1967 beacon proxy.
bytes32 internal constant ERC1967_BEACON_PROXY_CODE_HASH =
0x14044459af17bc4f0f5aa2f658cb692add77d1302c29fe2aebab005eea9d1162;
/// @dev The keccak256 of the deployed code for the ERC1967 beacon proxy.
bytes32 internal constant ERC1967I_BEACON_PROXY_CODE_HASH =
0xf8c46d2793d5aa984eb827aeaba4b63aedcab80119212fce827309788735519a;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Unable to deploy the clone.
error DeploymentFailed();
/// @dev The salt must start with either the zero address or `by`.
error SaltDoesNotStartWith();
/// @dev The ETH transfer has failed.
error ETHTransferFailed();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* MINIMAL PROXY OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Deploys a clone of `implementation`.
function clone(address implementation) internal returns (address instance) {
instance = clone(0, implementation);
}
/// @dev Deploys a clone of `implementation`.
/// Deposits `value` ETH during deployment.
function clone(uint256 value, address implementation) internal returns (address instance) {
/// @solidity memory-safe-assembly
assembly {
/**
* --------------------------------------------------------------------------+
* CREATION (9 bytes) |
* --------------------------------------------------------------------------|
* Opcode | Mnemonic | Stack | Memory |
* --------------------------------------------------------------------------|
* 60 runSize | PUSH1 runSize | r | |
* 3d | RETURNDATASIZE | 0 r | |
* 81 | DUP2 | r 0 r | |
* 60 offset | PUSH1 offset | o r 0 r | |
* 3d | RETURNDATASIZE | 0 o r 0 r | |
* 39 | CODECOPY | 0 r | [0..runSize): runtime code |
* f3 | RETURN | | [0..runSize): runtime code |
* --------------------------------------------------------------------------|
* RUNTIME (44 bytes) |
* --------------------------------------------------------------------------|
* Opcode | Mnemonic | Stack | Memory |
* --------------------------------------------------------------------------|
* |
* ::: keep some values in stack ::::::::::::::::::::::::::::::::::::::::::: |
* 3d | RETURNDATASIZE | 0 | |
* 3d | RETURNDATASIZE | 0 0 | |
* 3d | RETURNDATASIZE | 0 0 0 | |
* 3d | RETURNDATASIZE | 0 0 0 0 | |
* |
* ::: copy calldata to memory ::::::::::::::::::::::::::::::::::::::::::::: |
* 36 | CALLDATASIZE | cds 0 0 0 0 | |
* 3d | RETURNDATASIZE | 0 cds 0 0 0 0 | |
* 3d | RETURNDATASIZE | 0 0 cds 0 0 0 0 | |
* 37 | CALLDATACOPY | 0 0 0 0 | [0..cds): calldata |
* |
* ::: delegate call to the implementation contract :::::::::::::::::::::::: |
* 36 | CALLDATASIZE | cds 0 0 0 0 | [0..cds): calldata |
* 3d | RETURNDATASIZE | 0 cds 0 0 0 0 | [0..cds): calldata |
* 73 addr | PUSH20 addr | addr 0 cds 0 0 0 0 | [0..cds): calldata |
* 5a | GAS | gas addr 0 cds 0 0 0 0 | [0..cds): calldata |
* f4 | DELEGATECALL | success 0 0 | [0..cds): calldata |
* |
* ::: copy return data to memory :::::::::::::::::::::::::::::::::::::::::: |
* 3d | RETURNDATASIZE | rds success 0 0 | [0..cds): calldata |
* 3d | RETURNDATASIZE | rds rds success 0 0 | [0..cds): calldata |
* 93 | SWAP4 | 0 rds success 0 rds | [0..cds): calldata |
* 80 | DUP1 | 0 0 rds success 0 rds | [0..cds): calldata |
* 3e | RETURNDATACOPY | success 0 rds | [0..rds): returndata |
* |
* 60 0x2a | PUSH1 0x2a | 0x2a success 0 rds | [0..rds): returndata |
* 57 | JUMPI | 0 rds | [0..rds): returndata |
* |
* ::: revert :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: |
* fd | REVERT | | [0..rds): returndata |
* |
* ::: return :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: |
* 5b | JUMPDEST | 0 rds | [0..rds): returndata |
* f3 | RETURN | | [0..rds): returndata |
* --------------------------------------------------------------------------+
*/
mstore(0x21, 0x5af43d3d93803e602a57fd5bf3)
mstore(0x14, implementation)
mstore(0x00, 0x602c3d8160093d39f33d3d3d3d363d3d37363d73)
instance := create(value, 0x0c, 0x35)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
}
}
/// @dev Deploys a deterministic clone of `implementation` with `salt`.
function cloneDeterministic(address implementation, bytes32 salt)
internal
returns (address instance)
{
instance = cloneDeterministic(0, implementation, salt);
}
/// @dev Deploys a deterministic clone of `implementation` with `salt`.
/// Deposits `value` ETH during deployment.
function cloneDeterministic(uint256 value, address implementation, bytes32 salt)
internal
returns (address instance)
{
/// @solidity memory-safe-assembly
assembly {
mstore(0x21, 0x5af43d3d93803e602a57fd5bf3)
mstore(0x14, implementation)
mstore(0x00, 0x602c3d8160093d39f33d3d3d3d363d3d37363d73)
instance := create2(value, 0x0c, 0x35, salt)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
}
}
/// @dev Returns the initialization code of the clone of `implementation`.
function initCode(address implementation) internal pure returns (bytes memory c) {
/// @solidity memory-safe-assembly
assembly {
c := mload(0x40)
mstore(add(c, 0x40), 0x5af43d3d93803e602a57fd5bf30000000000000000000000)
mstore(add(c, 0x28), implementation)
mstore(add(c, 0x14), 0x602c3d8160093d39f33d3d3d3d363d3d37363d73)
mstore(c, 0x35) // Store the length.
mstore(0x40, add(c, 0x60)) // Allocate memory.
}
}
/// @dev Returns the initialization code hash of the clone of `implementation`.
function initCodeHash(address implementation) internal pure returns (bytes32 hash) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x21, 0x5af43d3d93803e602a57fd5bf3)
mstore(0x14, implementation)
mstore(0x00, 0x602c3d8160093d39f33d3d3d3d363d3d37363d73)
hash := keccak256(0x0c, 0x35)
mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
}
}
/// @dev Returns the address of the clone of `implementation`, with `salt` by `deployer`.
/// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly.
function predictDeterministicAddress(address implementation, bytes32 salt, address deployer)
internal
pure
returns (address predicted)
{
bytes32 hash = initCodeHash(implementation);
predicted = predictDeterministicAddress(hash, salt, deployer);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* MINIMAL PROXY OPERATIONS (PUSH0 VARIANT) */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Deploys a PUSH0 clone of `implementation`.
function clone_PUSH0(address implementation) internal returns (address instance) {
instance = clone_PUSH0(0, implementation);
}
/// @dev Deploys a PUSH0 clone of `implementation`.
/// Deposits `value` ETH during deployment.
function clone_PUSH0(uint256 value, address implementation)
internal
returns (address instance)
{
/// @solidity memory-safe-assembly
assembly {
/**
* --------------------------------------------------------------------------+
* CREATION (9 bytes) |
* --------------------------------------------------------------------------|
* Opcode | Mnemonic | Stack | Memory |
* --------------------------------------------------------------------------|
* 60 runSize | PUSH1 runSize | r | |
* 5f | PUSH0 | 0 r | |
* 81 | DUP2 | r 0 r | |
* 60 offset | PUSH1 offset | o r 0 r | |
* 5f | PUSH0 | 0 o r 0 r | |
* 39 | CODECOPY | 0 r | [0..runSize): runtime code |
* f3 | RETURN | | [0..runSize): runtime code |
* --------------------------------------------------------------------------|
* RUNTIME (45 bytes) |
* --------------------------------------------------------------------------|
* Opcode | Mnemonic | Stack | Memory |
* --------------------------------------------------------------------------|
* |
* ::: keep some values in stack ::::::::::::::::::::::::::::::::::::::::::: |
* 5f | PUSH0 | 0 | |
* 5f | PUSH0 | 0 0 | |
* |
* ::: copy calldata to memory ::::::::::::::::::::::::::::::::::::::::::::: |
* 36 | CALLDATASIZE | cds 0 0 | |
* 5f | PUSH0 | 0 cds 0 0 | |
* 5f | PUSH0 | 0 0 cds 0 0 | |
* 37 | CALLDATACOPY | 0 0 | [0..cds): calldata |
* |
* ::: delegate call to the implementation contract :::::::::::::::::::::::: |
* 36 | CALLDATASIZE | cds 0 0 | [0..cds): calldata |
* 5f | PUSH0 | 0 cds 0 0 | [0..cds): calldata |
* 73 addr | PUSH20 addr | addr 0 cds 0 0 | [0..cds): calldata |
* 5a | GAS | gas addr 0 cds 0 0 | [0..cds): calldata |
* f4 | DELEGATECALL | success | [0..cds): calldata |
* |
* ::: copy return data to memory :::::::::::::::::::::::::::::::::::::::::: |
* 3d | RETURNDATASIZE | rds success | [0..cds): calldata |
* 5f | PUSH0 | 0 rds success | [0..cds): calldata |
* 5f | PUSH0 | 0 0 rds success | [0..cds): calldata |
* 3e | RETURNDATACOPY | success | [0..rds): returndata |
* |
* 60 0x29 | PUSH1 0x29 | 0x29 success | [0..rds): returndata |
* 57 | JUMPI | | [0..rds): returndata |
* |
* ::: revert :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: |
* 3d | RETURNDATASIZE | rds | [0..rds): returndata |
* 5f | PUSH0 | 0 rds | [0..rds): returndata |
* fd | REVERT | | [0..rds): returndata |
* |
* ::: return :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: |
* 5b | JUMPDEST | | [0..rds): returndata |
* 3d | RETURNDATASIZE | rds | [0..rds): returndata |
* 5f | PUSH0 | 0 rds | [0..rds): returndata |
* f3 | RETURN | | [0..rds): returndata |
* --------------------------------------------------------------------------+
*/
mstore(0x24, 0x5af43d5f5f3e6029573d5ffd5b3d5ff3) // 16
mstore(0x14, implementation) // 20
mstore(0x00, 0x602d5f8160095f39f35f5f365f5f37365f73) // 9 + 9
instance := create(value, 0x0e, 0x36)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
mstore(0x24, 0) // Restore the overwritten part of the free memory pointer.
}
}
/// @dev Deploys a deterministic PUSH0 clone of `implementation` with `salt`.
function cloneDeterministic_PUSH0(address implementation, bytes32 salt)
internal
returns (address instance)
{
instance = cloneDeterministic_PUSH0(0, implementation, salt);
}
/// @dev Deploys a deterministic PUSH0 clone of `implementation` with `salt`.
/// Deposits `value` ETH during deployment.
function cloneDeterministic_PUSH0(uint256 value, address implementation, bytes32 salt)
internal
returns (address instance)
{
/// @solidity memory-safe-assembly
assembly {
mstore(0x24, 0x5af43d5f5f3e6029573d5ffd5b3d5ff3) // 16
mstore(0x14, implementation) // 20
mstore(0x00, 0x602d5f8160095f39f35f5f365f5f37365f73) // 9 + 9
instance := create2(value, 0x0e, 0x36, salt)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
mstore(0x24, 0) // Restore the overwritten part of the free memory pointer.
}
}
/// @dev Returns the initialization code of the PUSH0 clone of `implementation`.
function initCode_PUSH0(address implementation) internal pure returns (bytes memory c) {
/// @solidity memory-safe-assembly
assembly {
c := mload(0x40)
mstore(add(c, 0x40), 0x5af43d5f5f3e6029573d5ffd5b3d5ff300000000000000000000) // 16
mstore(add(c, 0x26), implementation) // 20
mstore(add(c, 0x12), 0x602d5f8160095f39f35f5f365f5f37365f73) // 9 + 9
mstore(c, 0x36) // Store the length.
mstore(0x40, add(c, 0x60)) // Allocate memory.
}
}
/// @dev Returns the initialization code hash of the PUSH0 clone of `implementation`.
function initCodeHash_PUSH0(address implementation) internal pure returns (bytes32 hash) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x24, 0x5af43d5f5f3e6029573d5ffd5b3d5ff3) // 16
mstore(0x14, implementation) // 20
mstore(0x00, 0x602d5f8160095f39f35f5f365f5f37365f73) // 9 + 9
hash := keccak256(0x0e, 0x36)
mstore(0x24, 0) // Restore the overwritten part of the free memory pointer.
}
}
/// @dev Returns the address of the PUSH0 clone of `implementation`, with `salt` by `deployer`.
/// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly.
function predictDeterministicAddress_PUSH0(
address implementation,
bytes32 salt,
address deployer
) internal pure returns (address predicted) {
bytes32 hash = initCodeHash_PUSH0(implementation);
predicted = predictDeterministicAddress(hash, salt, deployer);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CLONES WITH IMMUTABLE ARGS OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Deploys a clone of `implementation` with immutable arguments encoded in `args`.
function clone(address implementation, bytes memory args) internal returns (address instance) {
instance = clone(0, implementation, args);
}
/// @dev Deploys a clone of `implementation` with immutable arguments encoded in `args`.
/// Deposits `value` ETH during deployment.
function clone(uint256 value, address implementation, bytes memory args)
internal
returns (address instance)
{
/// @solidity memory-safe-assembly
assembly {
/**
* ---------------------------------------------------------------------------+
* CREATION (10 bytes) |
* ---------------------------------------------------------------------------|
* Opcode | Mnemonic | Stack | Memory |
* ---------------------------------------------------------------------------|
* 61 runSize | PUSH2 runSize | r | |
* 3d | RETURNDATASIZE | 0 r | |
* 81 | DUP2 | r 0 r | |
* 60 offset | PUSH1 offset | o r 0 r | |
* 3d | RETURNDATASIZE | 0 o r 0 r | |
* 39 | CODECOPY | 0 r | [0..runSize): runtime code |
* f3 | RETURN | | [0..runSize): runtime code |
* ---------------------------------------------------------------------------|
* RUNTIME (45 bytes + extraLength) |
* ---------------------------------------------------------------------------|
* Opcode | Mnemonic | Stack | Memory |
* ---------------------------------------------------------------------------|
* |
* ::: copy calldata to memory :::::::::::::::::::::::::::::::::::::::::::::: |
* 36 | CALLDATASIZE | cds | |
* 3d | RETURNDATASIZE | 0 cds | |
* 3d | RETURNDATASIZE | 0 0 cds | |
* 37 | CALLDATACOPY | | [0..cds): calldata |
* |
* ::: delegate call to the implementation contract ::::::::::::::::::::::::: |
* 3d | RETURNDATASIZE | 0 | [0..cds): calldata |
* 3d | RETURNDATASIZE | 0 0 | [0..cds): calldata |
* 3d | RETURNDATASIZE | 0 0 0 | [0..cds): calldata |
* 36 | CALLDATASIZE | cds 0 0 0 | [0..cds): calldata |
* 3d | RETURNDATASIZE | 0 cds 0 0 0 0 | [0..cds): calldata |
* 73 addr | PUSH20 addr | addr 0 cds 0 0 0 0 | [0..cds): calldata |
* 5a | GAS | gas addr 0 cds 0 0 0 0 | [0..cds): calldata |
* f4 | DELEGATECALL | success 0 0 | [0..cds): calldata |
* |
* ::: copy return data to memory ::::::::::::::::::::::::::::::::::::::::::: |
* 3d | RETURNDATASIZE | rds success 0 | [0..cds): calldata |
* 82 | DUP3 | 0 rds success 0 | [0..cds): calldata |
* 80 | DUP1 | 0 0 rds success 0 | [0..cds): calldata |
* 3e | RETURNDATACOPY | success 0 | [0..rds): returndata |
* 90 | SWAP1 | 0 success | [0..rds): returndata |
* 3d | RETURNDATASIZE | rds 0 success | [0..rds): returndata |
* 91 | SWAP2 | success 0 rds | [0..rds): returndata |
* |
* 60 0x2b | PUSH1 0x2b | 0x2b success 0 rds | [0..rds): returndata |
* 57 | JUMPI | 0 rds | [0..rds): returndata |
* |
* ::: revert ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: |
* fd | REVERT | | [0..rds): returndata |
* |
* ::: return ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: |
* 5b | JUMPDEST | 0 rds | [0..rds): returndata |
* f3 | RETURN | | [0..rds): returndata |
* ---------------------------------------------------------------------------+
*/
let m := mload(0x40)
let n := mload(args)
pop(staticcall(gas(), 4, add(args, 0x20), n, add(m, 0x43), n))
mstore(add(m, 0x23), 0x5af43d82803e903d91602b57fd5bf3)
mstore(add(m, 0x14), implementation)
mstore(m, add(0xfe61002d3d81600a3d39f3363d3d373d3d3d363d73, shl(136, n)))
// Do a out-of-gas revert if `n` is greater than `0xffff - 0x2d = 0xffd2`.
instance := create(value, add(m, add(0x0b, lt(n, 0xffd3))), add(n, 0x37))
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Deploys a deterministic clone of `implementation`
/// with immutable arguments encoded in `args` and `salt`.
function cloneDeterministic(address implementation, bytes memory args, bytes32 salt)
internal
returns (address instance)
{
instance = cloneDeterministic(0, implementation, args, salt);
}
/// @dev Deploys a deterministic clone of `implementation`
/// with immutable arguments encoded in `args` and `salt`.
function cloneDeterministic(
uint256 value,
address implementation,
bytes memory args,
bytes32 salt
) internal returns (address instance) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
let n := mload(args)
pop(staticcall(gas(), 4, add(args, 0x20), n, add(m, 0x43), n))
mstore(add(m, 0x23), 0x5af43d82803e903d91602b57fd5bf3)
mstore(add(m, 0x14), implementation)
mstore(m, add(0xfe61002d3d81600a3d39f3363d3d373d3d3d363d73, shl(136, n)))
// Do a out-of-gas revert if `n` is greater than `0xffff - 0x2d = 0xffd2`.
instance := create2(value, add(m, add(0x0b, lt(n, 0xffd3))), add(n, 0x37), salt)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Deploys a deterministic clone of `implementation`
/// with immutable arguments encoded in `args` and `salt`.
/// This method does not revert if the clone has already been deployed.
function createDeterministicClone(address implementation, bytes memory args, bytes32 salt)
internal
returns (bool alreadyDeployed, address instance)
{
return createDeterministicClone(0, implementation, args, salt);
}
/// @dev Deploys a deterministic clone of `implementation`
/// with immutable arguments encoded in `args` and `salt`.
/// This method does not revert if the clone has already been deployed.
function createDeterministicClone(
uint256 value,
address implementation,
bytes memory args,
bytes32 salt
) internal returns (bool alreadyDeployed, address instance) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
let n := mload(args)
pop(staticcall(gas(), 4, add(args, 0x20), n, add(m, 0x43), n))
mstore(add(m, 0x23), 0x5af43d82803e903d91602b57fd5bf3)
mstore(add(m, 0x14), implementation)
// Do a out-of-gas revert if `n` is greater than `0xffff - 0x2d = 0xffd2`.
// forgefmt: disable-next-item
mstore(add(m, gt(n, 0xffd2)), add(0xfe61002d3d81600a3d39f3363d3d373d3d3d363d73, shl(136, n)))
// Compute and store the bytecode hash.
mstore8(0x00, 0xff) // Write the prefix.
mstore(0x35, keccak256(add(m, 0x0c), add(n, 0x37)))
mstore(0x01, shl(96, address()))
mstore(0x15, salt)
instance := keccak256(0x00, 0x55)
for {} 1 {} {
if iszero(extcodesize(instance)) {
instance := create2(value, add(m, 0x0c), add(n, 0x37), salt)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
break
}
alreadyDeployed := 1
if iszero(value) { break }
if iszero(call(gas(), instance, value, codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
break
}
mstore(0x35, 0) // Restore the overwritten part of the free memory pointer.
}
}
/// @dev Returns the initialization code hash of the clone of `implementation`
/// using immutable arguments encoded in `args`.
function initCode(address implementation, bytes memory args)
internal
pure
returns (bytes memory c)
{
/// @solidity memory-safe-assembly
assembly {
c := mload(0x40)
let n := mload(args)
// Do a out-of-gas revert if `n` is greater than `0xffff - 0x2d = 0xffd2`.
returndatacopy(returndatasize(), returndatasize(), gt(n, 0xffd2))
for { let i := 0 } lt(i, n) { i := add(i, 0x20) } {
mstore(add(add(c, 0x57), i), mload(add(add(args, 0x20), i)))
}
mstore(add(c, 0x37), 0x5af43d82803e903d91602b57fd5bf3)
mstore(add(c, 0x28), implementation)
mstore(add(c, 0x14), add(0x61002d3d81600a3d39f3363d3d373d3d3d363d73, shl(136, n)))
mstore(c, add(0x37, n)) // Store the length.
mstore(add(c, add(n, 0x57)), 0) // Zeroize the slot after the bytes.
mstore(0x40, add(c, add(n, 0x77))) // Allocate memory.
}
}
/// @dev Returns the initialization code hash of the clone of `implementation`
/// using immutable arguments encoded in `args`.
function initCodeHash(address implementation, bytes memory args)
internal
pure
returns (bytes32 hash)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
let n := mload(args)
// Do a out-of-gas revert if `n` is greater than `0xffff - 0x2d = 0xffd2`.
returndatacopy(returndatasize(), returndatasize(), gt(n, 0xffd2))
for { let i := 0 } lt(i, n) { i := add(i, 0x20) } {
mstore(add(add(m, 0x43), i), mload(add(add(args, 0x20), i)))
}
mstore(add(m, 0x23), 0x5af43d82803e903d91602b57fd5bf3)
mstore(add(m, 0x14), implementation)
mstore(m, add(0x61002d3d81600a3d39f3363d3d373d3d3d363d73, shl(136, n)))
hash := keccak256(add(m, 0x0c), add(n, 0x37))
}
}
/// @dev Returns the address of the clone of
/// `implementation` using immutable arguments encoded in `args`, with `salt`, by `deployer`.
/// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly.
function predictDeterministicAddress(
address implementation,
bytes memory data,
bytes32 salt,
address deployer
) internal pure returns (address predicted) {
bytes32 hash = initCodeHash(implementation, data);
predicted = predictDeterministicAddress(hash, salt, deployer);
}
/// @dev Equivalent to `argsOnClone(instance, 0, 2 ** 256 - 1)`.
function argsOnClone(address instance) internal view returns (bytes memory args) {
/// @solidity memory-safe-assembly
assembly {
args := mload(0x40)
mstore(args, and(0xffffffffff, sub(extcodesize(instance), 0x2d))) // Store the length.
extcodecopy(instance, add(args, 0x20), 0x2d, add(mload(args), 0x20))
mstore(0x40, add(mload(args), add(args, 0x40))) // Allocate memory.
}
}
/// @dev Equivalent to `argsOnClone(instance, start, 2 ** 256 - 1)`.
function argsOnClone(address instance, uint256 start)
internal
view
returns (bytes memory args)
{
/// @solidity memory-safe-assembly
assembly {
args := mload(0x40)
let n := and(0xffffffffff, sub(extcodesize(instance), 0x2d))
extcodecopy(instance, add(args, 0x20), add(start, 0x2d), add(n, 0x20))
mstore(args, mul(sub(n, start), lt(start, n))) // Store the length.
mstore(0x40, add(args, add(0x40, mload(args)))) // Allocate memory.
}
}
/// @dev Returns a slice of the immutable arguments on `instance` from `start` to `end`.
/// `start` and `end` will be clamped to the range `[0, args.length]`.
/// The `instance` MUST be deployed via the clone with immutable args functions.
/// Otherwise, the behavior is undefined.
/// Out-of-gas reverts if `instance` does not have any code.
function argsOnClone(address instance, uint256 start, uint256 end)
internal
view
returns (bytes memory args)
{
/// @solidity memory-safe-assembly
assembly {
args := mload(0x40)
if iszero(lt(end, 0xffff)) { end := 0xffff }
let d := mul(sub(end, start), lt(start, end))
extcodecopy(instance, args, add(start, 0x0d), add(d, 0x20))
if iszero(and(0xff, mload(add(args, d)))) {
let n := sub(extcodesize(instance), 0x2d)
returndatacopy(returndatasize(), returndatasize(), shr(40, n))
d := mul(gt(n, start), sub(d, mul(gt(end, n), sub(end, n))))
}
mstore(args, d) // Store the length.
mstore(add(add(args, 0x20), d), 0) // Zeroize the slot after the bytes.
mstore(0x40, add(add(args, 0x40), d)) // Allocate memory.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* MINIMAL ERC1967 PROXY OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// Note: The ERC1967 proxy here is intended to be upgraded with UUPS.
// This is NOT the same as ERC1967Factory's transparent proxy, which includes admin logic.
/// @dev Deploys a minimal ERC1967 proxy with `implementation`.
function deployERC1967(address implementation) internal returns (address instance) {
instance = deployERC1967(0, implementation);
}
/// @dev Deploys a minimal ERC1967 proxy with `implementation`.
/// Deposits `value` ETH during deployment.
function deployERC1967(uint256 value, address implementation)
internal
returns (address instance)
{
/// @solidity memory-safe-assembly
assembly {
/**
* ---------------------------------------------------------------------------------+
* CREATION (34 bytes) |
* ---------------------------------------------------------------------------------|
* Opcode | Mnemonic | Stack | Memory |
* ---------------------------------------------------------------------------------|
* 60 runSize | PUSH1 runSize | r | |
* 3d | RETURNDATASIZE | 0 r | |
* 81 | DUP2 | r 0 r | |
* 60 offset | PUSH1 offset | o r 0 r | |
* 3d | RETURNDATASIZE | 0 o r 0 r | |
* 39 | CODECOPY | 0 r | [0..runSize): runtime code |
* 73 impl | PUSH20 impl | impl 0 r | [0..runSize): runtime code |
* 60 slotPos | PUSH1 slotPos | slotPos impl 0 r | [0..runSize): runtime code |
* 51 | MLOAD | slot impl 0 r | [0..runSize): runtime code |
* 55 | SSTORE | 0 r | [0..runSize): runtime code |
* f3 | RETURN | | [0..runSize): runtime code |
* ---------------------------------------------------------------------------------|
* RUNTIME (61 bytes) |
* ---------------------------------------------------------------------------------|
* Opcode | Mnemonic | Stack | Memory |
* ---------------------------------------------------------------------------------|
* |
* ::: copy calldata to memory :::::::::::::::::::::::::::::::::::::::::::::::::::: |
* 36 | CALLDATASIZE | cds | |
* 3d | RETURNDATASIZE | 0 cds | |
* 3d | RETURNDATASIZE | 0 0 cds | |
* 37 | CALLDATACOPY | | [0..calldatasize): calldata |
* |
* ::: delegatecall to implementation ::::::::::::::::::::::::::::::::::::::::::::: |
* 3d | RETURNDATASIZE | 0 | |
* 3d | RETURNDATASIZE | 0 0 | |
* 36 | CALLDATASIZE | cds 0 0 | [0..calldatasize): calldata |
* 3d | RETURNDATASIZE | 0 cds 0 0 | [0..calldatasize): calldata |
* 7f slot | PUSH32 slot | s 0 cds 0 0 | [0..calldatasize): calldata |
* 54 | SLOAD | i 0 cds 0 0 | [0..calldatasize): calldata |
* 5a | GAS | g i 0 cds 0 0 | [0..calldatasize): calldata |
* f4 | DELEGATECALL | succ | [0..calldatasize): calldata |
* |
* ::: copy returndata to memory :::::::::::::::::::::::::::::::::::::::::::::::::: |
* 3d | RETURNDATASIZE | rds succ | [0..calldatasize): calldata |
* 60 0x00 | PUSH1 0x00 | 0 rds succ | [0..calldatasize): calldata |
* 80 | DUP1 | 0 0 rds succ | [0..calldatasize): calldata |
* 3e | RETURNDATACOPY | succ | [0..returndatasize): returndata |
* |
* ::: branch on delegatecall status :::::::::::::::::::::::::::::::::::::::::::::: |
* 60 0x38 | PUSH1 0x38 | dest succ | [0..returndatasize): returndata |
* 57 | JUMPI | | [0..returndatasize): returndata |
* |
* ::: delegatecall failed, revert :::::::::::::::::::::::::::::::::::::::::::::::: |
* 3d | RETURNDATASIZE | rds | [0..returndatasize): returndata |
* 60 0x00 | PUSH1 0x00 | 0 rds | [0..returndatasize): returndata |
* fd | REVERT | | [0..returndatasize): returndata |
* |
* ::: delegatecall succeeded, return ::::::::::::::::::::::::::::::::::::::::::::: |
* 5b | JUMPDEST | | [0..returndatasize): returndata |
* 3d | RETURNDATASIZE | rds | [0..returndatasize): returndata |
* 60 0x00 | PUSH1 0x00 | 0 rds | [0..returndatasize): returndata |
* f3 | RETURN | | [0..returndatasize): returndata |
* ---------------------------------------------------------------------------------+
*/
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, 0xcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f3)
mstore(0x40, 0x5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076)
mstore(0x20, 0x6009)
mstore(0x1e, implementation)
mstore(0x0a, 0x603d3d8160223d3973)
instance := create(value, 0x21, 0x5f)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero slot.
}
}
/// @dev Deploys a deterministic minimal ERC1967 proxy with `implementation` and `salt`.
function deployDeterministicERC1967(address implementation, bytes32 salt)
internal
returns (address instance)
{
instance = deployDeterministicERC1967(0, implementation, salt);
}
/// @dev Deploys a deterministic minimal ERC1967 proxy with `implementation` and `salt`.
/// Deposits `value` ETH during deployment.
function deployDeterministicERC1967(uint256 value, address implementation, bytes32 salt)
internal
returns (address instance)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, 0xcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f3)
mstore(0x40, 0x5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076)
mstore(0x20, 0x6009)
mstore(0x1e, implementation)
mstore(0x0a, 0x603d3d8160223d3973)
instance := create2(value, 0x21, 0x5f, salt)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero slot.
}
}
/// @dev Creates a deterministic minimal ERC1967 proxy with `implementation` and `salt`.
/// Note: This method is intended for use in ERC4337 factories,
/// which are expected to NOT revert if the proxy is already deployed.
function createDeterministicERC1967(address implementation, bytes32 salt)
internal
returns (bool alreadyDeployed, address instance)
{
return createDeterministicERC1967(0, implementation, salt);
}
/// @dev Creates a deterministic minimal ERC1967 proxy with `implementation` and `salt`.
/// Deposits `value` ETH during deployment.
/// Note: This method is intended for use in ERC4337 factories,
/// which are expected to NOT revert if the proxy is already deployed.
function createDeterministicERC1967(uint256 value, address implementation, bytes32 salt)
internal
returns (bool alreadyDeployed, address instance)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, 0xcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f3)
mstore(0x40, 0x5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076)
mstore(0x20, 0x6009)
mstore(0x1e, implementation)
mstore(0x0a, 0x603d3d8160223d3973)
// Compute and store the bytecode hash.
mstore(add(m, 0x35), keccak256(0x21, 0x5f))
mstore(m, shl(88, address()))
mstore8(m, 0xff) // Write the prefix.
mstore(add(m, 0x15), salt)
instance := keccak256(m, 0x55)
for {} 1 {} {
if iszero(extcodesize(instance)) {
instance := create2(value, 0x21, 0x5f, salt)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
break
}
alreadyDeployed := 1
if iszero(value) { break }
if iszero(call(gas(), instance, value, codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
break
}
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero slot.
}
}
/// @dev Returns the initialization code of the minimal ERC1967 proxy of `implementation`.
function initCodeERC1967(address implementation) internal pure returns (bytes memory c) {
/// @solidity memory-safe-assembly
assembly {
c := mload(0x40)
mstore(add(c, 0x60), 0x3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f300)
mstore(add(c, 0x40), 0x55f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076cc)
mstore(add(c, 0x20), or(shl(24, implementation), 0x600951))
mstore(add(c, 0x09), 0x603d3d8160223d3973)
mstore(c, 0x5f) // Store the length.
mstore(0x40, add(c, 0x80)) // Allocate memory.
}
}
/// @dev Returns the initialization code hash of the minimal ERC1967 proxy of `implementation`.
function initCodeHashERC1967(address implementation) internal pure returns (bytes32 hash) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, 0xcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f3)
mstore(0x40, 0x5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076)
mstore(0x20, 0x6009)
mstore(0x1e, implementation)
mstore(0x0a, 0x603d3d8160223d3973)
hash := keccak256(0x21, 0x5f)
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero slot.
}
}
/// @dev Returns the address of the ERC1967 proxy of `implementation`, with `salt` by `deployer`.
/// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly.
function predictDeterministicAddressERC1967(
address implementation,
bytes32 salt,
address deployer
) internal pure returns (address predicted) {
bytes32 hash = initCodeHashERC1967(implementation);
predicted = predictDeterministicAddress(hash, salt, deployer);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* MINIMAL ERC1967 PROXY WITH IMMUTABLE ARGS OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Deploys a minimal ERC1967 proxy with `implementation` and `args`.
function deployERC1967(address implementation, bytes memory args)
internal
returns (address instance)
{
instance = deployERC1967(0, implementation, args);
}
/// @dev Deploys a minimal ERC1967 proxy with `implementation` and `args`.
/// Deposits `value` ETH during deployment.
function deployERC1967(uint256 value, address implementation, bytes memory args)
internal
returns (address instance)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
let n := mload(args)
pop(staticcall(gas(), 4, add(args, 0x20), n, add(m, 0x60), n))
mstore(add(m, 0x40), 0xcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f3)
mstore(add(m, 0x20), 0x5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076)
mstore(0x16, 0x6009)
mstore(0x14, implementation)
// Do a out-of-gas revert if `n` is greater than `0xffff - 0x3d = 0xffc2`.
mstore(gt(n, 0xffc2), add(0xfe61003d3d8160233d3973, shl(56, n)))
mstore(m, mload(0x16))
instance := create(value, m, add(n, 0x60))
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Deploys a deterministic minimal ERC1967 proxy with `implementation`, `args` and `salt`.
function deployDeterministicERC1967(address implementation, bytes memory args, bytes32 salt)
internal
returns (address instance)
{
instance = deployDeterministicERC1967(0, implementation, args, salt);
}
/// @dev Deploys a deterministic minimal ERC1967 proxy with `implementation`, `args` and `salt`.
/// Deposits `value` ETH during deployment.
function deployDeterministicERC1967(
uint256 value,
address implementation,
bytes memory args,
bytes32 salt
) internal returns (address instance) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
let n := mload(args)
pop(staticcall(gas(), 4, add(args, 0x20), n, add(m, 0x60), n))
mstore(add(m, 0x40), 0xcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f3)
mstore(add(m, 0x20), 0x5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076)
mstore(0x16, 0x6009)
mstore(0x14, implementation)
// Do a out-of-gas revert if `n` is greater than `0xffff - 0x3d = 0xffc2`.
mstore(gt(n, 0xffc2), add(0xfe61003d3d8160233d3973, shl(56, n)))
mstore(m, mload(0x16))
instance := create2(value, m, add(n, 0x60), salt)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Creates a deterministic minimal ERC1967 proxy with `implementation`, `args` and `salt`.
/// Note: This method is intended for use in ERC4337 factories,
/// which are expected to NOT revert if the proxy is already deployed.
function createDeterministicERC1967(address implementation, bytes memory args, bytes32 salt)
internal
returns (bool alreadyDeployed, address instance)
{
return createDeterministicERC1967(0, implementation, args, salt);
}
/// @dev Creates a deterministic minimal ERC1967 proxy with `implementation`, `args` and `salt`.
/// Deposits `value` ETH during deployment.
/// Note: This method is intended for use in ERC4337 factories,
/// which are expected to NOT revert if the proxy is already deployed.
function createDeterministicERC1967(
uint256 value,
address implementation,
bytes memory args,
bytes32 salt
) internal returns (bool alreadyDeployed, address instance) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
let n := mload(args)
pop(staticcall(gas(), 4, add(args, 0x20), n, add(m, 0x60), n))
mstore(add(m, 0x40), 0xcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f3)
mstore(add(m, 0x20), 0x5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076)
mstore(0x16, 0x6009)
mstore(0x14, implementation)
// Do a out-of-gas revert if `n` is greater than `0xffff - 0x3d = 0xffc2`.
mstore(gt(n, 0xffc2), add(0xfe61003d3d8160233d3973, shl(56, n)))
mstore(m, mload(0x16))
// Compute and store the bytecode hash.
mstore8(0x00, 0xff) // Write the prefix.
mstore(0x35, keccak256(m, add(n, 0x60)))
mstore(0x01, shl(96, address()))
mstore(0x15, salt)
instance := keccak256(0x00, 0x55)
for {} 1 {} {
if iszero(extcodesize(instance)) {
instance := create2(value, m, add(n, 0x60), salt)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
break
}
alreadyDeployed := 1
if iszero(value) { break }
if iszero(call(gas(), instance, value, codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
break
}
mstore(0x35, 0) // Restore the overwritten part of the free memory pointer.
}
}
/// @dev Returns the initialization code of the minimal ERC1967 proxy of `implementation` and `args`.
function initCodeERC1967(address implementation, bytes memory args)
internal
pure
returns (bytes memory c)
{
/// @solidity memory-safe-assembly
assembly {
c := mload(0x40)
let n := mload(args)
// Do a out-of-gas revert if `n` is greater than `0xffff - 0x3d = 0xffc2`.
returndatacopy(returndatasize(), returndatasize(), gt(n, 0xffc2))
for { let i := 0 } lt(i, n) { i := add(i, 0x20) } {
mstore(add(add(c, 0x80), i), mload(add(add(args, 0x20), i)))
}
mstore(add(c, 0x60), 0xcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f3)
mstore(add(c, 0x40), 0x5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076)
mstore(add(c, 0x20), 0x6009)
mstore(add(c, 0x1e), implementation)
mstore(add(c, 0x0a), add(0x61003d3d8160233d3973, shl(56, n)))
mstore(c, add(n, 0x60)) // Store the length.
mstore(add(c, add(n, 0x80)), 0) // Zeroize the slot after the bytes.
mstore(0x40, add(c, add(n, 0xa0))) // Allocate memory.
}
}
/// @dev Returns the initialization code hash of the minimal ERC1967 proxy of `implementation` and `args`.
function initCodeHashERC1967(address implementation, bytes memory args)
internal
pure
returns (bytes32 hash)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
let n := mload(args)
// Do a out-of-gas revert if `n` is greater than `0xffff - 0x3d = 0xffc2`.
returndatacopy(returndatasize(), returndatasize(), gt(n, 0xffc2))
for { let i := 0 } lt(i, n) { i := add(i, 0x20) } {
mstore(add(add(m, 0x60), i), mload(add(add(args, 0x20), i)))
}
mstore(add(m, 0x40), 0xcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f3)
mstore(add(m, 0x20), 0x5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076)
mstore(0x16, 0x6009)
mstore(0x14, implementation)
mstore(0x00, add(0x61003d3d8160233d3973, shl(56, n)))
mstore(m, mload(0x16))
hash := keccak256(m, add(n, 0x60))
}
}
/// @dev Returns the address of the ERC1967 proxy of `implementation`, `args`, with `salt` by `deployer`.
/// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly.
function predictDeterministicAddressERC1967(
address implementation,
bytes memory args,
bytes32 salt,
address deployer
) internal pure returns (address predicted) {
bytes32 hash = initCodeHashERC1967(implementation, args);
predicted = predictDeterministicAddress(hash, salt, deployer);
}
/// @dev Equivalent to `argsOnERC1967(instance, start, 2 ** 256 - 1)`.
function argsOnERC1967(address instance) internal view returns (bytes memory args) {
/// @solidity memory-safe-assembly
assembly {
args := mload(0x40)
mstore(args, and(0xffffffffff, sub(extcodesize(instance), 0x3d))) // Store the length.
extcodecopy(instance, add(args, 0x20), 0x3d, add(mload(args), 0x20))
mstore(0x40, add(mload(args), add(args, 0x40))) // Allocate memory.
}
}
/// @dev Equivalent to `argsOnERC1967(instance, start, 2 ** 256 - 1)`.
function argsOnERC1967(address instance, uint256 start)
internal
view
returns (bytes memory args)
{
/// @solidity memory-safe-assembly
assembly {
args := mload(0x40)
let n := and(0xffffffffff, sub(extcodesize(instance), 0x3d))
extcodecopy(instance, add(args, 0x20), add(start, 0x3d), add(n, 0x20))
mstore(args, mul(sub(n, start), lt(start, n))) // Store the length.
mstore(0x40, add(args, add(0x40, mload(args)))) // Allocate memory.
}
}
/// @dev Returns a slice of the immutable arguments on `instance` from `start` to `end`.
/// `start` and `end` will be clamped to the range `[0, args.length]`.
/// The `instance` MUST be deployed via the ERC1967 with immutable args functions.
/// Otherwise, the behavior is undefined.
/// Out-of-gas reverts if `instance` does not have any code.
function argsOnERC1967(address instance, uint256 start, uint256 end)
internal
view
returns (bytes memory args)
{
/// @solidity memory-safe-assembly
assembly {
args := mload(0x40)
if iszero(lt(end, 0xffff)) { end := 0xffff }
let d := mul(sub(end, start), lt(start, end))
extcodecopy(instance, args, add(start, 0x1d), add(d, 0x20))
if iszero(and(0xff, mload(add(args, d)))) {
let n := sub(extcodesize(instance), 0x3d)
returndatacopy(returndatasize(), returndatasize(), shr(40, n))
d := mul(gt(n, start), sub(d, mul(gt(end, n), sub(end, n))))
}
mstore(args, d) // Store the length.
mstore(add(add(args, 0x20), d), 0) // Zeroize the slot after the bytes.
mstore(0x40, add(add(args, 0x40), d)) // Allocate memory.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ERC1967I PROXY OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// Note: This proxy has a special code path that activates if `calldatasize() == 1`.
// This code path skips the delegatecall and directly returns the `implementation` address.
// The returned implementation is guaranteed to be valid if the keccak256 of the
// proxy's code is equal to `ERC1967I_CODE_HASH`.
/// @dev Deploys a ERC1967I proxy with `implementation`.
function deployERC1967I(address implementation) internal returns (address instance) {
instance = deployERC1967I(0, implementation);
}
/// @dev Deploys a ERC1967I proxy with `implementation`.
/// Deposits `value` ETH during deployment.
function deployERC1967I(uint256 value, address implementation)
internal
returns (address instance)
{
/// @solidity memory-safe-assembly
assembly {
/**
* ---------------------------------------------------------------------------------+
* CREATION (34 bytes) |
* ---------------------------------------------------------------------------------|
* Opcode | Mnemonic | Stack | Memory |
* ---------------------------------------------------------------------------------|
* 60 runSize | PUSH1 runSize | r | |
* 3d | RETURNDATASIZE | 0 r | |
* 81 | DUP2 | r 0 r | |
* 60 offset | PUSH1 offset | o r 0 r | |
* 3d | RETURNDATASIZE | 0 o r 0 r | |
* 39 | CODECOPY | 0 r | [0..runSize): runtime code |
* 73 impl | PUSH20 impl | impl 0 r | [0..runSize): runtime code |
* 60 slotPos | PUSH1 slotPos | slotPos impl 0 r | [0..runSize): runtime code |
* 51 | MLOAD | slot impl 0 r | [0..runSize): runtime code |
* 55 | SSTORE | 0 r | [0..runSize): runtime code |
* f3 | RETURN | | [0..runSize): runtime code |
* ---------------------------------------------------------------------------------|
* RUNTIME (82 bytes) |
* ---------------------------------------------------------------------------------|
* Opcode | Mnemonic | Stack | Memory |
* ---------------------------------------------------------------------------------|
* |
* ::: check calldatasize ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: |
* 36 | CALLDATASIZE | cds | |
* 58 | PC | 1 cds | |
* 14 | EQ | eqs | |
* 60 0x43 | PUSH1 0x43 | dest eqs | |
* 57 | JUMPI | | |
* |
* ::: copy calldata to memory :::::::::::::::::::::::::::::::::::::::::::::::::::: |
* 36 | CALLDATASIZE | cds | |
* 3d | RETURNDATASIZE | 0 cds | |
* 3d | RETURNDATASIZE | 0 0 cds | |
* 37 | CALLDATACOPY | | [0..calldatasize): calldata |
* |
* ::: delegatecall to implementation ::::::::::::::::::::::::::::::::::::::::::::: |
* 3d | RETURNDATASIZE | 0 | |
* 3d | RETURNDATASIZE | 0 0 | |
* 36 | CALLDATASIZE | cds 0 0 | [0..calldatasize): calldata |
* 3d | RETURNDATASIZE | 0 cds 0 0 | [0..calldatasize): calldata |
* 7f slot | PUSH32 slot | s 0 cds 0 0 | [0..calldatasize): calldata |
* 54 | SLOAD | i 0 cds 0 0 | [0..calldatasize): calldata |
* 5a | GAS | g i 0 cds 0 0 | [0..calldatasize): calldata |
* f4 | DELEGATECALL | succ | [0..calldatasize): calldata |
* |
* ::: copy returndata to memory :::::::::::::::::::::::::::::::::::::::::::::::::: |
* 3d | RETURNDATASIZE | rds succ | [0..calldatasize): calldata |
* 60 0x00 | PUSH1 0x00 | 0 rds succ | [0..calldatasize): calldata |
* 80 | DUP1 | 0 0 rds succ | [0..calldatasize): calldata |
* 3e | RETURNDATACOPY | succ | [0..returndatasize): returndata |
* |
* ::: branch on delegatecall status :::::::::::::::::::::::::::::::::::::::::::::: |
* 60 0x3E | PUSH1 0x3E | dest succ | [0..returndatasize): returndata |
* 57 | JUMPI | | [0..returndatasize): returndata |
* |
* ::: delegatecall failed, revert :::::::::::::::::::::::::::::::::::::::::::::::: |
* 3d | RETURNDATASIZE | rds | [0..returndatasize): returndata |
* 60 0x00 | PUSH1 0x00 | 0 rds | [0..returndatasize): returndata |
* fd | REVERT | | [0..returndatasize): returndata |
* |
* ::: delegatecall succeeded, return ::::::::::::::::::::::::::::::::::::::::::::: |
* 5b | JUMPDEST | | [0..returndatasize): returndata |
* 3d | RETURNDATASIZE | rds | [0..returndatasize): returndata |
* 60 0x00 | PUSH1 0x00 | 0 rds | [0..returndatasize): returndata |
* f3 | RETURN | | [0..returndatasize): returndata |
* |
* ::: implementation , return :::::::::::::::::::::::::::::::::::::::::::::::::::: |
* 5b | JUMPDEST | | |
* 60 0x20 | PUSH1 0x20 | 32 | |
* 60 0x0F | PUSH1 0x0F | o 32 | |
* 3d | RETURNDATASIZE | 0 o 32 | |
* 39 | CODECOPY | | [0..32): implementation slot |
* 3d | RETURNDATASIZE | 0 | [0..32): implementation slot |
* 51 | MLOAD | slot | [0..32): implementation slot |
* 54 | SLOAD | impl | [0..32): implementation slot |
* 3d | RETURNDATASIZE | 0 impl | [0..32): implementation slot |
* 52 | MSTORE | | [0..32): implementation address |
* 59 | MSIZE | 32 | [0..32): implementation address |
* 3d | RETURNDATASIZE | 0 32 | [0..32): implementation address |
* f3 | RETURN | | [0..32): implementation address |
* ---------------------------------------------------------------------------------+
*/
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, 0x3d6000803e603e573d6000fd5b3d6000f35b6020600f3d393d51543d52593df3)
mstore(0x40, 0xa13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af4)
mstore(0x20, 0x600f5155f3365814604357363d3d373d3d363d7f360894)
mstore(0x09, or(shl(160, 0x60523d8160223d3973), shr(96, shl(96, implementation))))
instance := create(value, 0x0c, 0x74)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero slot.
}
}
/// @dev Deploys a deterministic ERC1967I proxy with `implementation` and `salt`.
function deployDeterministicERC1967I(address implementation, bytes32 salt)
internal
returns (address instance)
{
instance = deployDeterministicERC1967I(0, implementation, salt);
}
/// @dev Deploys a deterministic ERC1967I proxy with `implementation` and `salt`.
/// Deposits `value` ETH during deployment.
function deployDeterministicERC1967I(uint256 value, address implementation, bytes32 salt)
internal
returns (address instance)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, 0x3d6000803e603e573d6000fd5b3d6000f35b6020600f3d393d51543d52593df3)
mstore(0x40, 0xa13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af4)
mstore(0x20, 0x600f5155f3365814604357363d3d373d3d363d7f360894)
mstore(0x09, or(shl(160, 0x60523d8160223d3973), shr(96, shl(96, implementation))))
instance := create2(value, 0x0c, 0x74, salt)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero slot.
}
}
/// @dev Creates a deterministic ERC1967I proxy with `implementation` and `salt`.
/// Note: This method is intended for use in ERC4337 factories,
/// which are expected to NOT revert if the proxy is already deployed.
function createDeterministicERC1967I(address implementation, bytes32 salt)
internal
returns (bool alreadyDeployed, address instance)
{
return createDeterministicERC1967I(0, implementation, salt);
}
/// @dev Creates a deterministic ERC1967I proxy with `implementation` and `salt`.
/// Deposits `value` ETH during deployment.
/// Note: This method is intended for use in ERC4337 factories,
/// which are expected to NOT revert if the proxy is already deployed.
function createDeterministicERC1967I(uint256 value, address implementation, bytes32 salt)
internal
returns (bool alreadyDeployed, address instance)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, 0x3d6000803e603e573d6000fd5b3d6000f35b6020600f3d393d51543d52593df3)
mstore(0x40, 0xa13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af4)
mstore(0x20, 0x600f5155f3365814604357363d3d373d3d363d7f360894)
mstore(0x09, or(shl(160, 0x60523d8160223d3973), shr(96, shl(96, implementation))))
// Compute and store the bytecode hash.
mstore(add(m, 0x35), keccak256(0x0c, 0x74))
mstore(m, shl(88, address()))
mstore8(m, 0xff) // Write the prefix.
mstore(add(m, 0x15), salt)
instance := keccak256(m, 0x55)
for {} 1 {} {
if iszero(extcodesize(instance)) {
instance := create2(value, 0x0c, 0x74, salt)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
break
}
alreadyDeployed := 1
if iszero(value) { break }
if iszero(call(gas(), instance, value, codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
break
}
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero slot.
}
}
/// @dev Returns the initialization code of the ERC1967I proxy of `implementation`.
function initCodeERC1967I(address implementation) internal pure returns (bytes memory c) {
/// @solidity memory-safe-assembly
assembly {
c := mload(0x40)
mstore(add(c, 0x74), 0x3d6000803e603e573d6000fd5b3d6000f35b6020600f3d393d51543d52593df3)
mstore(add(c, 0x54), 0xa13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af4)
mstore(add(c, 0x34), 0x600f5155f3365814604357363d3d373d3d363d7f360894)
mstore(add(c, 0x1d), implementation)
mstore(add(c, 0x09), 0x60523d8160223d3973)
mstore(add(c, 0x94), 0)
mstore(c, 0x74) // Store the length.
mstore(0x40, add(c, 0xa0)) // Allocate memory.
}
}
/// @dev Returns the initialization code hash of the ERC1967I proxy of `implementation`.
function initCodeHashERC1967I(address implementation) internal pure returns (bytes32 hash) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, 0x3d6000803e603e573d6000fd5b3d6000f35b6020600f3d393d51543d52593df3)
mstore(0x40, 0xa13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af4)
mstore(0x20, 0x600f5155f3365814604357363d3d373d3d363d7f360894)
mstore(0x09, or(shl(160, 0x60523d8160223d3973), shr(96, shl(96, implementation))))
hash := keccak256(0x0c, 0x74)
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero slot.
}
}
/// @dev Returns the address of the ERC1967I proxy of `implementation`, with `salt` by `deployer`.
/// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly.
function predictDeterministicAddressERC1967I(
address implementation,
bytes32 salt,
address deployer
) internal pure returns (address predicted) {
bytes32 hash = initCodeHashERC1967I(implementation);
predicted = predictDeterministicAddress(hash, salt, deployer);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ERC1967I PROXY WITH IMMUTABLE ARGS OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Deploys a minimal ERC1967I proxy with `implementation` and `args`.
function deployERC1967I(address implementation, bytes memory args) internal returns (address) {
return deployERC1967I(0, implementation, args);
}
/// @dev Deploys a minimal ERC1967I proxy with `implementation` and `args`.
/// Deposits `value` ETH during deployment.
function deployERC1967I(uint256 value, address implementation, bytes memory args)
internal
returns (address instance)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
let n := mload(args)
pop(staticcall(gas(), 4, add(args, 0x20), n, add(m, 0x8b), n))
mstore(add(m, 0x6b), 0x3d6000803e603e573d6000fd5b3d6000f35b6020600f3d393d51543d52593df3)
mstore(add(m, 0x4b), 0xa13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af4)
mstore(add(m, 0x2b), 0x600f5155f3365814604357363d3d373d3d363d7f360894)
mstore(add(m, 0x14), implementation)
mstore(m, add(0xfe6100523d8160233d3973, shl(56, n)))
// Do a out-of-gas revert if `n` is greater than `0xffff - 0x52 = 0xffad`.
instance := create(value, add(m, add(0x15, lt(n, 0xffae))), add(0x75, n))
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Deploys a deterministic ERC1967I proxy with `implementation`, `args`, and `salt`.
function deployDeterministicERC1967I(address implementation, bytes memory args, bytes32 salt)
internal
returns (address instance)
{
instance = deployDeterministicERC1967I(0, implementation, args, salt);
}
/// @dev Deploys a deterministic ERC1967I proxy with `implementation`,`args`, and `salt`.
/// Deposits `value` ETH during deployment.
function deployDeterministicERC1967I(
uint256 value,
address implementation,
bytes memory args,
bytes32 salt
) internal returns (address instance) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
let n := mload(args)
pop(staticcall(gas(), 4, add(args, 0x20), n, add(m, 0x8b), n))
mstore(add(m, 0x6b), 0x3d6000803e603e573d6000fd5b3d6000f35b6020600f3d393d51543d52593df3)
mstore(add(m, 0x4b), 0xa13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af4)
mstore(add(m, 0x2b), 0x600f5155f3365814604357363d3d373d3d363d7f360894)
mstore(add(m, 0x14), implementation)
mstore(m, add(0xfe6100523d8160233d3973, shl(56, n)))
// Do a out-of-gas revert if `n` is greater than `0xffff - 0x52 = 0xffad`.
instance := create2(value, add(m, add(0x15, lt(n, 0xffae))), add(0x75, n), salt)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Creates a deterministic ERC1967I proxy with `implementation`, `args` and `salt`.
/// Note: This method is intended for use in ERC4337 factories,
/// which are expected to NOT revert if the proxy is already deployed.
function createDeterministicERC1967I(address implementation, bytes memory args, bytes32 salt)
internal
returns (bool alreadyDeployed, address instance)
{
return createDeterministicERC1967I(0, implementation, args, salt);
}
/// @dev Creates a deterministic ERC1967I proxy with `implementation`,`args` and `salt`.
/// Deposits `value` ETH during deployment.
/// Note: This method is intended for use in ERC4337 factories,
/// which are expected to NOT revert if the proxy is already deployed.
function createDeterministicERC1967I(
uint256 value,
address implementation,
bytes memory args,
bytes32 salt
) internal returns (bool alreadyDeployed, address instance) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
let n := mload(args)
pop(staticcall(gas(), 4, add(args, 0x20), n, add(m, 0x75), n))
mstore(add(m, 0x55), 0x3d6000803e603e573d6000fd5b3d6000f35b6020600f3d393d51543d52593df3)
mstore(add(m, 0x35), 0xa13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af4)
mstore(add(m, 0x15), 0x5155f3365814604357363d3d373d3d363d7f360894)
mstore(0x16, 0x600f)
mstore(0x14, implementation)
// Do a out-of-gas revert if `n` is greater than `0xffff - 0x52 = 0xffad`.
mstore(gt(n, 0xffad), add(0xfe6100523d8160233d3973, shl(56, n)))
mstore(m, mload(0x16))
// Compute and store the bytecode hash.
mstore8(0x00, 0xff) // Write the prefix.
mstore(0x35, keccak256(m, add(n, 0x75)))
mstore(0x01, shl(96, address()))
mstore(0x15, salt)
instance := keccak256(0x00, 0x55)
for {} 1 {} {
if iszero(extcodesize(instance)) {
instance := create2(value, m, add(0x75, n), salt)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
break
}
alreadyDeployed := 1
if iszero(value) { break }
if iszero(call(gas(), instance, value, codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
break
}
mstore(0x35, 0) // Restore the overwritten part of the free memory pointer.
}
}
/// @dev Returns the initialization code of the ERC1967I proxy of `implementation`and `args`.
function initCodeERC1967I(address implementation, bytes memory args)
internal
pure
returns (bytes memory c)
{
/// @solidity memory-safe-assembly
assembly {
c := mload(0x40)
let n := mload(args)
// Do a out-of-gas revert if `n` is greater than `0xffff - 0x52 = 0xffad`.
returndatacopy(returndatasize(), returndatasize(), gt(n, 0xffad))
for { let i := 0 } lt(i, n) { i := add(i, 0x20) } {
mstore(add(add(c, 0x95), i), mload(add(add(args, 0x20), i)))
}
mstore(add(c, 0x75), 0x3d6000803e603e573d6000fd5b3d6000f35b6020600f3d393d51543d52593df3)
mstore(add(c, 0x55), 0xa13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af4)
mstore(add(c, 0x35), 0x600f5155f3365814604357363d3d373d3d363d7f360894)
mstore(add(c, 0x1e), implementation)
mstore(add(c, 0x0a), add(0x6100523d8160233d3973, shl(56, n)))
mstore(add(c, add(n, 0x95)), 0)
mstore(c, add(0x75, n)) // Store the length.
mstore(0x40, add(c, add(n, 0xb5))) // Allocate memory.
}
}
/// @dev Returns the initialization code hash of the ERC1967I proxy of `implementation` and `args.
function initCodeHashERC1967I(address implementation, bytes memory args)
internal
pure
returns (bytes32 hash)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
let n := mload(args)
// Do a out-of-gas revert if `n` is greater than `0xffff - 0x52 = 0xffad`.
returndatacopy(returndatasize(), returndatasize(), gt(n, 0xffad))
for { let i := 0 } lt(i, n) { i := add(i, 0x20) } {
mstore(add(add(m, 0x75), i), mload(add(add(args, 0x20), i)))
}
mstore(add(m, 0x55), 0x3d6000803e603e573d6000fd5b3d6000f35b6020600f3d393d51543d52593df3)
mstore(add(m, 0x35), 0xa13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af4)
mstore(add(m, 0x15), 0x5155f3365814604357363d3d373d3d363d7f360894)
mstore(0x16, 0x600f)
mstore(0x14, implementation)
mstore(0x00, add(0x6100523d8160233d3973, shl(56, n)))
mstore(m, mload(0x16))
hash := keccak256(m, add(0x75, n))
}
}
/// @dev Returns the address of the ERC1967I proxy of `implementation`, 'args` with `salt` by `deployer`.
/// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly.
function predictDeterministicAddressERC1967I(
address implementation,
bytes memory args,
bytes32 salt,
address deployer
) internal pure returns (address predicted) {
bytes32 hash = initCodeHashERC1967I(implementation, args);
predicted = predictDeterministicAddress(hash, salt, deployer);
}
/// @dev Equivalent to `argsOnERC1967I(instance, start, 2 ** 256 - 1)`.
function argsOnERC1967I(address instance) internal view returns (bytes memory args) {
/// @solidity memory-safe-assembly
assembly {
args := mload(0x40)
mstore(args, and(0xffffffffff, sub(extcodesize(instance), 0x52))) // Store the length.
extcodecopy(instance, add(args, 0x20), 0x52, add(mload(args), 0x20))
mstore(0x40, add(mload(args), add(args, 0x40))) // Allocate memory.
}
}
/// @dev Equivalent to `argsOnERC1967I(instance, start, 2 ** 256 - 1)`.
function argsOnERC1967I(address instance, uint256 start)
internal
view
returns (bytes memory args)
{
/// @solidity memory-safe-assembly
assembly {
args := mload(0x40)
let n := and(0xffffffffff, sub(extcodesize(instance), 0x52))
extcodecopy(instance, add(args, 0x20), add(start, 0x52), add(n, 0x20))
mstore(args, mul(sub(n, start), lt(start, n))) // Store the length.
mstore(0x40, add(mload(args), add(args, 0x40))) // Allocate memory.
}
}
/// @dev Returns a slice of the immutable arguments on `instance` from `start` to `end`.
/// `start` and `end` will be clamped to the range `[0, args.length]`.
/// The `instance` MUST be deployed via the ERC1967 with immutable args functions.
/// Otherwise, the behavior is undefined.
/// Out-of-gas reverts if `instance` does not have any code.
function argsOnERC1967I(address instance, uint256 start, uint256 end)
internal
view
returns (bytes memory args)
{
/// @solidity memory-safe-assembly
assembly {
args := mload(0x40)
if iszero(lt(end, 0xffff)) { end := 0xffff }
let d := mul(sub(end, start), lt(start, end))
extcodecopy(instance, args, add(start, 0x32), add(d, 0x20))
if iszero(and(0xff, mload(add(args, d)))) {
let n := sub(extcodesize(instance), 0x52)
returndatacopy(returndatasize(), returndatasize(), shr(40, n))
d := mul(gt(n, start), sub(d, mul(gt(end, n), sub(end, n))))
}
mstore(args, d) // Store the length.
mstore(add(add(args, 0x20), d), 0) // Zeroize the slot after the bytes.
mstore(0x40, add(add(args, 0x40), d)) // Allocate memory.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ERC1967 BOOTSTRAP OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// A bootstrap is a minimal UUPS implementation that allows an ERC1967 proxy
// pointing to it to be upgraded. The ERC1967 proxy can then be deployed to a
// deterministic address independent of the implementation:
// ```
// address bootstrap = LibClone.erc1967Bootstrap();
// address instance = LibClone.deployDeterministicERC1967(0, bootstrap, salt);
// LibClone.bootstrapERC1967(bootstrap, implementation);
// ```
/// @dev Deploys the ERC1967 bootstrap if it has not been deployed.
function erc1967Bootstrap() internal returns (address) {
return erc1967Bootstrap(address(this));
}
/// @dev Deploys the ERC1967 bootstrap if it has not been deployed.
function erc1967Bootstrap(address authorizedUpgrader) internal returns (address bootstrap) {
bytes memory c = initCodeERC1967Bootstrap(authorizedUpgrader);
bootstrap = predictDeterministicAddress(keccak256(c), bytes32(0), address(this));
/// @solidity memory-safe-assembly
assembly {
if iszero(extcodesize(bootstrap)) {
if iszero(create2(0, add(c, 0x20), mload(c), 0)) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
}
}
}
/// @dev Replaces the implementation at `instance`.
function bootstrapERC1967(address instance, address implementation) internal {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, implementation)
if iszero(call(gas(), instance, 0, 0x0c, 0x14, codesize(), 0x00)) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Replaces the implementation at `instance`, and then call it with `data`.
function bootstrapERC1967AndCall(address instance, address implementation, bytes memory data)
internal
{
/// @solidity memory-safe-assembly
assembly {
let n := mload(data)
mstore(data, implementation)
if iszero(call(gas(), instance, 0, add(data, 0x0c), add(n, 0x14), codesize(), 0x00)) {
if iszero(returndatasize()) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
returndatacopy(mload(0x40), 0x00, returndatasize())
revert(mload(0x40), returndatasize())
}
mstore(data, n) // Restore the length of `data`.
}
}
/// @dev Returns the implementation address of the ERC1967 bootstrap for this contract.
function predictDeterministicAddressERC1967Bootstrap() internal view returns (address) {
return predictDeterministicAddressERC1967Bootstrap(address(this), address(this));
}
/// @dev Returns the implementation address of the ERC1967 bootstrap for this contract.
function predictDeterministicAddressERC1967Bootstrap(
address authorizedUpgrader,
address deployer
) internal pure returns (address) {
bytes32 hash = initCodeHashERC1967Bootstrap(authorizedUpgrader);
return predictDeterministicAddress(hash, bytes32(0), deployer);
}
/// @dev Returns the initialization code of the ERC1967 bootstrap.
function initCodeERC1967Bootstrap(address authorizedUpgrader)
internal
pure
returns (bytes memory c)
{
/// @solidity memory-safe-assembly
assembly {
c := mload(0x40)
mstore(add(c, 0x80), 0x3d3560601c5af46047573d6000383e3d38fd0000000000000000000000000000)
mstore(add(c, 0x60), 0xa920a3ca505d382bbc55601436116049575b005b363d3d373d3d601436036014)
mstore(add(c, 0x40), 0x0338573d3560601c7f360894a13ba1a3210667c828492db98dca3e2076cc3735)
mstore(add(c, 0x20), authorizedUpgrader)
mstore(add(c, 0x0c), 0x606880600a3d393df3fe3373)
mstore(c, 0x72)
mstore(0x40, add(c, 0xa0))
}
}
/// @dev Returns the initialization code hash of the ERC1967 bootstrap.
function initCodeHashERC1967Bootstrap(address authorizedUpgrader)
internal
pure
returns (bytes32)
{
return keccak256(initCodeERC1967Bootstrap(authorizedUpgrader));
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* MINIMAL ERC1967 BEACON PROXY OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// Note: If you use this proxy, you MUST make sure that the beacon is a
// valid ERC1967 beacon. This means that the beacon must always return a valid
// address upon a staticcall to `implementation()`, given sufficient gas.
// For performance, the deployment operations and the proxy assumes that the
// beacon is always valid and will NOT validate it.
/// @dev Deploys a minimal ERC1967 beacon proxy.
function deployERC1967BeaconProxy(address beacon) internal returns (address instance) {
instance = deployERC1967BeaconProxy(0, beacon);
}
/// @dev Deploys a minimal ERC1967 beacon proxy.
/// Deposits `value` ETH during deployment.
function deployERC1967BeaconProxy(uint256 value, address beacon)
internal
returns (address instance)
{
/// @solidity memory-safe-assembly
assembly {
/**
* ---------------------------------------------------------------------------------+
* CREATION (34 bytes) |
* ---------------------------------------------------------------------------------|
* Opcode | Mnemonic | Stack | Memory |
* ---------------------------------------------------------------------------------|
* 60 runSize | PUSH1 runSize | r | |
* 3d | RETURNDATASIZE | 0 r | |
* 81 | DUP2 | r 0 r | |
* 60 offset | PUSH1 offset | o r 0 r | |
* 3d | RETURNDATASIZE | 0 o r 0 r | |
* 39 | CODECOPY | 0 r | [0..runSize): runtime code |
* 73 beac | PUSH20 beac | beac 0 r | [0..runSize): runtime code |
* 60 slotPos | PUSH1 slotPos | slotPos beac 0 r | [0..runSize): runtime code |
* 51 | MLOAD | slot beac 0 r | [0..runSize): runtime code |
* 55 | SSTORE | 0 r | [0..runSize): runtime code |
* f3 | RETURN | | [0..runSize): runtime code |
* ---------------------------------------------------------------------------------|
* RUNTIME (82 bytes) |
* ---------------------------------------------------------------------------------|
* Opcode | Mnemonic | Stack | Memory |
* ---------------------------------------------------------------------------------|
* |
* ::: copy calldata to memory :::::::::::::::::::::::::::::::::::::::::::::::::::: |
* 36 | CALLDATASIZE | cds | |
* 3d | RETURNDATASIZE | 0 cds | |
* 3d | RETURNDATASIZE | 0 0 cds | |
* 37 | CALLDATACOPY | | [0..calldatasize): calldata |
* |
* ::: delegatecall to implementation ::::::::::::::::::::::::::::::::::::::::::::: |
* 3d | RETURNDATASIZE | 0 | |
* 3d | RETURNDATASIZE | 0 0 | |
* 36 | CALLDATASIZE | cds 0 0 | [0..calldatasize): calldata |
* 3d | RETURNDATASIZE | 0 cds 0 0 | [0..calldatasize): calldata |
* |
* ~~~~~~~ beacon staticcall sub procedure ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
* 60 0x20 | PUSH1 0x20 | 32 | |
* 36 | CALLDATASIZE | cds 32 | |
* 60 0x04 | PUSH1 0x04 | 4 cds 32 | |
* 36 | CALLDATASIZE | cds 4 cds 32 | |
* 63 0x5c60da1b | PUSH4 0x5c60da1b | 0x5c60da1b cds 4 cds 32 | |
* 60 0xe0 | PUSH1 0xe0 | 224 0x5c60da1b cds 4 cds 32 | |
* 1b | SHL | sel cds 4 cds 32 | |
* 36 | CALLDATASIZE | cds sel cds 4 cds 32 | |
* 52 | MSTORE | cds 4 cds 32 | sel |
* 7f slot | PUSH32 slot | s cds 4 cds 32 | sel |
* 54 | SLOAD | beac cds 4 cds 32 | sel |
* 5a | GAS | g beac cds 4 cds 32 | sel |
* fa | STATICCALL | succ | impl |
* 50 | POP | | impl |
* 36 | CALLDATASIZE | cds | impl |
* 51 | MLOAD | impl | impl |
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
* 5a | GAS | g impl 0 cds 0 0 | [0..calldatasize): calldata |
* f4 | DELEGATECALL | succ | [0..calldatasize): calldata |
* |
* ::: copy returndata to memory :::::::::::::::::::::::::::::::::::::::::::::::::: |
* 3d | RETURNDATASIZE | rds succ | [0..calldatasize): calldata |
* 60 0x00 | PUSH1 0x00 | 0 rds succ | [0..calldatasize): calldata |
* 80 | DUP1 | 0 0 rds succ | [0..calldatasize): calldata |
* 3e | RETURNDATACOPY | succ | [0..returndatasize): returndata |
* |
* ::: branch on delegatecall status :::::::::::::::::::::::::::::::::::::::::::::: |
* 60 0x4d | PUSH1 0x4d | dest succ | [0..returndatasize): returndata |
* 57 | JUMPI | | [0..returndatasize): returndata |
* |
* ::: delegatecall failed, revert :::::::::::::::::::::::::::::::::::::::::::::::: |
* 3d | RETURNDATASIZE | rds | [0..returndatasize): returndata |
* 60 0x00 | PUSH1 0x00 | 0 rds | [0..returndatasize): returndata |
* fd | REVERT | | [0..returndatasize): returndata |
* |
* ::: delegatecall succeeded, return ::::::::::::::::::::::::::::::::::::::::::::: |
* 5b | JUMPDEST | | [0..returndatasize): returndata |
* 3d | RETURNDATASIZE | rds | [0..returndatasize): returndata |
* 60 0x00 | PUSH1 0x00 | 0 rds | [0..returndatasize): returndata |
* f3 | RETURN | | [0..returndatasize): returndata |
* ---------------------------------------------------------------------------------+
*/
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, 0xb3582b35133d50545afa5036515af43d6000803e604d573d6000fd5b3d6000f3)
mstore(0x40, 0x1b60e01b36527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6c)
mstore(0x20, 0x60195155f3363d3d373d3d363d602036600436635c60da)
mstore(0x09, or(shl(160, 0x60523d8160223d3973), shr(96, shl(96, beacon))))
instance := create(value, 0x0c, 0x74)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero slot.
}
}
/// @dev Deploys a deterministic minimal ERC1967 beacon proxy with `salt`.
function deployDeterministicERC1967BeaconProxy(address beacon, bytes32 salt)
internal
returns (address instance)
{
instance = deployDeterministicERC1967BeaconProxy(0, beacon, salt);
}
/// @dev Deploys a deterministic minimal ERC1967 beacon proxy with `salt`.
/// Deposits `value` ETH during deployment.
function deployDeterministicERC1967BeaconProxy(uint256 value, address beacon, bytes32 salt)
internal
returns (address instance)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, 0xb3582b35133d50545afa5036515af43d6000803e604d573d6000fd5b3d6000f3)
mstore(0x40, 0x1b60e01b36527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6c)
mstore(0x20, 0x60195155f3363d3d373d3d363d602036600436635c60da)
mstore(0x09, or(shl(160, 0x60523d8160223d3973), shr(96, shl(96, beacon))))
instance := create2(value, 0x0c, 0x74, salt)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero slot.
}
}
/// @dev Creates a deterministic minimal ERC1967 beacon proxy with `salt`.
/// Note: This method is intended for use in ERC4337 factories,
/// which are expected to NOT revert if the proxy is already deployed.
function createDeterministicERC1967BeaconProxy(address beacon, bytes32 salt)
internal
returns (bool alreadyDeployed, address instance)
{
return createDeterministicERC1967BeaconProxy(0, beacon, salt);
}
/// @dev Creates a deterministic minimal ERC1967 beacon proxy with `salt`.
/// Deposits `value` ETH during deployment.
/// Note: This method is intended for use in ERC4337 factories,
/// which are expected to NOT revert if the proxy is already deployed.
function createDeterministicERC1967BeaconProxy(uint256 value, address beacon, bytes32 salt)
internal
returns (bool alreadyDeployed, address instance)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, 0xb3582b35133d50545afa5036515af43d6000803e604d573d6000fd5b3d6000f3)
mstore(0x40, 0x1b60e01b36527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6c)
mstore(0x20, 0x60195155f3363d3d373d3d363d602036600436635c60da)
mstore(0x09, or(shl(160, 0x60523d8160223d3973), shr(96, shl(96, beacon))))
// Compute and store the bytecode hash.
mstore(add(m, 0x35), keccak256(0x0c, 0x74))
mstore(m, shl(88, address()))
mstore8(m, 0xff) // Write the prefix.
mstore(add(m, 0x15), salt)
instance := keccak256(m, 0x55)
for {} 1 {} {
if iszero(extcodesize(instance)) {
instance := create2(value, 0x0c, 0x74, salt)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
break
}
alreadyDeployed := 1
if iszero(value) { break }
if iszero(call(gas(), instance, value, codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
break
}
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero slot.
}
}
/// @dev Returns the initialization code of the minimal ERC1967 beacon proxy.
function initCodeERC1967BeaconProxy(address beacon) internal pure returns (bytes memory c) {
/// @solidity memory-safe-assembly
assembly {
c := mload(0x40)
mstore(add(c, 0x74), 0xb3582b35133d50545afa5036515af43d6000803e604d573d6000fd5b3d6000f3)
mstore(add(c, 0x54), 0x1b60e01b36527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6c)
mstore(add(c, 0x34), 0x60195155f3363d3d373d3d363d602036600436635c60da)
mstore(add(c, 0x1d), beacon)
mstore(add(c, 0x09), 0x60523d8160223d3973)
mstore(add(c, 0x94), 0)
mstore(c, 0x74) // Store the length.
mstore(0x40, add(c, 0xa0)) // Allocate memory.
}
}
/// @dev Returns the initialization code hash of the minimal ERC1967 beacon proxy.
function initCodeHashERC1967BeaconProxy(address beacon) internal pure returns (bytes32 hash) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, 0xb3582b35133d50545afa5036515af43d6000803e604d573d6000fd5b3d6000f3)
mstore(0x40, 0x1b60e01b36527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6c)
mstore(0x20, 0x60195155f3363d3d373d3d363d602036600436635c60da)
mstore(0x09, or(shl(160, 0x60523d8160223d3973), shr(96, shl(96, beacon))))
hash := keccak256(0x0c, 0x74)
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero slot.
}
}
/// @dev Returns the address of the ERC1967 beacon proxy, with `salt` by `deployer`.
/// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly.
function predictDeterministicAddressERC1967BeaconProxy(
address beacon,
bytes32 salt,
address deployer
) internal pure returns (address predicted) {
bytes32 hash = initCodeHashERC1967BeaconProxy(beacon);
predicted = predictDeterministicAddress(hash, salt, deployer);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ERC1967 BEACON PROXY WITH IMMUTABLE ARGS OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Deploys a minimal ERC1967 beacon proxy with `args`.
function deployERC1967BeaconProxy(address beacon, bytes memory args)
internal
returns (address instance)
{
instance = deployERC1967BeaconProxy(0, beacon, args);
}
/// @dev Deploys a minimal ERC1967 beacon proxy with `args`.
/// Deposits `value` ETH during deployment.
function deployERC1967BeaconProxy(uint256 value, address beacon, bytes memory args)
internal
returns (address instance)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
let n := mload(args)
pop(staticcall(gas(), 4, add(args, 0x20), n, add(m, 0x8b), n))
mstore(add(m, 0x6b), 0xb3582b35133d50545afa5036515af43d6000803e604d573d6000fd5b3d6000f3)
mstore(add(m, 0x4b), 0x1b60e01b36527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6c)
mstore(add(m, 0x2b), 0x60195155f3363d3d373d3d363d602036600436635c60da)
mstore(add(m, 0x14), beacon)
// Do a out-of-gas revert if `n` is greater than `0xffff - 0x52 = 0xffad`.
mstore(add(m, gt(n, 0xffad)), add(0xfe6100523d8160233d3973, shl(56, n)))
instance := create(value, add(m, 0x16), add(n, 0x75))
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Deploys a deterministic minimal ERC1967 beacon proxy with `args` and `salt`.
function deployDeterministicERC1967BeaconProxy(address beacon, bytes memory args, bytes32 salt)
internal
returns (address instance)
{
instance = deployDeterministicERC1967BeaconProxy(0, beacon, args, salt);
}
/// @dev Deploys a deterministic minimal ERC1967 beacon proxy with `args` and `salt`.
/// Deposits `value` ETH during deployment.
function deployDeterministicERC1967BeaconProxy(
uint256 value,
address beacon,
bytes memory args,
bytes32 salt
) internal returns (address instance) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
let n := mload(args)
pop(staticcall(gas(), 4, add(args, 0x20), n, add(m, 0x8b), n))
mstore(add(m, 0x6b), 0xb3582b35133d50545afa5036515af43d6000803e604d573d6000fd5b3d6000f3)
mstore(add(m, 0x4b), 0x1b60e01b36527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6c)
mstore(add(m, 0x2b), 0x60195155f3363d3d373d3d363d602036600436635c60da)
mstore(add(m, 0x14), beacon)
// Do a out-of-gas revert if `n` is greater than `0xffff - 0x52 = 0xffad`.
mstore(add(m, gt(n, 0xffad)), add(0xfe6100523d8160233d3973, shl(56, n)))
instance := create2(value, add(m, 0x16), add(n, 0x75), salt)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Creates a deterministic minimal ERC1967 beacon proxy with `args` and `salt`.
/// Note: This method is intended for use in ERC4337 factories,
/// which are expected to NOT revert if the proxy is already deployed.
function createDeterministicERC1967BeaconProxy(address beacon, bytes memory args, bytes32 salt)
internal
returns (bool alreadyDeployed, address instance)
{
return createDeterministicERC1967BeaconProxy(0, beacon, args, salt);
}
/// @dev Creates a deterministic minimal ERC1967 beacon proxy with `args` and `salt`.
/// Deposits `value` ETH during deployment.
/// Note: This method is intended for use in ERC4337 factories,
/// which are expected to NOT revert if the proxy is already deployed.
function createDeterministicERC1967BeaconProxy(
uint256 value,
address beacon,
bytes memory args,
bytes32 salt
) internal returns (bool alreadyDeployed, address instance) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
let n := mload(args)
pop(staticcall(gas(), 4, add(args, 0x20), n, add(m, 0x8b), n))
mstore(add(m, 0x6b), 0xb3582b35133d50545afa5036515af43d6000803e604d573d6000fd5b3d6000f3)
mstore(add(m, 0x4b), 0x1b60e01b36527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6c)
mstore(add(m, 0x2b), 0x60195155f3363d3d373d3d363d602036600436635c60da)
mstore(add(m, 0x14), beacon)
// Do a out-of-gas revert if `n` is greater than `0xffff - 0x52 = 0xffad`.
mstore(add(m, gt(n, 0xffad)), add(0xfe6100523d8160233d3973, shl(56, n)))
// Compute and store the bytecode hash.
mstore8(0x00, 0xff) // Write the prefix.
mstore(0x35, keccak256(add(m, 0x16), add(n, 0x75)))
mstore(0x01, shl(96, address()))
mstore(0x15, salt)
instance := keccak256(0x00, 0x55)
for {} 1 {} {
if iszero(extcodesize(instance)) {
instance := create2(value, add(m, 0x16), add(n, 0x75), salt)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
break
}
alreadyDeployed := 1
if iszero(value) { break }
if iszero(call(gas(), instance, value, codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
break
}
mstore(0x35, 0) // Restore the overwritten part of the free memory pointer.
}
}
/// @dev Returns the initialization code of the minimal ERC1967 beacon proxy.
function initCodeERC1967BeaconProxy(address beacon, bytes memory args)
internal
pure
returns (bytes memory c)
{
/// @solidity memory-safe-assembly
assembly {
c := mload(0x40)
let n := mload(args)
// Do a out-of-gas revert if `n` is greater than `0xffff - 0x52 = 0xffad`.
returndatacopy(returndatasize(), returndatasize(), gt(n, 0xffad))
for { let i := 0 } lt(i, n) { i := add(i, 0x20) } {
mstore(add(add(c, 0x95), i), mload(add(add(args, 0x20), i)))
}
mstore(add(c, 0x75), 0xb3582b35133d50545afa5036515af43d6000803e604d573d6000fd5b3d6000f3)
mstore(add(c, 0x55), 0x1b60e01b36527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6c)
mstore(add(c, 0x35), 0x60195155f3363d3d373d3d363d602036600436635c60da)
mstore(add(c, 0x1e), beacon)
mstore(add(c, 0x0a), add(0x6100523d8160233d3973, shl(56, n)))
mstore(c, add(n, 0x75)) // Store the length.
mstore(add(c, add(n, 0x95)), 0) // Zeroize the slot after the bytes.
mstore(0x40, add(c, add(n, 0xb5))) // Allocate memory.
}
}
/// @dev Returns the initialization code hash of the minimal ERC1967 beacon proxy with `args`.
function initCodeHashERC1967BeaconProxy(address beacon, bytes memory args)
internal
pure
returns (bytes32 hash)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
let n := mload(args)
// Do a out-of-gas revert if `n` is greater than `0xffff - 0x52 = 0xffad`.
returndatacopy(returndatasize(), returndatasize(), gt(n, 0xffad))
for { let i := 0 } lt(i, n) { i := add(i, 0x20) } {
mstore(add(add(m, 0x8b), i), mload(add(add(args, 0x20), i)))
}
mstore(add(m, 0x6b), 0xb3582b35133d50545afa5036515af43d6000803e604d573d6000fd5b3d6000f3)
mstore(add(m, 0x4b), 0x1b60e01b36527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6c)
mstore(add(m, 0x2b), 0x60195155f3363d3d373d3d363d602036600436635c60da)
mstore(add(m, 0x14), beacon)
mstore(m, add(0x6100523d8160233d3973, shl(56, n)))
hash := keccak256(add(m, 0x16), add(n, 0x75))
}
}
/// @dev Returns the address of the ERC1967 beacon proxy with `args`, with `salt` by `deployer`.
/// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly.
function predictDeterministicAddressERC1967BeaconProxy(
address beacon,
bytes memory args,
bytes32 salt,
address deployer
) internal pure returns (address predicted) {
bytes32 hash = initCodeHashERC1967BeaconProxy(beacon, args);
predicted = predictDeterministicAddress(hash, salt, deployer);
}
/// @dev Equivalent to `argsOnERC1967BeaconProxy(instance, start, 2 ** 256 - 1)`.
function argsOnERC1967BeaconProxy(address instance) internal view returns (bytes memory args) {
/// @solidity memory-safe-assembly
assembly {
args := mload(0x40)
mstore(args, and(0xffffffffff, sub(extcodesize(instance), 0x52))) // Store the length.
extcodecopy(instance, add(args, 0x20), 0x52, add(mload(args), 0x20))
mstore(0x40, add(mload(args), add(args, 0x40))) // Allocate memory.
}
}
/// @dev Equivalent to `argsOnERC1967BeaconProxy(instance, start, 2 ** 256 - 1)`.
function argsOnERC1967BeaconProxy(address instance, uint256 start)
internal
view
returns (bytes memory args)
{
/// @solidity memory-safe-assembly
assembly {
args := mload(0x40)
let n := and(0xffffffffff, sub(extcodesize(instance), 0x52))
extcodecopy(instance, add(args, 0x20), add(start, 0x52), add(n, 0x20))
mstore(args, mul(sub(n, start), lt(start, n))) // Store the length.
mstore(0x40, add(args, add(0x40, mload(args)))) // Allocate memory.
}
}
/// @dev Returns a slice of the immutable arguments on `instance` from `start` to `end`.
/// `start` and `end` will be clamped to the range `[0, args.length]`.
/// The `instance` MUST be deployed via the ERC1967 beacon proxy with immutable args functions.
/// Otherwise, the behavior is undefined.
/// Out-of-gas reverts if `instance` does not have any code.
function argsOnERC1967BeaconProxy(address instance, uint256 start, uint256 end)
internal
view
returns (bytes memory args)
{
/// @solidity memory-safe-assembly
assembly {
args := mload(0x40)
if iszero(lt(end, 0xffff)) { end := 0xffff }
let d := mul(sub(end, start), lt(start, end))
extcodecopy(instance, args, add(start, 0x32), add(d, 0x20))
if iszero(and(0xff, mload(add(args, d)))) {
let n := sub(extcodesize(instance), 0x52)
returndatacopy(returndatasize(), returndatasize(), shr(40, n))
d := mul(gt(n, start), sub(d, mul(gt(end, n), sub(end, n))))
}
mstore(args, d) // Store the length.
mstore(add(add(args, 0x20), d), 0) // Zeroize the slot after the bytes.
mstore(0x40, add(add(args, 0x40), d)) // Allocate memory.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ERC1967I BEACON PROXY OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// Note: This proxy has a special code path that activates if `calldatasize() == 1`.
// This code path skips the delegatecall and directly returns the `implementation` address.
// The returned implementation is guaranteed to be valid if the keccak256 of the
// proxy's code is equal to `ERC1967_BEACON_PROXY_CODE_HASH`.
//
// If you use this proxy, you MUST make sure that the beacon is a
// valid ERC1967 beacon. This means that the beacon must always return a valid
// address upon a staticcall to `implementation()`, given sufficient gas.
// For performance, the deployment operations and the proxy assumes that the
// beacon is always valid and will NOT validate it.
/// @dev Deploys a ERC1967I beacon proxy.
function deployERC1967IBeaconProxy(address beacon) internal returns (address instance) {
instance = deployERC1967IBeaconProxy(0, beacon);
}
/// @dev Deploys a ERC1967I beacon proxy.
/// Deposits `value` ETH during deployment.
function deployERC1967IBeaconProxy(uint256 value, address beacon)
internal
returns (address instance)
{
/// @solidity memory-safe-assembly
assembly {
/**
* ---------------------------------------------------------------------------------+
* CREATION (34 bytes) |
* ---------------------------------------------------------------------------------|
* Opcode | Mnemonic | Stack | Memory |
* ---------------------------------------------------------------------------------|
* 60 runSize | PUSH1 runSize | r | |
* 3d | RETURNDATASIZE | 0 r | |
* 81 | DUP2 | r 0 r | |
* 60 offset | PUSH1 offset | o r 0 r | |
* 3d | RETURNDATASIZE | 0 o r 0 r | |
* 39 | CODECOPY | 0 r | [0..runSize): runtime code |
* 73 beac | PUSH20 beac | beac 0 r | [0..runSize): runtime code |
* 60 slotPos | PUSH1 slotPos | slotPos beac 0 r | [0..runSize): runtime code |
* 51 | MLOAD | slot beac 0 r | [0..runSize): runtime code |
* 55 | SSTORE | 0 r | [0..runSize): runtime code |
* f3 | RETURN | | [0..runSize): runtime code |
* ---------------------------------------------------------------------------------|
* RUNTIME (87 bytes) |
* ---------------------------------------------------------------------------------|
* Opcode | Mnemonic | Stack | Memory |
* ---------------------------------------------------------------------------------|
* |
* ::: copy calldata to memory :::::::::::::::::::::::::::::::::::::::::::::::::::: |
* 36 | CALLDATASIZE | cds | |
* 3d | RETURNDATASIZE | 0 cds | |
* 3d | RETURNDATASIZE | 0 0 cds | |
* 37 | CALLDATACOPY | | [0..calldatasize): calldata |
* |
* ::: delegatecall to implementation ::::::::::::::::::::::::::::::::::::::::::::: |
* 3d | RETURNDATASIZE | 0 | |
* 3d | RETURNDATASIZE | 0 0 | |
* 36 | CALLDATASIZE | cds 0 0 | [0..calldatasize): calldata |
* 3d | RETURNDATASIZE | 0 cds 0 0 | [0..calldatasize): calldata |
* |
* ~~~~~~~ beacon staticcall sub procedure ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
* 60 0x20 | PUSH1 0x20 | 32 | |
* 36 | CALLDATASIZE | cds 32 | |
* 60 0x04 | PUSH1 0x04 | 4 cds 32 | |
* 36 | CALLDATASIZE | cds 4 cds 32 | |
* 63 0x5c60da1b | PUSH4 0x5c60da1b | 0x5c60da1b cds 4 cds 32 | |
* 60 0xe0 | PUSH1 0xe0 | 224 0x5c60da1b cds 4 cds 32 | |
* 1b | SHL | sel cds 4 cds 32 | |
* 36 | CALLDATASIZE | cds sel cds 4 cds 32 | |
* 52 | MSTORE | cds 4 cds 32 | sel |
* 7f slot | PUSH32 slot | s cds 4 cds 32 | sel |
* 54 | SLOAD | beac cds 4 cds 32 | sel |
* 5a | GAS | g beac cds 4 cds 32 | sel |
* fa | STATICCALL | succ | impl |
* ~~~~~~ check calldatasize ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
* 36 | CALLDATASIZE | cds succ | |
* 14 | EQ | | impl |
* 60 0x52 | PUSH1 0x52 | | impl |
* 57 | JUMPI | | impl |
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
* 36 | CALLDATASIZE | cds | impl |
* 51 | MLOAD | impl | impl |
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
* 5a | GAS | g impl 0 cds 0 0 | [0..calldatasize): calldata |
* f4 | DELEGATECALL | succ | [0..calldatasize): calldata |
* |
* ::: copy returndata to memory :::::::::::::::::::::::::::::::::::::::::::::::::: |
* 3d | RETURNDATASIZE | rds succ | [0..calldatasize): calldata |
* 60 0x00 | PUSH1 0x00 | 0 rds succ | [0..calldatasize): calldata |
* 60 0x01 | PUSH1 0x01 | 1 0 rds succ | [0..calldatasize): calldata |
* 3e | RETURNDATACOPY | succ | [1..returndatasize): returndata |
* |
* ::: branch on delegatecall status :::::::::::::::::::::::::::::::::::::::::::::: |
* 60 0x52 | PUSH1 0x52 | dest succ | [1..returndatasize): returndata |
* 57 | JUMPI | | [1..returndatasize): returndata |
* |
* ::: delegatecall failed, revert :::::::::::::::::::::::::::::::::::::::::::::::: |
* 3d | RETURNDATASIZE | rds | [1..returndatasize): returndata |
* 60 0x01 | PUSH1 0x01 | 1 rds | [1..returndatasize): returndata |
* fd | REVERT | | [1..returndatasize): returndata |
* |
* ::: delegatecall succeeded, return ::::::::::::::::::::::::::::::::::::::::::::: |
* 5b | JUMPDEST | | [1..returndatasize): returndata |
* 3d | RETURNDATASIZE | rds | [1..returndatasize): returndata |
* 60 0x01 | PUSH1 0x01 | 1 rds | [1..returndatasize): returndata |
* f3 | RETURN | | [1..returndatasize): returndata |
* ---------------------------------------------------------------------------------+
*/
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, 0x3d50545afa361460525736515af43d600060013e6052573d6001fd5b3d6001f3)
mstore(0x40, 0x527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b3513)
mstore(0x20, 0x60195155f3363d3d373d3d363d602036600436635c60da1b60e01b36)
mstore(0x04, or(shl(160, 0x60573d8160223d3973), shr(96, shl(96, beacon))))
instance := create(value, 0x07, 0x79)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero slot.
}
}
/// @dev Deploys a deterministic ERC1967I beacon proxy with `salt`.
function deployDeterministicERC1967IBeaconProxy(address beacon, bytes32 salt)
internal
returns (address instance)
{
instance = deployDeterministicERC1967IBeaconProxy(0, beacon, salt);
}
/// @dev Deploys a deterministic ERC1967I beacon proxy with `salt`.
/// Deposits `value` ETH during deployment.
function deployDeterministicERC1967IBeaconProxy(uint256 value, address beacon, bytes32 salt)
internal
returns (address instance)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, 0x3d50545afa361460525736515af43d600060013e6052573d6001fd5b3d6001f3)
mstore(0x40, 0x527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b3513)
mstore(0x20, 0x60195155f3363d3d373d3d363d602036600436635c60da1b60e01b36)
mstore(0x04, or(shl(160, 0x60573d8160223d3973), shr(96, shl(96, beacon))))
instance := create2(value, 0x07, 0x79, salt)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero slot.
}
}
/// @dev Creates a deterministic ERC1967I beacon proxy with `salt`.
/// Note: This method is intended for use in ERC4337 factories,
/// which are expected to NOT revert if the proxy is already deployed.
function createDeterministicERC1967IBeaconProxy(address beacon, bytes32 salt)
internal
returns (bool alreadyDeployed, address instance)
{
return createDeterministicERC1967IBeaconProxy(0, beacon, salt);
}
/// @dev Creates a deterministic ERC1967I beacon proxy with `salt`.
/// Deposits `value` ETH during deployment.
/// Note: This method is intended for use in ERC4337 factories,
/// which are expected to NOT revert if the proxy is already deployed.
function createDeterministicERC1967IBeaconProxy(uint256 value, address beacon, bytes32 salt)
internal
returns (bool alreadyDeployed, address instance)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, 0x3d50545afa361460525736515af43d600060013e6052573d6001fd5b3d6001f3)
mstore(0x40, 0x527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b3513)
mstore(0x20, 0x60195155f3363d3d373d3d363d602036600436635c60da1b60e01b36)
mstore(0x04, or(shl(160, 0x60573d8160223d3973), shr(96, shl(96, beacon))))
// Compute and store the bytecode hash.
mstore(add(m, 0x35), keccak256(0x07, 0x79))
mstore(m, shl(88, address()))
mstore8(m, 0xff) // Write the prefix.
mstore(add(m, 0x15), salt)
instance := keccak256(m, 0x55)
for {} 1 {} {
if iszero(extcodesize(instance)) {
instance := create2(value, 0x07, 0x79, salt)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
break
}
alreadyDeployed := 1
if iszero(value) { break }
if iszero(call(gas(), instance, value, codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
break
}
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero slot.
}
}
/// @dev Returns the initialization code of the ERC1967I beacon proxy.
function initCodeERC1967IBeaconProxy(address beacon) internal pure returns (bytes memory c) {
/// @solidity memory-safe-assembly
assembly {
c := mload(0x40)
mstore(add(c, 0x79), 0x3d50545afa361460525736515af43d600060013e6052573d6001fd5b3d6001f3)
mstore(add(c, 0x59), 0x527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b3513)
mstore(add(c, 0x39), 0x60195155f3363d3d373d3d363d602036600436635c60da1b60e01b36)
mstore(add(c, 0x1d), beacon)
mstore(add(c, 0x09), 0x60573d8160223d3973)
mstore(add(c, 0x99), 0)
mstore(c, 0x79) // Store the length.
mstore(0x40, add(c, 0xa0)) // Allocate memory.
}
}
/// @dev Returns the initialization code hash of the ERC1967I beacon proxy.
function initCodeHashERC1967IBeaconProxy(address beacon) internal pure returns (bytes32 hash) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, 0x3d50545afa361460525736515af43d600060013e6052573d6001fd5b3d6001f3)
mstore(0x40, 0x527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b3513)
mstore(0x20, 0x60195155f3363d3d373d3d363d602036600436635c60da1b60e01b36)
mstore(0x04, or(shl(160, 0x60573d8160223d3973), shr(96, shl(96, beacon))))
hash := keccak256(0x07, 0x79)
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero slot.
}
}
/// @dev Returns the address of the ERC1967I beacon proxy, with `salt` by `deployer`.
/// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly.
function predictDeterministicAddressERC1967IBeaconProxy(
address beacon,
bytes32 salt,
address deployer
) internal pure returns (address predicted) {
bytes32 hash = initCodeHashERC1967IBeaconProxy(beacon);
predicted = predictDeterministicAddress(hash, salt, deployer);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ERC1967I BEACON PROXY WITH IMMUTABLE ARGS OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Deploys a ERC1967I beacon proxy with `args.
function deployERC1967IBeaconProxy(address beacon, bytes memory args)
internal
returns (address instance)
{
instance = deployERC1967IBeaconProxy(0, beacon, args);
}
/// @dev Deploys a ERC1967I beacon proxy with `args.
/// Deposits `value` ETH during deployment.
function deployERC1967IBeaconProxy(uint256 value, address beacon, bytes memory args)
internal
returns (address instance)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
let n := mload(args)
pop(staticcall(gas(), 4, add(args, 0x20), n, add(m, 0x90), n))
mstore(add(m, 0x70), 0x3d50545afa361460525736515af43d600060013e6052573d6001fd5b3d6001f3)
mstore(add(m, 0x50), 0x527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b3513)
mstore(add(m, 0x30), 0x60195155f3363d3d373d3d363d602036600436635c60da1b60e01b36)
mstore(add(m, 0x14), beacon)
// Do a out-of-gas revert if `n` is greater than `0xffff - 0x57 = 0xffa8`.
mstore(add(m, gt(n, 0xffa8)), add(0xfe6100573d8160233d3973, shl(56, n)))
instance := create(value, add(m, 0x16), add(n, 0x7a))
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Deploys a deterministic ERC1967I beacon proxy with `args` and `salt`.
function deployDeterministicERC1967IBeaconProxy(address beacon, bytes memory args, bytes32 salt)
internal
returns (address instance)
{
instance = deployDeterministicERC1967IBeaconProxy(0, beacon, args, salt);
}
/// @dev Deploys a deterministic ERC1967I beacon proxy with `args` and `salt`.
/// Deposits `value` ETH during deployment.
function deployDeterministicERC1967IBeaconProxy(
uint256 value,
address beacon,
bytes memory args,
bytes32 salt
) internal returns (address instance) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
let n := mload(args)
pop(staticcall(gas(), 4, add(args, 0x20), n, add(m, 0x90), n))
mstore(add(m, 0x70), 0x3d50545afa361460525736515af43d600060013e6052573d6001fd5b3d6001f3)
mstore(add(m, 0x50), 0x527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b3513)
mstore(add(m, 0x30), 0x60195155f3363d3d373d3d363d602036600436635c60da1b60e01b36)
mstore(add(m, 0x14), beacon)
// Do a out-of-gas revert if `n` is greater than `0xffff - 0x57 = 0xffa8`.
mstore(add(m, gt(n, 0xffa8)), add(0xfe6100573d8160233d3973, shl(56, n)))
instance := create2(value, add(m, 0x16), add(n, 0x7a), salt)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Creates a deterministic ERC1967I beacon proxy with `args` and `salt`.
/// Note: This method is intended for use in ERC4337 factories,
/// which are expected to NOT revert if the proxy is already deployed.
function createDeterministicERC1967IBeaconProxy(address beacon, bytes memory args, bytes32 salt)
internal
returns (bool alreadyDeployed, address instance)
{
return createDeterministicERC1967IBeaconProxy(0, beacon, args, salt);
}
/// @dev Creates a deterministic ERC1967I beacon proxy with `args` and `salt`.
/// Deposits `value` ETH during deployment.
/// Note: This method is intended for use in ERC4337 factories,
/// which are expected to NOT revert if the proxy is already deployed.
function createDeterministicERC1967IBeaconProxy(
uint256 value,
address beacon,
bytes memory args,
bytes32 salt
) internal returns (bool alreadyDeployed, address instance) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
let n := mload(args)
pop(staticcall(gas(), 4, add(args, 0x20), n, add(m, 0x90), n))
mstore(add(m, 0x70), 0x3d50545afa361460525736515af43d600060013e6052573d6001fd5b3d6001f3)
mstore(add(m, 0x50), 0x527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b3513)
mstore(add(m, 0x30), 0x60195155f3363d3d373d3d363d602036600436635c60da1b60e01b36)
mstore(add(m, 0x14), beacon)
// Do a out-of-gas revert if `n` is greater than `0xffff - 0x57 = 0xffa8`.
mstore(add(m, gt(n, 0xffa8)), add(0xfe6100573d8160233d3973, shl(56, n)))
// Compute and store the bytecode hash.
mstore8(0x00, 0xff) // Write the prefix.
mstore(0x35, keccak256(add(m, 0x16), add(n, 0x7a)))
mstore(0x01, shl(96, address()))
mstore(0x15, salt)
instance := keccak256(0x00, 0x55)
for {} 1 {} {
if iszero(extcodesize(instance)) {
instance := create2(value, add(m, 0x16), add(n, 0x7a), salt)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
break
}
alreadyDeployed := 1
if iszero(value) { break }
if iszero(call(gas(), instance, value, codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
break
}
mstore(0x35, 0) // Restore the overwritten part of the free memory pointer.
}
}
/// @dev Returns the initialization code of the ERC1967I beacon proxy with `args`.
function initCodeERC1967IBeaconProxy(address beacon, bytes memory args)
internal
pure
returns (bytes memory c)
{
/// @solidity memory-safe-assembly
assembly {
c := mload(0x40)
let n := mload(args)
// Do a out-of-gas revert if `n` is greater than `0xffff - 0x57 = 0xffa8`.
returndatacopy(returndatasize(), returndatasize(), gt(n, 0xffa8))
for { let i := 0 } lt(i, n) { i := add(i, 0x20) } {
mstore(add(add(c, 0x9a), i), mload(add(add(args, 0x20), i)))
}
mstore(add(c, 0x7a), 0x3d50545afa361460525736515af43d600060013e6052573d6001fd5b3d6001f3)
mstore(add(c, 0x5a), 0x527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b3513)
mstore(add(c, 0x3a), 0x60195155f3363d3d373d3d363d602036600436635c60da1b60e01b36)
mstore(add(c, 0x1e), beacon)
mstore(add(c, 0x0a), add(0x6100573d8160233d3973, shl(56, n)))
mstore(add(c, add(n, 0x9a)), 0)
mstore(c, add(n, 0x7a)) // Store the length.
mstore(0x40, add(c, add(n, 0xba))) // Allocate memory.
}
}
/// @dev Returns the initialization code hash of the ERC1967I beacon proxy with `args`.
function initCodeHashERC1967IBeaconProxy(address beacon, bytes memory args)
internal
pure
returns (bytes32 hash)
{
/// @solidity memory-safe-assembly
assembly {
let c := mload(0x40) // Cache the free memory pointer.
let n := mload(args)
// Do a out-of-gas revert if `n` is greater than `0xffff - 0x57 = 0xffa8`.
returndatacopy(returndatasize(), returndatasize(), gt(n, 0xffa8))
for { let i := 0 } lt(i, n) { i := add(i, 0x20) } {
mstore(add(add(c, 0x90), i), mload(add(add(args, 0x20), i)))
}
mstore(add(c, 0x70), 0x3d50545afa361460525736515af43d600060013e6052573d6001fd5b3d6001f3)
mstore(add(c, 0x50), 0x527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b3513)
mstore(add(c, 0x30), 0x60195155f3363d3d373d3d363d602036600436635c60da1b60e01b36)
mstore(add(c, 0x14), beacon)
mstore(c, add(0x6100573d8160233d3973, shl(56, n)))
hash := keccak256(add(c, 0x16), add(n, 0x7a))
}
}
/// @dev Returns the address of the ERC1967I beacon proxy, with `args` and salt` by `deployer`.
/// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly.
function predictDeterministicAddressERC1967IBeaconProxy(
address beacon,
bytes memory args,
bytes32 salt,
address deployer
) internal pure returns (address predicted) {
bytes32 hash = initCodeHashERC1967IBeaconProxy(beacon, args);
predicted = predictDeterministicAddress(hash, salt, deployer);
}
/// @dev Equivalent to `argsOnERC1967IBeaconProxy(instance, start, 2 ** 256 - 1)`.
function argsOnERC1967IBeaconProxy(address instance)
internal
view
returns (bytes memory args)
{
/// @solidity memory-safe-assembly
assembly {
args := mload(0x40)
mstore(args, and(0xffffffffff, sub(extcodesize(instance), 0x57))) // Store the length.
extcodecopy(instance, add(args, 0x20), 0x57, add(mload(args), 0x20))
mstore(0x40, add(mload(args), add(args, 0x40))) // Allocate memory.
}
}
/// @dev Equivalent to `argsOnERC1967IBeaconProxy(instance, start, 2 ** 256 - 1)`.
function argsOnERC1967IBeaconProxy(address instance, uint256 start)
internal
view
returns (bytes memory args)
{
/// @solidity memory-safe-assembly
assembly {
args := mload(0x40)
let n := and(0xffffffffff, sub(extcodesize(instance), 0x57))
extcodecopy(instance, add(args, 0x20), add(start, 0x57), add(n, 0x20))
mstore(args, mul(sub(n, start), lt(start, n))) // Store the length.
mstore(0x40, add(args, add(0x40, mload(args)))) // Allocate memory.
}
}
/// @dev Returns a slice of the immutable arguments on `instance` from `start` to `end`.
/// `start` and `end` will be clamped to the range `[0, args.length]`.
/// The `instance` MUST be deployed via the ERC1967I beacon proxy with immutable args functions.
/// Otherwise, the behavior is undefined.
/// Out-of-gas reverts if `instance` does not have any code.
function argsOnERC1967IBeaconProxy(address instance, uint256 start, uint256 end)
internal
view
returns (bytes memory args)
{
/// @solidity memory-safe-assembly
assembly {
args := mload(0x40)
if iszero(lt(end, 0xffff)) { end := 0xffff }
let d := mul(sub(end, start), lt(start, end))
extcodecopy(instance, args, add(start, 0x37), add(d, 0x20))
if iszero(and(0xff, mload(add(args, d)))) {
let n := sub(extcodesize(instance), 0x57)
returndatacopy(returndatasize(), returndatasize(), shr(40, n))
d := mul(gt(n, start), sub(d, mul(gt(end, n), sub(end, n))))
}
mstore(args, d) // Store the length.
mstore(add(add(args, 0x20), d), 0) // Zeroize the slot after the bytes.
mstore(0x40, add(add(args, 0x40), d)) // Allocate memory.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* OTHER OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns `address(0)` if the implementation address cannot be determined.
function implementationOf(address instance) internal view returns (address result) {
/// @solidity memory-safe-assembly
assembly {
for { extcodecopy(instance, 0x00, 0x00, 0x57) } 1 {} {
if mload(0x2d) {
// ERC1967I and ERC1967IBeaconProxy detection.
if or(
eq(keccak256(0x00, 0x52), ERC1967I_CODE_HASH),
eq(keccak256(0x00, 0x57), ERC1967I_BEACON_PROXY_CODE_HASH)
) {
pop(staticcall(gas(), instance, 0x00, 0x01, 0x00, 0x20))
result := mload(0x0c)
break
}
}
// 0age clone detection.
result := mload(0x0b)
codecopy(0x0b, codesize(), 0x14) // Zeroize the 20 bytes for the address.
if iszero(xor(keccak256(0x00, 0x2c), CLONE_CODE_HASH)) { break }
mstore(0x0b, result) // Restore the zeroized memory.
// CWIA detection.
result := mload(0x0a)
codecopy(0x0a, codesize(), 0x14) // Zeroize the 20 bytes for the address.
if iszero(xor(keccak256(0x00, 0x2d), CWIA_CODE_HASH)) { break }
mstore(0x0a, result) // Restore the zeroized memory.
// PUSH0 clone detection.
result := mload(0x09)
codecopy(0x09, codesize(), 0x14) // Zeroize the 20 bytes for the address.
result := shr(xor(keccak256(0x00, 0x2d), PUSH0_CLONE_CODE_HASH), result)
break
}
result := shr(96, result)
mstore(0x37, 0) // Restore the overwritten part of the free memory pointer.
}
}
/// @dev Returns the address when a contract with initialization code hash,
/// `hash`, is deployed with `salt`, by `deployer`.
/// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly.
function predictDeterministicAddress(bytes32 hash, bytes32 salt, address deployer)
internal
pure
returns (address predicted)
{
/// @solidity memory-safe-assembly
assembly {
// Compute and store the bytecode hash.
mstore8(0x00, 0xff) // Write the prefix.
mstore(0x35, hash)
mstore(0x01, shl(96, deployer))
mstore(0x15, salt)
predicted := keccak256(0x00, 0x55)
mstore(0x35, 0) // Restore the overwritten part of the free memory pointer.
}
}
/// @dev Requires that `salt` starts with either the zero address or `by`.
function checkStartsWith(bytes32 salt, address by) internal pure {
/// @solidity memory-safe-assembly
assembly {
// If the salt does not start with the zero address or `by`.
if iszero(or(iszero(shr(96, salt)), eq(shr(96, shl(96, by)), shr(96, salt)))) {
mstore(0x00, 0x0c4549ef) // `SaltDoesNotStartWith()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Returns the `bytes32` at `offset` in `args`, without any bounds checks.
/// To load an address, you can use `address(bytes20(argLoad(args, offset)))`.
function argLoad(bytes memory args, uint256 offset) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(add(add(args, 0x20), offset))
}
}
}// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; // ────────────────────────────────────────────────────────────────────────────── // _ __ _ __ // / | / /__ | |/ /_ _______ // / |/ / _ \| / / / / ___/ // / /| / __/ / /_/ (__ ) // /_/ |_/\___/_/|_\__,_/____/ // // ────────────────────────────────────────────────────────────────────────────── // Nexus: A suite of contracts for Modular Smart Accounts compliant with ERC-7579 and ERC-4337, developed by Biconomy. // Learn more at https://biconomy.io. To report security issues, please contact us at: [email protected] import { IERC4337Account } from "./IERC4337Account.sol"; import { IERC7579Account } from "./IERC7579Account.sol"; import { INexusEventsAndErrors } from "./INexusEventsAndErrors.sol"; /// @title Nexus - INexus Interface /// @notice Integrates ERC-4337 and ERC-7579 standards to manage smart accounts within the Nexus suite. /// @dev Consolidates ERC-4337 user operations and ERC-7579 configurations into a unified interface for smart account management. /// It extends both IERC4337Account and IERC7579Account, enhancing modular capabilities and supporting advanced contract architectures. /// Includes error definitions for robust handling of common issues such as unsupported module types and execution failures. /// The initialize function sets up the account with validators and configurations, ensuring readiness for use. /// @author @livingrockrises | Biconomy | [email protected] /// @author @aboudjem | Biconomy | [email protected] /// @author @filmakarov | Biconomy | [email protected] /// @author @zeroknots | Rhinestone.wtf | zeroknots.eth /// Special thanks to the Solady team for foundational contributions: https://github.com/Vectorized/solady interface INexus is IERC4337Account, IERC7579Account, INexusEventsAndErrors { /// @notice Initializes the smart account with a validator and custom data. /// @dev This method sets up the account for operation, linking it with a validator and initializing it with specific data. /// Can be called directly or via a factory. /// @param initData Encoded data used for the account's configuration during initialization. function initializeAccount(bytes calldata initData) external payable; }
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;
import { BootstrapConfig } from "../utils/NexusBootstrap.sol";
/// @title NexusBootstrap Configuration Library
/// @notice Provides utility functions to create and manage BootstrapConfig structures.
/// @author @livingrockrises | Biconomy | [email protected]
/// @author @aboudjem | Biconomy | [email protected]
/// @author @filmakarov | Biconomy | [email protected]
/// @author @zeroknots | Rhinestone.wtf | zeroknots.eth
/// Special thanks to the Solady team for foundational contributions: https://github.com/Vectorized/solady
library BootstrapLib {
/// @notice Creates a single BootstrapConfig structure.
/// @param module The address of the module.
/// @param data The initialization data for the module.
/// @return config A BootstrapConfig structure containing the module and its data.
function createSingleConfig(address module, bytes memory data) internal pure returns (BootstrapConfig memory config) {
config.module = module;
config.data = data;
}
/// @notice Creates an array with a single BootstrapConfig structure.
/// @param module The address of the module.
/// @param data The initialization data for the module.
/// @return config An array containing a single BootstrapConfig structure.
function createArrayConfig(address module, bytes memory data) internal pure returns (BootstrapConfig[] memory config) {
config = new BootstrapConfig[](1);
config[0].module = module;
config[0].data = data;
}
/// @notice Creates an array of BootstrapConfig structures.
/// @param modules An array of module addresses.
/// @param datas An array of initialization data for each module.
/// @return configs An array of BootstrapConfig structures.
function createMultipleConfigs(address[] memory modules, bytes[] memory datas) internal pure returns (BootstrapConfig[] memory configs) {
require(modules.length == datas.length, "BootstrapLib: length mismatch");
configs = new BootstrapConfig[](modules.length);
for (uint256 i = 0; i < modules.length; i++) {
configs[i] = createSingleConfig(modules[i], datas[i]);
}
}
}// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; // ────────────────────────────────────────────────────────────────────────────── // _ __ _ __ // / | / /__ | |/ /_ _______ // / |/ / _ \| / / / / ___/ // / /| / __/ / /_/ (__ ) // /_/ |_/\___/_/|_\__,_/____/ // // ────────────────────────────────────────────────────────────────────────────── // Nexus: A suite of contracts for Modular Smart Accounts compliant with ERC-7579 and ERC-4337, developed by Biconomy. // Learn more at https://biconomy.io. For security issues, contact: [email protected] import { ModuleManager } from "../base/ModuleManager.sol"; import { IModule } from "../interfaces/modules/IModule.sol"; import { IERC7484 } from "../interfaces/IERC7484.sol"; /// @title NexusBootstrap Configuration for Nexus /// @notice Provides configuration and initialization for Nexus smart accounts. /// @author @livingrockrises | Biconomy | [email protected] /// @author @aboudjem | Biconomy | [email protected] /// @author @filmakarov | Biconomy | [email protected] /// @author @zeroknots | Rhinestone.wtf | zeroknots.eth /// Special thanks to the Solady team for foundational contributions: https://github.com/Vectorized/solady struct BootstrapConfig { address module; bytes data; } /// @title NexusBootstrap /// @notice Manages the installation of modules into Nexus smart accounts using delegatecalls. contract NexusBootstrap is ModuleManager { /// @notice Initializes the Nexus account with a single validator. /// @dev Intended to be called by the Nexus with a delegatecall. /// @param validator The address of the validator module. /// @param data The initialization data for the validator module. function initNexusWithSingleValidator( IModule validator, bytes calldata data, IERC7484 registry, address[] calldata attesters, uint8 threshold ) external { _configureRegistry(registry, attesters, threshold); _installValidator(address(validator), data); } /// @notice Initializes the Nexus account with multiple modules. /// @dev Intended to be called by the Nexus with a delegatecall. /// @param validators The configuration array for validator modules. /// @param executors The configuration array for executor modules. /// @param hook The configuration for the hook module. /// @param fallbacks The configuration array for fallback handler modules. function initNexus( BootstrapConfig[] calldata validators, BootstrapConfig[] calldata executors, BootstrapConfig calldata hook, BootstrapConfig[] calldata fallbacks, IERC7484 registry, address[] calldata attesters, uint8 threshold ) external { _configureRegistry(registry, attesters, threshold); // Initialize validators for (uint256 i = 0; i < validators.length; i++) { _installValidator(validators[i].module, validators[i].data); } // Initialize executors for (uint256 i = 0; i < executors.length; i++) { if (executors[i].module == address(0)) continue; _installExecutor(executors[i].module, executors[i].data); } // Initialize hook if (hook.module != address(0)) { _installHook(hook.module, hook.data); } // Initialize fallback handlers for (uint256 i = 0; i < fallbacks.length; i++) { if (fallbacks[i].module == address(0)) continue; _installFallbackHandler(fallbacks[i].module, fallbacks[i].data); } } /// @notice Initializes the Nexus account with a scoped set of modules. /// @dev Intended to be called by the Nexus with a delegatecall. /// @param validators The configuration array for validator modules. /// @param hook The configuration for the hook module. function initNexusScoped( BootstrapConfig[] calldata validators, BootstrapConfig calldata hook, IERC7484 registry, address[] calldata attesters, uint8 threshold ) external { _configureRegistry(registry, attesters, threshold); // Initialize validators for (uint256 i = 0; i < validators.length; i++) { _installValidator(validators[i].module, validators[i].data); } // Initialize hook if (hook.module != address(0)) { _installHook(hook.module, hook.data); } } /// @notice Prepares calldata for the initNexus function. /// @param validators The configuration array for validator modules. /// @param executors The configuration array for executor modules. /// @param hook The configuration for the hook module. /// @param fallbacks The configuration array for fallback handler modules. /// @return init The prepared calldata for initNexus. function getInitNexusCalldata( BootstrapConfig[] calldata validators, BootstrapConfig[] calldata executors, BootstrapConfig calldata hook, BootstrapConfig[] calldata fallbacks, IERC7484 registry, address[] calldata attesters, uint8 threshold ) external view returns (bytes memory init) { init = abi.encode(address(this), abi.encodeCall(this.initNexus, (validators, executors, hook, fallbacks, registry, attesters, threshold))); } /// @notice Prepares calldata for the initNexusScoped function. /// @param validators The configuration array for validator modules. /// @param hook The configuration for the hook module. /// @return init The prepared calldata for initNexusScoped. function getInitNexusScopedCalldata( BootstrapConfig[] calldata validators, BootstrapConfig calldata hook, IERC7484 registry, address[] calldata attesters, uint8 threshold ) external view returns (bytes memory init) { init = abi.encode(address(this), abi.encodeCall(this.initNexusScoped, (validators, hook, registry, attesters, threshold))); } /// @notice Prepares calldata for the initNexusWithSingleValidator function. /// @param validator The configuration for the validator module. /// @return init The prepared calldata for initNexusWithSingleValidator. function getInitNexusWithSingleValidatorCalldata( BootstrapConfig calldata validator, IERC7484 registry, address[] calldata attesters, uint8 threshold ) external view returns (bytes memory init) { init = abi.encode( address(this), abi.encodeCall(this.initNexusWithSingleValidator, (IModule(validator.module), validator.data, registry, attesters, threshold)) ); } /// @dev EIP712 domain name and version. function _domainNameAndVersion() internal pure override returns (string memory name, string memory version) { name = "NexusBootstrap"; version = "1.0.0"; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; // ────────────────────────────────────────────────────────────────────────────── // _ __ _ __ // / | / /__ | |/ /_ _______ // / |/ / _ \| / / / / ___/ // / /| / __/ / /_/ (__ ) // /_/ |_/\___/_/|_\__,_/____/ // // ────────────────────────────────────────────────────────────────────────────── // Nexus: A suite of contracts for Modular Smart Accounts compliant with ERC-7579 and ERC-4337, developed by Biconomy. // Learn more at https://biconomy.io. For security issues, contact: [email protected] import { Ownable } from "solady/auth/Ownable.sol"; import { IEntryPoint } from "account-abstraction/interfaces/IEntryPoint.sol"; import { IStakeable } from "../interfaces/common/IStakeable.sol"; /// @title Stakeable Entity /// @notice Provides functionality to stake, unlock, and withdraw Ether on an EntryPoint. /// @author @livingrockrises | Biconomy | [email protected] /// @author @aboudjem | Biconomy | [email protected] /// @author @filmakarov | Biconomy | [email protected] /// @author @zeroknots | Rhinestone.wtf | zeroknots.eth /// Special thanks to the Solady team for foundational contributions: https://github.com/Vectorized/solady contract Stakeable is Ownable, IStakeable { /// @notice Error thrown when an invalid EntryPoint address is provided. error InvalidEntryPointAddress(); constructor(address newOwner) { _setOwner(newOwner); } /// @notice Stakes a certain amount of Ether on an EntryPoint. /// @dev The contract should have enough Ether to cover the stake. /// @param epAddress The address of the EntryPoint where the stake is added. /// @param unstakeDelaySec The delay in seconds before the stake can be unlocked. function addStake(address epAddress, uint32 unstakeDelaySec) external payable onlyOwner { require(epAddress != address(0), InvalidEntryPointAddress()); IEntryPoint(epAddress).addStake{ value: msg.value }(unstakeDelaySec); } /// @notice Unlocks the stake on an EntryPoint. /// @dev This starts the unstaking delay after which funds can be withdrawn. /// @param epAddress The address of the EntryPoint from which the stake is to be unlocked. function unlockStake(address epAddress) external onlyOwner { require(epAddress != address(0), InvalidEntryPointAddress()); IEntryPoint(epAddress).unlockStake(); } /// @notice Withdraws the stake from an EntryPoint to a specified address. /// @dev This can only be done after the unstaking delay has passed since the unlock. /// @param epAddress The address of the EntryPoint where the stake is withdrawn from. /// @param withdrawAddress The address to receive the withdrawn stake. function withdrawStake(address epAddress, address payable withdrawAddress) external onlyOwner { require(epAddress != address(0), InvalidEntryPointAddress()); IEntryPoint(epAddress).withdrawStake(withdrawAddress); } }
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;
interface IERC7484 {
event NewTrustedAttesters();
/**
* Allows Smart Accounts - the end users of the registry - to appoint
* one or many attesters as trusted.
* @dev this function reverts, if address(0), or duplicates are provided in attesters[]
*
* @param threshold The minimum number of attestations required for a module
* to be considered secure.
* @param attesters The addresses of the attesters to be trusted.
*/
function trustAttesters(uint8 threshold, address[] calldata attesters) external;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* Check with Registry internal attesters */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
function check(address module) external view;
function checkForAccount(address smartAccount, address module) external view;
function check(address module, uint256 moduleType) external view;
function checkForAccount(address smartAccount, address module, uint256 moduleType) external view;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* Check with external attester(s) */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
function check(address module, address[] calldata attesters, uint256 threshold) external view;
function check(address module, uint256 moduleType, address[] calldata attesters, uint256 threshold) external view;
}// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; // ────────────────────────────────────────────────────────────────────────────── // _ __ _ __ // / | / /__ | |/ /_ _______ // / |/ / _ \| / / / / ___/ // / /| / __/ / /_/ (__ ) // /_/ |_/\___/_/|_\__,_/____/ // // ────────────────────────────────────────────────────────────────────────────── // Nexus: A suite of contracts for Modular Smart Accounts compliant with ERC-7579 and ERC-4337, developed by Biconomy. // Learn more at https://biconomy.io. To report security issues, please contact us at: [email protected] import { PackedUserOperation } from "account-abstraction/interfaces/PackedUserOperation.sol"; /// @title Nexus - IERC4337Account /// @notice This interface defines the necessary validation and execution methods for smart accounts under the ERC-4337 standard. /// @dev Provides a structure for implementing custom validation logic and execution methods that comply with ERC-4337 "account abstraction" specs. /// The validation method ensures proper signature and nonce verification before proceeding with transaction execution, critical for securing userOps. /// Also allows for the optional definition of an execution method to handle transactions post-validation, enhancing flexibility. /// @author @livingrockrises | Biconomy | [email protected] /// @author @aboudjem | Biconomy | [email protected] /// @author @filmakarov | Biconomy | [email protected] /// @author @zeroknots | Rhinestone.wtf | zeroknots.eth /// Special thanks to the Solady team for foundational contributions: https://github.com/Vectorized/solady interface IERC4337Account { /// Validate user's signature and nonce /// the entryPoint will make the call to the recipient only if this validation call returns successfully. /// signature failure should be reported by returning SIG_VALIDATION_FAILED (1). /// This allows making a "simulation call" without a valid signature /// Other failures (e.g. nonce mismatch, or invalid signature format) should still revert to signal failure. /// /// @dev ERC-4337-v-0.7 validation stage /// @dev Must validate caller is the entryPoint. /// Must validate the signature and nonce /// @param userOp - The user operation that is about to be executed. /// @param userOpHash - Hash of the user's request data. can be used as the basis for signature. /// @param missingAccountFunds - Missing funds on the account's deposit in the entrypoint. /// This is the minimum amount to transfer to the sender(entryPoint) to be /// able to make the call. The excess is left as a deposit in the entrypoint /// for future calls. Can be withdrawn anytime using "entryPoint.withdrawTo()". /// In case there is a paymaster in the request (or the current deposit is high /// enough), this value will be zero. /// @return validationData - Packaged ValidationData structure. use `_packValidationData` and /// `_unpackValidationData` to encode and decode. /// <20-byte> sigAuthorizer - 0 for valid signature, 1 to mark signature failure, /// otherwise, an address of an "authorizer" contract. /// <6-byte> validUntil - Last timestamp this operation is valid. 0 for "indefinite" /// <6-byte> validAfter - First timestamp this operation is valid /// If an account doesn't use time-range, it is enough to /// return SIG_VALIDATION_FAILED value (1) for signature failure. /// Note that the validation code cannot use block.timestamp (or block.number) directly. function validateUserOp( PackedUserOperation calldata userOp, bytes32 userOpHash, uint256 missingAccountFunds ) external returns (uint256 validationData); /// Account may implement this execute method. /// passing this methodSig at the beginning of callData will cause the entryPoint to pass the /// full UserOp (and hash) /// to the account. /// The account should skip the methodSig, and use the callData (and optionally, other UserOp /// fields) /// @dev ERC-4337-v-0.7 optional execution path /// @param userOp - The operation that was just validated. /// @param userOpHash - Hash of the user's request data. function executeUserOp(PackedUserOperation calldata userOp, bytes32 userOpHash) external payable; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; // ────────────────────────────────────────────────────────────────────────────── // _ __ _ __ // / | / /__ | |/ /_ _______ // / |/ / _ \| / / / / ___/ // / /| / __/ / /_/ (__ ) // /_/ |_/\___/_/|_\__,_/____/ // // ────────────────────────────────────────────────────────────────────────────── // Nexus: A suite of contracts for Modular Smart Accounts compliant with ERC-7579 and ERC-4337, developed by Biconomy. // Learn more at https://biconomy.io. To report security issues, please contact us at: [email protected] import { IAccountConfig } from "./base/IAccountConfig.sol"; import { IExecutionHelper } from "./base/IExecutionHelper.sol"; import { IModuleManager } from "./base/IModuleManager.sol"; /// @title Nexus - IERC7579Account /// @notice This interface integrates the functionalities required for a modular smart account compliant with ERC-7579 and ERC-4337 standards. /// @dev Combines configurations and operational management for smart accounts, bridging IAccountConfig, IExecutionHelper, and IModuleManager. /// Interfaces designed to support the comprehensive management of smart account operations including execution management and modular configurations. /// @author @livingrockrises | Biconomy | [email protected] /// @author @aboudjem | Biconomy | [email protected] /// @author @filmakarov | Biconomy | [email protected] /// @author @zeroknots | Rhinestone.wtf | zeroknots.eth /// Special thanks to the Solady team for foundational contributions: https://github.com/Vectorized/solady interface IERC7579Account is IAccountConfig, IExecutionHelper, IModuleManager { /// @dev Validates a smart account signature according to ERC-1271 standards. /// This method may delegate the call to a validator module to check the signature. /// @param hash The hash of the data being validated. /// @param data The signed data to validate. function isValidSignature(bytes32 hash, bytes calldata data) external view returns (bytes4); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; // ────────────────────────────────────────────────────────────────────────────── // _ __ _ __ // / | / /__ | |/ /_ _______ // / |/ / _ \| / / / / ___/ // / /| / __/ / /_/ (__ ) // /_/ |_/\___/_/|_\__,_/____/ // // ────────────────────────────────────────────────────────────────────────────── // Nexus: A suite of contracts for Modular Smart Accounts compliant with ERC-7579 and ERC-4337, developed by Biconomy. // Learn more at https://biconomy.io. To report security issues, please contact us at: [email protected] import { PackedUserOperation } from "account-abstraction/interfaces/PackedUserOperation.sol"; /// @title Nexus - INexus Events and Errors /// @notice Defines common errors for the Nexus smart account management interface. /// @author @livingrockrises | Biconomy | [email protected] /// @author @aboudjem | Biconomy | [email protected] /// @author @filmakarov | Biconomy | [email protected] /// @author @zeroknots | Rhinestone.wtf | zeroknots.eth /// Special thanks to the Solady team for foundational contributions: https://github.com/Vectorized/solady interface INexusEventsAndErrors { /// @notice Emitted when a user operation is executed from `executeUserOp` /// @param userOp The user operation that was executed. /// @param innerCallRet The return data from the inner call execution. event Executed(PackedUserOperation userOp, bytes innerCallRet); /// @notice Error thrown when an unsupported ModuleType is requested. /// @param moduleTypeId The ID of the unsupported module type. error UnsupportedModuleType(uint256 moduleTypeId); /// @notice Error thrown on failed execution. error ExecutionFailed(); /// @notice Error thrown when the Factory fails to initialize the account with posted bootstrap data. error NexusInitializationFailed(); /// @notice Error thrown when a zero address is provided as the Entry Point address. error EntryPointCanNotBeZero(); /// @notice Error thrown when the provided implementation address is invalid. error InvalidImplementationAddress(); /// @notice Error thrown when the provided implementation address is not a contract. error ImplementationIsNotAContract(); /// @notice Error thrown when an inner call fails. error InnerCallFailed(); /// @notice Error thrown when attempted to emergency-uninstall a hook error EmergencyTimeLockNotExpired(); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; // ────────────────────────────────────────────────────────────────────────────── // _ __ _ __ // / | / /__ | |/ /_ _______ // / |/ / _ \| / / / / ___/ // / /| / __/ / /_/ (__ ) // /_/ |_/\___/_/|_\__,_/____/ // // ────────────────────────────────────────────────────────────────────────────── // Nexus: A suite of contracts for Modular Smart Accounts compliant with ERC-7579 and ERC-4337, developed by Biconomy. // Learn more at https://biconomy.io. To report security issues, please contact us at: [email protected] import { SentinelListLib } from "sentinellist/SentinelList.sol"; import { Storage } from "./Storage.sol"; import { IHook } from "../interfaces/modules/IHook.sol"; import { IModule } from "../interfaces/modules/IModule.sol"; import { IExecutor } from "../interfaces/modules/IExecutor.sol"; import { IFallback } from "../interfaces/modules/IFallback.sol"; import { IValidator } from "../interfaces/modules/IValidator.sol"; import { CallType, CALLTYPE_SINGLE, CALLTYPE_STATIC } from "../lib/ModeLib.sol"; import { ExecLib } from "../lib/ExecLib.sol"; import { LocalCallDataParserLib } from "../lib/local/LocalCallDataParserLib.sol"; import { IModuleManagerEventsAndErrors } from "../interfaces/base/IModuleManagerEventsAndErrors.sol"; import { MODULE_TYPE_VALIDATOR, MODULE_TYPE_EXECUTOR, MODULE_TYPE_FALLBACK, MODULE_TYPE_HOOK, MODULE_TYPE_MULTI, MODULE_ENABLE_MODE_TYPE_HASH, ERC1271_MAGICVALUE } from "../types/Constants.sol"; import { EIP712 } from "solady/utils/EIP712.sol"; import { ExcessivelySafeCall } from "excessively-safe-call/ExcessivelySafeCall.sol"; import { RegistryAdapter } from "./RegistryAdapter.sol"; /// @title Nexus - ModuleManager /// @notice Manages Validator, Executor, Hook, and Fallback modules within the Nexus suite, supporting /// @dev Implements SentinelList for managing modules via a linked list structure, adhering to ERC-7579. /// @author @livingrockrises | Biconomy | [email protected] /// @author @aboudjem | Biconomy | [email protected] /// @author @filmakarov | Biconomy | [email protected] /// @author @zeroknots | Rhinestone.wtf | zeroknots.eth /// Special thanks to the Solady team for foundational contributions: https://github.com/Vectorized/solady abstract contract ModuleManager is Storage, EIP712, IModuleManagerEventsAndErrors, RegistryAdapter { using SentinelListLib for SentinelListLib.SentinelList; using LocalCallDataParserLib for bytes; using ExecLib for address; using ExcessivelySafeCall for address; /// @notice Ensures the message sender is a registered executor module. modifier onlyExecutorModule() virtual { require(_getAccountStorage().executors.contains(msg.sender), InvalidModule(msg.sender)); _; } /// @notice Does pre-checks and post-checks using an installed hook on the account. /// @dev sender, msg.data and msg.value is passed to the hook to implement custom flows. modifier withHook() { address hook = _getHook(); if (hook == address(0)) { _; } else { bytes memory hookData = IHook(hook).preCheck(msg.sender, msg.value, msg.data); _; IHook(hook).postCheck(hookData); } } receive() external payable {} /// @dev Fallback function to manage incoming calls using designated handlers based on the call type. fallback(bytes calldata callData) external payable withHook returns (bytes memory) { return _fallback(callData); } /// @dev Retrieves a paginated list of validator addresses from the linked list. /// This utility function is not defined by the ERC-7579 standard and is implemented to facilitate /// easier management and retrieval of large sets of validator modules. /// @param cursor The address to start pagination from, or zero to start from the first entry. /// @param size The number of validator addresses to return. /// @return array An array of validator addresses. /// @return next The address to use as a cursor for the next page of results. function getValidatorsPaginated(address cursor, uint256 size) external view returns (address[] memory array, address next) { (array, next) = _paginate(_getAccountStorage().validators, cursor, size); } /// @dev Retrieves a paginated list of executor addresses from the linked list. /// This utility function is not defined by the ERC-7579 standard and is implemented to facilitate /// easier management and retrieval of large sets of executor modules. /// @param cursor The address to start pagination from, or zero to start from the first entry. /// @param size The number of executor addresses to return. /// @return array An array of executor addresses. /// @return next The address to use as a cursor for the next page of results. function getExecutorsPaginated(address cursor, uint256 size) external view returns (address[] memory array, address next) { (array, next) = _paginate(_getAccountStorage().executors, cursor, size); } /// @notice Retrieves the currently active hook address. /// @return hook The address of the active hook module. function getActiveHook() external view returns (address hook) { return _getHook(); } /// @notice Fetches the fallback handler for a specific selector. /// @param selector The function selector to query. /// @return calltype The type of call that the handler manages. /// @return handler The address of the fallback handler. function getFallbackHandlerBySelector(bytes4 selector) external view returns (CallType, address) { FallbackHandler memory handler = _getAccountStorage().fallbacks[selector]; return (handler.calltype, handler.handler); } /// @dev Initializes the module manager by setting up default states for validators and executors. function _initModuleManager() internal virtual { // account module storage AccountStorage storage ams = _getAccountStorage(); ams.executors.init(); ams.validators.init(); } /// @dev Implements Module Enable Mode flow. /// @param packedData Data source to parse data required to perform Module Enable mode from. /// @return userOpSignature the clean signature which can be further used for userOp validation function _enableMode(bytes32 userOpHash, bytes calldata packedData) internal returns (bytes calldata userOpSignature) { address module; uint256 moduleType; bytes calldata moduleInitData; bytes calldata enableModeSignature; (module, moduleType, moduleInitData, enableModeSignature, userOpSignature) = packedData.parseEnableModeData(); if (!_checkEnableModeSignature(_getEnableModeDataHash(module, moduleType, userOpHash, moduleInitData), enableModeSignature)) revert EnableModeSigError(); _installModule(moduleType, module, moduleInitData); } /// @notice Installs a new module to the smart account. /// @param moduleTypeId The type identifier of the module being installed, which determines its role: /// - 0 for MultiType /// - 1 for Validator /// - 2 for Executor /// - 3 for Fallback /// - 4 for Hook /// @param module The address of the module to install. /// @param initData Initialization data for the module. /// @dev This function goes through hook checks via withHook modifier. /// @dev No need to check that the module is already installed, as this check is done /// when trying to sstore the module in an appropriate SentinelList function _installModule(uint256 moduleTypeId, address module, bytes calldata initData) internal withHook { if (module == address(0)) revert ModuleAddressCanNotBeZero(); if (moduleTypeId == MODULE_TYPE_VALIDATOR) { _installValidator(module, initData); } else if (moduleTypeId == MODULE_TYPE_EXECUTOR) { _installExecutor(module, initData); } else if (moduleTypeId == MODULE_TYPE_FALLBACK) { _installFallbackHandler(module, initData); } else if (moduleTypeId == MODULE_TYPE_HOOK) { _installHook(module, initData); } else if (moduleTypeId == MODULE_TYPE_MULTI) { _multiTypeInstall(module, initData); } else { revert InvalidModuleTypeId(moduleTypeId); } } /// @dev Installs a new validator module after checking if it matches the required module type. /// @param validator The address of the validator module to be installed. /// @param data Initialization data to configure the validator upon installation. function _installValidator(address validator, bytes calldata data) internal virtual withRegistry(validator, MODULE_TYPE_VALIDATOR) { if (!IValidator(validator).isModuleType(MODULE_TYPE_VALIDATOR)) revert MismatchModuleTypeId(MODULE_TYPE_VALIDATOR); _getAccountStorage().validators.push(validator); IValidator(validator).onInstall(data); } /// @dev Uninstalls a validator module /!\ ensuring the account retains at least one validator. /// @param validator The address of the validator to be uninstalled. /// @param data De-initialization data to configure the validator upon uninstallation. function _uninstallValidator(address validator, bytes calldata data) internal virtual { SentinelListLib.SentinelList storage validators = _getAccountStorage().validators; (address prev, bytes memory disableModuleData) = abi.decode(data, (address, bytes)); // Perform the removal first validators.pop(prev, validator); // Sentinel pointing to itself / zero means the list is empty / uninitialized, so check this after removal // Below error is very specific to uninstalling validators. require(_hasValidators(), CanNotRemoveLastValidator()); validator.excessivelySafeCall(gasleft(), 0, 0, abi.encodeWithSelector(IModule.onUninstall.selector, disableModuleData)); } /// @dev Installs a new executor module after checking if it matches the required module type. /// @param executor The address of the executor module to be installed. /// @param data Initialization data to configure the executor upon installation. function _installExecutor(address executor, bytes calldata data) internal virtual withRegistry(executor, MODULE_TYPE_EXECUTOR) { if (!IExecutor(executor).isModuleType(MODULE_TYPE_EXECUTOR)) revert MismatchModuleTypeId(MODULE_TYPE_EXECUTOR); _getAccountStorage().executors.push(executor); IExecutor(executor).onInstall(data); } /// @dev Uninstalls an executor module by removing it from the executors list. /// @param executor The address of the executor to be uninstalled. /// @param data De-initialization data to configure the executor upon uninstallation. function _uninstallExecutor(address executor, bytes calldata data) internal virtual { (address prev, bytes memory disableModuleData) = abi.decode(data, (address, bytes)); _getAccountStorage().executors.pop(prev, executor); executor.excessivelySafeCall(gasleft(), 0, 0, abi.encodeWithSelector(IModule.onUninstall.selector, disableModuleData)); } /// @dev Installs a hook module, ensuring no other hooks are installed before proceeding. /// @param hook The address of the hook to be installed. /// @param data Initialization data to configure the hook upon installation. function _installHook(address hook, bytes calldata data) internal virtual withRegistry(hook, MODULE_TYPE_HOOK) { if (!IHook(hook).isModuleType(MODULE_TYPE_HOOK)) revert MismatchModuleTypeId(MODULE_TYPE_HOOK); address currentHook = _getHook(); require(currentHook == address(0), HookAlreadyInstalled(currentHook)); _setHook(hook); IHook(hook).onInstall(data); } /// @dev Uninstalls a hook module, ensuring the current hook matches the one intended for uninstallation. /// @param hook The address of the hook to be uninstalled. /// @param data De-initialization data to configure the hook upon uninstallation. function _uninstallHook(address hook, bytes calldata data) internal virtual { _setHook(address(0)); hook.excessivelySafeCall(gasleft(), 0, 0, abi.encodeWithSelector(IModule.onUninstall.selector, data)); } /// @dev Sets the current hook in the storage to the specified address. /// @param hook The new hook address. function _setHook(address hook) internal virtual { _getAccountStorage().hook = IHook(hook); } /// @dev Installs a fallback handler for a given selector with initialization data. /// @param handler The address of the fallback handler to install. /// @param params The initialization parameters including the selector and call type. function _installFallbackHandler(address handler, bytes calldata params) internal virtual withRegistry(handler, MODULE_TYPE_FALLBACK) { if (!IFallback(handler).isModuleType(MODULE_TYPE_FALLBACK)) revert MismatchModuleTypeId(MODULE_TYPE_FALLBACK); // Extract the function selector from the provided parameters. bytes4 selector = bytes4(params[0:4]); // Extract the call type from the provided parameters. CallType calltype = CallType.wrap(bytes1(params[4])); require(calltype == CALLTYPE_SINGLE || calltype == CALLTYPE_STATIC, FallbackCallTypeInvalid()); // Extract the initialization data from the provided parameters. bytes memory initData = params[5:]; // Revert if the selector is either `onInstall(bytes)` (0x6d61fe70) or `onUninstall(bytes)` (0x8a91b0e3) or explicit bytes(0). // These selectors are explicitly forbidden to prevent security vulnerabilities. // Allowing these selectors would enable unauthorized users to uninstall and reinstall critical modules. // If a validator module is uninstalled and reinstalled without proper authorization, it can compromise // the account's security and integrity. By restricting these selectors, we ensure that the fallback handler // cannot be manipulated to disrupt the expected behavior and security of the account. require(!(selector == bytes4(0x6d61fe70) || selector == bytes4(0x8a91b0e3) || selector == bytes4(0)), FallbackSelectorForbidden()); // Revert if a fallback handler is already installed for the given selector. // This check ensures that we do not overwrite an existing fallback handler, which could lead to unexpected behavior. require(!_isFallbackHandlerInstalled(selector), FallbackAlreadyInstalledForSelector(selector)); // Store the fallback handler and its call type in the account storage. // This maps the function selector to the specified fallback handler and call type. _getAccountStorage().fallbacks[selector] = FallbackHandler(handler, calltype); // Invoke the `onInstall` function of the fallback handler with the provided initialization data. // This step allows the fallback handler to perform any necessary setup or initialization. IFallback(handler).onInstall(initData); } /// @dev Uninstalls a fallback handler for a given selector. /// @param fallbackHandler The address of the fallback handler to uninstall. /// @param data The de-initialization data containing the selector. function _uninstallFallbackHandler(address fallbackHandler, bytes calldata data) internal virtual { _getAccountStorage().fallbacks[bytes4(data[0:4])] = FallbackHandler(address(0), CallType.wrap(0x00)); fallbackHandler.excessivelySafeCall(gasleft(), 0, 0, abi.encodeWithSelector(IModule.onUninstall.selector, data[4:])); } /// @notice Installs a module with multiple types in a single operation. /// @dev This function handles installing a multi-type module by iterating through each type and initializing it. /// The initData should include an ABI-encoded tuple of (uint[] types, bytes[] initDatas). /// @param module The address of the multi-type module. /// @param initData Initialization data for each type within the module. function _multiTypeInstall(address module, bytes calldata initData) internal virtual { (uint256[] calldata types, bytes[] calldata initDatas) = initData.parseMultiTypeInitData(); uint256 length = types.length; if (initDatas.length != length) revert InvalidInput(); // iterate over all module types and install the module as a type accordingly for (uint256 i; i < length; i++) { uint256 theType = types[i]; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INSTALL VALIDATORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ if (theType == MODULE_TYPE_VALIDATOR) { _installValidator(module, initDatas[i]); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INSTALL EXECUTORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ else if (theType == MODULE_TYPE_EXECUTOR) { _installExecutor(module, initDatas[i]); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INSTALL FALLBACK */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ else if (theType == MODULE_TYPE_FALLBACK) { _installFallbackHandler(module, initDatas[i]); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INSTALL HOOK (global only, not sig-specific) */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ else if (theType == MODULE_TYPE_HOOK) { _installHook(module, initDatas[i]); } } } /// @notice Checks if an enable mode signature is valid. /// @param structHash data hash. /// @param sig Signature. function _checkEnableModeSignature(bytes32 structHash, bytes calldata sig) internal view returns (bool) { address enableModeSigValidator = address(bytes20(sig[0:20])); if (!_isValidatorInstalled(enableModeSigValidator)) { revert ValidatorNotInstalled(enableModeSigValidator); } bytes32 eip712Digest = _hashTypedData(structHash); // Use standard IERC-1271/ERC-7739 interface. // Even if the validator doesn't support 7739 under the hood, it is still secure, // as eip712digest is already built based on 712Domain of this Smart Account // This interface should always be exposed by validators as per ERC-7579 try IValidator(enableModeSigValidator).isValidSignatureWithSender(address(this), eip712Digest, sig[20:]) returns (bytes4 res) { return res == ERC1271_MAGICVALUE; } catch { return false; } } /// @notice Builds the enable mode data hash as per eip712 /// @param module Module being enabled /// @param moduleType Type of the module as per EIP-7579 /// @param userOpHash Hash of the User Operation /// @param initData Module init data. /// @return structHash data hash function _getEnableModeDataHash(address module, uint256 moduleType, bytes32 userOpHash, bytes calldata initData) internal view returns (bytes32) { return keccak256(abi.encode(MODULE_ENABLE_MODE_TYPE_HASH, module, moduleType, userOpHash, keccak256(initData))); } /// @notice Checks if a module is installed on the smart account. /// @param moduleTypeId The module type ID. /// @param module The module address. /// @param additionalContext Additional context for checking installation. /// @return True if the module is installed, false otherwise. function _isModuleInstalled(uint256 moduleTypeId, address module, bytes calldata additionalContext) internal view returns (bool) { additionalContext; if (moduleTypeId == MODULE_TYPE_VALIDATOR) { return _isValidatorInstalled(module); } else if (moduleTypeId == MODULE_TYPE_EXECUTOR) { return _isExecutorInstalled(module); } else if (moduleTypeId == MODULE_TYPE_FALLBACK) { bytes4 selector; if (additionalContext.length >= 4) { selector = bytes4(additionalContext[0:4]); } else { selector = bytes4(0x00000000); } return _isFallbackHandlerInstalled(selector, module); } else if (moduleTypeId == MODULE_TYPE_HOOK) { return _isHookInstalled(module); } else { return false; } } /// @dev Checks if a fallback handler is set for a given selector. /// @param selector The function selector to check. /// @return True if a fallback handler is set, otherwise false. function _isFallbackHandlerInstalled(bytes4 selector) internal view virtual returns (bool) { FallbackHandler storage handler = _getAccountStorage().fallbacks[selector]; return handler.handler != address(0); } /// @dev Checks if the expected fallback handler is installed for a given selector. /// @param selector The function selector to check. /// @param expectedHandler The address of the handler expected to be installed. /// @return True if the installed handler matches the expected handler, otherwise false. function _isFallbackHandlerInstalled(bytes4 selector, address expectedHandler) internal view returns (bool) { FallbackHandler storage handler = _getAccountStorage().fallbacks[selector]; return handler.handler == expectedHandler; } /// @dev Checks if a validator is currently installed. /// @param validator The address of the validator to check. /// @return True if the validator is installed, otherwise false. function _isValidatorInstalled(address validator) internal view virtual returns (bool) { return _getAccountStorage().validators.contains(validator); } /// @dev Checks if there is at least one validator installed. /// @return True if there is at least one validator, otherwise false. function _hasValidators() internal view returns (bool) { return _getAccountStorage().validators.getNext(address(0x01)) != address(0x01) && _getAccountStorage().validators.getNext(address(0x01)) != address(0x00); } /// @dev Checks if an executor is currently installed. /// @param executor The address of the executor to check. /// @return True if the executor is installed, otherwise false. function _isExecutorInstalled(address executor) internal view virtual returns (bool) { return _getAccountStorage().executors.contains(executor); } /// @dev Checks if a hook is currently installed. /// @param hook The address of the hook to check. /// @return True if the hook is installed, otherwise false. function _isHookInstalled(address hook) internal view returns (bool) { return _getHook() == hook; } /// @dev Retrieves the current hook from the storage. /// @return hook The address of the current hook. function _getHook() internal view returns (address hook) { hook = address(_getAccountStorage().hook); } function _fallback(bytes calldata callData) private returns (bytes memory result) { bool success; FallbackHandler storage $fallbackHandler = _getAccountStorage().fallbacks[msg.sig]; address handler = $fallbackHandler.handler; CallType calltype = $fallbackHandler.calltype; if (handler != address(0)) { //if there's a fallback handler, call it if (calltype == CALLTYPE_STATIC) { (success, result) = handler.staticcall(ExecLib.get2771CallData(callData)); } else if (calltype == CALLTYPE_SINGLE) { (success, result) = handler.call{ value: msg.value }(ExecLib.get2771CallData(callData)); } else { revert UnsupportedCallType(calltype); } // Use revert message from fallback handler if the call was not successful if (!success) { assembly { revert(add(result, 0x20), mload(result)) } } } else { // If there's no handler, the call can be one of onERCXXXReceived() bytes32 s; /// @solidity memory-safe-assembly assembly { s := shr(224, calldataload(0)) // 0x150b7a02: `onERC721Received(address,address,uint256,bytes)`. // 0xf23a6e61: `onERC1155Received(address,address,uint256,uint256,bytes)`. // 0xbc197c81: `onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)`. if or(eq(s, 0x150b7a02), or(eq(s, 0xf23a6e61), eq(s, 0xbc197c81))) { success := true // it is one of onERCXXXReceived result := mload(0x40) //result was set to 0x60 as it was empty, so we need to find a new space for it mstore(result, 0x04) //store length mstore(add(result, 0x20), shl(224, s)) //store calldata mstore(0x40, add(result, 0x24)) //allocate memory } } // if there was no handler and it is not the onERCXXXReceived call, revert require(success, MissingFallbackHandler(msg.sig)); } } /// @dev Helper function to paginate entries in a SentinelList. /// @param list The SentinelList to paginate. /// @param cursor The cursor to start paginating from. /// @param size The number of entries to return. /// @return array The array of addresses in the list. /// @return nextCursor The cursor for the next page of entries. function _paginate( SentinelListLib.SentinelList storage list, address cursor, uint256 size ) private view returns (address[] memory array, address nextCursor) { (array, nextCursor) = list.getEntriesPaginated(cursor, size); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; // ────────────────────────────────────────────────────────────────────────────── // _ __ _ __ // / | / /__ | |/ /_ _______ // / |/ / _ \| / / / / ___/ // / /| / __/ / /_/ (__ ) // /_/ |_/\___/_/|_\__,_/____/ // // ────────────────────────────────────────────────────────────────────────────── // Nexus: A suite of contracts for Modular Smart Accounts compliant with ERC-7579 and ERC-4337, developed by Biconomy. // Learn more at https://biconomy.io. To report security issues, please contact us at: [email protected] /// @title Nexus - ERC-7579 Module Base Interface /// @notice Interface for module management in smart accounts, complying with ERC-7579 specifications. /// @dev Defines the lifecycle hooks and checks for modules within the smart account architecture. /// This interface includes methods for installing, uninstalling, and verifying module types and initialization status. /// @author @livingrockrises | Biconomy | [email protected] /// @author @aboudjem | Biconomy | [email protected] /// @author @filmakarov | Biconomy | [email protected] /// @author @zeroknots | Rhinestone.wtf | zeroknots.eth /// Special thanks to the Solady team for foundational contributions: https://github.com/Vectorized/solady interface IModule { /// @notice Installs the module with necessary initialization data. /// @dev Reverts if the module is already initialized. /// @param data Arbitrary data required for initializing the module during `onInstall`. function onInstall(bytes calldata data) external; /// @notice Uninstalls the module and allows for cleanup via arbitrary data. /// @dev Reverts if any issues occur that prevent clean uninstallation. /// @param data Arbitrary data required for deinitializing the module during `onUninstall`. function onUninstall(bytes calldata data) external; /// @notice Determines if the module matches a specific module type. /// @dev Should return true if the module corresponds to the type ID, false otherwise. /// @param moduleTypeId Numeric ID of the module type as per ERC-7579 specifications. /// @return True if the module is of the specified type, false otherwise. function isModuleType(uint256 moduleTypeId) external view returns (bool); /// @notice Checks if the module has been initialized for a specific smart account. /// @dev Returns true if initialized, false otherwise. /// @param smartAccount Address of the smart account to check for initialization status. /// @return True if the module is initialized for the given smart account, false otherwise. function isInitialized(address smartAccount) external view returns (bool); }
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Simple single owner authorization mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol)
///
/// @dev Note:
/// This implementation does NOT auto-initialize the owner to `msg.sender`.
/// You MUST call the `_initializeOwner` in the constructor / initializer.
///
/// While the ownable portion follows
/// [EIP-173](https://eips.ethereum.org/EIPS/eip-173) for compatibility,
/// the nomenclature for the 2-step ownership handover may be unique to this codebase.
abstract contract Ownable {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The caller is not authorized to call the function.
error Unauthorized();
/// @dev The `newOwner` cannot be the zero address.
error NewOwnerIsZeroAddress();
/// @dev The `pendingOwner` does not have a valid handover request.
error NoHandoverRequest();
/// @dev Cannot double-initialize.
error AlreadyInitialized();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EVENTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The ownership is transferred from `oldOwner` to `newOwner`.
/// This event is intentionally kept the same as OpenZeppelin's Ownable to be
/// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173),
/// despite it not being as lightweight as a single argument event.
event OwnershipTransferred(address indexed oldOwner, address indexed newOwner);
/// @dev An ownership handover to `pendingOwner` has been requested.
event OwnershipHandoverRequested(address indexed pendingOwner);
/// @dev The ownership handover to `pendingOwner` has been canceled.
event OwnershipHandoverCanceled(address indexed pendingOwner);
/// @dev `keccak256(bytes("OwnershipTransferred(address,address)"))`.
uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE =
0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0;
/// @dev `keccak256(bytes("OwnershipHandoverRequested(address)"))`.
uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE =
0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d;
/// @dev `keccak256(bytes("OwnershipHandoverCanceled(address)"))`.
uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE =
0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STORAGE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The owner slot is given by:
/// `bytes32(~uint256(uint32(bytes4(keccak256("_OWNER_SLOT_NOT")))))`.
/// It is intentionally chosen to be a high value
/// to avoid collision with lower slots.
/// The choice of manual storage layout is to enable compatibility
/// with both regular and upgradeable contracts.
bytes32 internal constant _OWNER_SLOT =
0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927;
/// The ownership handover slot of `newOwner` is given by:
/// ```
/// mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED))
/// let handoverSlot := keccak256(0x00, 0x20)
/// ```
/// It stores the expiry timestamp of the two-step ownership handover.
uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Override to return true to make `_initializeOwner` prevent double-initialization.
function _guardInitializeOwner() internal pure virtual returns (bool guard) {}
/// @dev Initializes the owner directly without authorization guard.
/// This function must be called upon initialization,
/// regardless of whether the contract is upgradeable or not.
/// This is to enable generalization to both regular and upgradeable contracts,
/// and to save gas in case the initial owner is not the caller.
/// For performance reasons, this function will not check if there
/// is an existing owner.
function _initializeOwner(address newOwner) internal virtual {
if (_guardInitializeOwner()) {
/// @solidity memory-safe-assembly
assembly {
let ownerSlot := _OWNER_SLOT
if sload(ownerSlot) {
mstore(0x00, 0x0dc149f0) // `AlreadyInitialized()`.
revert(0x1c, 0x04)
}
// Clean the upper 96 bits.
newOwner := shr(96, shl(96, newOwner))
// Store the new value.
sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
// Emit the {OwnershipTransferred} event.
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
}
} else {
/// @solidity memory-safe-assembly
assembly {
// Clean the upper 96 bits.
newOwner := shr(96, shl(96, newOwner))
// Store the new value.
sstore(_OWNER_SLOT, newOwner)
// Emit the {OwnershipTransferred} event.
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
}
}
}
/// @dev Sets the owner directly without authorization guard.
function _setOwner(address newOwner) internal virtual {
if (_guardInitializeOwner()) {
/// @solidity memory-safe-assembly
assembly {
let ownerSlot := _OWNER_SLOT
// Clean the upper 96 bits.
newOwner := shr(96, shl(96, newOwner))
// Emit the {OwnershipTransferred} event.
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
// Store the new value.
sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
}
} else {
/// @solidity memory-safe-assembly
assembly {
let ownerSlot := _OWNER_SLOT
// Clean the upper 96 bits.
newOwner := shr(96, shl(96, newOwner))
// Emit the {OwnershipTransferred} event.
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
// Store the new value.
sstore(ownerSlot, newOwner)
}
}
}
/// @dev Throws if the sender is not the owner.
function _checkOwner() internal view virtual {
/// @solidity memory-safe-assembly
assembly {
// If the caller is not the stored owner, revert.
if iszero(eq(caller(), sload(_OWNER_SLOT))) {
mstore(0x00, 0x82b42900) // `Unauthorized()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Returns how long a two-step ownership handover is valid for in seconds.
/// Override to return a different value if needed.
/// Made internal to conserve bytecode. Wrap it in a public function if needed.
function _ownershipHandoverValidFor() internal view virtual returns (uint64) {
return 48 * 3600;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PUBLIC UPDATE FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Allows the owner to transfer the ownership to `newOwner`.
function transferOwnership(address newOwner) public payable virtual onlyOwner {
/// @solidity memory-safe-assembly
assembly {
if iszero(shl(96, newOwner)) {
mstore(0x00, 0x7448fbae) // `NewOwnerIsZeroAddress()`.
revert(0x1c, 0x04)
}
}
_setOwner(newOwner);
}
/// @dev Allows the owner to renounce their ownership.
function renounceOwnership() public payable virtual onlyOwner {
_setOwner(address(0));
}
/// @dev Request a two-step ownership handover to the caller.
/// The request will automatically expire in 48 hours (172800 seconds) by default.
function requestOwnershipHandover() public payable virtual {
unchecked {
uint256 expires = block.timestamp + _ownershipHandoverValidFor();
/// @solidity memory-safe-assembly
assembly {
// Compute and set the handover slot to `expires`.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, caller())
sstore(keccak256(0x0c, 0x20), expires)
// Emit the {OwnershipHandoverRequested} event.
log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller())
}
}
}
/// @dev Cancels the two-step ownership handover to the caller, if any.
function cancelOwnershipHandover() public payable virtual {
/// @solidity memory-safe-assembly
assembly {
// Compute and set the handover slot to 0.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, caller())
sstore(keccak256(0x0c, 0x20), 0)
// Emit the {OwnershipHandoverCanceled} event.
log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller())
}
}
/// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`.
/// Reverts if there is no existing ownership handover requested by `pendingOwner`.
function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner {
/// @solidity memory-safe-assembly
assembly {
// Compute and set the handover slot to 0.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, pendingOwner)
let handoverSlot := keccak256(0x0c, 0x20)
// If the handover does not exist, or has expired.
if gt(timestamp(), sload(handoverSlot)) {
mstore(0x00, 0x6f5e8818) // `NoHandoverRequest()`.
revert(0x1c, 0x04)
}
// Set the handover slot to 0.
sstore(handoverSlot, 0)
}
_setOwner(pendingOwner);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PUBLIC READ FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the owner of the contract.
function owner() public view virtual returns (address result) {
/// @solidity memory-safe-assembly
assembly {
result := sload(_OWNER_SLOT)
}
}
/// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`.
function ownershipHandoverExpiresAt(address pendingOwner)
public
view
virtual
returns (uint256 result)
{
/// @solidity memory-safe-assembly
assembly {
// Compute the handover slot.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, pendingOwner)
// Load the handover slot.
result := sload(keccak256(0x0c, 0x20))
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* MODIFIERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Marks a function as only callable by the owner.
modifier onlyOwner() virtual {
_checkOwner();
_;
}
}/**
** Account-Abstraction (EIP-4337) singleton EntryPoint implementation.
** Only one instance required on each chain.
**/
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.5;
/* solhint-disable avoid-low-level-calls */
/* solhint-disable no-inline-assembly */
/* solhint-disable reason-string */
import "./PackedUserOperation.sol";
import "./IStakeManager.sol";
import "./IAggregator.sol";
import "./INonceManager.sol";
interface IEntryPoint is IStakeManager, INonceManager {
/***
* An event emitted after each successful request.
* @param userOpHash - Unique identifier for the request (hash its entire content, except signature).
* @param sender - The account that generates this request.
* @param paymaster - If non-null, the paymaster that pays for this request.
* @param nonce - The nonce value from the request.
* @param success - True if the sender transaction succeeded, false if reverted.
* @param actualGasCost - Actual amount paid (by account or paymaster) for this UserOperation.
* @param actualGasUsed - Total gas used by this UserOperation (including preVerification, creation,
* validation and execution).
*/
event UserOperationEvent(
bytes32 indexed userOpHash,
address indexed sender,
address indexed paymaster,
uint256 nonce,
bool success,
uint256 actualGasCost,
uint256 actualGasUsed
);
/**
* Account "sender" was deployed.
* @param userOpHash - The userOp that deployed this account. UserOperationEvent will follow.
* @param sender - The account that is deployed
* @param factory - The factory used to deploy this account (in the initCode)
* @param paymaster - The paymaster used by this UserOp
*/
event AccountDeployed(
bytes32 indexed userOpHash,
address indexed sender,
address factory,
address paymaster
);
/**
* An event emitted if the UserOperation "callData" reverted with non-zero length.
* @param userOpHash - The request unique identifier.
* @param sender - The sender of this request.
* @param nonce - The nonce used in the request.
* @param revertReason - The return bytes from the (reverted) call to "callData".
*/
event UserOperationRevertReason(
bytes32 indexed userOpHash,
address indexed sender,
uint256 nonce,
bytes revertReason
);
/**
* An event emitted if the UserOperation Paymaster's "postOp" call reverted with non-zero length.
* @param userOpHash - The request unique identifier.
* @param sender - The sender of this request.
* @param nonce - The nonce used in the request.
* @param revertReason - The return bytes from the (reverted) call to "callData".
*/
event PostOpRevertReason(
bytes32 indexed userOpHash,
address indexed sender,
uint256 nonce,
bytes revertReason
);
/**
* UserOp consumed more than prefund. The UserOperation is reverted, and no refund is made.
* @param userOpHash - The request unique identifier.
* @param sender - The sender of this request.
* @param nonce - The nonce used in the request.
*/
event UserOperationPrefundTooLow(
bytes32 indexed userOpHash,
address indexed sender,
uint256 nonce
);
/**
* An event emitted by handleOps(), before starting the execution loop.
* Any event emitted before this event, is part of the validation.
*/
event BeforeExecution();
/**
* Signature aggregator used by the following UserOperationEvents within this bundle.
* @param aggregator - The aggregator used for the following UserOperationEvents.
*/
event SignatureAggregatorChanged(address indexed aggregator);
/**
* A custom revert error of handleOps, to identify the offending op.
* Should be caught in off-chain handleOps simulation and not happen on-chain.
* Useful for mitigating DoS attempts against batchers or for troubleshooting of factory/account/paymaster reverts.
* NOTE: If simulateValidation passes successfully, there should be no reason for handleOps to fail on it.
* @param opIndex - Index into the array of ops to the failed one (in simulateValidation, this is always zero).
* @param reason - Revert reason. The string starts with a unique code "AAmn",
* where "m" is "1" for factory, "2" for account and "3" for paymaster issues,
* so a failure can be attributed to the correct entity.
*/
error FailedOp(uint256 opIndex, string reason);
/**
* A custom revert error of handleOps, to report a revert by account or paymaster.
* @param opIndex - Index into the array of ops to the failed one (in simulateValidation, this is always zero).
* @param reason - Revert reason. see FailedOp(uint256,string), above
* @param inner - data from inner cought revert reason
* @dev note that inner is truncated to 2048 bytes
*/
error FailedOpWithRevert(uint256 opIndex, string reason, bytes inner);
error PostOpReverted(bytes returnData);
/**
* Error case when a signature aggregator fails to verify the aggregated signature it had created.
* @param aggregator The aggregator that failed to verify the signature
*/
error SignatureValidationFailed(address aggregator);
// Return value of getSenderAddress.
error SenderAddressResult(address sender);
// UserOps handled, per aggregator.
struct UserOpsPerAggregator {
PackedUserOperation[] userOps;
// Aggregator address
IAggregator aggregator;
// Aggregated signature
bytes signature;
}
/**
* Execute a batch of UserOperations.
* No signature aggregator is used.
* If any account requires an aggregator (that is, it returned an aggregator when
* performing simulateValidation), then handleAggregatedOps() must be used instead.
* @param ops - The operations to execute.
* @param beneficiary - The address to receive the fees.
*/
function handleOps(
PackedUserOperation[] calldata ops,
address payable beneficiary
) external;
/**
* Execute a batch of UserOperation with Aggregators
* @param opsPerAggregator - The operations to execute, grouped by aggregator (or address(0) for no-aggregator accounts).
* @param beneficiary - The address to receive the fees.
*/
function handleAggregatedOps(
UserOpsPerAggregator[] calldata opsPerAggregator,
address payable beneficiary
) external;
/**
* Generate a request Id - unique identifier for this request.
* The request ID is a hash over the content of the userOp (except the signature), the entrypoint and the chainid.
* @param userOp - The user operation to generate the request ID for.
* @return hash the hash of this UserOperation
*/
function getUserOpHash(
PackedUserOperation calldata userOp
) external view returns (bytes32);
/**
* Gas and return values during simulation.
* @param preOpGas - The gas used for validation (including preValidationGas)
* @param prefund - The required prefund for this operation
* @param accountValidationData - returned validationData from account.
* @param paymasterValidationData - return validationData from paymaster.
* @param paymasterContext - Returned by validatePaymasterUserOp (to be passed into postOp)
*/
struct ReturnInfo {
uint256 preOpGas;
uint256 prefund;
uint256 accountValidationData;
uint256 paymasterValidationData;
bytes paymasterContext;
}
/**
* Returned aggregated signature info:
* The aggregator returned by the account, and its current stake.
*/
struct AggregatorStakeInfo {
address aggregator;
StakeInfo stakeInfo;
}
/**
* Get counterfactual sender address.
* Calculate the sender contract address that will be generated by the initCode and salt in the UserOperation.
* This method always revert, and returns the address in SenderAddressResult error
* @param initCode - The constructor code to be passed into the UserOperation.
*/
function getSenderAddress(bytes memory initCode) external;
error DelegateAndRevert(bool success, bytes ret);
/**
* Helper method for dry-run testing.
* @dev calling this method, the EntryPoint will make a delegatecall to the given data, and report (via revert) the result.
* The method always revert, so is only useful off-chain for dry run calls, in cases where state-override to replace
* actual EntryPoint code is less convenient.
* @param target a target contract to make a delegatecall from entrypoint
* @param data data to pass to target in a delegatecall
*/
function delegateAndRevert(address target, bytes calldata data) external;
}// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; // ────────────────────────────────────────────────────────────────────────────── // _ __ _ __ // / | / /__ | |/ /_ _______ // / |/ / _ \| / / / / ___/ // / /| / __/ / /_/ (__ ) // /_/ |_/\___/_/|_\__,_/____/ // // ────────────────────────────────────────────────────────────────────────────── // Nexus: A suite of contracts for Modular Smart Accounts compliant with ERC-7579 and ERC-4337, developed by Biconomy. // Learn more at https://biconomy.io. For security issues, contact: [email protected] /// @title Stakeable Entity Interface /// @notice Interface for staking, unlocking, and withdrawing Ether on an EntryPoint. /// @dev Defines functions for managing stakes on an EntryPoint. /// @author @livingrockrises | Biconomy | [email protected] /// @author @aboudjem | Biconomy | [email protected] /// @author @filmakarov | Biconomy | [email protected] /// @author @zeroknots | Rhinestone.wtf | zeroknots.eth /// Special thanks to the Solady team for foundational contributions: https://github.com/Vectorized/solady interface IStakeable { /// @notice Stakes a certain amount of Ether on an EntryPoint. /// @dev The contract should have enough Ether to cover the stake. /// @param epAddress The address of the EntryPoint where the stake is added. /// @param unstakeDelaySec The delay in seconds before the stake can be unlocked. function addStake(address epAddress, uint32 unstakeDelaySec) external payable; /// @notice Unlocks the stake on an EntryPoint. /// @dev This starts the unstaking delay after which funds can be withdrawn. /// @param epAddress The address of the EntryPoint from which the stake is to be unlocked. function unlockStake(address epAddress) external; /// @notice Withdraws the stake from an EntryPoint to a specified address. /// @dev This can only be done after the unstaking delay has passed since the unlock. /// @param epAddress The address of the EntryPoint where the stake is withdrawn from. /// @param withdrawAddress The address to receive the withdrawn stake. function withdrawStake(address epAddress, address payable withdrawAddress) external; }
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.5;
/**
* User Operation struct
* @param sender - The sender account of this request.
* @param nonce - Unique value the sender uses to verify it is not a replay.
* @param initCode - If set, the account contract will be created by this constructor/
* @param callData - The method call to execute on this account.
* @param accountGasLimits - Packed gas limits for validateUserOp and gas limit passed to the callData method call.
* @param preVerificationGas - Gas not calculated by the handleOps method, but added to the gas paid.
* Covers batch overhead.
* @param gasFees - packed gas fields maxPriorityFeePerGas and maxFeePerGas - Same as EIP-1559 gas parameters.
* @param paymasterAndData - If set, this field holds the paymaster address, verification gas limit, postOp gas limit and paymaster-specific extra data
* The paymaster will pay for the transaction instead of the sender.
* @param signature - Sender-verified signature over the entire request, the EntryPoint address and the chain ID.
*/
struct PackedUserOperation {
address sender;
uint256 nonce;
bytes initCode;
bytes callData;
bytes32 accountGasLimits;
uint256 preVerificationGas;
bytes32 gasFees;
bytes paymasterAndData;
bytes signature;
}// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; // ────────────────────────────────────────────────────────────────────────────── // _ __ _ __ // / | / /__ | |/ /_ _______ // / |/ / _ \| / / / / ___/ // / /| / __/ / /_/ (__ ) // /_/ |_/\___/_/|_\__,_/____/ // // ────────────────────────────────────────────────────────────────────────────── // Nexus: A suite of contracts for Modular Smart Accounts compliant with ERC-7579 and ERC-4337, developed by Biconomy. // Learn more at https://biconomy.io. To report security issues, please contact us at: [email protected] import { ExecutionMode } from "../../lib/ModeLib.sol"; /// @title Nexus - ERC-7579 Account Configuration Interface /// @notice Interface for querying and verifying configurations of Smart Accounts compliant with ERC-7579. /// @dev Provides methods to check supported execution modes and module types for Smart Accounts, ensuring flexible and extensible configuration. /// @author @livingrockrises | Biconomy | [email protected] /// @author @aboudjem | Biconomy | [email protected] /// @author @filmakarov | Biconomy | [email protected] /// @author @zeroknots | Rhinestone.wtf | zeroknots.eth /// Special thanks to the Solady team for foundational contributions: https://github.com/Vectorized/solady interface IAccountConfig { /// @notice Returns the account ID in a structured format: "vendorname.accountname.semver" /// @return accountImplementationId The account ID of the smart account function accountId() external view returns (string memory accountImplementationId); /// @notice Checks if the account supports a certain execution mode. /// @param encodedMode The encoded mode to verify. /// @return supported True if the account supports the mode, false otherwise. function supportsExecutionMode(ExecutionMode encodedMode) external view returns (bool supported); /// @notice Checks if the account supports a specific module type. /// @param moduleTypeId The module type ID to verify. /// @return supported True if the account supports the module type, false otherwise. function supportsModule(uint256 moduleTypeId) external view returns (bool supported); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; // ────────────────────────────────────────────────────────────────────────────── // _ __ _ __ // / | / /__ | |/ /_ _______ // / |/ / _ \| / / / / ___/ // / /| / __/ / /_/ (__ ) // /_/ |_/\___/_/|_\__,_/____/ // // ────────────────────────────────────────────────────────────────────────────── // Nexus: A suite of contracts for Modular Smart Accounts compliant with ERC-7579 and ERC-4337, developed by Biconomy. // Learn more at https://biconomy.io. To report security issues, please contact us at: [email protected] import { ExecutionMode } from "../../lib/ModeLib.sol"; import { IExecutionHelperEventsAndErrors } from "./IExecutionHelperEventsAndErrors.sol"; /// @title Nexus - IExecutionHelper /// @notice Interface for executing transactions on behalf of smart accounts within the Nexus system. /// @dev Extends functionality for transaction execution with error handling as defined in IExecutionHelperEventsAndErrors. /// @author @livingrockrises | Biconomy | [email protected] /// @author @aboudjem | Biconomy | [email protected] /// @author @filmakarov | Biconomy | [email protected] /// @author @zeroknots | Rhinestone.wtf | zeroknots.eth /// Special thanks to the Solady team for foundational contributions: https://github.com/Vectorized/solady interface IExecutionHelper is IExecutionHelperEventsAndErrors { /// @notice Executes a transaction with specified execution mode and calldata. /// @param mode The execution mode, defining how the transaction is processed. /// @param executionCalldata The calldata to execute. /// @dev This function ensures that the execution complies with smart account execution policies and handles errors appropriately. function execute(ExecutionMode mode, bytes calldata executionCalldata) external payable; /// @notice Allows an executor module to perform transactions on behalf of the account. /// @param mode The execution mode that details how the transaction should be handled. /// @param executionCalldata The transaction data to be executed. /// @return returnData The result of the execution, allowing for error handling and results interpretation by the executor module. function executeFromExecutor(ExecutionMode mode, bytes calldata executionCalldata) external payable returns (bytes[] memory returnData); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; // ────────────────────────────────────────────────────────────────────────────── // _ __ _ __ // / | / /__ | |/ /_ _______ // / |/ / _ \| / / / / ___/ // / /| / __/ / /_/ (__ ) // /_/ |_/\___/_/|_\__,_/____/ // // ────────────────────────────────────────────────────────────────────────────── // Nexus: A suite of contracts for Modular Smart Accounts compliant with ERC-7579 and ERC-4337, developed by Biconomy. // Learn more at https://biconomy.io. To report security issues, please contact us at: [email protected] import { IModuleManagerEventsAndErrors } from "./IModuleManagerEventsAndErrors.sol"; /// @title Nexus - IModuleManager /// @notice Interface for managing modules within Smart Accounts, providing methods for installation and removal of modules. /// @dev Extends the IModuleManagerEventsAndErrors interface to include event and error definitions. /// @author @livingrockrises | Biconomy | [email protected] /// @author @aboudjem | Biconomy | [email protected] /// @author @filmakarov | Biconomy | [email protected] /// @author @zeroknots | Rhinestone.wtf | zeroknots.eth /// Special thanks to the Solady team for foundational contributions: https://github.com/Vectorized/solady interface IModuleManager is IModuleManagerEventsAndErrors { /// @notice Installs a Module of a specific type onto the smart account. /// @param moduleTypeId The identifier for the module type. /// @param module The address of the module to be installed. /// @param initData Initialization data for configuring the module upon installation. function installModule(uint256 moduleTypeId, address module, bytes calldata initData) external payable; /// @notice Uninstalls a Module of a specific type from the smart account. /// @param moduleTypeId The identifier for the module type being uninstalled. /// @param module The address of the module to uninstall. /// @param deInitData De-initialization data for configuring the module upon uninstallation. function uninstallModule(uint256 moduleTypeId, address module, bytes calldata deInitData) external payable; /// @notice Checks if a specific module is installed on the smart account. /// @param moduleTypeId The module type identifier to check. /// @param module The address of the module. /// @param additionalContext Additional information that may be required to verify the module's installation. /// @return installed True if the module is installed, false otherwise. function isModuleInstalled(uint256 moduleTypeId, address module, bytes calldata additionalContext) external view returns (bool installed); }
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Sentinel address
address constant SENTINEL = address(0x1);
// Zero address
address constant ZERO_ADDRESS = address(0x0);
/**
* @title SentinelListLib
* @dev Library for managing a linked list of addresses
* @author Rhinestone
*/
library SentinelListLib {
// Struct to hold the linked list
struct SentinelList {
mapping(address => address) entries;
}
error LinkedList_AlreadyInitialized();
error LinkedList_InvalidPage();
error LinkedList_InvalidEntry(address entry);
error LinkedList_EntryAlreadyInList(address entry);
/**
* Initialize the linked list
*
* @param self The linked list
*/
function init(SentinelList storage self) internal {
if (alreadyInitialized(self)) revert LinkedList_AlreadyInitialized();
self.entries[SENTINEL] = SENTINEL;
}
/**
* Check if the linked list is already initialized
*
* @param self The linked list
*
* @return bool True if the linked list is already initialized
*/
function alreadyInitialized(SentinelList storage self) internal view returns (bool) {
return self.entries[SENTINEL] != ZERO_ADDRESS;
}
/**
* Get the next entry in the linked list
*
* @param self The linked list
* @param entry The current entry
*
* @return address The next entry
*/
function getNext(SentinelList storage self, address entry) internal view returns (address) {
if (entry == ZERO_ADDRESS) {
revert LinkedList_InvalidEntry(entry);
}
return self.entries[entry];
}
/**
* Push a new entry to the linked list
*
* @param self The linked list
* @param newEntry The new entry
*/
function push(SentinelList storage self, address newEntry) internal {
if (newEntry == ZERO_ADDRESS || newEntry == SENTINEL) {
revert LinkedList_InvalidEntry(newEntry);
}
if (self.entries[newEntry] != ZERO_ADDRESS) revert LinkedList_EntryAlreadyInList(newEntry);
self.entries[newEntry] = self.entries[SENTINEL];
self.entries[SENTINEL] = newEntry;
}
/**
* Safe push a new entry to the linked list
* @dev This ensures that the linked list is initialized and initializes it if it is not
*
* @param self The linked list
* @param newEntry The new entry
*/
function safePush(SentinelList storage self, address newEntry) internal {
if (!alreadyInitialized({ self: self })) {
init({ self: self });
}
push({ self: self, newEntry: newEntry });
}
/**
* Pop an entry from the linked list
*
* @param self The linked list
* @param prevEntry The entry before the entry to pop
* @param popEntry The entry to pop
*/
function pop(SentinelList storage self, address prevEntry, address popEntry) internal {
if (popEntry == ZERO_ADDRESS || popEntry == SENTINEL) {
revert LinkedList_InvalidEntry(prevEntry);
}
if (self.entries[prevEntry] != popEntry) revert LinkedList_InvalidEntry(popEntry);
self.entries[prevEntry] = self.entries[popEntry];
self.entries[popEntry] = ZERO_ADDRESS;
}
/**
* Pop all entries from the linked list
*
* @param self The linked list
*/
function popAll(SentinelList storage self) internal {
address next = self.entries[SENTINEL];
while (next != ZERO_ADDRESS) {
address current = next;
next = self.entries[next];
self.entries[current] = ZERO_ADDRESS;
}
}
/**
* Check if the linked list contains an entry
*
* @param self The linked list
* @param entry The entry to check
*
* @return bool True if the linked list contains the entry
*/
function contains(SentinelList storage self, address entry) internal view returns (bool) {
return SENTINEL != entry && self.entries[entry] != ZERO_ADDRESS;
}
/**
* Get all entries in the linked list
*
* @param self The linked list
* @param start The start entry
* @param pageSize The page size
*
* @return array All entries in the linked list
* @return next The next entry
*/
function getEntriesPaginated(
SentinelList storage self,
address start,
uint256 pageSize
)
internal
view
returns (address[] memory array, address next)
{
if (start != SENTINEL && !contains(self, start)) revert LinkedList_InvalidEntry(start);
if (pageSize == 0) revert LinkedList_InvalidPage();
// Init array with max page size
array = new address[](pageSize);
// Populate return array
uint256 entryCount = 0;
next = self.entries[start];
while (next != ZERO_ADDRESS && next != SENTINEL && entryCount < pageSize) {
array[entryCount] = next;
next = self.entries[next];
entryCount++;
}
/**
* Because of the argument validation, we can assume that the loop will always iterate over
* the valid entry list values
* and the `next` variable will either be an enabled entry or a sentinel address
* (signalling the end).
*
* If we haven't reached the end inside the loop, we need to set the next pointer to
* the last element of the entry array
* because the `next` variable (which is a entry by itself) acting as a pointer to the
* start of the next page is neither
* incSENTINELrent page, nor will it be included in the next one if you pass it as a
* start.
*/
if (next != SENTINEL && entryCount > 0) {
next = array[entryCount - 1];
}
// Set correct size of returned array
// solhint-disable-next-line no-inline-assembly
/// @solidity memory-safe-assembly
assembly {
mstore(array, entryCount)
}
}
}// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; // ────────────────────────────────────────────────────────────────────────────── // _ __ _ __ // / | / /__ | |/ /_ _______ // / |/ / _ \| / / / / ___/ // / /| / __/ / /_/ (__ ) // /_/ |_/\___/_/|_\__,_/____/ // // ────────────────────────────────────────────────────────────────────────────── // Nexus: A suite of contracts for Modular Smart Accounts compliant with ERC-7579 and ERC-4337, developed by Biconomy. // Learn more at https://biconomy.io. To report security issues, please contact us at: [email protected] import { IStorage } from "../interfaces/base/IStorage.sol"; /// @title Nexus - Storage /// @notice Manages isolated storage spaces for Modular Smart Account in compliance with ERC-7201 standard to ensure collision-resistant storage. /// @dev Implements the ERC-7201 namespaced storage pattern to maintain secure and isolated storage sections for different states within Nexus suite. /// @author @livingrockrises | Biconomy | [email protected] /// @author @aboudjem | Biconomy | [email protected] /// @author @filmakarov | Biconomy | [email protected] /// @author @zeroknots | Rhinestone.wtf | zeroknots.eth /// Special thanks to the Solady team for foundational contributions: https://github.com/Vectorized/solady contract Storage is IStorage { /// @custom:storage-location erc7201:biconomy.storage.Nexus /// ERC-7201 namespaced via `keccak256(abi.encode(uint256(keccak256(bytes("biconomy.storage.Nexus"))) - 1)) & ~bytes32(uint256(0xff));` bytes32 private constant _STORAGE_LOCATION = 0x0bb70095b32b9671358306b0339b4c06e7cbd8cb82505941fba30d1eb5b82f00; /// @dev Utilizes ERC-7201's namespaced storage pattern for isolated storage access. This method computes /// the storage slot based on a predetermined location, ensuring collision-resistant storage for contract states. /// @custom:storage-location ERC-7201 formula applied to "biconomy.storage.Nexus", facilitating unique /// namespace identification and storage segregation, as detailed in the specification. /// @return $ The proxy to the `AccountStorage` struct, providing a reference to the namespaced storage slot. function _getAccountStorage() internal pure returns (AccountStorage storage $) { assembly { $.slot := _STORAGE_LOCATION } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; // ────────────────────────────────────────────────────────────────────────────── // _ __ _ __ // / | / /__ | |/ /_ _______ // / |/ / _ \| / / / / ___/ // / /| / __/ / /_/ (__ ) // /_/ |_/\___/_/|_\__,_/____/ // // ────────────────────────────────────────────────────────────────────────────── // Nexus: A suite of contracts for Modular Smart Accounts compliant with ERC-7579 and ERC-4337, developed by Biconomy. // Learn more at https://biconomy.io. To report security issues, please contact us at: [email protected] import { IModule } from "./IModule.sol"; /// @title Hook Management Interface /// @notice Provides methods for pre-checks and post-checks of transactions to ensure conditions and state consistency. /// @dev Defines two critical lifecycle hooks in the transaction process: `preCheck` and `postCheck`. /// These methods facilitate validating conditions prior to execution and verifying state changes afterwards, respectively. interface IHook is IModule { /// @notice Performs checks before a transaction is executed, potentially modifying the transaction context. /// @dev This method is called before the execution of a transaction to validate and possibly adjust execution context. /// @param msgSender The original sender of the transaction. /// @param msgValue The amount of wei sent with the call. /// @param msgData The calldata of the transaction. /// @return hookData Data that may be used or modified throughout the transaction lifecycle, passed to `postCheck`. function preCheck(address msgSender, uint256 msgValue, bytes calldata msgData) external returns (bytes memory hookData); /// @notice Performs checks after a transaction is executed to ensure state consistency and log results. /// @dev This method is called after the execution of a transaction to verify and react to the execution outcome. /// @param hookData Data returned from `preCheck`, containing execution context or modifications. function postCheck(bytes calldata hookData) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; // ────────────────────────────────────────────────────────────────────────────── // _ __ _ __ // / | / /__ | |/ /_ _______ // / |/ / _ \| / / / / ___/ // / /| / __/ / /_/ (__ ) // /_/ |_/\___/_/|_\__,_/____/ // // ────────────────────────────────────────────────────────────────────────────── // Nexus: A suite of contracts for Modular Smart Accounts compliant with ERC-7579 and ERC-4337, developed by Biconomy. // Learn more at https://biconomy.io. To report security issues, please contact us at: [email protected] import { IModule } from "./IModule.sol"; /// @title Nexus - IExecutor Interface /// @notice Defines the interface for Executor modules within the Nexus Smart Account framework, compliant with the ERC-7579 standard. /// @dev Extends IModule to include functionalities specific to execution modules. /// This interface is future-proof, allowing for expansion and integration of advanced features in subsequent versions. /// @author @livingrockrises | Biconomy | [email protected] /// @author @aboudjem | Biconomy | [email protected] /// @author @filmakarov | Biconomy | [email protected] /// @author @zeroknots | Rhinestone.wtf | zeroknots.eth /// Special thanks to the Solady team for foundational contributions: https://github.com/Vectorized/solady interface IExecutor is IModule { // Future methods for execution management will be defined here to accommodate evolving requirements. }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; // ────────────────────────────────────────────────────────────────────────────── // _ __ _ __ // / | / /__ | |/ /_ _______ // / |/ / _ \| / / / / ___/ // / /| / __/ / /_/ (__ ) // /_/ |_/\___/_/|_\__,_/____/ // // ────────────────────────────────────────────────────────────────────────────── // Nexus: A suite of contracts for Modular Smart Accounts compliant with ERC-7579 and ERC-4337, developed by Biconomy. // Learn more at https://biconomy.io. To report security issues, please contact us at: [email protected] import { IModule } from "./IModule.sol"; /// @title Nexus - IFallback Interface /// @notice Defines the interface for Fallback modules within the Nexus Smart Account framework, compliant with the ERC-7579 standard. /// @dev Extends IModule to include functionalities specific to fallback modules. /// This interface is future-proof, allowing for expansion and integration of advanced features in subsequent versions. /// @author @livingrockrises | Biconomy | [email protected] /// @author @aboudjem | Biconomy | [email protected] /// @author @filmakarov | Biconomy | [email protected] /// @author @zeroknots | Rhinestone.wtf | zeroknots.eth /// Special thanks to the Solady team for foundational contributions: https://github.com/Vectorized/solady interface IFallback is IModule { // Future methods for fallback management will be defined here to accommodate evolving blockchain technologies. }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; // ────────────────────────────────────────────────────────────────────────────── // _ __ _ __ // / | / /__ | |/ /_ _______ // / |/ / _ \| / / / / ___/ // / /| / __/ / /_/ (__ ) // /_/ |_/\___/_/|_\__,_/____/ // // ────────────────────────────────────────────────────────────────────────────── // Nexus: A suite of contracts for Modular Smart Accounts compliant with ERC-7579 and ERC-4337, developed by Biconomy. // Learn more at https://biconomy.io. To report security issues, please contact us at: [email protected] import { PackedUserOperation } from "account-abstraction/interfaces/PackedUserOperation.sol"; import { IModule } from "./IModule.sol"; /// @author @livingrockrises | Biconomy | [email protected] /// @author @aboudjem | Biconomy | [email protected] /// @author @filmakarov | Biconomy | [email protected] /// @author @zeroknots | Rhinestone.wtf | zeroknots.eth /// Special thanks to the Solady team for foundational contributions: https://github.com/Vectorized/solady interface IValidator is IModule { /// @notice Validates a user operation as per ERC-4337 standard requirements. /// @dev Should ensure that the signature and nonce are verified correctly before the transaction is allowed to proceed. /// The function returns a status code indicating validation success or failure. /// @param userOp The user operation containing transaction details to be validated. /// @param userOpHash The hash of the user operation data, used for verifying the signature. /// @return status The result of the validation process, typically indicating success or the type of failure. function validateUserOp(PackedUserOperation calldata userOp, bytes32 userOpHash) external returns (uint256); /// @notice Verifies a signature against a hash, using the sender's address as a contextual check. /// @dev Used to confirm the validity of a signature against the specific conditions set by the sender. /// @param sender The address from which the operation was initiated, adding an additional layer of validation against the signature. /// @param hash The hash of the data signed. /// @param data The signature data to validate. /// @return magicValue A bytes4 value that corresponds to the ERC-1271 standard, indicating the validity of the signature. function isValidSignatureWithSender(address sender, bytes32 hash, bytes calldata data) external view returns (bytes4); }
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;
/// @title ModeLib
/// @author zeroknots.eth | rhinestone.wtf
/// To allow smart accounts to be very simple, but allow for more complex execution, A custom mode
/// encoding is used.
/// Function Signature of execute function:
/// function execute(ExecutionMode mode, bytes calldata executionCalldata) external payable;
/// This allows for a single bytes32 to be used to encode the execution mode, calltype, execType and
/// context.
/// NOTE: Simple Account implementations only have to scope for the most significant byte. Account that
/// implement
/// more complex execution modes may use the entire bytes32.
///
/// |--------------------------------------------------------------------|
/// | CALLTYPE | EXECTYPE | UNUSED | ModeSelector | ModePayload |
/// |--------------------------------------------------------------------|
/// | 1 byte | 1 byte | 4 bytes | 4 bytes | 22 bytes |
/// |--------------------------------------------------------------------|
///
/// CALLTYPE: 1 byte
/// CallType is used to determine how the executeCalldata paramter of the execute function has to be
/// decoded.
/// It can be either single, batch or delegatecall. In the future different calls could be added.
/// CALLTYPE can be used by a validation module to determine how to decode <userOp.callData[36:]>.
///
/// EXECTYPE: 1 byte
/// ExecType is used to determine how the account should handle the execution.
/// It can indicate if the execution should revert on failure or continue execution.
/// In the future more execution modes may be added.
/// Default Behavior (EXECTYPE = 0x00) is to revert on a single failed execution. If one execution in
/// a batch fails, the entire batch is reverted
///
/// UNUSED: 4 bytes
/// Unused bytes are reserved for future use.
///
/// ModeSelector: bytes4
/// The "optional" mode selector can be used by account vendors, to implement custom behavior in
/// their accounts.
/// the way a ModeSelector is to be calculated is bytes4(keccak256("vendorname.featurename"))
/// this is to prevent collisions between different vendors, while allowing innovation and the
/// development of new features without coordination between ERC-7579 implementing accounts
///
/// ModePayload: 22 bytes
/// Mode payload is used to pass additional data to the smart account execution, this may be
/// interpreted depending on the ModeSelector
///
/// ExecutionCallData: n bytes
/// single, delegatecall or batch exec abi.encoded as bytes
// Custom type for improved developer experience
type ExecutionMode is bytes32;
type CallType is bytes1;
type ExecType is bytes1;
type ModeSelector is bytes4;
type ModePayload is bytes22;
// Default CallType
CallType constant CALLTYPE_SINGLE = CallType.wrap(0x00);
// Batched CallType
CallType constant CALLTYPE_BATCH = CallType.wrap(0x01);
CallType constant CALLTYPE_STATIC = CallType.wrap(0xFE);
// @dev Implementing delegatecall is OPTIONAL!
// implement delegatecall with extreme care.
CallType constant CALLTYPE_DELEGATECALL = CallType.wrap(0xFF);
// @dev default behavior is to revert on failure
// To allow very simple accounts to use mode encoding, the default behavior is to revert on failure
// Since this is value 0x00, no additional encoding is required for simple accounts
ExecType constant EXECTYPE_DEFAULT = ExecType.wrap(0x00);
// @dev account may elect to change execution behavior. For example "try exec" / "allow fail"
ExecType constant EXECTYPE_TRY = ExecType.wrap(0x01);
ModeSelector constant MODE_DEFAULT = ModeSelector.wrap(bytes4(0x00000000));
// Example declaration of a custom mode selector
ModeSelector constant MODE_OFFSET = ModeSelector.wrap(bytes4(keccak256("default.mode.offset")));
/// @dev ModeLib is a helper library to encode/decode ModeCodes
library ModeLib {
function decode(
ExecutionMode mode
) internal pure returns (CallType _calltype, ExecType _execType, ModeSelector _modeSelector, ModePayload _modePayload) {
assembly {
_calltype := mode
_execType := shl(8, mode)
_modeSelector := shl(48, mode)
_modePayload := shl(80, mode)
}
}
function decodeBasic(ExecutionMode mode) internal pure returns (CallType _calltype, ExecType _execType) {
assembly {
_calltype := mode
_execType := shl(8, mode)
}
}
function encode(CallType callType, ExecType execType, ModeSelector mode, ModePayload payload) internal pure returns (ExecutionMode) {
return ExecutionMode.wrap(bytes32(abi.encodePacked(callType, execType, bytes4(0), ModeSelector.unwrap(mode), payload)));
}
function encodeSimpleBatch() internal pure returns (ExecutionMode mode) {
mode = encode(CALLTYPE_BATCH, EXECTYPE_DEFAULT, MODE_DEFAULT, ModePayload.wrap(0x00));
}
function encodeSimpleSingle() internal pure returns (ExecutionMode mode) {
mode = encode(CALLTYPE_SINGLE, EXECTYPE_DEFAULT, MODE_DEFAULT, ModePayload.wrap(0x00));
}
function encodeTrySingle() internal pure returns (ExecutionMode mode) {
mode = encode(CALLTYPE_SINGLE, EXECTYPE_TRY, MODE_DEFAULT, ModePayload.wrap(0x00));
}
function encodeTryBatch() internal pure returns (ExecutionMode mode) {
mode = encode(CALLTYPE_BATCH, EXECTYPE_TRY, MODE_DEFAULT, ModePayload.wrap(0x00));
}
function encodeCustom(CallType callType, ExecType execType) internal pure returns (ExecutionMode mode) {
mode = encode(callType, execType, MODE_DEFAULT, ModePayload.wrap(0x00));
}
function getCallType(ExecutionMode mode) internal pure returns (CallType calltype) {
assembly {
calltype := mode
}
}
}
using { _eqModeSelector as == } for ModeSelector global;
using { _eqCallType as == } for CallType global;
using { _uneqCallType as != } for CallType global;
using { _eqExecType as == } for ExecType global;
function _eqCallType(CallType a, CallType b) pure returns (bool) {
return CallType.unwrap(a) == CallType.unwrap(b);
}
function _uneqCallType(CallType a, CallType b) pure returns (bool) {
return CallType.unwrap(a) != CallType.unwrap(b);
}
function _eqExecType(ExecType a, ExecType b) pure returns (bool) {
return ExecType.unwrap(a) == ExecType.unwrap(b);
}
//slither-disable-next-line dead-code
function _eqModeSelector(ModeSelector a, ModeSelector b) pure returns (bool) {
return ModeSelector.unwrap(a) == ModeSelector.unwrap(b);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;
import { Execution } from "../types/DataTypes.sol";
/// @title ExecutionLib
/// @author zeroknots.eth | rhinestone.wtf
/// Helper Library for decoding Execution calldata
/// malloc for memory allocation is bad for gas. use this assembly instead
library ExecLib {
error InvalidBatchCallData();
function get2771CallData(bytes calldata cd) internal view returns (bytes memory callData) {
/// @solidity memory-safe-assembly
(cd);
assembly {
// as per solidity docs
function allocate(length) -> pos {
pos := mload(0x40)
mstore(0x40, add(pos, length))
}
callData := allocate(add(calldatasize(), 0x20)) //allocate extra 0x20 to store length
mstore(callData, add(calldatasize(), 0x14)) //store length, extra 0x14 is for msg.sender address
calldatacopy(add(callData, 0x20), 0, calldatasize())
// The msg.sender address is shifted to the left by 12 bytes to remove the padding
// Then the address without padding is stored right after the calldata
let senderPtr := allocate(0x14)
mstore(senderPtr, shl(96, caller()))
}
}
function decodeBatch(bytes calldata callData) internal view returns (Execution[] calldata executionBatch) {
/*
* Batch Call Calldata Layout
* Offset (in bytes) | Length (in bytes) | Contents
* 0x0 | 0x4 | bytes4 function selector
* 0x4 | - | abi.encode(IERC7579Execution.Execution[])
*/
assembly ("memory-safe") {
let dataPointer := add(callData.offset, calldataload(callData.offset))
// Extract the ERC7579 Executions
executionBatch.offset := add(dataPointer, 32)
executionBatch.length := calldataload(dataPointer)
}
}
function encodeBatch(Execution[] memory executions) internal pure returns (bytes memory callData) {
callData = abi.encode(executions);
}
function decodeSingle(bytes calldata executionCalldata) internal pure returns (address target, uint256 value, bytes calldata callData) {
target = address(bytes20(executionCalldata[0:20]));
value = uint256(bytes32(executionCalldata[20:52]));
callData = executionCalldata[52:];
}
function decodeDelegateCall(bytes calldata executionCalldata) internal pure returns (address delegate, bytes calldata callData) {
// destructure executionCallData according to single exec
delegate = address(uint160(bytes20(executionCalldata[0:20])));
callData = executionCalldata[20:];
}
function encodeSingle(address target, uint256 value, bytes memory callData) internal pure returns (bytes memory userOpCalldata) {
userOpCalldata = abi.encodePacked(target, value, callData);
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.27;
library LocalCallDataParserLib {
/// @dev Parses the `userOp.signature` to extract the module type, module initialization data,
/// enable mode signature, and user operation signature. The `userOp.signature` must be
/// encoded in a specific way to be parsed correctly.
/// @param packedData The packed signature data, typically coming from `userOp.signature`.
/// @return module The address of the module.
/// @return moduleType The type of module as a `uint256`.
/// @return moduleInitData Initialization data specific to the module.
/// @return enableModeSignature Signature used to enable the module mode.
/// @return userOpSignature The remaining user operation signature data.
function parseEnableModeData(
bytes calldata packedData
)
internal
pure
returns (
address module,
uint256 moduleType,
bytes calldata moduleInitData,
bytes calldata enableModeSignature,
bytes calldata userOpSignature
)
{
uint256 p;
assembly ("memory-safe") {
p := packedData.offset
module := shr(96, calldataload(p))
p := add(p, 0x14)
moduleType := calldataload(p)
moduleInitData.length := shr(224, calldataload(add(p, 0x20)))
moduleInitData.offset := add(p, 0x24)
p := add(moduleInitData.offset, moduleInitData.length)
enableModeSignature.length := shr(224, calldataload(p))
enableModeSignature.offset := add(p, 0x04)
p := sub(add(enableModeSignature.offset, enableModeSignature.length), packedData.offset)
}
userOpSignature = packedData[p:];
}
/// @dev Parses the data to obtain types and initdata's for Multi Type module install mode
/// @param initData Multi Type module init data, abi.encoded
function parseMultiTypeInitData(bytes calldata initData) internal pure returns (uint256[] calldata types, bytes[] calldata initDatas) {
// equivalent of:
// (types, initDatas) = abi.decode(initData,(uint[],bytes[]))
assembly ("memory-safe") {
let offset := initData.offset
let baseOffset := offset
let dataPointer := add(baseOffset, calldataload(offset))
types.offset := add(dataPointer, 32)
types.length := calldataload(dataPointer)
offset := add(offset, 32)
dataPointer := add(baseOffset, calldataload(offset))
initDatas.offset := add(dataPointer, 32)
initDatas.length := calldataload(dataPointer)
}
}
}// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; // ────────────────────────────────────────────────────────────────────────────── // _ __ _ __ // / | / /__ | |/ /_ _______ // / |/ / _ \| / / / / ___/ // / /| / __/ / /_/ (__ ) // /_/ |_/\___/_/|_\__,_/____/ // // ────────────────────────────────────────────────────────────────────────────── // Nexus: A suite of contracts for Modular Smart Accounts compliant with ERC-7579 and ERC-4337, developed by Biconomy. // Learn more at https://biconomy.io. To report security issues, please contact us at: [email protected] import { CallType } from "../../lib/ModeLib.sol"; /// @title ERC-7579 Module Manager Events and Errors Interface /// @notice Provides event and error definitions for actions related to module management in smart accounts. /// @dev Used by IModuleManager to define the events and errors associated with the installation and management of modules. /// @author @livingrockrises | Biconomy | [email protected] /// @author @aboudjem | Biconomy | [email protected] /// @author @filmakarov | Biconomy | [email protected] /// @author @zeroknots | Rhinestone.wtf | zeroknots.eth /// Special thanks to the Solady team for foundational contributions: https://github.com/Vectorized/solady interface IModuleManagerEventsAndErrors { /// @notice Emitted when a module is installed onto a smart account. /// @param moduleTypeId The identifier for the type of module installed. /// @param module The address of the installed module. event ModuleInstalled(uint256 moduleTypeId, address module); /// @notice Emitted when a module is uninstalled from a smart account. /// @param moduleTypeId The identifier for the type of module uninstalled. /// @param module The address of the uninstalled module. event ModuleUninstalled(uint256 moduleTypeId, address module); /// @notice Thrown when attempting to remove the last validator. error CanNotRemoveLastValidator(); /// @dev Thrown when the specified module address is not recognized as valid. error ValidatorNotInstalled(address module); /// @dev Thrown when there is no installed validator detected. error NoValidatorInstalled(); /// @dev Thrown when the specified module address is not recognized as valid. error InvalidModule(address module); /// @dev Thrown when an invalid module type identifier is provided. error InvalidModuleTypeId(uint256 moduleTypeId); /// @dev Thrown when there is an attempt to install a module that is already installed. error ModuleAlreadyInstalled(uint256 moduleTypeId, address module); /// @dev Thrown when an operation is performed by an unauthorized operator. error UnauthorizedOperation(address operator); /// @dev Thrown when there is an attempt to uninstall a module that is not installed. error ModuleNotInstalled(uint256 moduleTypeId, address module); /// @dev Thrown when a module address is set to zero. error ModuleAddressCanNotBeZero(); /// @dev Thrown when a post-check fails after hook execution. error HookPostCheckFailed(); /// @dev Thrown when there is an attempt to install a hook while another is already installed. error HookAlreadyInstalled(address currentHook); /// @dev Thrown when there is an attempt to install a fallback handler for a selector already having one. error FallbackAlreadyInstalledForSelector(bytes4 selector); /// @dev Thrown when there is an attempt to uninstall a fallback handler for a selector that does not have one installed. error FallbackNotInstalledForSelector(bytes4 selector); /// @dev Thrown when a fallback handler fails to uninstall properly. error FallbackHandlerUninstallFailed(); /// @dev Thrown when no fallback handler is available for a given selector. error MissingFallbackHandler(bytes4 selector); /// @dev Thrown when Invalid data is provided for MultiType install flow error InvalidInput(); /// @dev Thrown when unable to validate Module Enable Mode signature error EnableModeSigError(); /// Error thrown when account installs/uninstalls module with mismatched input `moduleTypeId` error MismatchModuleTypeId(uint256 moduleTypeId); /// @dev Thrown when there is an attempt to install a forbidden selector as a fallback handler. error FallbackSelectorForbidden(); /// @dev Thrown when there is an attempt to install a fallback handler with an invalid calltype for a given selector. error FallbackCallTypeInvalid(); /// @notice Error thrown when an execution with an unsupported CallType was made. /// @param callType The unsupported call type. error UnsupportedCallType(CallType callType); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; // ────────────────────────────────────────────────────────────────────────────── // _ __ _ __ // / | / /__ | |/ /_ _______ // / |/ / _ \| / / / / ___/ // / /| / __/ / /_/ (__ ) // /_/ |_/\___/_/|_\__,_/____/ // // ────────────────────────────────────────────────────────────────────────────── // Nexus: A suite of contracts for Modular Smart Accounts compliant with ERC-7579 and ERC-4337, developed by Biconomy. // Learn more at https://biconomy.io. To report security issues, please contact us at: [email protected] // Magic value for ERC-1271 valid signature bytes4 constant ERC1271_MAGICVALUE = 0x1626ba7e; // Value indicating an invalid ERC-1271 signature bytes4 constant ERC1271_INVALID = 0xFFFFFFFF; // Value indicating successful validation uint256 constant VALIDATION_SUCCESS = 0; // Value indicating failed validation uint256 constant VALIDATION_FAILED = 1; // Module type identifier for Multitype install uint256 constant MODULE_TYPE_MULTI = 0; // Module type identifier for validators uint256 constant MODULE_TYPE_VALIDATOR = 1; // Module type identifier for executors uint256 constant MODULE_TYPE_EXECUTOR = 2; // Module type identifier for fallback handlers uint256 constant MODULE_TYPE_FALLBACK = 3; // Module type identifier for hooks uint256 constant MODULE_TYPE_HOOK = 4; string constant MODULE_ENABLE_MODE_NOTATION = "ModuleEnableMode(address module,uint256 moduleType,bytes32 userOpHash,bytes32 initDataHash)"; bytes32 constant MODULE_ENABLE_MODE_TYPE_HASH = keccak256(bytes(MODULE_ENABLE_MODE_NOTATION)); // Validation modes bytes1 constant MODE_VALIDATION = 0x00; bytes1 constant MODE_MODULE_ENABLE = 0x01; bytes4 constant SUPPORTS_ERC7739 = 0x77390000; bytes4 constant SUPPORTS_ERC7739_V1 = 0x77390001;
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Contract for EIP-712 typed structured data hashing and signing.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/EIP712.sol)
/// @author Modified from Solbase (https://github.com/Sol-DAO/solbase/blob/main/src/utils/EIP712.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/EIP712.sol)
///
/// @dev Note, this implementation:
/// - Uses `address(this)` for the `verifyingContract` field.
/// - Does NOT use the optional EIP-712 salt.
/// - Does NOT use any EIP-712 extensions.
/// This is for simplicity and to save gas.
/// If you need to customize, please fork / modify accordingly.
abstract contract EIP712 {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS AND IMMUTABLES */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev `keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")`.
bytes32 internal constant _DOMAIN_TYPEHASH =
0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f;
uint256 private immutable _cachedThis;
uint256 private immutable _cachedChainId;
bytes32 private immutable _cachedNameHash;
bytes32 private immutable _cachedVersionHash;
bytes32 private immutable _cachedDomainSeparator;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTRUCTOR */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Cache the hashes for cheaper runtime gas costs.
/// In the case of upgradeable contracts (i.e. proxies),
/// or if the chain id changes due to a hard fork,
/// the domain separator will be seamlessly calculated on-the-fly.
constructor() {
_cachedThis = uint256(uint160(address(this)));
_cachedChainId = block.chainid;
string memory name;
string memory version;
if (!_domainNameAndVersionMayChange()) (name, version) = _domainNameAndVersion();
bytes32 nameHash = _domainNameAndVersionMayChange() ? bytes32(0) : keccak256(bytes(name));
bytes32 versionHash =
_domainNameAndVersionMayChange() ? bytes32(0) : keccak256(bytes(version));
_cachedNameHash = nameHash;
_cachedVersionHash = versionHash;
bytes32 separator;
if (!_domainNameAndVersionMayChange()) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Load the free memory pointer.
mstore(m, _DOMAIN_TYPEHASH)
mstore(add(m, 0x20), nameHash)
mstore(add(m, 0x40), versionHash)
mstore(add(m, 0x60), chainid())
mstore(add(m, 0x80), address())
separator := keccak256(m, 0xa0)
}
}
_cachedDomainSeparator = separator;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* FUNCTIONS TO OVERRIDE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Please override this function to return the domain name and version.
/// ```
/// function _domainNameAndVersion()
/// internal
/// pure
/// virtual
/// returns (string memory name, string memory version)
/// {
/// name = "Solady";
/// version = "1";
/// }
/// ```
///
/// Note: If the returned result may change after the contract has been deployed,
/// you must override `_domainNameAndVersionMayChange()` to return true.
function _domainNameAndVersion()
internal
view
virtual
returns (string memory name, string memory version);
/// @dev Returns if `_domainNameAndVersion()` may change
/// after the contract has been deployed (i.e. after the constructor).
/// Default: false.
function _domainNameAndVersionMayChange() internal pure virtual returns (bool result) {}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* HASHING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the EIP-712 domain separator.
function _domainSeparator() internal view virtual returns (bytes32 separator) {
if (_domainNameAndVersionMayChange()) {
separator = _buildDomainSeparator();
} else {
separator = _cachedDomainSeparator;
if (_cachedDomainSeparatorInvalidated()) separator = _buildDomainSeparator();
}
}
/// @dev Returns the hash of the fully encoded EIP-712 message for this domain,
/// given `structHash`, as defined in
/// https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct.
///
/// The hash can be used together with {ECDSA-recover} to obtain the signer of a message:
/// ```
/// bytes32 digest = _hashTypedData(keccak256(abi.encode(
/// keccak256("Mail(address to,string contents)"),
/// mailTo,
/// keccak256(bytes(mailContents))
/// )));
/// address signer = ECDSA.recover(digest, signature);
/// ```
function _hashTypedData(bytes32 structHash) internal view virtual returns (bytes32 digest) {
// We will use `digest` to store the domain separator to save a bit of gas.
if (_domainNameAndVersionMayChange()) {
digest = _buildDomainSeparator();
} else {
digest = _cachedDomainSeparator;
if (_cachedDomainSeparatorInvalidated()) digest = _buildDomainSeparator();
}
/// @solidity memory-safe-assembly
assembly {
// Compute the digest.
mstore(0x00, 0x1901000000000000) // Store "\x19\x01".
mstore(0x1a, digest) // Store the domain separator.
mstore(0x3a, structHash) // Store the struct hash.
digest := keccak256(0x18, 0x42)
// Restore the part of the free memory slot that was overwritten.
mstore(0x3a, 0)
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EIP-5267 OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev See: https://eips.ethereum.org/EIPS/eip-5267
function eip712Domain()
public
view
virtual
returns (
bytes1 fields,
string memory name,
string memory version,
uint256 chainId,
address verifyingContract,
bytes32 salt,
uint256[] memory extensions
)
{
fields = hex"0f"; // `0b01111`.
(name, version) = _domainNameAndVersion();
chainId = block.chainid;
verifyingContract = address(this);
salt = salt; // `bytes32(0)`.
extensions = extensions; // `new uint256[](0)`.
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PRIVATE HELPERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the EIP-712 domain separator.
function _buildDomainSeparator() private view returns (bytes32 separator) {
// We will use `separator` to store the name hash to save a bit of gas.
bytes32 versionHash;
if (_domainNameAndVersionMayChange()) {
(string memory name, string memory version) = _domainNameAndVersion();
separator = keccak256(bytes(name));
versionHash = keccak256(bytes(version));
} else {
separator = _cachedNameHash;
versionHash = _cachedVersionHash;
}
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Load the free memory pointer.
mstore(m, _DOMAIN_TYPEHASH)
mstore(add(m, 0x20), separator) // Name hash.
mstore(add(m, 0x40), versionHash)
mstore(add(m, 0x60), chainid())
mstore(add(m, 0x80), address())
separator := keccak256(m, 0xa0)
}
}
/// @dev Returns if the cached domain separator has been invalidated.
function _cachedDomainSeparatorInvalidated() private view returns (bool result) {
uint256 cachedChainId = _cachedChainId;
uint256 cachedThis = _cachedThis;
/// @solidity memory-safe-assembly
assembly {
result := iszero(and(eq(chainid(), cachedChainId), eq(address(), cachedThis)))
}
}
}// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.7.6;
library ExcessivelySafeCall {
uint256 constant LOW_28_MASK =
0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff;
/// @notice Use when you _really_ really _really_ don't trust the called
/// contract. This prevents the called contract from causing reversion of
/// the caller in as many ways as we can.
/// @dev The main difference between this and a solidity low-level call is
/// that we limit the number of bytes that the callee can cause to be
/// copied to caller memory. This prevents stupid things like malicious
/// contracts returning 10,000,000 bytes causing a local OOG when copying
/// to memory.
/// @param _target The address to call
/// @param _gas The amount of gas to forward to the remote contract
/// @param _value The value in wei to send to the remote contract
/// @param _maxCopy The maximum number of bytes of returndata to copy
/// to memory.
/// @param _calldata The data to send to the remote contract
/// @return success and returndata, as `.call()`. Returndata is capped to
/// `_maxCopy` bytes.
function excessivelySafeCall(
address _target,
uint256 _gas,
uint256 _value,
uint16 _maxCopy,
bytes memory _calldata
) internal returns (bool, bytes memory) {
// set up for assembly call
uint256 _toCopy;
bool _success;
bytes memory _returnData = new bytes(_maxCopy);
// dispatch message to recipient
// by assembly calling "handle" function
// we call via assembly to avoid memcopying a very large returndata
// returned by a malicious contract
assembly {
_success := call(
_gas, // gas
_target, // recipient
_value, // ether value
add(_calldata, 0x20), // inloc
mload(_calldata), // inlen
0, // outloc
0 // outlen
)
// limit our copy to 256 bytes
_toCopy := returndatasize()
if gt(_toCopy, _maxCopy) {
_toCopy := _maxCopy
}
// Store the length of the copied bytes
mstore(_returnData, _toCopy)
// copy the bytes from returndata[0:_toCopy]
returndatacopy(add(_returnData, 0x20), 0, _toCopy)
}
return (_success, _returnData);
}
/// @notice Use when you _really_ really _really_ don't trust the called
/// contract. This prevents the called contract from causing reversion of
/// the caller in as many ways as we can.
/// @dev The main difference between this and a solidity low-level call is
/// that we limit the number of bytes that the callee can cause to be
/// copied to caller memory. This prevents stupid things like malicious
/// contracts returning 10,000,000 bytes causing a local OOG when copying
/// to memory.
/// @param _target The address to call
/// @param _gas The amount of gas to forward to the remote contract
/// @param _maxCopy The maximum number of bytes of returndata to copy
/// to memory.
/// @param _calldata The data to send to the remote contract
/// @return success and returndata, as `.call()`. Returndata is capped to
/// `_maxCopy` bytes.
function excessivelySafeStaticCall(
address _target,
uint256 _gas,
uint16 _maxCopy,
bytes memory _calldata
) internal view returns (bool, bytes memory) {
// set up for assembly call
uint256 _toCopy;
bool _success;
bytes memory _returnData = new bytes(_maxCopy);
// dispatch message to recipient
// by assembly calling "handle" function
// we call via assembly to avoid memcopying a very large returndata
// returned by a malicious contract
assembly {
_success := staticcall(
_gas, // gas
_target, // recipient
add(_calldata, 0x20), // inloc
mload(_calldata), // inlen
0, // outloc
0 // outlen
)
// limit our copy to 256 bytes
_toCopy := returndatasize()
if gt(_toCopy, _maxCopy) {
_toCopy := _maxCopy
}
// Store the length of the copied bytes
mstore(_returnData, _toCopy)
// copy the bytes from returndata[0:_toCopy]
returndatacopy(add(_returnData, 0x20), 0, _toCopy)
}
return (_success, _returnData);
}
/**
* @notice Swaps function selectors in encoded contract calls
* @dev Allows reuse of encoded calldata for functions with identical
* argument types but different names. It simply swaps out the first 4 bytes
* for the new selector. This function modifies memory in place, and should
* only be used with caution.
* @param _newSelector The new 4-byte selector
* @param _buf The encoded contract args
*/
function swapSelector(bytes4 _newSelector, bytes memory _buf)
internal
pure
{
require(_buf.length >= 4);
uint256 _mask = LOW_28_MASK;
assembly {
// load the first word of
let _word := mload(add(_buf, 0x20))
// mask out the top 4 bytes
// /x
_word := and(_word, _mask)
_word := or(_newSelector, _word)
mstore(add(_buf, 0x20), _word)
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;
import { IERC7484 } from "../interfaces/IERC7484.sol";
/// @title RegistryAdapter
/// @notice This contract provides an interface for interacting with an ERC-7484 compliant registry.
/// @dev The registry feature is opt-in, allowing the smart account owner to select and trust specific attesters.
abstract contract RegistryAdapter {
IERC7484 public registry;
/// @notice Emitted when a new ERC-7484 registry is configured for the account.
/// @param registry The configured registry contract.
event ERC7484RegistryConfigured(IERC7484 indexed registry);
/// @notice Modifier to check if a module meets the required attestations in the registry.
/// @param module The module to check.
/// @param moduleType The type of the module to verify in the registry.
modifier withRegistry(address module, uint256 moduleType) {
_checkRegistry(module, moduleType);
_;
}
/// @notice Configures the ERC-7484 registry and sets trusted attesters.
/// @param newRegistry The new registry contract to use.
/// @param attesters The list of attesters to trust.
/// @param threshold The number of attestations required.
function _configureRegistry(IERC7484 newRegistry, address[] calldata attesters, uint8 threshold) internal {
registry = newRegistry;
if (address(newRegistry) != address(0)) {
newRegistry.trustAttesters(threshold, attesters);
}
emit ERC7484RegistryConfigured(newRegistry);
}
/// @notice Checks the registry to ensure sufficient valid attestations for a module.
/// @param module The module to check.
/// @param moduleType The type of the module to verify in the registry.
/// @dev Reverts if the required attestations are not met.
function _checkRegistry(address module, uint256 moduleType) internal view {
IERC7484 moduleRegistry = registry;
if (address(moduleRegistry) != address(0)) {
// This will revert if attestations or the threshold are not met.
moduleRegistry.check(module, moduleType);
}
}
}// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.7.5;
/**
* Manage deposits and stakes.
* Deposit is just a balance used to pay for UserOperations (either by a paymaster or an account).
* Stake is value locked for at least "unstakeDelay" by the staked entity.
*/
interface IStakeManager {
event Deposited(address indexed account, uint256 totalDeposit);
event Withdrawn(
address indexed account,
address withdrawAddress,
uint256 amount
);
// Emitted when stake or unstake delay are modified.
event StakeLocked(
address indexed account,
uint256 totalStaked,
uint256 unstakeDelaySec
);
// Emitted once a stake is scheduled for withdrawal.
event StakeUnlocked(address indexed account, uint256 withdrawTime);
event StakeWithdrawn(
address indexed account,
address withdrawAddress,
uint256 amount
);
/**
* @param deposit - The entity's deposit.
* @param staked - True if this entity is staked.
* @param stake - Actual amount of ether staked for this entity.
* @param unstakeDelaySec - Minimum delay to withdraw the stake.
* @param withdrawTime - First block timestamp where 'withdrawStake' will be callable, or zero if already locked.
* @dev Sizes were chosen so that deposit fits into one cell (used during handleOp)
* and the rest fit into a 2nd cell (used during stake/unstake)
* - 112 bit allows for 10^15 eth
* - 48 bit for full timestamp
* - 32 bit allows 150 years for unstake delay
*/
struct DepositInfo {
uint256 deposit;
bool staked;
uint112 stake;
uint32 unstakeDelaySec;
uint48 withdrawTime;
}
// API struct used by getStakeInfo and simulateValidation.
struct StakeInfo {
uint256 stake;
uint256 unstakeDelaySec;
}
/**
* Get deposit info.
* @param account - The account to query.
* @return info - Full deposit information of given account.
*/
function getDepositInfo(
address account
) external view returns (DepositInfo memory info);
/**
* Get account balance.
* @param account - The account to query.
* @return - The deposit (for gas payment) of the account.
*/
function balanceOf(address account) external view returns (uint256);
/**
* Add to the deposit of the given account.
* @param account - The account to add to.
*/
function depositTo(address account) external payable;
/**
* Add to the account's stake - amount and delay
* any pending unstake is first cancelled.
* @param _unstakeDelaySec - The new lock duration before the deposit can be withdrawn.
*/
function addStake(uint32 _unstakeDelaySec) external payable;
/**
* Attempt to unlock the stake.
* The value can be withdrawn (using withdrawStake) after the unstake delay.
*/
function unlockStake() external;
/**
* Withdraw from the (unlocked) stake.
* Must first call unlockStake and wait for the unstakeDelay to pass.
* @param withdrawAddress - The address to send withdrawn value.
*/
function withdrawStake(address payable withdrawAddress) external;
/**
* Withdraw from the deposit.
* @param withdrawAddress - The address to send withdrawn value.
* @param withdrawAmount - The amount to withdraw.
*/
function withdrawTo(
address payable withdrawAddress,
uint256 withdrawAmount
) external;
}// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.5;
import "./PackedUserOperation.sol";
/**
* Aggregated Signatures validator.
*/
interface IAggregator {
/**
* Validate aggregated signature.
* Revert if the aggregated signature does not match the given list of operations.
* @param userOps - Array of UserOperations to validate the signature for.
* @param signature - The aggregated signature.
*/
function validateSignatures(
PackedUserOperation[] calldata userOps,
bytes calldata signature
) external view;
/**
* Validate signature of a single userOp.
* This method should be called by bundler after EntryPointSimulation.simulateValidation() returns
* the aggregator this account uses.
* First it validates the signature over the userOp. Then it returns data to be used when creating the handleOps.
* @param userOp - The userOperation received from the user.
* @return sigForUserOp - The value to put into the signature field of the userOp when calling handleOps.
* (usually empty, unless account and aggregator support some kind of "multisig".
*/
function validateUserOpSignature(
PackedUserOperation calldata userOp
) external view returns (bytes memory sigForUserOp);
/**
* Aggregate multiple signatures into a single value.
* This method is called off-chain to calculate the signature to pass with handleOps()
* bundler MAY use optimized custom code perform this aggregation.
* @param userOps - Array of UserOperations to collect the signatures from.
* @return aggregatedSignature - The aggregated signature.
*/
function aggregateSignatures(
PackedUserOperation[] calldata userOps
) external view returns (bytes memory aggregatedSignature);
}// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.5;
interface INonceManager {
/**
* Return the next nonce for this sender.
* Within a given key, the nonce values are sequenced (starting with zero, and incremented by one on each userop)
* But UserOp with different keys can come with arbitrary order.
*
* @param sender the account address
* @param key the high 192 bit of the nonce
* @return nonce a full nonce to pass for next UserOp with this sender.
*/
function getNonce(address sender, uint192 key)
external view returns (uint256 nonce);
/**
* Manually increment the nonce of the sender.
* This method is exposed just for completeness..
* Account does NOT need to call it, neither during validation, nor elsewhere,
* as the EntryPoint will update the nonce regardless.
* Possible use-case is call it with various keys to "initialize" their nonces to one, so that future
* UserOperations will not pay extra for the first transaction with a given key.
*/
function incrementNonce(uint192 key) external;
}// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; // ────────────────────────────────────────────────────────────────────────────── // _ __ _ __ // / | / /__ | |/ /_ _______ // / |/ / _ \| / / / / ___/ // / /| / __/ / /_/ (__ ) // /_/ |_/\___/_/|_\__,_/____/ // // ────────────────────────────────────────────────────────────────────────────── // Nexus: A suite of contracts for Modular Smart Accounts compliant with ERC-7579 and ERC-4337, developed by Biconomy. // Learn more at https://biconomy.io. To report security issues, please contact us at: [email protected] /// @title Execution Manager Events and Errors Interface /// @notice Interface for defining events and errors related to transaction execution processes within smart accounts. /// @dev This interface defines events and errors used by execution manager to handle and report the operational status of smart account transactions. /// It is a part of the Nexus suite of contracts aimed at implementing flexible and secure smart account operations. /// @author @livingrockrises | Biconomy | [email protected] /// @author @aboudjem | Biconomy | [email protected] /// @author @filmakarov | Biconomy | [email protected] /// @author @zeroknots | Rhinestone.wtf | zeroknots.eth /// Special thanks to the Solady team for foundational contributions: https://github.com/Vectorized/solady import { ExecType } from "../../lib/ModeLib.sol"; interface IExecutionHelperEventsAndErrors { /// @notice Event emitted when a transaction fails to execute successfully. event TryExecuteUnsuccessful(bytes callData, bytes result); /// @notice Event emitted when a transaction fails to execute successfully. event TryDelegateCallUnsuccessful(bytes callData, bytes result); /// @notice Error thrown when an execution with an unsupported ExecType was made. /// @param execType The unsupported execution type. error UnsupportedExecType(ExecType execType); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; // ────────────────────────────────────────────────────────────────────────────── // _ __ _ __ // / | / /__ | |/ /_ _______ // / |/ / _ \| / / / / ___/ // / /| / __/ / /_/ (__ ) // /_/ |_/\___/_/|_\__,_/____/ // // ────────────────────────────────────────────────────────────────────────────── // Nexus: A suite of contracts for Modular Smart Accounts compliant with ERC-7579 and ERC-4337, developed by Biconomy. // Learn more at https://biconomy.io. To report security issues, please contact us at: [email protected] import { SentinelListLib } from "sentinellist/SentinelList.sol"; import { IHook } from "../modules/IHook.sol"; import { CallType } from "../../lib/ModeLib.sol"; /// @title Nexus - IStorage Interface /// @notice Provides structured storage for Modular Smart Account under the Nexus suite, compliant with ERC-7579 and ERC-4337. /// @dev Manages structured storage using SentinelListLib for validators and executors, and a mapping for fallback handlers. /// This interface utilizes ERC-7201 storage location practices to ensure isolated and collision-resistant storage spaces within smart contracts. /// It is designed to support dynamic execution and modular management strategies essential for advanced smart account architectures. /// @custom:storage-location erc7201:biconomy.storage.Nexus /// @author @livingrockrises | Biconomy | [email protected] /// @author @aboudjem | Biconomy | [email protected] /// @author @filmakarov | Biconomy | [email protected] /// @author @zeroknots | Rhinestone.wtf | zeroknots.eth /// Special thanks to the Solady team for foundational contributions: https://github.com/Vectorized/solady interface IStorage { /// @notice Struct storing validators and executors using Sentinel lists, and fallback handlers via mapping. struct AccountStorage { SentinelListLib.SentinelList validators; ///< List of validators, initialized upon contract deployment. SentinelListLib.SentinelList executors; ///< List of executors, similarly initialized. mapping(bytes4 => FallbackHandler) fallbacks; ///< Mapping of selectors to their respective fallback handlers. IHook hook; ///< Current hook module associated with this account. mapping(address hook => uint256) emergencyUninstallTimelock; ///< Mapping of hooks to requested timelocks. } /// @notice Defines a fallback handler with an associated handler address and a call type. struct FallbackHandler { address handler; ///< The address of the fallback function handler. CallType calltype; ///< The type of call this handler supports (e.g., static or call). } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; // ────────────────────────────────────────────────────────────────────────────── // _ __ _ __ // / | / /__ | |/ /_ _______ // / |/ / _ \| / / / / ___/ // / /| / __/ / /_/ (__ ) // /_/ |_/\___/_/|_\__,_/____/ // // ────────────────────────────────────────────────────────────────────────────── // Nexus: A suite of contracts for Modular Smart Accounts compliant with ERC-7579 and ERC-4337, developed by Biconomy. // Learn more at https://biconomy.io. To report security issues, please contact us at: [email protected] /// @title Execution /// @notice Struct to encapsulate execution data for a transaction struct Execution { /// @notice The target address for the transaction address target; /// @notice The value in wei to send with the transaction uint256 value; /// @notice The calldata for the transaction bytes callData; }
{
"remappings": [
"@openzeppelin/=node_modules/@openzeppelin/",
"forge-std/=node_modules/forge-std/src/",
"account-abstraction/=node_modules/account-abstraction/contracts/",
"solady/=node_modules/solady/src/",
"excessively-safe-call/=node_modules/excessively-safe-call/src/",
"sentinellist/=node_modules/sentinellist/src/",
"solarray/=node_modules/solarray/src/",
"erc7739Validator/=node_modules/erc7739-validator-base/src/",
"@ERC4337/=node_modules/@ERC4337/",
"@gnosis.pm/=node_modules/@gnosis.pm/",
"@openzeppelin/contracts/=node_modules/@openzeppelin/contracts/",
"@prb/=node_modules/@prb/",
"@prb/math/=node_modules/erc7739-validator-base/node_modules/@prb/math/src/",
"@rhinestone/=node_modules/@rhinestone/",
"@safe-global/=node_modules/@safe-global/",
"@zerodev/=node_modules/@zerodev/",
"ExcessivelySafeCall/=node_modules/erc7739-validator-base/node_modules/excessively-safe-call/src/",
"account-abstraction-v0.6/=node_modules/account-abstraction-v0.6/",
"ds-test/=node_modules/ds-test/",
"enumerablemap/=node_modules/enumerablemap/",
"enumerableset4337/=node_modules/erc7739-validator-base/node_modules/enumerablemap/src/",
"erc4337-validation/=node_modules/erc7739-validator-base/node_modules/@rhinestone/erc4337-validation/src/",
"erc7579/=node_modules/erc7579/",
"erc7739-validator-base/=node_modules/erc7739-validator-base/",
"eth-gas-reporter/=node_modules/eth-gas-reporter/",
"hardhat-deploy/=node_modules/hardhat-deploy/",
"hardhat/=node_modules/hardhat/",
"kernel/=node_modules/erc7739-validator-base/node_modules/@zerodev/kernel/src/",
"module-bases/=node_modules/erc7739-validator-base/node_modules/@rhinestone/module-bases/src/",
"modulekit/=node_modules/modulekit/",
"registry/=node_modules/modulekit/node_modules/@rhinestone/registry/src/",
"safe7579/=node_modules/erc7739-validator-base/node_modules/@rhinestone/safe7579/src/",
"stringutils/=node_modules/stringutils/"
],
"optimizer": {
"enabled": true,
"runs": 999
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "none",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "cancun",
"viaIR": false,
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"implementation","type":"address"},{"internalType":"address","name":"factoryOwner","type":"address"},{"internalType":"address","name":"k1Validator","type":"address"},{"internalType":"contract NexusBootstrap","name":"bootstrapper","type":"address"},{"internalType":"contract IERC7484","name":"registry","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[],"name":"InnerCallFailed","type":"error"},{"inputs":[],"name":"InvalidEntryPointAddress","type":"error"},{"inputs":[],"name":"NewOwnerIsZeroAddress","type":"error"},{"inputs":[],"name":"NoHandoverRequest","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"inputs":[],"name":"ZeroAddressNotAllowed","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"uint256","name":"index","type":"uint256"}],"name":"AccountCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[],"name":"ACCOUNT_IMPLEMENTATION","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"BOOTSTRAPPER","outputs":[{"internalType":"contract NexusBootstrap","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"K1_VALIDATOR","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REGISTRY","outputs":[{"internalType":"contract IERC7484","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"epAddress","type":"address"},{"internalType":"uint32","name":"unstakeDelaySec","type":"uint32"}],"name":"addStake","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"cancelOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"completeOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"eoaOwner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"address[]","name":"attesters","type":"address[]"},{"internalType":"uint8","name":"threshold","type":"uint8"}],"name":"computeAccountAddress","outputs":[{"internalType":"address payable","name":"expectedAddress","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"eoaOwner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"address[]","name":"attesters","type":"address[]"},{"internalType":"uint8","name":"threshold","type":"uint8"}],"name":"createAccount","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"ownershipHandoverExpiresAt","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"requestOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"epAddress","type":"address"}],"name":"unlockStake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"epAddress","type":"address"},{"internalType":"address payable","name":"withdrawAddress","type":"address"}],"name":"withdrawStake","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
610100604052348015610010575f5ffd5b50604051610fdd380380610fdd83398101604081905261002f91610113565b83610039816100be565b506001600160a01b038516158061005757506001600160a01b038316155b8061006957506001600160a01b038216155b8061007b57506001600160a01b038416155b15610099576040516342bcdf7f60e11b815260040160405180910390fd5b6001600160a01b0394851660805291841660a052831660c05290911660e05250610184565b638b78c6d81980546001600160a01b039092169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a38190555b50565b6001600160a01b03811681146100fc575f5ffd5b5f5f5f5f5f60a08688031215610127575f5ffd5b8551610132816100ff565b6020870151909550610143816100ff565b6040870151909450610154816100ff565b6060870151909350610165816100ff565b6080870151909250610176816100ff565b809150509295509295909350565b60805160a05160c05160e051610e036101da5f395f818161010401526103f601525f81816101fd01526103c601525f818161026f015261038e01525f81816101710152818161033101526105c80152610e035ff3fe6080604052600436106100ef575f3560e01c806367d2d1c911610087578063d0733b3b11610057578063d0733b3b1461025e578063f04e283e14610291578063f2fde38b146102a4578063fee81cf4146102b7575f5ffd5b806367d2d1c9146101ec578063715018a61461021f5780638da5cb5b14610227578063b36f97051461023f575f5ffd5b8063322cc8ca116100c2578063322cc8ca1461019357806345171159146101b25780634a1ce599146101c557806354d1f13d146101e4575f5ffd5b806306433b1b146100f35780630d51f0b7146101435780632569296214610156578063290ab98414610160575b5f5ffd5b3480156100fe575f5ffd5b506101267f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020015b60405180910390f35b610126610151366004610a80565b6102f6565b61015e610543565b005b34801561016b575f5ffd5b506101267f000000000000000000000000000000000000000000000000000000000000000081565b34801561019e575f5ffd5b506101266101ad366004610a80565b610590565b61015e6101c0366004610b1d565b6105f9565b3480156101d0575f5ffd5b5061015e6101df366004610b5c565b6106a2565b61015e610722565b3480156101f7575f5ffd5b506101267f000000000000000000000000000000000000000000000000000000000000000081565b61015e61075b565b348015610232575f5ffd5b50638b78c6d81954610126565b34801561024a575f5ffd5b5061015e610259366004610b7e565b61076e565b348015610269575f5ffd5b506101267f000000000000000000000000000000000000000000000000000000000000000081565b61015e61029f366004610b5c565b610810565b61015e6102b2366004610b5c565b61084d565b3480156102c2575f5ffd5b506102e86102d1366004610b5c565b63389a75e1600c9081525f91909152602090205490565b60405190815260200161013a565b5f5f8686868686604051602001610311959493929190610baa565b6040516020818303038152906040528051906020012090505f5f610356347f000000000000000000000000000000000000000000000000000000000000000085610873565b6040805160608d901b6bffffffffffffffffffffffff1916602082015281516014818303018152607482019092526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166034820190815260549091019190915291935091505f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663837b892e837f00000000000000000000000000000000000000000000000000000000000000008c8c8c6040518663ffffffff1660e01b8152600401610438959493929190610c65565b5f60405180830381865afa158015610452573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526104799190810190610d31565b905083610534576040517f4b6a14190000000000000000000000000000000000000000000000000000000081526001600160a01b03841690634b6a1419906104c5908490600401610de4565b5f604051808303815f87803b1580156104dc575f5ffd5b505af11580156104ee573d5f5f3e3d5ffd5b50505050898b6001600160a01b0316846001600160a01b03167f33310a89c32d8cc00057ad6ef6274d2f8fe22389a992cf89983e09fc84f6cfff60405160405180910390a45b50909998505050505050505050565b5f6202a30067ffffffffffffffff164201905063389a75e1600c52335f52806020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d5f5fa250565b5f5f86868686866040516020016105ab959493929190610baa565b6040516020818303038152906040528051906020012090506105ee7f00000000000000000000000000000000000000000000000000000000000000008230610951565b979650505050505050565b6106016109e1565b6001600160a01b038216610628576040516391fdf19160e01b815260040160405180910390fd5b6040517f0396cb6000000000000000000000000000000000000000000000000000000000815263ffffffff821660048201526001600160a01b03831690630396cb609034906024015f604051808303818588803b158015610687575f5ffd5b505af1158015610699573d5f5f3e3d5ffd5b50505050505050565b6106aa6109e1565b6001600160a01b0381166106d1576040516391fdf19160e01b815260040160405180910390fd5b806001600160a01b031663bb9fe6bf6040518163ffffffff1660e01b81526004015f604051808303815f87803b158015610709575f5ffd5b505af115801561071b573d5f5f3e3d5ffd5b5050505050565b63389a75e1600c52335f525f6020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c925f5fa2565b6107636109e1565b61076c5f6109fb565b565b6107766109e1565b6001600160a01b03821661079d576040516391fdf19160e01b815260040160405180910390fd5b6040517fc23a5cea0000000000000000000000000000000000000000000000000000000081526001600160a01b03828116600483015283169063c23a5cea906024015f604051808303815f87803b1580156107f6575f5ffd5b505af1158015610808573d5f5f3e3d5ffd5b505050505050565b6108186109e1565b63389a75e1600c52805f526020600c20805442111561083e57636f5e88185f526004601cfd5b5f905561084a816109fb565b50565b6108556109e1565b8060601b61086a57637448fbae5f526004601cfd5b61084a816109fb565b5f5f6040517fcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f36060527f5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e207660405261600960205284601e5268603d3d8160223d3973600a52605f60212060358201523060581b815260ff8153836015820152605581209150813b61091d5783605f602188f59150816109185763301164255f526004601cfd5b610940565b600192508515610940575f385f3889865af16109405763b12d13eb5f526004601cfd5b80604052505f606052935093915050565b5f5f6109cb85604080517fcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f360609081527f5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e20768352616009602052601e9390935268603d3d8160223d3973600a52605f60212091525f90915290565b90506109d8818585610a38565b95945050505050565b638b78c6d81954331461076c576382b429005f526004601cfd5b638b78c6d81980546001600160a01b039092169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a355565b5f60ff5f5350603592835260601b60015260155260555f908120915290565b6001600160a01b038116811461084a575f5ffd5b803560ff81168114610a7b575f5ffd5b919050565b5f5f5f5f5f60808688031215610a94575f5ffd5b8535610a9f81610a57565b945060208601359350604086013567ffffffffffffffff811115610ac1575f5ffd5b8601601f81018813610ad1575f5ffd5b803567ffffffffffffffff811115610ae7575f5ffd5b8860208260051b8401011115610afb575f5ffd5b60209190910193509150610b1160608701610a6b565b90509295509295909350565b5f5f60408385031215610b2e575f5ffd5b8235610b3981610a57565b9150602083013563ffffffff81168114610b51575f5ffd5b809150509250929050565b5f60208284031215610b6c575f5ffd5b8135610b7781610a57565b9392505050565b5f5f60408385031215610b8f575f5ffd5b8235610b9a81610a57565b91506020830135610b5181610a57565b6bffffffffffffffffffffffff198660601b1681528460148201525f60348201855f5b86811015610bfe578135610be081610a57565b6001600160a01b031683526020928301929190910190600101610bcd565b505060f89390931b7fff000000000000000000000000000000000000000000000000000000000000001683525050600101949350505050565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b608081526001600160a01b0386511660808201525f6020870151604060a0840152610c9360c0840182610c37565b6001600160a01b0388166020858101919091528482036040860152868252879250015f5b86811015610ce7578235610cca81610a57565b6001600160a01b0316825260209283019290910190600101610cb7565b5060ff851660608501529150610cfa9050565b9695505050505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b5f60208284031215610d41575f5ffd5b815167ffffffffffffffff811115610d57575f5ffd5b8201601f81018413610d67575f5ffd5b805167ffffffffffffffff811115610d8157610d81610d04565b604051601f8201601f19908116603f0116810167ffffffffffffffff81118282101715610db057610db0610d04565b604052818152828201602001861015610dc7575f5ffd5b8160208401602083015e5f91810160200191909152949350505050565b602081525f610b776020830184610c3756fea164736f6c634300081b000a000000000000000000000000000000008761e87f023f65c49dc9cb1c7edfeaaf000000000000000000000000129443ca2a9dec2020808a2868b38dda457eacc70000000000000000000000000000002d6db27c52e3c11c1cf24072004ac75cba000000000000000000000000000000f5b753fdd20c5ca2d7c1210b3ab1ea5903000000000000000000000000000000000069e2a187aeffb852bf3ccdc95151b2
Deployed Bytecode
0x6080604052600436106100ef575f3560e01c806367d2d1c911610087578063d0733b3b11610057578063d0733b3b1461025e578063f04e283e14610291578063f2fde38b146102a4578063fee81cf4146102b7575f5ffd5b806367d2d1c9146101ec578063715018a61461021f5780638da5cb5b14610227578063b36f97051461023f575f5ffd5b8063322cc8ca116100c2578063322cc8ca1461019357806345171159146101b25780634a1ce599146101c557806354d1f13d146101e4575f5ffd5b806306433b1b146100f35780630d51f0b7146101435780632569296214610156578063290ab98414610160575b5f5ffd5b3480156100fe575f5ffd5b506101267f000000000000000000000000000000000069e2a187aeffb852bf3ccdc95151b281565b6040516001600160a01b0390911681526020015b60405180910390f35b610126610151366004610a80565b6102f6565b61015e610543565b005b34801561016b575f5ffd5b506101267f000000000000000000000000000000008761e87f023f65c49dc9cb1c7edfeaaf81565b34801561019e575f5ffd5b506101266101ad366004610a80565b610590565b61015e6101c0366004610b1d565b6105f9565b3480156101d0575f5ffd5b5061015e6101df366004610b5c565b6106a2565b61015e610722565b3480156101f7575f5ffd5b506101267f000000000000000000000000000000f5b753fdd20c5ca2d7c1210b3ab1ea590381565b61015e61075b565b348015610232575f5ffd5b50638b78c6d81954610126565b34801561024a575f5ffd5b5061015e610259366004610b7e565b61076e565b348015610269575f5ffd5b506101267f0000000000000000000000000000002d6db27c52e3c11c1cf24072004ac75cba81565b61015e61029f366004610b5c565b610810565b61015e6102b2366004610b5c565b61084d565b3480156102c2575f5ffd5b506102e86102d1366004610b5c565b63389a75e1600c9081525f91909152602090205490565b60405190815260200161013a565b5f5f8686868686604051602001610311959493929190610baa565b6040516020818303038152906040528051906020012090505f5f610356347f000000000000000000000000000000008761e87f023f65c49dc9cb1c7edfeaaf85610873565b6040805160608d901b6bffffffffffffffffffffffff1916602082015281516014818303018152607482019092526001600160a01b037f0000000000000000000000000000002d6db27c52e3c11c1cf24072004ac75cba166034820190815260549091019190915291935091505f7f000000000000000000000000000000f5b753fdd20c5ca2d7c1210b3ab1ea59036001600160a01b031663837b892e837f000000000000000000000000000000000069e2a187aeffb852bf3ccdc95151b28c8c8c6040518663ffffffff1660e01b8152600401610438959493929190610c65565b5f60405180830381865afa158015610452573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526104799190810190610d31565b905083610534576040517f4b6a14190000000000000000000000000000000000000000000000000000000081526001600160a01b03841690634b6a1419906104c5908490600401610de4565b5f604051808303815f87803b1580156104dc575f5ffd5b505af11580156104ee573d5f5f3e3d5ffd5b50505050898b6001600160a01b0316846001600160a01b03167f33310a89c32d8cc00057ad6ef6274d2f8fe22389a992cf89983e09fc84f6cfff60405160405180910390a45b50909998505050505050505050565b5f6202a30067ffffffffffffffff164201905063389a75e1600c52335f52806020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d5f5fa250565b5f5f86868686866040516020016105ab959493929190610baa565b6040516020818303038152906040528051906020012090506105ee7f000000000000000000000000000000008761e87f023f65c49dc9cb1c7edfeaaf8230610951565b979650505050505050565b6106016109e1565b6001600160a01b038216610628576040516391fdf19160e01b815260040160405180910390fd5b6040517f0396cb6000000000000000000000000000000000000000000000000000000000815263ffffffff821660048201526001600160a01b03831690630396cb609034906024015f604051808303818588803b158015610687575f5ffd5b505af1158015610699573d5f5f3e3d5ffd5b50505050505050565b6106aa6109e1565b6001600160a01b0381166106d1576040516391fdf19160e01b815260040160405180910390fd5b806001600160a01b031663bb9fe6bf6040518163ffffffff1660e01b81526004015f604051808303815f87803b158015610709575f5ffd5b505af115801561071b573d5f5f3e3d5ffd5b5050505050565b63389a75e1600c52335f525f6020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c925f5fa2565b6107636109e1565b61076c5f6109fb565b565b6107766109e1565b6001600160a01b03821661079d576040516391fdf19160e01b815260040160405180910390fd5b6040517fc23a5cea0000000000000000000000000000000000000000000000000000000081526001600160a01b03828116600483015283169063c23a5cea906024015f604051808303815f87803b1580156107f6575f5ffd5b505af1158015610808573d5f5f3e3d5ffd5b505050505050565b6108186109e1565b63389a75e1600c52805f526020600c20805442111561083e57636f5e88185f526004601cfd5b5f905561084a816109fb565b50565b6108556109e1565b8060601b61086a57637448fbae5f526004601cfd5b61084a816109fb565b5f5f6040517fcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f36060527f5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e207660405261600960205284601e5268603d3d8160223d3973600a52605f60212060358201523060581b815260ff8153836015820152605581209150813b61091d5783605f602188f59150816109185763301164255f526004601cfd5b610940565b600192508515610940575f385f3889865af16109405763b12d13eb5f526004601cfd5b80604052505f606052935093915050565b5f5f6109cb85604080517fcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f360609081527f5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e20768352616009602052601e9390935268603d3d8160223d3973600a52605f60212091525f90915290565b90506109d8818585610a38565b95945050505050565b638b78c6d81954331461076c576382b429005f526004601cfd5b638b78c6d81980546001600160a01b039092169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a355565b5f60ff5f5350603592835260601b60015260155260555f908120915290565b6001600160a01b038116811461084a575f5ffd5b803560ff81168114610a7b575f5ffd5b919050565b5f5f5f5f5f60808688031215610a94575f5ffd5b8535610a9f81610a57565b945060208601359350604086013567ffffffffffffffff811115610ac1575f5ffd5b8601601f81018813610ad1575f5ffd5b803567ffffffffffffffff811115610ae7575f5ffd5b8860208260051b8401011115610afb575f5ffd5b60209190910193509150610b1160608701610a6b565b90509295509295909350565b5f5f60408385031215610b2e575f5ffd5b8235610b3981610a57565b9150602083013563ffffffff81168114610b51575f5ffd5b809150509250929050565b5f60208284031215610b6c575f5ffd5b8135610b7781610a57565b9392505050565b5f5f60408385031215610b8f575f5ffd5b8235610b9a81610a57565b91506020830135610b5181610a57565b6bffffffffffffffffffffffff198660601b1681528460148201525f60348201855f5b86811015610bfe578135610be081610a57565b6001600160a01b031683526020928301929190910190600101610bcd565b505060f89390931b7fff000000000000000000000000000000000000000000000000000000000000001683525050600101949350505050565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b608081526001600160a01b0386511660808201525f6020870151604060a0840152610c9360c0840182610c37565b6001600160a01b0388166020858101919091528482036040860152868252879250015f5b86811015610ce7578235610cca81610a57565b6001600160a01b0316825260209283019290910190600101610cb7565b5060ff851660608501529150610cfa9050565b9695505050505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b5f60208284031215610d41575f5ffd5b815167ffffffffffffffff811115610d57575f5ffd5b8201601f81018413610d67575f5ffd5b805167ffffffffffffffff811115610d8157610d81610d04565b604051601f8201601f19908116603f0116810167ffffffffffffffff81118282101715610db057610db0610d04565b604052818152828201602001861015610dc7575f5ffd5b8160208401602083015e5f91810160200191909152949350505050565b602081525f610b776020830184610c3756fea164736f6c634300081b000a
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000000000008761e87f023f65c49dc9cb1c7edfeaaf000000000000000000000000129443ca2a9dec2020808a2868b38dda457eacc70000000000000000000000000000002d6db27c52e3c11c1cf24072004ac75cba000000000000000000000000000000f5b753fdd20c5ca2d7c1210b3ab1ea5903000000000000000000000000000000000069e2a187aeffb852bf3ccdc95151b2
-----Decoded View---------------
Arg [0] : implementation (address): 0x000000008761E87F023f65c49DC9cb1C7EdFEaaf
Arg [1] : factoryOwner (address): 0x129443cA2a9Dec2020808a2868b38dDA457eaCC7
Arg [2] : k1Validator (address): 0x0000002D6DB27c52E3C11c1Cf24072004AC75cBa
Arg [3] : bootstrapper (address): 0x000000F5b753Fdd20C5CA2D7c1210b3Ab1EA5903
Arg [4] : registry (address): 0x000000000069E2a187AEFFb852bF3cCdC95151B2
-----Encoded View---------------
5 Constructor Arguments found :
Arg [0] : 000000000000000000000000000000008761e87f023f65c49dc9cb1c7edfeaaf
Arg [1] : 000000000000000000000000129443ca2a9dec2020808a2868b38dda457eacc7
Arg [2] : 0000000000000000000000000000002d6db27c52e3c11c1cf24072004ac75cba
Arg [3] : 000000000000000000000000000000f5b753fdd20c5ca2d7c1210b3ab1ea5903
Arg [4] : 000000000000000000000000000000000069e2a187aeffb852bf3ccdc95151b2
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 34 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.