ETH Price: $2,072.62 (-0.23%)
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Execute244604022026-02-15 6:26:472 hrs ago1771136807IN
0xEc2C96e7...9eF9AA7c7
0.00021191 ETH0.000011630.03143285
Execute244434892026-02-12 21:50:112 days ago1770933011IN
0xEc2C96e7...9eF9AA7c7
0.00006839 ETH0.000019060.05345824
Execute244422772026-02-12 17:46:592 days ago1770918419IN
0xEc2C96e7...9eF9AA7c7
0.00006839 ETH0.000050790.14332648
Execute244406912026-02-12 12:27:112 days ago1770899231IN
0xEc2C96e7...9eF9AA7c7
0.00006839 ETH0.001332313.59014068
Execute244346902026-02-11 16:21:233 days ago1770826883IN
0xEc2C96e7...9eF9AA7c7
0.00021191 ETH0.000115670.32756786
Execute244346682026-02-11 16:16:593 days ago1770826619IN
0xEc2C96e7...9eF9AA7c7
0.00021191 ETH0.00013690.38492706
Execute244346082026-02-11 16:04:473 days ago1770825887IN
0xEc2C96e7...9eF9AA7c7
0.00021191 ETH0.000127420.34613353
Execute244345582026-02-11 15:54:473 days ago1770825287IN
0xEc2C96e7...9eF9AA7c7
0.00021191 ETH0.000133380.37244784
Execute244345262026-02-11 15:48:233 days ago1770824903IN
0xEc2C96e7...9eF9AA7c7
0.00021191 ETH0.000128230.34637204
Execute244343902026-02-11 15:21:113 days ago1770823271IN
0xEc2C96e7...9eF9AA7c7
0.00021191 ETH0.000238280.62666272
Execute244340762026-02-11 14:18:113 days ago1770819491IN
0xEc2C96e7...9eF9AA7c7
0.00021191 ETH0.000185810.52614139
Execute244340742026-02-11 14:17:473 days ago1770819467IN
0xEc2C96e7...9eF9AA7c7
0.00021191 ETH0.000175270.49284224
Execute244340602026-02-11 14:14:593 days ago1770819299IN
0xEc2C96e7...9eF9AA7c7
0.00021191 ETH0.000179820.49859477
Execute244340552026-02-11 14:13:593 days ago1770819239IN
0xEc2C96e7...9eF9AA7c7
0.00021191 ETH0.000186090.49210262
Execute244339652026-02-11 13:55:473 days ago1770818147IN
0xEc2C96e7...9eF9AA7c7
0.00021191 ETH0.000051440.13711189
Execute244339062026-02-11 13:43:593 days ago1770817439IN
0xEc2C96e7...9eF9AA7c7
0.00021191 ETH0.000041840.1130229
Execute244337302026-02-11 13:08:233 days ago1770815303IN
0xEc2C96e7...9eF9AA7c7
0.00021191 ETH0.000037450.10315169
Execute244334462026-02-11 12:11:353 days ago1770811895IN
0xEc2C96e7...9eF9AA7c7
0.00021191 ETH0.000041640.11793261
Execute244333332026-02-11 11:48:473 days ago1770810527IN
0xEc2C96e7...9eF9AA7c7
0.00021191 ETH0.000032350.0873049
Execute244332372026-02-11 11:29:353 days ago1770809375IN
0xEc2C96e7...9eF9AA7c7
0.00006839 ETH0.00003410.09181015
Execute244331932026-02-11 11:20:473 days ago1770808847IN
0xEc2C96e7...9eF9AA7c7
0.00021191 ETH0.000032490.09200901
Execute244324042026-02-11 8:42:114 days ago1770799331IN
0xEc2C96e7...9eF9AA7c7
0.00021191 ETH0.000025730.06905248
Execute244324002026-02-11 8:41:234 days ago1770799283IN
0xEc2C96e7...9eF9AA7c7
0.00021191 ETH0.00002790.07791015
Execute244321132026-02-11 7:43:474 days ago1770795827IN
0xEc2C96e7...9eF9AA7c7
0.00021191 ETH0.000019410.05498428
Execute244320022026-02-11 7:21:234 days ago1770794483IN
0xEc2C96e7...9eF9AA7c7
0.00021191 ETH0.000024650.06658971
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Method Block
From
To
Execute From Exe...244604022026-02-15 6:26:472 hrs ago1771136807
0xEc2C96e7...9eF9AA7c7
0.00021191 ETH
Execute From Exe...244434892026-02-12 21:50:112 days ago1770933011
0xEc2C96e7...9eF9AA7c7
0.00006839 ETH
Execute From Exe...244422772026-02-12 17:46:592 days ago1770918419
0xEc2C96e7...9eF9AA7c7
0.00006839 ETH
Execute From Exe...244406912026-02-12 12:27:112 days ago1770899231
0xEc2C96e7...9eF9AA7c7
0.00006839 ETH
Execute From Exe...244346902026-02-11 16:21:233 days ago1770826883
0xEc2C96e7...9eF9AA7c7
0.00021191 ETH
Execute From Exe...244346682026-02-11 16:16:593 days ago1770826619
0xEc2C96e7...9eF9AA7c7
0.00021191 ETH
Execute From Exe...244346082026-02-11 16:04:473 days ago1770825887
0xEc2C96e7...9eF9AA7c7
0.00021191 ETH
Execute From Exe...244345582026-02-11 15:54:473 days ago1770825287
0xEc2C96e7...9eF9AA7c7
0.00021191 ETH
Execute From Exe...244345262026-02-11 15:48:233 days ago1770824903
0xEc2C96e7...9eF9AA7c7
0.00021191 ETH
Execute From Exe...244343902026-02-11 15:21:113 days ago1770823271
0xEc2C96e7...9eF9AA7c7
0.00021191 ETH
Execute From Exe...244340762026-02-11 14:18:113 days ago1770819491
0xEc2C96e7...9eF9AA7c7
0.00021191 ETH
Execute From Exe...244340742026-02-11 14:17:473 days ago1770819467
0xEc2C96e7...9eF9AA7c7
0.00021191 ETH
Execute From Exe...244340602026-02-11 14:14:593 days ago1770819299
0xEc2C96e7...9eF9AA7c7
0.00021191 ETH
Execute From Exe...244340552026-02-11 14:13:593 days ago1770819239
0xEc2C96e7...9eF9AA7c7
0.00021191 ETH
Execute From Exe...244339652026-02-11 13:55:473 days ago1770818147
0xEc2C96e7...9eF9AA7c7
0.00021191 ETH
Execute From Exe...244339062026-02-11 13:43:593 days ago1770817439
0xEc2C96e7...9eF9AA7c7
0.00021191 ETH
Execute From Exe...244337302026-02-11 13:08:233 days ago1770815303
0xEc2C96e7...9eF9AA7c7
0.00021191 ETH
Execute From Exe...244334462026-02-11 12:11:353 days ago1770811895
0xEc2C96e7...9eF9AA7c7
0.00021191 ETH
Execute From Exe...244333332026-02-11 11:48:473 days ago1770810527
0xEc2C96e7...9eF9AA7c7
0.00021191 ETH
Execute From Exe...244332372026-02-11 11:29:353 days ago1770809375
0xEc2C96e7...9eF9AA7c7
0.00006839 ETH
Execute From Exe...244331932026-02-11 11:20:473 days ago1770808847
0xEc2C96e7...9eF9AA7c7
0.00021191 ETH
Execute From Exe...244324042026-02-11 8:42:114 days ago1770799331
0xEc2C96e7...9eF9AA7c7
0.00021191 ETH
Execute From Exe...244324002026-02-11 8:41:234 days ago1770799283
0xEc2C96e7...9eF9AA7c7
0.00021191 ETH
Execute From Exe...244321132026-02-11 7:43:474 days ago1770795827
0xEc2C96e7...9eF9AA7c7
0.00021191 ETH
Execute From Exe...244320022026-02-11 7:21:234 days ago1770794483
0xEc2C96e7...9eF9AA7c7
0.00021191 ETH
View All Internal Transactions
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
ECDSAExecutor

Compiler Version
v0.8.27+commit.40a35a09

Optimization Enabled:
Yes with 200 runs

Other Settings:
shanghai EvmVersion
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import {ECDSA} from "solady/utils/ECDSA.sol";
import {EIP712} from "solady/utils/EIP712.sol";
import {ReentrancyGuard} from "solady/utils/ReentrancyGuard.sol";
import {IExecutor} from "../interfaces/IERC7579Modules.sol";
import {IERC7579Account, ExecMode} from "../interfaces/IERC7579Account.sol";
import {MODULE_TYPE_EXECUTOR} from "../types/Constants.sol";

struct ECDSAExecutorStorage {
    address owner;
    mapping(uint192 => uint64) nonceSequenceNumber;
}

/**
 * @notice ECDSAExecutor enables external execution of transactions via ECDSA signatures
 * @dev This executor uses EIP-712 typed data signing which includes chainId in the domain separator.
 * Signatures are chain-specific and cannot be replayed across different chains.
 * If deploying on multiple chains, ensure each deployment uses unique nonces or different owners.
 *
 * Security features:
 * - EIP-712 domain separation (includes chainId)
 * - 2D nonce system for parallel execution
 * - Time-bound signatures with maximum 30-day validity
 * - Signature malleability protection
 * - Reentrancy guard protection
 */
contract ECDSAExecutor is IExecutor, EIP712, ReentrancyGuard {
    error InvalidNonce(address account, uint256 expected, uint256 actual);
    error SignatureExpired(address account, uint256 expiration);
    error InvalidSignature();
    error InvalidOwner();
    error InvalidAccount();
    error MalleableSignature();
    error ExpirationTooFar(uint256 expiration);

    bytes32 constant EXECUTE_TYPEHASH = keccak256(
        "Execute(address account,uint256 mode,bytes executionCalldata,uint256 nonce,uint256 expiration)"
    );
    uint256 constant MAX_EXPIRATION_DURATION = 30 days;
    
    event OwnerRegistered(address indexed kernel, address indexed owner);
    event OwnerUnregistered(address indexed kernel, address indexed owner);
    event OwnerTransferred(address indexed account, address indexed oldOwner, address indexed newOwner);
    event ExecutionRequested(address indexed kernel, bytes32 indexed executionHash);
    event NonceIncremented(address indexed account, uint192 indexed key, uint64 newSequence);

    /// @dev Storage for each account's executor configuration
    /// Nonce keys should be derived using deriveNonceKey() to avoid collisions
    /// Common patterns:
    /// - Key 0: Default sequential execution
    /// - Key 1-999: Reserved for protocol use
    /// - Key 1000+: Application-specific channels
    mapping(address => ECDSAExecutorStorage) internal ecdsaExecutorStorage;

    function onInstall(bytes calldata _data) external payable override {
        if (_isInitialized(msg.sender)) revert AlreadyInitialized(msg.sender);
        address owner = abi.decode(_data, (address));
        if (owner == address(0)) revert InvalidOwner();
        ecdsaExecutorStorage[msg.sender].owner = owner;
        emit OwnerRegistered(msg.sender, owner);
    }

    function onUninstall(bytes calldata) external payable override {
        if (!_isInitialized(msg.sender)) revert NotInitialized(msg.sender);
        address owner = ecdsaExecutorStorage[msg.sender].owner;
        delete ecdsaExecutorStorage[msg.sender];
        emit OwnerUnregistered(msg.sender, owner);
    }

    function isModuleType(uint256 typeID) external pure override returns (bool) {
        return typeID == MODULE_TYPE_EXECUTOR;
    }

    function isInitialized(address smartAccount) external view override returns (bool) {
        return _isInitialized(smartAccount);
    }

    function _isInitialized(address smartAccount) internal view returns (bool) {
        return ecdsaExecutorStorage[smartAccount].owner != address(0);
    }

    /// @notice Validates that a signature is not malleable (s <= N/2)
    /// @param signature The signature bytes to validate
    function _validateSignatureNotMalleable(bytes calldata signature) internal pure {
        // Extract 's' value from signature (bytes 32-63)
        bytes32 s;
        assembly {
            s := calldataload(add(signature.offset, 0x20))
        }
        // Check if s > N/2 (N is the secp256k1 curve order)
        // N/2 + 1 = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A1
        if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
            revert MalleableSignature();
        }
    }

    function getOwner(address account) external view returns (address) {
        return ecdsaExecutorStorage[account].owner;
    }

    /// @notice Transfers ownership to a new address
    /// @dev Can only be called by the account itself (not the owner)
    /// @param newOwner The address of the new owner
    function transferOwnership(address newOwner) external {
        if (!_isInitialized(msg.sender)) revert NotInitialized(msg.sender);
        if (newOwner == address(0)) revert InvalidOwner();

        address oldOwner = ecdsaExecutorStorage[msg.sender].owner;
        ecdsaExecutorStorage[msg.sender].owner = newOwner;

        emit OwnerTransferred(msg.sender, oldOwner, newOwner);
    }

    function getNonce(address account, uint192 key) external view returns (uint64) {
        return ecdsaExecutorStorage[account].nonceSequenceNumber[key];
    }

    function _packNonce(uint192 key, uint256 sequence) internal pure returns (uint256) {
        return (uint256(key) << 64) | sequence;
    }

    function _unpackNonce(uint256 nonce) internal pure returns (uint192 key, uint64 sequence) {
        key = uint192(nonce >> 64);
        sequence = uint64(nonce);
    }

    function incrementNonce(uint192 key) external {
        // Only the account itself can increment its nonce
        if (!_isInitialized(msg.sender)) revert NotInitialized(msg.sender);
        ecdsaExecutorStorage[msg.sender].nonceSequenceNumber[key]++;
        emit NonceIncremented(msg.sender, key, ecdsaExecutorStorage[msg.sender].nonceSequenceNumber[key]);
    }

    /// @notice Derives a deterministic nonce key from a purpose identifier
    /// @dev Helps prevent nonce key collisions by using a hash-based derivation
    /// @param purposeSalt A unique identifier for the nonce channel purpose
    /// @return The derived nonce key
    function deriveNonceKey(bytes32 purposeSalt) external pure returns (uint192) {
        return uint192(uint256(keccak256(abi.encodePacked("ECDSAExecutor", purposeSalt))) >> 64);
    }

    function execute(
        address account,
        ExecMode mode,
        bytes calldata executionCalldata,
        uint256 nonce,
        uint256 expiration,
        bytes calldata signature
    ) external payable nonReentrant returns (bytes[] memory returnData) {
        // Validate account is not zero address
        if (account == address(0)) revert InvalidAccount();
        if (!_isInitialized(account)) revert NotInitialized(account);

        // Validate expiration is within allowed range
        if (expiration > block.timestamp + MAX_EXPIRATION_DURATION) {
            revert ExpirationTooFar(expiration);
        }
        if (block.timestamp > expiration) {
            revert SignatureExpired(account, expiration);
        }

        // Validate and update nonce
        _validateAndUpdateNonce(account, nonce);

        // Validate signature is not malleable
        _validateSignatureNotMalleable(signature);

        // Build EIP-712 struct hash
        bytes32 structHash = keccak256(
            abi.encode(
                EXECUTE_TYPEHASH,
                account,
                ExecMode.unwrap(mode),
                keccak256(executionCalldata),
                nonce,
                expiration
            )
        );

        bytes32 digest = _hashTypedData(structHash);

        emit ExecutionRequested(account, digest);

        // Verify signature using EIP-712 digest
        address owner = ecdsaExecutorStorage[account].owner;
        if (owner != ECDSA.recover(digest, signature)) {
            revert InvalidSignature();
        }

        return IERC7579Account(account).executeFromExecutor{value: msg.value}(mode, executionCalldata);
    }
    
    function _validateAndUpdateNonce(address account, uint256 nonce) internal {
        (uint192 key, uint256 sequence) = _unpackNonce(nonce);
        uint64 expectedSequence = ecdsaExecutorStorage[account].nonceSequenceNumber[key];
        if (sequence != expectedSequence) {
            revert InvalidNonce(account, _packNonce(key, expectedSequence), nonce);
        }
        ecdsaExecutorStorage[account].nonceSequenceNumber[key] = uint64(sequence + 1);
        emit NonceIncremented(account, key, uint64(sequence + 1));
    }

    function _domainNameAndVersion()
        internal
        pure
        override
        returns (string memory name, string memory version)
    {
        name = "ECDSAExecutor";
        version = "1";
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Gas optimized ECDSA wrapper.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/ECDSA.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/ECDSA.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/ECDSA.sol)
///
/// @dev Note:
/// - The recovery functions use the ecrecover precompile (0x1).
/// - As of Solady version 0.0.68, the `recover` variants will revert upon recovery failure.
///   This is for more safety by default.
///   Use the `tryRecover` variants if you need to get the zero address back
///   upon recovery failure instead.
/// - As of Solady version 0.0.134, all `bytes signature` variants accept both
///   regular 65-byte `(r, s, v)` and EIP-2098 `(r, vs)` short form signatures.
///   See: https://eips.ethereum.org/EIPS/eip-2098
///   This is for calldata efficiency on smart accounts prevalent on L2s.
///
/// WARNING! Do NOT directly use signatures as unique identifiers:
/// - The recovery operations do NOT check if a signature is non-malleable.
/// - Use a nonce in the digest to prevent replay attacks on the same contract.
/// - Use EIP-712 for the digest to prevent replay attacks across different chains and contracts.
///   EIP-712 also enables readable signing of typed data for better user safety.
/// - If you need a unique hash from a signature, please use the `canonicalHash` functions.
library ECDSA {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         CONSTANTS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The order of the secp256k1 elliptic curve.
    uint256 internal constant N = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141;

    /// @dev `N/2 + 1`. Used for checking the malleability of the signature.
    uint256 private constant _HALF_N_PLUS_1 =
        0x7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a1;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                        CUSTOM ERRORS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The signature is invalid.
    error InvalidSignature();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                    RECOVERY OPERATIONS                     */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
    function recover(bytes32 hash, bytes memory signature) internal view returns (address result) {
        /// @solidity memory-safe-assembly
        assembly {
            for { let m := mload(0x40) } 1 {
                mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
                revert(0x1c, 0x04)
            } {
                switch mload(signature)
                case 64 {
                    let vs := mload(add(signature, 0x40))
                    mstore(0x20, add(shr(255, vs), 27)) // `v`.
                    mstore(0x60, shr(1, shl(1, vs))) // `s`.
                }
                case 65 {
                    mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`.
                    mstore(0x60, mload(add(signature, 0x40))) // `s`.
                }
                default { continue }
                mstore(0x00, hash)
                mstore(0x40, mload(add(signature, 0x20))) // `r`.
                result := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20))
                mstore(0x60, 0) // Restore the zero slot.
                mstore(0x40, m) // Restore the free memory pointer.
                // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                if returndatasize() { break }
            }
        }
    }

    /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
    function recoverCalldata(bytes32 hash, bytes calldata signature)
        internal
        view
        returns (address result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            for { let m := mload(0x40) } 1 {
                mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
                revert(0x1c, 0x04)
            } {
                switch signature.length
                case 64 {
                    let vs := calldataload(add(signature.offset, 0x20))
                    mstore(0x20, add(shr(255, vs), 27)) // `v`.
                    mstore(0x40, calldataload(signature.offset)) // `r`.
                    mstore(0x60, shr(1, shl(1, vs))) // `s`.
                }
                case 65 {
                    mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`.
                    calldatacopy(0x40, signature.offset, 0x40) // Copy `r` and `s`.
                }
                default { continue }
                mstore(0x00, hash)
                result := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20))
                mstore(0x60, 0) // Restore the zero slot.
                mstore(0x40, m) // Restore the free memory pointer.
                // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                if returndatasize() { break }
            }
        }
    }

    /// @dev Recovers the signer's address from a message digest `hash`,
    /// and the EIP-2098 short form signature defined by `r` and `vs`.
    function recover(bytes32 hash, bytes32 r, bytes32 vs) internal view returns (address result) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x00, hash)
            mstore(0x20, add(shr(255, vs), 27)) // `v`.
            mstore(0x40, r)
            mstore(0x60, shr(1, shl(1, vs))) // `s`.
            result := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20))
            // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
            if iszero(returndatasize()) {
                mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
                revert(0x1c, 0x04)
            }
            mstore(0x60, 0) // Restore the zero slot.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Recovers the signer's address from a message digest `hash`,
    /// and the signature defined by `v`, `r`, `s`.
    function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s)
        internal
        view
        returns (address result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x00, hash)
            mstore(0x20, and(v, 0xff))
            mstore(0x40, r)
            mstore(0x60, s)
            result := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20))
            // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
            if iszero(returndatasize()) {
                mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
                revert(0x1c, 0x04)
            }
            mstore(0x60, 0) // Restore the zero slot.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   TRY-RECOVER OPERATIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    // WARNING!
    // These functions will NOT revert upon recovery failure.
    // Instead, they will return the zero address upon recovery failure.
    // It is critical that the returned address is NEVER compared against
    // a zero address (e.g. an uninitialized address variable).

    /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
    function tryRecover(bytes32 hash, bytes memory signature)
        internal
        view
        returns (address result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            for { let m := mload(0x40) } 1 {} {
                switch mload(signature)
                case 64 {
                    let vs := mload(add(signature, 0x40))
                    mstore(0x20, add(shr(255, vs), 27)) // `v`.
                    mstore(0x60, shr(1, shl(1, vs))) // `s`.
                }
                case 65 {
                    mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`.
                    mstore(0x60, mload(add(signature, 0x40))) // `s`.
                }
                default { break }
                mstore(0x00, hash)
                mstore(0x40, mload(add(signature, 0x20))) // `r`.
                pop(staticcall(gas(), 1, 0x00, 0x80, 0x40, 0x20))
                mstore(0x60, 0) // Restore the zero slot.
                // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                result := mload(xor(0x60, returndatasize()))
                mstore(0x40, m) // Restore the free memory pointer.
                break
            }
        }
    }

    /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
    function tryRecoverCalldata(bytes32 hash, bytes calldata signature)
        internal
        view
        returns (address result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            for { let m := mload(0x40) } 1 {} {
                switch signature.length
                case 64 {
                    let vs := calldataload(add(signature.offset, 0x20))
                    mstore(0x20, add(shr(255, vs), 27)) // `v`.
                    mstore(0x40, calldataload(signature.offset)) // `r`.
                    mstore(0x60, shr(1, shl(1, vs))) // `s`.
                }
                case 65 {
                    mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`.
                    calldatacopy(0x40, signature.offset, 0x40) // Copy `r` and `s`.
                }
                default { break }
                mstore(0x00, hash)
                pop(staticcall(gas(), 1, 0x00, 0x80, 0x40, 0x20))
                mstore(0x60, 0) // Restore the zero slot.
                // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                result := mload(xor(0x60, returndatasize()))
                mstore(0x40, m) // Restore the free memory pointer.
                break
            }
        }
    }

    /// @dev Recovers the signer's address from a message digest `hash`,
    /// and the EIP-2098 short form signature defined by `r` and `vs`.
    function tryRecover(bytes32 hash, bytes32 r, bytes32 vs)
        internal
        view
        returns (address result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x00, hash)
            mstore(0x20, add(shr(255, vs), 27)) // `v`.
            mstore(0x40, r)
            mstore(0x60, shr(1, shl(1, vs))) // `s`.
            pop(staticcall(gas(), 1, 0x00, 0x80, 0x40, 0x20))
            mstore(0x60, 0) // Restore the zero slot.
            // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
            result := mload(xor(0x60, returndatasize()))
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Recovers the signer's address from a message digest `hash`,
    /// and the signature defined by `v`, `r`, `s`.
    function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s)
        internal
        view
        returns (address result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x00, hash)
            mstore(0x20, and(v, 0xff))
            mstore(0x40, r)
            mstore(0x60, s)
            pop(staticcall(gas(), 1, 0x00, 0x80, 0x40, 0x20))
            mstore(0x60, 0) // Restore the zero slot.
            // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
            result := mload(xor(0x60, returndatasize()))
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                     HASHING OPERATIONS                     */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns an Ethereum Signed Message, created from a `hash`.
    /// This produces a hash corresponding to the one signed with the
    /// [`eth_sign`](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sign)
    /// JSON-RPC method as part of EIP-191.
    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x20, hash) // Store into scratch space for keccak256.
            mstore(0x00, "\x00\x00\x00\x00\x19Ethereum Signed Message:\n32") // 28 bytes.
            result := keccak256(0x04, 0x3c) // `32 * 2 - (32 - 28) = 60 = 0x3c`.
        }
    }

    /// @dev Returns an Ethereum Signed Message, created from `s`.
    /// This produces a hash corresponding to the one signed with the
    /// [`eth_sign`](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sign)
    /// JSON-RPC method as part of EIP-191.
    /// Note: Supports lengths of `s` up to 999999 bytes.
    function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let sLength := mload(s)
            let o := 0x20
            mstore(o, "\x19Ethereum Signed Message:\n") // 26 bytes, zero-right-padded.
            mstore(0x00, 0x00)
            // Convert the `s.length` to ASCII decimal representation: `base10(s.length)`.
            for { let temp := sLength } 1 {} {
                o := sub(o, 1)
                mstore8(o, add(48, mod(temp, 10)))
                temp := div(temp, 10)
                if iszero(temp) { break }
            }
            let n := sub(0x3a, o) // Header length: `26 + 32 - o`.
            // Throw an out-of-offset error (consumes all gas) if the header exceeds 32 bytes.
            returndatacopy(returndatasize(), returndatasize(), gt(n, 0x20))
            mstore(s, or(mload(0x00), mload(n))) // Temporarily store the header.
            result := keccak256(add(s, sub(0x20, n)), add(n, sLength))
            mstore(s, sLength) // Restore the length.
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  CANONICAL HASH FUNCTIONS                  */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    // The following functions returns the hash of the signature in it's canonicalized format,
    // which is the 65-byte `abi.encodePacked(r, s, uint8(v))`, where `v` is either 27 or 28.
    // If `s` is greater than `N / 2` then it will be converted to `N - s`
    // and the `v` value will be flipped.
    // If the signature has an invalid length, or if `v` is invalid,
    // a uniquely corrupt hash will be returned.
    // These functions are useful for "poor-mans-VRF".

    /// @dev Returns the canonical hash of `signature`.
    function canonicalHash(bytes memory signature) internal pure returns (bytes32 result) {
        // @solidity memory-safe-assembly
        assembly {
            let l := mload(signature)
            for {} 1 {} {
                mstore(0x00, mload(add(signature, 0x20))) // `r`.
                let s := mload(add(signature, 0x40))
                let v := mload(add(signature, 0x41))
                if eq(l, 64) {
                    v := add(shr(255, s), 27)
                    s := shr(1, shl(1, s))
                }
                if iszero(lt(s, _HALF_N_PLUS_1)) {
                    v := xor(v, 7)
                    s := sub(N, s)
                }
                mstore(0x21, v)
                mstore(0x20, s)
                result := keccak256(0x00, 0x41)
                mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
                break
            }

            // If the length is neither 64 nor 65, return a uniquely corrupted hash.
            if iszero(lt(sub(l, 64), 2)) {
                // `bytes4(keccak256("InvalidSignatureLength"))`.
                result := xor(keccak256(add(signature, 0x20), l), 0xd62f1ab2)
            }
        }
    }

    /// @dev Returns the canonical hash of `signature`.
    function canonicalHashCalldata(bytes calldata signature)
        internal
        pure
        returns (bytes32 result)
    {
        // @solidity memory-safe-assembly
        assembly {
            for {} 1 {} {
                mstore(0x00, calldataload(signature.offset)) // `r`.
                let s := calldataload(add(signature.offset, 0x20))
                let v := calldataload(add(signature.offset, 0x21))
                if eq(signature.length, 64) {
                    v := add(shr(255, s), 27)
                    s := shr(1, shl(1, s))
                }
                if iszero(lt(s, _HALF_N_PLUS_1)) {
                    v := xor(v, 7)
                    s := sub(N, s)
                }
                mstore(0x21, v)
                mstore(0x20, s)
                result := keccak256(0x00, 0x41)
                mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
                break
            }
            // If the length is neither 64 nor 65, return a uniquely corrupted hash.
            if iszero(lt(sub(signature.length, 64), 2)) {
                calldatacopy(mload(0x40), signature.offset, signature.length)
                // `bytes4(keccak256("InvalidSignatureLength"))`.
                result := xor(keccak256(mload(0x40), signature.length), 0xd62f1ab2)
            }
        }
    }

    /// @dev Returns the canonical hash of `signature`.
    function canonicalHash(bytes32 r, bytes32 vs) internal pure returns (bytes32 result) {
        // @solidity memory-safe-assembly
        assembly {
            mstore(0x00, r) // `r`.
            let v := add(shr(255, vs), 27)
            let s := shr(1, shl(1, vs))
            mstore(0x21, v)
            mstore(0x20, s)
            result := keccak256(0x00, 0x41)
            mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
        }
    }

    /// @dev Returns the canonical hash of `signature`.
    function canonicalHash(uint8 v, bytes32 r, bytes32 s) internal pure returns (bytes32 result) {
        // @solidity memory-safe-assembly
        assembly {
            mstore(0x00, r) // `r`.
            if iszero(lt(s, _HALF_N_PLUS_1)) {
                v := xor(v, 7)
                s := sub(N, s)
            }
            mstore(0x21, v)
            mstore(0x20, s)
            result := keccak256(0x00, 0x41)
            mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   EMPTY CALLDATA HELPERS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns an empty calldata bytes.
    function emptySignature() internal pure returns (bytes calldata signature) {
        /// @solidity memory-safe-assembly
        assembly {
            signature.length := 0
        }
    }
}

// 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)))
        }
    }
}

File 4 of 12 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Reentrancy guard mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/ReentrancyGuard.sol)
abstract contract ReentrancyGuard {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Unauthorized reentrant call.
    error Reentrancy();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                          STORAGE                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Equivalent to: `uint72(bytes9(keccak256("_REENTRANCY_GUARD_SLOT")))`.
    /// 9 bytes is large enough to avoid collisions with lower slots,
    /// but not too large to result in excessive bytecode bloat.
    uint256 private constant _REENTRANCY_GUARD_SLOT = 0x929eee149b4bd21268;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                      REENTRANCY GUARD                      */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Guards a function from reentrancy.
    modifier nonReentrant() virtual {
        /// @solidity memory-safe-assembly
        assembly {
            if eq(sload(_REENTRANCY_GUARD_SLOT), address()) {
                mstore(0x00, 0xab143c06) // `Reentrancy()`.
                revert(0x1c, 0x04)
            }
            sstore(_REENTRANCY_GUARD_SLOT, address())
        }
        _;
        /// @solidity memory-safe-assembly
        assembly {
            sstore(_REENTRANCY_GUARD_SLOT, codesize())
        }
    }

    /// @dev Guards a view function from read-only reentrancy.
    modifier nonReadReentrant() virtual {
        /// @solidity memory-safe-assembly
        assembly {
            if eq(sload(_REENTRANCY_GUARD_SLOT), address()) {
                mstore(0x00, 0xab143c06) // `Reentrancy()`.
                revert(0x1c, 0x04)
            }
        }
        _;
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

import {PackedUserOperation} from "./PackedUserOperation.sol";

interface IModule {
    error AlreadyInitialized(address smartAccount);
    error NotInitialized(address smartAccount);

    /**
     * @dev This function is called by the smart account during installation of the module
     * @param data arbitrary data that may be required on the module during `onInstall`
     * initialization
     *
     * MUST revert on error (i.e. if module is already enabled)
     */
    function onInstall(bytes calldata data) external payable;

    /**
     * @dev This function is called by the smart account during uninstallation of the module
     * @param data arbitrary data that may be required on the module during `onUninstall`
     * de-initialization
     *
     * MUST revert on error
     */
    function onUninstall(bytes calldata data) external payable;

    /**
     * @dev Returns boolean value if module is a certain type
     * @param moduleTypeId the module type ID according the ERC-7579 spec
     *
     * MUST return true if the module is of the given type and false otherwise
     */
    function isModuleType(uint256 moduleTypeId) external view returns (bool);

    /**
     * @dev Returns if the module was already initialized for a provided smartaccount
     */
    function isInitialized(address smartAccount) external view returns (bool);
}

interface IValidator is IModule {
    error InvalidTargetAddress(address target);

    /**
     * @dev Validates a transaction on behalf of the account.
     *         This function is intended to be called by the MSA during the ERC-4337 validation phase
     *         Note: solely relying on bytes32 hash and signature is not sufficient for some
     * validation implementations (i.e. SessionKeys often need access to userOp.calldata)
     * @param userOp The user operation to be validated. The userOp MUST NOT contain any metadata.
     * The MSA MUST clean up the userOp before sending it to the validator.
     * @param userOpHash The hash of the user operation to be validated
     * @return return value according to ERC-4337
     */
    function validateUserOp(PackedUserOperation calldata userOp, bytes32 userOpHash)
        external
        payable
        returns (uint256);

    /**
     * Validator can be used for ERC-1271 validation
     */
    function isValidSignatureWithSender(address sender, bytes32 hash, bytes calldata data)
        external
        view
        returns (bytes4);
}

interface IExecutor is IModule {}

interface IHook is IModule {
    function preCheck(address msgSender, uint256 msgValue, bytes calldata msgData)
        external
        payable
        returns (bytes memory hookData);

    function postCheck(bytes calldata hookData) external payable;
}

interface IFallback is IModule {}

interface IPolicy is IModule {
    function checkUserOpPolicy(bytes32 id, PackedUserOperation calldata userOp) external payable returns (uint256);
    function checkSignaturePolicy(bytes32 id, address sender, bytes32 hash, bytes calldata sig)
        external
        view
        returns (uint256);
}

interface ISigner is IModule {
    function checkUserOpSignature(bytes32 id, PackedUserOperation calldata userOp, bytes32 userOpHash)
        external
        payable
        returns (uint256);
    function checkSignature(bytes32 id, address sender, bytes32 hash, bytes calldata sig)
        external
        view
        returns (bytes4);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

import {CallType, ExecType, ExecMode} from "../utils/ExecLib.sol";
import {PackedUserOperation} from "./PackedUserOperation.sol";

struct Execution {
    address target;
    uint256 value;
    bytes callData;
}

interface IERC7579Account {
    event ModuleInstalled(uint256 moduleTypeId, address module);
    event ModuleUninstalled(uint256 moduleTypeId, address module);

    /**
     * @dev Executes a transaction on behalf of the account.
     *         This function is intended to be called by ERC-4337 EntryPoint.sol
     * @dev Ensure adequate authorization control: i.e. onlyEntryPointOrSelf
     *
     * @dev MSA MUST implement this function signature.
     * If a mode is requested that is not supported by the Account, it MUST revert
     * @param mode The encoded execution mode of the transaction. See ModeLib.sol for details
     * @param executionCalldata The encoded execution call data
     */
    function execute(ExecMode mode, bytes calldata executionCalldata) external payable;

    /**
     * @dev Executes a transaction on behalf of the account.
     *         This function is intended to be called by Executor Modules
     * @dev Ensure adequate authorization control: i.e. onlyExecutorModule
     *
     * @dev MSA MUST implement this function signature.
     * If a mode is requested that is not supported by the Account, it MUST revert
     * @param mode The encoded execution mode of the transaction. See ModeLib.sol for details
     * @param executionCalldata The encoded execution call data
     */
    function executeFromExecutor(ExecMode mode, bytes calldata executionCalldata)
        external
        payable
        returns (bytes[] memory returnData);

    /**
     * @dev ERC-1271 isValidSignature
     *         This function is intended to be used to validate a smart account signature
     * and may forward the call to a validator module
     *
     * @param hash The hash of the data that is signed
     * @param data The data that is signed
     */
    function isValidSignature(bytes32 hash, bytes calldata data) external view returns (bytes4);

    /**
     * @dev installs a Module of a certain type on the smart account
     * @dev Implement Authorization control of your choosing
     * @param moduleTypeId the module type ID according the ERC-7579 spec
     * @param module the module address
     * @param initData arbitrary data that may be required on the module during `onInstall`
     * initialization.
     */
    function installModule(uint256 moduleTypeId, address module, bytes calldata initData) external payable;

    /**
     * @dev uninstalls a Module of a certain type on the smart account
     * @dev Implement Authorization control of your choosing
     * @param moduleTypeId the module type ID according the ERC-7579 spec
     * @param module the module address
     * @param deInitData arbitrary data that may be required on the module during `onUninstall`
     * de-initialization.
     */
    function uninstallModule(uint256 moduleTypeId, address module, bytes calldata deInitData) external payable;

    /**
     * Function to check if the account supports a certain CallType or ExecType (see ModeLib.sol)
     * @param encodedMode the encoded mode
     */
    function supportsExecutionMode(ExecMode encodedMode) external view returns (bool);

    /**
     * Function to check if the account supports installation of a certain module type Id
     * @param moduleTypeId the module type ID according the ERC-7579 spec
     */
    function supportsModule(uint256 moduleTypeId) external view returns (bool);

    /**
     * Function to check if the account has a certain module installed
     * @param moduleTypeId the module type ID according the ERC-7579 spec
     *      Note: keep in mind that some contracts can be multiple module types at the same time. It
     *            thus may be necessary to query multiple module types
     * @param module the module address
     * @param additionalContext additional context data that the smart account may interpret to
     *                          identify conditions under which the module is installed.
     *                          usually this is not necessary, but for some special hooks that
     *                          are stored in mappings, this param might be needed
     */
    function isModuleInstalled(uint256 moduleTypeId, address module, bytes calldata additionalContext)
        external
        view
        returns (bool);

    /**
     * @dev Returns the account id of the smart account
     * @return accountImplementationId the account id of the smart account
     * the accountId should be structured like so:
     *        "vendorname.accountname.semver"
     */
    function accountId() external view returns (string memory accountImplementationId);
}

File 7 of 12 : Constants.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import {CallType, ExecType, ExecModeSelector} from "./Types.sol";
import {PassFlag, ValidationMode, ValidationType} from "./Types.sol";
import {ValidationData} from "./Types.sol";

// --- ERC7579 calltypes ---
// 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);

// --- ERC7579 exectypes ---
// @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);

// --- ERC7579 mode selector ---
ExecModeSelector constant EXEC_MODE_DEFAULT = ExecModeSelector.wrap(bytes4(0x00000000));

// --- Kernel permission skip flags ---
PassFlag constant SKIP_USEROP = PassFlag.wrap(0x0001);
PassFlag constant SKIP_SIGNATURE = PassFlag.wrap(0x0002);

// --- Kernel validation modes ---
ValidationMode constant VALIDATION_MODE_DEFAULT = ValidationMode.wrap(0x00);
ValidationMode constant VALIDATION_MODE_ENABLE = ValidationMode.wrap(0x01);
ValidationMode constant VALIDATION_MODE_INSTALL = ValidationMode.wrap(0x02);

// --- Kernel validation types ---
ValidationType constant VALIDATION_TYPE_ROOT = ValidationType.wrap(0x00);
ValidationType constant VALIDATION_TYPE_7702 = ValidationType.wrap(0x00);
ValidationType constant VALIDATION_TYPE_VALIDATOR = ValidationType.wrap(0x01);
ValidationType constant VALIDATION_TYPE_PERMISSION = ValidationType.wrap(0x02);

// --- Kernel Hook constants ---
address constant HOOK_MODULE_NOT_INSTALLED = address(0);
address constant HOOK_MODULE_INSTALLED = address(1);
address constant HOOK_ONLY_ENTRYPOINT = address(0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF);

// --- EIP7702 constants ---
bytes3 constant EIP7702_PREFIX = bytes3(0xef0100);

// --- storage slots ---
// bytes32(uint256(keccak256('kernel.v3.selector')) - 1)
bytes32 constant SELECTOR_MANAGER_STORAGE_SLOT = 0x7c341349a4360fdd5d5bc07e69f325dc6aaea3eb018b3e0ea7e53cc0bb0d6f3b;
// bytes32(uint256(keccak256('kernel.v3.executor')) - 1)
bytes32 constant EXECUTOR_MANAGER_STORAGE_SLOT = 0x1bbee3173dbdc223633258c9f337a0fff8115f206d302bea0ed3eac003b68b86;
// bytes32(uint256(keccak256('kernel.v3.hook')) - 1)
bytes32 constant HOOK_MANAGER_STORAGE_SLOT = 0x4605d5f70bb605094b2e761eccdc27bed9a362d8612792676bf3fb9b12832ffc;
// bytes32(uint256(keccak256('kernel.v3.validation')) - 1)
bytes32 constant VALIDATION_MANAGER_STORAGE_SLOT = 0x7bcaa2ced2a71450ed5a9a1b4848e8e5206dbc3f06011e595f7f55428cc6f84f;
bytes32 constant ERC1967_IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;

bytes32 constant MAGIC_VALUE_SIG_REPLAYABLE = keccak256("kernel.replayable.signature");

// --- Kernel validation nonce incremental size limit ---
uint32 constant MAX_NONCE_INCREMENT_SIZE = 10;

// -- EIP712 type hash ---
bytes32 constant ENABLE_TYPE_HASH = 0xb17ab1224aca0d4255ef8161acaf2ac121b8faa32a4b2258c912cc5f8308c505;
bytes32 constant KERNEL_WRAPPER_TYPE_HASH = 0x1547321c374afde8a591d972a084b071c594c275e36724931ff96c25f2999c83;

// --- ERC constants ---
// ERC4337 constants
uint256 constant SIG_VALIDATION_FAILED_UINT = 1;
uint256 constant SIG_VALIDATION_SUCCESS_UINT = 0;
ValidationData constant SIG_VALIDATION_FAILED = ValidationData.wrap(SIG_VALIDATION_FAILED_UINT);

// ERC-1271 constants
bytes4 constant ERC1271_MAGICVALUE = 0x1626ba7e;
bytes4 constant ERC1271_INVALID = 0xffffffff;

uint256 constant MODULE_TYPE_VALIDATOR = 1;
uint256 constant MODULE_TYPE_EXECUTOR = 2;
uint256 constant MODULE_TYPE_FALLBACK = 3;
uint256 constant MODULE_TYPE_HOOK = 4;
uint256 constant MODULE_TYPE_POLICY = 5;
uint256 constant MODULE_TYPE_SIGNER = 6;

File 8 of 12 : PackedUserOperation.sol
// 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 maxFeePerGas and maxPriorityFeePerGas - Same as EIP-1559 gas parameter.
 * @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; //maxPriorityFee and maxFeePerGas;
    bytes paymasterAndData;
    bytes signature;
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import {ExecMode, CallType, ExecType, ExecModeSelector, ExecModePayload} from "../types/Types.sol";
import {LibERC7579} from "solady/accounts/LibERC7579.sol";
import {
    CALLTYPE_SINGLE,
    CALLTYPE_BATCH,
    EXECTYPE_DEFAULT,
    EXEC_MODE_DEFAULT,
    EXECTYPE_TRY,
    CALLTYPE_DELEGATECALL
} from "../types/Constants.sol";
import {Execution} from "../types/Structs.sol";

/**
 * @dev ExecLib is a helper library for execution
 */
library ExecLib {
    error ExecutionFailed();

    event TryExecuteUnsuccessful(uint256 batchExecutionindex, bytes result);

    function execute(ExecMode execMode, bytes calldata executionCalldata)
        internal
        returns (bytes[] memory returnData)
    {
        (CallType callType, ExecType execType,,) = decode(execMode);

        // check if calltype is batch or single
        if (callType == CALLTYPE_BATCH) {
            // destructure executionCallData according to batched exec
            bytes32[] calldata pointers = LibERC7579.decodeBatch(executionCalldata);
            // check if execType is revert or try
            if (execType == EXECTYPE_DEFAULT) returnData = execute(pointers);
            else if (execType == EXECTYPE_TRY) returnData = tryExecute(pointers);
            else revert("Unsupported");
        } else if (callType == CALLTYPE_SINGLE) {
            // destructure executionCallData according to single exec
            (address target, uint256 value, bytes calldata callData) = LibERC7579.decodeSingle(executionCalldata);
            returnData = new bytes[](1);
            bool success;
            // check if execType is revert or try
            if (execType == EXECTYPE_DEFAULT) {
                returnData[0] = execute(target, value, callData);
            } else if (execType == EXECTYPE_TRY) {
                (success, returnData[0]) = tryExecute(target, value, callData);
                if (!success) emit TryExecuteUnsuccessful(0, returnData[0]);
            } else {
                revert("Unsupported");
            }
        } else if (callType == CALLTYPE_DELEGATECALL) {
            returnData = new bytes[](1);
            (address delegate, bytes calldata callData) = LibERC7579.decodeDelegate(executionCalldata);
            bool success;
            (success, returnData[0]) = executeDelegatecall(delegate, callData);
            if (execType == EXECTYPE_TRY) {
                if (!success) emit TryExecuteUnsuccessful(0, returnData[0]);
            } else if (execType == EXECTYPE_DEFAULT) {
                if (!success) revert("Delegatecall failed");
            } else {
                revert("Unsupported");
            }
        } else {
            revert("Unsupported");
        }
    }

    function execute(bytes32[] calldata pointers) internal returns (bytes[] memory result) {
        uint256 length = pointers.length;
        result = new bytes[](length);
        unchecked {
            for (uint256 i; i < length; i++) {
                (address target, uint256 value, bytes calldata data) = LibERC7579.getExecution(pointers, i);
                result[i] = execute(target, value, data);
            }
        }
    }

    function tryExecute(bytes32[] calldata pointers) internal returns (bytes[] memory result) {
        uint256 length = pointers.length;
        result = new bytes[](length);
        unchecked {
            for (uint256 i; i < length; i++) {
                (address target, uint256 value, bytes calldata data) = LibERC7579.getExecution(pointers, i);
                bool success;
                (success, result[i]) = tryExecute(target, value, data);
                if (!success) emit TryExecuteUnsuccessful(i, result[i]);
            }
        }
    }

    function execute(address target, uint256 value, bytes calldata callData) internal returns (bytes memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := mload(0x40)
            calldatacopy(result, callData.offset, callData.length)
            if iszero(call(gas(), target, value, result, callData.length, codesize(), 0x00)) {
                // Bubble up the revert if the call reverts.
                returndatacopy(result, 0x00, returndatasize())
                revert(result, returndatasize())
            }
            mstore(result, returndatasize()) // Store the length.
            let o := add(result, 0x20)
            returndatacopy(o, 0x00, returndatasize()) // Copy the returndata.
            mstore(0x40, add(o, returndatasize())) // Allocate the memory.
        }
    }

    function tryExecute(address target, uint256 value, bytes calldata callData)
        internal
        returns (bool success, bytes memory result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            result := mload(0x40)
            calldatacopy(result, callData.offset, callData.length)
            success := call(gas(), target, value, result, callData.length, codesize(), 0x00)
            mstore(result, returndatasize()) // Store the length.
            let o := add(result, 0x20)
            returndatacopy(o, 0x00, returndatasize()) // Copy the returndata.
            mstore(0x40, add(o, returndatasize())) // Allocate the memory.
        }
    }

    /// @dev Execute a delegatecall with `delegate` on this account.
    function executeDelegatecall(address delegate, bytes calldata callData)
        internal
        returns (bool success, bytes memory result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            result := mload(0x40)
            calldatacopy(result, callData.offset, callData.length)
            // Forwards the `data` to `delegate` via delegatecall.
            success := delegatecall(gas(), delegate, result, callData.length, codesize(), 0x00)
            mstore(result, returndatasize()) // Store the length.
            let o := add(result, 0x20)
            returndatacopy(o, 0x00, returndatasize()) // Copy the returndata.
            mstore(0x40, add(o, returndatasize())) // Allocate the memory.
        }
    }

    function decode(ExecMode mode)
        internal
        pure
        returns (CallType _calltype, ExecType _execType, ExecModeSelector _modeSelector, ExecModePayload _modePayload)
    {
        assembly {
            _calltype := mode
            _execType := shl(8, mode)
            _modeSelector := shl(48, mode)
            _modePayload := shl(80, mode)
        }
    }

    function encode(CallType callType, ExecType execType, ExecModeSelector mode, ExecModePayload payload)
        internal
        pure
        returns (ExecMode)
    {
        return ExecMode.wrap(
            bytes32(abi.encodePacked(callType, execType, bytes4(0), ExecModeSelector.unwrap(mode), payload))
        );
    }

    function getCallType(ExecMode mode) internal pure returns (CallType calltype) {
        assembly {
            calltype := mode
        }
    }

    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 encodeSingle(address target, uint256 value, bytes memory callData)
        internal
        pure
        returns (bytes memory userOpCalldata)
    {
        userOpCalldata = abi.encodePacked(target, value, callData);
    }

    function doFallback2771Static(address fallbackHandler) internal view returns (bool success, bytes memory result) {
        assembly {
            function allocate(length) -> pos {
                pos := mload(0x40)
                mstore(0x40, add(pos, length))
            }

            let calldataPtr := allocate(calldatasize())
            calldatacopy(calldataPtr, 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(20)
            mstore(senderPtr, shl(96, caller()))

            // Add 20 bytes for the address appended add the end
            success := staticcall(gas(), fallbackHandler, calldataPtr, add(calldatasize(), 20), 0, 0)

            result := mload(0x40)
            mstore(result, returndatasize()) // Store the length.
            let o := add(result, 0x20)
            returndatacopy(o, 0x00, returndatasize()) // Copy the returndata.
            mstore(0x40, add(o, returndatasize())) // Allocate the memory.
        }
    }

    function doFallback2771Call(address target) internal returns (bool success, bytes memory result) {
        assembly {
            function allocate(length) -> pos {
                pos := mload(0x40)
                mstore(0x40, add(pos, length))
            }

            let calldataPtr := allocate(calldatasize())
            calldatacopy(calldataPtr, 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(20)
            mstore(senderPtr, shl(96, caller()))

            // Add 20 bytes for the address appended add the end
            success := call(gas(), target, 0, calldataPtr, add(calldatasize(), 20), 0, 0)

            result := mload(0x40)
            mstore(result, returndatasize()) // Store the length.
            let o := add(result, 0x20)
            returndatacopy(o, 0x00, returndatasize()) // Copy the returndata.
            mstore(0x40, add(o, returndatasize())) // Allocate the memory.
        }
    }
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.23;

// Custom type for improved developer experience
type ExecMode is bytes32;

type CallType is bytes1;

type ExecType is bytes1;

type ExecModeSelector is bytes4;

type ExecModePayload is bytes22;

using {eqModeSelector as ==} for ExecModeSelector global;
using {eqCallType as ==} for CallType global;
using {notEqCallType 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 notEqCallType(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);
}

function eqModeSelector(ExecModeSelector a, ExecModeSelector b) pure returns (bool) {
    return ExecModeSelector.unwrap(a) == ExecModeSelector.unwrap(b);
}

type ValidationMode is bytes1;

type ValidationId is bytes21;

type ValidationType is bytes1;

type PermissionId is bytes4;

type PolicyData is bytes22; // 2bytes for flag on skip, 20 bytes for validator address

type PassFlag is bytes2;

using {vModeEqual as ==} for ValidationMode global;
using {vTypeEqual as ==} for ValidationType global;
using {vIdentifierEqual as ==} for ValidationId global;
using {vModeNotEqual as !=} for ValidationMode global;
using {vTypeNotEqual as !=} for ValidationType global;
using {vIdentifierNotEqual as !=} for ValidationId global;

// nonce = uint192(key) + nonce
// key = mode + (vtype + validationDataWithoutType) + 2bytes parallelNonceKey
// key = 0x00 + 0x00 + 0x000 .. 00 + 0x0000
// key = 0x00 + 0x01 + 0x1234...ff + 0x0000
// key = 0x00 + 0x02 + ( ) + 0x000

function vModeEqual(ValidationMode a, ValidationMode b) pure returns (bool) {
    return ValidationMode.unwrap(a) == ValidationMode.unwrap(b);
}

function vModeNotEqual(ValidationMode a, ValidationMode b) pure returns (bool) {
    return ValidationMode.unwrap(a) != ValidationMode.unwrap(b);
}

function vTypeEqual(ValidationType a, ValidationType b) pure returns (bool) {
    return ValidationType.unwrap(a) == ValidationType.unwrap(b);
}

function vTypeNotEqual(ValidationType a, ValidationType b) pure returns (bool) {
    return ValidationType.unwrap(a) != ValidationType.unwrap(b);
}

function vIdentifierEqual(ValidationId a, ValidationId b) pure returns (bool) {
    return ValidationId.unwrap(a) == ValidationId.unwrap(b);
}

function vIdentifierNotEqual(ValidationId a, ValidationId b) pure returns (bool) {
    return ValidationId.unwrap(a) != ValidationId.unwrap(b);
}

type ValidationData is uint256;

type ValidAfter is uint48;

type ValidUntil is uint48;

function getValidationResult(ValidationData validationData) pure returns (address result) {
    assembly {
        result := validationData
    }
}

function packValidationData(ValidAfter validAfter, ValidUntil validUntil) pure returns (uint256) {
    return uint256(ValidAfter.unwrap(validAfter)) << 208 | uint256(ValidUntil.unwrap(validUntil)) << 160;
}

function parseValidationData(uint256 validationData)
    pure
    returns (ValidAfter validAfter, ValidUntil validUntil, address result)
{
    assembly {
        result := validationData
        validUntil := and(shr(160, validationData), 0xffffffffffff)
        switch iszero(validUntil)
        case 1 { validUntil := 0xffffffffffff }
        validAfter := shr(208, validationData)
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Library for handling ERC7579 mode and execution data.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/accounts/LibERC7579.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/main/contracts/account/utils/draft-ERC7579Utils.sol)
library LibERC7579 {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Cannot decode `executionData`.
    error DecodingError();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         CONSTANTS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev A single execution.
    bytes1 internal constant CALLTYPE_SINGLE = 0x00;

    /// @dev A batch of executions.
    bytes1 internal constant CALLTYPE_BATCH = 0x01;

    /// @dev A `delegatecall` execution.
    bytes1 internal constant CALLTYPE_DELEGATECALL = 0xff;

    /// @dev Default execution type that reverts on failure.
    bytes1 internal constant EXECTYPE_DEFAULT = 0x00;

    /// @dev Execution type that does not revert on failure.
    bytes1 internal constant EXECTYPE_TRY = 0x01;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                      MODE OPERATIONS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Encodes the fields into a mode.
    function encodeMode(bytes1 callType, bytes1 execType, bytes4 selector, bytes22 payload)
        internal
        pure
        returns (bytes32 result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, callType)
            mstore(0x01, execType)
            mstore(0x02, selector)
            mstore(0x06, 0)
            mstore(0x0a, payload)
            result := mload(0x00)
        }
    }

    /// @dev Returns the call type of the mode.
    function getCallType(bytes32 mode) internal pure returns (bytes1) {
        return bytes1(mode);
    }

    /// @dev Returns the call type of the mode.
    function getExecType(bytes32 mode) internal pure returns (bytes1) {
        return mode[1];
    }

    /// @dev Returns the selector of the mode.
    function getSelector(bytes32 mode) internal pure returns (bytes4) {
        return bytes4(bytes32(uint256(mode) << 16));
    }

    /// @dev Returns the payload stored in the mode.
    function getPayload(bytes32 mode) internal pure returns (bytes22) {
        return bytes22(bytes32(uint256(mode) << 80));
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                 EXECUTION DATA OPERATIONS                  */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Decodes a single call execution.
    /// Reverts if `executionData` is not correctly encoded.
    function decodeSingle(bytes calldata executionData)
        internal
        pure
        returns (address target, uint256 value, bytes calldata data)
    {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(gt(executionData.length, 0x33)) {
                mstore(0x00, 0xba597e7e) // `DecodingError()`.
                revert(0x1c, 0x04)
            }
            target := shr(96, calldataload(executionData.offset))
            value := calldataload(add(executionData.offset, 0x14))
            data.offset := add(executionData.offset, 0x34)
            data.length := sub(executionData.length, 0x34)
        }
    }

    /// @dev Decodes a single call execution without bounds checks.
    function decodeSingleUnchecked(bytes calldata executionData)
        internal
        pure
        returns (address target, uint256 value, bytes calldata data)
    {
        /// @solidity memory-safe-assembly
        assembly {
            target := shr(96, calldataload(executionData.offset))
            value := calldataload(add(executionData.offset, 0x14))
            data.offset := add(executionData.offset, 0x34)
            data.length := sub(executionData.length, 0x34)
        }
    }

    /// @dev Decodes a single delegate execution.
    /// Reverts if `executionData` is not correctly encoded.
    function decodeDelegate(bytes calldata executionData)
        internal
        pure
        returns (address target, bytes calldata data)
    {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(gt(executionData.length, 0x13)) {
                mstore(0x00, 0xba597e7e) // `DecodingError()`.
                revert(0x1c, 0x04)
            }
            target := shr(96, calldataload(executionData.offset))
            data.offset := add(executionData.offset, 0x14)
            data.length := sub(executionData.length, 0x14)
        }
    }

    /// @dev Decodes a single delegate execution without bounds checks.
    function decodeDelegateUnchecked(bytes calldata executionData)
        internal
        pure
        returns (address target, bytes calldata data)
    {
        /// @solidity memory-safe-assembly
        assembly {
            target := shr(96, calldataload(executionData.offset))
            data.offset := add(executionData.offset, 0x14)
            data.length := sub(executionData.length, 0x14)
        }
    }

    /// @dev Decodes a batch.
    /// Reverts if `executionData` is not correctly encoded.
    function decodeBatch(bytes calldata executionData)
        internal
        pure
        returns (bytes32[] calldata pointers)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let u := calldataload(executionData.offset)
            if or(shr(64, u), gt(0x20, executionData.length)) {
                mstore(0x00, 0xba597e7e) // `DecodingError()`.
                revert(0x1c, 0x04)
            }
            pointers.offset := add(add(executionData.offset, u), 0x20)
            pointers.length := calldataload(add(executionData.offset, u))
            if pointers.length {
                let e := sub(add(executionData.offset, executionData.length), 0x20)
                // Perform bounds checks on the decoded `pointers`.
                // Does an out-of-gas revert.
                for { let i := pointers.length } 1 {} {
                    i := sub(i, 1)
                    let p := calldataload(add(pointers.offset, shl(5, i)))
                    let c := add(pointers.offset, p)
                    let q := calldataload(add(c, 0x40))
                    let o := add(c, q)
                    // forgefmt: disable-next-item
                    if or(shr(64, or(calldataload(o), or(p, q))),
                        or(gt(add(c, 0x40), e), gt(add(o, calldataload(o)), e))) {
                        mstore(0x00, 0xba597e7e) // `DecodingError()`.
                        revert(0x1c, 0x04)
                    }
                    if iszero(i) { break }
                }
            }
        }
    }

    /// @dev Decodes a batch without bounds checks.
    /// This function can be used in `execute`, if the validation phase has already
    /// decoded the `executionData` with checks via `decodeBatch`.
    function decodeBatchUnchecked(bytes calldata executionData)
        internal
        pure
        returns (bytes32[] calldata pointers)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let o := add(executionData.offset, calldataload(executionData.offset))
            pointers.offset := add(o, 0x20)
            pointers.length := calldataload(o)
        }
    }

    /// @dev Decodes a batch and optional `opData`.
    /// Reverts if `executionData` is not correctly encoded.
    function decodeBatchAndOpData(bytes calldata executionData)
        internal
        pure
        returns (bytes32[] calldata pointers, bytes calldata opData)
    {
        opData = emptyCalldataBytes();
        pointers = decodeBatch(executionData);
        if (hasOpData(executionData)) {
            /// @solidity memory-safe-assembly
            assembly {
                let e := sub(add(executionData.offset, executionData.length), 0x20)
                let p := calldataload(add(0x20, executionData.offset))
                let q := add(executionData.offset, p)
                opData.offset := add(q, 0x20)
                opData.length := calldataload(q)
                if or(shr(64, or(opData.length, p)), gt(add(q, opData.length), e)) {
                    mstore(0x00, 0xba597e7e) // `DecodingError()`.
                    revert(0x1c, 0x04)
                }
            }
        }
    }

    /// @dev Decodes a batch without bounds checks.
    /// This function can be used in `execute`, if the validation phase has already
    /// decoded the `executionData` with checks via `decodeBatchAndOpData`.
    function decodeBatchAndOpDataUnchecked(bytes calldata executionData)
        internal
        pure
        returns (bytes32[] calldata pointers, bytes calldata opData)
    {
        opData = emptyCalldataBytes();
        pointers = decodeBatchUnchecked(executionData);
        if (hasOpData(executionData)) {
            /// @solidity memory-safe-assembly
            assembly {
                let q := add(executionData.offset, calldataload(add(0x20, executionData.offset)))
                opData.offset := add(q, 0x20)
                opData.length := calldataload(q)
            }
        }
    }

    /// @dev Returns whether the `executionData` has optional `opData`.
    function hasOpData(bytes calldata executionData) internal pure returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            result :=
                iszero(or(lt(executionData.length, 0x40), lt(calldataload(executionData.offset), 0x40)))
        }
    }

    /// @dev Returns the `i`th execution at `pointers`, without bounds checks.
    /// The bounds check is excluded as this function is intended to be called in a bounded loop.
    function getExecution(bytes32[] calldata pointers, uint256 i)
        internal
        pure
        returns (address target, uint256 value, bytes calldata data)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let c := add(pointers.offset, calldataload(add(pointers.offset, shl(5, i))))
            target := calldataload(c)
            value := calldataload(add(c, 0x20))
            let o := add(c, calldataload(add(c, 0x40)))
            data.offset := add(o, 0x20)
            data.length := calldataload(o)
        }
    }

    /// @dev Reencodes `executionData` such that it has `opData` added to it.
    /// Like `abi.encode(abi.decode(executionData, (Call[])), opData)`.
    /// Useful for forwarding `executionData` with extra `opData`.
    /// This function does not perform any check on the validity of `executionData`.
    function reencodeBatch(bytes calldata executionData, bytes memory opData)
        internal
        pure
        returns (bytes memory result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            result := add(0x64, mload(0x40)) // Give some space for `reencodeBatchAsExecuteCalldata`.
            let s := calldataload(executionData.offset) // Offset of `calls`.
            let n := sub(executionData.length, s) // Byte length of `calls`.
            mstore(add(result, 0x20), 0x40) // Store the new offset of `calls`.
            calldatacopy(add(result, 0x60), add(executionData.offset, s), n)
            mstore(add(result, 0x40), add(0x40, n)) // Store the new offset of `opData`.
            let o := add(add(result, 0x60), n) // Start offset of `opData` destination in memory.
            let d := sub(opData, o) // Offset difference between `opData` source and `o`.
            let end := add(mload(opData), add(0x20, o)) // End of `opData` destination in memory.
            for {} 1 {} {
                mstore(o, mload(add(o, d)))
                o := add(o, 0x20)
                if iszero(lt(o, end)) { break }
            }
            mstore(result, sub(o, add(result, 0x20))) // Store the length of `result`.
            calldatacopy(end, calldatasize(), 0x40) // Zeroize the bytes after `end`.
            mstore(0x40, add(0x20, o)) // Allocate memory.
        }
    }

    /// @dev `abi.encodeWithSignature("execute(bytes32,bytes)", mode, reencodeBatch(executionData, opData))`.
    function reencodeBatchAsExecuteCalldata(
        bytes32 mode,
        bytes calldata executionData,
        bytes memory opData
    ) internal pure returns (bytes memory result) {
        result = reencodeBatch(executionData, opData);
        /// @solidity memory-safe-assembly
        assembly {
            let n := mload(result)
            result := sub(result, 0x64)
            mstore(add(result, 0x44), 0x40) // Offset of `executionData`.
            mstore(add(result, 0x24), mode)
            mstore(add(result, 0x04), 0xe9ae5c53) // `execute(bytes32,bytes)`.
            mstore(result, add(0x64, n))
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                          HELPERS                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Helper function to return empty calldata bytes.
    function emptyCalldataBytes() internal pure returns (bytes calldata result) {
        /// @solidity memory-safe-assembly
        assembly {
            result.offset := 0
            result.length := 0
        }
    }
}

File 12 of 12 : Structs.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import {ValidationData, PermissionId, PassFlag} from "./Types.sol";
import {IPolicy} from "../interfaces/IERC7579Modules.sol";

struct Execution {
    address target;
    uint256 value;
    bytes callData;
}

// === for internal usage ===
struct PermissionSigMemory {
    uint8 idx;
    uint256 length;
    ValidationData validationData;
    PermissionId permission;
    PassFlag flag;
    IPolicy policy;
    bytes permSig;
    address caller;
    bytes32 digest;
}

struct PermissionDisableDataFormat {
    bytes[] data;
}

struct PermissionEnableDataFormat {
    bytes[] data;
}

struct UserOpSigEnableDataFormat {
    bytes validatorData;
    bytes hookData;
    bytes selectorData;
    bytes enableSig;
    bytes userOpSig;
}

struct SelectorDataFormat {
    bytes selectorInitData;
    bytes hookInitData;
}

struct SelectorDataFormatWithExecutorData {
    bytes selectorInitData;
    bytes hookInitData;
    bytes executorHookData;
}

struct InstallValidatorDataFormat {
    bytes validatorData;
    bytes hookData;
    bytes selectorData;
}

struct InstallExecutorDataFormat {
    bytes executorData;
    bytes hookData;
}

struct InstallFallbackDataFormat {
    bytes selectorData;
    bytes hookData;
}

Settings
{
  "remappings": [
    "ExcessivelySafeCall/=lib/ExcessivelySafeCall/src/",
    "forge-std/=lib/forge-std/src/",
    "solady/=lib/solady/src/",
    "solady_test/=lib/solady/test/",
    "ds-test/=lib/ExcessivelySafeCall/lib/ds-test/src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "none",
    "appendCBOR": false
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "shanghai",
  "viaIR": false
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"smartAccount","type":"address"}],"name":"AlreadyInitialized","type":"error"},{"inputs":[{"internalType":"uint256","name":"expiration","type":"uint256"}],"name":"ExpirationTooFar","type":"error"},{"inputs":[],"name":"InvalidAccount","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"expected","type":"uint256"},{"internalType":"uint256","name":"actual","type":"uint256"}],"name":"InvalidNonce","type":"error"},{"inputs":[],"name":"InvalidOwner","type":"error"},{"inputs":[],"name":"InvalidSignature","type":"error"},{"inputs":[],"name":"MalleableSignature","type":"error"},{"inputs":[{"internalType":"address","name":"smartAccount","type":"address"}],"name":"NotInitialized","type":"error"},{"inputs":[],"name":"Reentrancy","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"expiration","type":"uint256"}],"name":"SignatureExpired","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"kernel","type":"address"},{"indexed":true,"internalType":"bytes32","name":"executionHash","type":"bytes32"}],"name":"ExecutionRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"uint192","name":"key","type":"uint192"},{"indexed":false,"internalType":"uint64","name":"newSequence","type":"uint64"}],"name":"NonceIncremented","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"kernel","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"}],"name":"OwnerRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnerTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"kernel","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"}],"name":"OwnerUnregistered","type":"event"},{"inputs":[{"internalType":"bytes32","name":"purposeSalt","type":"bytes32"}],"name":"deriveNonceKey","outputs":[{"internalType":"uint192","name":"","type":"uint192"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"eip712Domain","outputs":[{"internalType":"bytes1","name":"fields","type":"bytes1"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"version","type":"string"},{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"address","name":"verifyingContract","type":"address"},{"internalType":"bytes32","name":"salt","type":"bytes32"},{"internalType":"uint256[]","name":"extensions","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"ExecMode","name":"mode","type":"bytes32"},{"internalType":"bytes","name":"executionCalldata","type":"bytes"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"expiration","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"execute","outputs":[{"internalType":"bytes[]","name":"returnData","type":"bytes[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint192","name":"key","type":"uint192"}],"name":"getNonce","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint192","name":"key","type":"uint192"}],"name":"incrementNonce","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"smartAccount","type":"address"}],"name":"isInitialized","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"typeID","type":"uint256"}],"name":"isModuleType","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"onInstall","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes","name":"","type":"bytes"}],"name":"onUninstall","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]

610120604052348015610010575f5ffd5b50306080524660a05260608061005e604080518082018252600d81526c22a1a229a0a2bc32b1baba37b960991b602080830191909152825180840190935260018352603160f81b9083015291565b815160209283012081519183019190912060c082905260e0819052604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f8152938401929092529082015246606082015230608082015260a0902061010052506100c79050565b60805160a05160c05160e051610100516111506101045f395f610a9e01525f610b5801525f610b3201525f610ae201525f610abf01526111505ff3fe60806040526004361061009a575f3560e01c8063d60b347f11610062578063d60b347f14610148578063db5c7e8814610177578063ecd0596114610197578063f2c5d61c146101b7578063f2fde38b14610230578063fa5441611461024f575f5ffd5b80630bd28e3b1461009e57806335567e1a146100bf5780636d61fe70146100fb57806384b0196e1461010e5780638a91b0e314610135575b5f5ffd5b3480156100a9575f5ffd5b506100bd6100b8366004610c59565b61029e565b005b3480156100ca575f5ffd5b506100de6100d9366004610c90565b610385565b6040516001600160401b0390911681526020015b60405180910390f35b6100bd610109366004610d07565b6103c2565b348015610119575f5ffd5b50610122610479565b6040516100f29796959493929190610d92565b6100bd610143366004610d07565b6104d9565b348015610153575f5ffd5b50610167610162366004610e28565b61055b565b60405190151581526020016100f2565b61018a610185366004610e43565b610565565b6040516100f29190610ede565b3480156101a2575f5ffd5b506101676101b1366004610f41565b60021490565b3480156101c2575f5ffd5b506102186101d1366004610f41565b604080516c22a1a229a0a2bc32b1baba37b960991b6020820152602d81018390525f9190604d0160408051601f198184030181529190528051602090910120901c92915050565b6040516001600160c01b0390911681526020016100f2565b34801561023b575f5ffd5b506100bd61024a366004610e28565b610852565b34801561025a575f5ffd5b50610286610269366004610e28565b6001600160a01b039081165f908152602081905260409020541690565b6040516001600160a01b0390911681526020016100f2565b6102a7336108fe565b6102cb5760405163f91bd6f160e01b81523360048201526024015b60405180910390fd5b335f908152602081815260408083206001600160c01b0385168452600101909152812080546001600160401b03169161030383610f6c565b82546101009290920a6001600160401b03818102199093169183160217909155335f818152602081815260408083206001600160c01b0388168085526001919091018352928190205490519416845290935090917f3c4422e7013dcec53c75bc4a839337cac5ca09bbb2aea3b2950fc04491583dd0910160405180910390a350565b6001600160a01b0382165f908152602081815260408083206001600160c01b03851684526001019091529020546001600160401b03165b92915050565b6103cb336108fe565b156103eb576040516393360fbf60e01b81523360048201526024016102c2565b5f6103f882840184610e28565b90506001600160a01b038116610421576040516349e27cff60e01b815260040160405180910390fd5b335f8181526020819052604080822080546001600160a01b0319166001600160a01b03861690811790915590519092917fa5e1f8b4009110f5525798d04ae2125421a12d0590aa52c13682ff1bd3c492ca91a3505050565b600f60f81b6060805f8080836104c7604080518082018252600d81526c22a1a229a0a2bc32b1baba37b960991b602080830191909152825180840190935260018352603160f81b9083015291565b97989097965046955030945091925090565b6104e2336108fe565b6105015760405163f91bd6f160e01b81523360048201526024016102c2565b335f8181526020819052604080822080546001600160a01b0319811690915590516001600160a01b0390911692839290917f2b019c4af52bef00865aed6c1141af64d6b25de072499d6ca81d9afcb20e86c39190a3505050565b5f6103bc826108fe565b60603068929eee149b4bd2126854036105855763ab143c065f526004601cfd5b3068929eee149b4bd21268556001600160a01b0389166105b857604051630da30f6560e31b815260040160405180910390fd5b6105c1896108fe565b6105e95760405163f91bd6f160e01b81526001600160a01b038a1660048201526024016102c2565b6105f662278d0042610f96565b84111561061957604051635170da8360e11b8152600481018590526024016102c2565b8342111561064c57604051631872770b60e01b81526001600160a01b038a166004820152602481018590526044016102c2565b610656898661091d565b6106608383610a51565b5f7f57163c36638b3a6c9b132da8051a766c3e21740a25b58e5d1671652192034d548a8a8a8a604051610694929190610fa9565b6040519081900381206106dc949392918b908b906020019586526001600160a01b0394909416602086015260408501929092526060840152608083015260a082015260c00190565b6040516020818303038152906040528051906020012090505f6106fe82610a9c565b9050808b6001600160a01b03167f8a887d1329b2ab70e3590efa71d8d5608fd795ad7798f4062b7e92bc87ab720c60405160405180910390a36001600160a01b03808c165f9081526020818152604091829020548251601f89018390048302810183019093528783529092169161079091849189908990819084018382808284375f92019190915250610bb292505050565b6001600160a01b0316816001600160a01b0316146107c157604051638baa579f60e01b815260040160405180910390fd5b6040516335a4725960e21b81526001600160a01b038d169063d691c9649034906107f3908f908f908f90600401610fb8565b5f6040518083038185885af115801561080e573d5f5f3e3d5ffd5b50505050506040513d5f823e601f3d908101601f191682016040526108369190810190611031565b3868929eee149b4bd21268559c9b505050505050505050505050565b61085b336108fe565b61087a5760405163f91bd6f160e01b81523360048201526024016102c2565b6001600160a01b0381166108a1576040516349e27cff60e01b815260040160405180910390fd5b335f8181526020819052604080822080546001600160a01b038681166001600160a01b0319831681179093559251921693909284927f73faf16c17e916c6f8862265d5560f565ca298b3a2bb70da5c3f979342a980779190a45050565b6001600160a01b039081165f9081526020819052604090205416151590565b6001600160a01b0382165f9081526020818152604080832084821c8085526001909101909252909120546001600160401b0380841691168082146109a8578467ffffffffffffffff19604085901b166001600160401b03831617604051630ec5782360e11b81526001600160a01b0390921660048301526024820152604481018590526064016102c2565b6109b3826001610f96565b6001600160a01b0386165f818152602081815260408083206001600160c01b03891680855260019182019093529220805467ffffffffffffffff19166001600160401b0395909516949094179093557f3c4422e7013dcec53c75bc4a839337cac5ca09bbb2aea3b2950fc04491583dd090610a2f908690610f96565b6040516001600160401b03909116815260200160405180910390a35050505050565b60208201357f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0811115610a975760405163f0ad0d0960e01b815260040160405180910390fd5b505050565b7f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000030147f0000000000000000000000000000000000000000000000000000000000000000461416610b8f5750604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f81527f000000000000000000000000000000000000000000000000000000000000000060208201527f00000000000000000000000000000000000000000000000000000000000000009181019190915246606082015230608082015260a090205b6719010000000000005f5280601a5281603a52604260182090505f603a52919050565b5f604051825160408114610bce5760418114610bef5750610c2a565b604084015160ff81901c601b016020526001600160ff1b0316606052610c02565b60608401515f1a60205260408401516060525b50835f5260208301516040526020600160805f60015afa5191505f606052806040523d610c37575b638baa579f5f526004601cfd5b5092915050565b80356001600160c01b0381168114610c54575f5ffd5b919050565b5f60208284031215610c69575f5ffd5b610c7282610c3e565b9392505050565b6001600160a01b0381168114610c8d575f5ffd5b50565b5f5f60408385031215610ca1575f5ffd5b8235610cac81610c79565b9150610cba60208401610c3e565b90509250929050565b5f5f83601f840112610cd3575f5ffd5b5081356001600160401b03811115610ce9575f5ffd5b602083019150836020828501011115610d00575f5ffd5b9250929050565b5f5f60208385031215610d18575f5ffd5b82356001600160401b03811115610d2d575f5ffd5b610d3985828601610cc3565b90969095509350505050565b5f5b83811015610d5f578181015183820152602001610d47565b50505f910152565b5f8151808452610d7e816020860160208601610d45565b601f01601f19169290920160200192915050565b60ff60f81b8816815260e060208201525f610db060e0830189610d67565b8281036040840152610dc28189610d67565b606084018890526001600160a01b038716608085015260a0840186905283810360c0850152845180825260208087019350909101905f5b81811015610e17578351835260209384019390920191600101610df9565b50909b9a5050505050505050505050565b5f60208284031215610e38575f5ffd5b8135610c7281610c79565b5f5f5f5f5f5f5f5f60c0898b031215610e5a575f5ffd5b8835610e6581610c79565b97506020890135965060408901356001600160401b03811115610e86575f5ffd5b610e928b828c01610cc3565b909750955050606089013593506080890135925060a08901356001600160401b03811115610ebe575f5ffd5b610eca8b828c01610cc3565b999c989b5096995094979396929594505050565b5f602082016020835280845180835260408501915060408160051b8601019250602086015f5b82811015610f3557603f19878603018452610f20858351610d67565b94506020938401939190910190600101610f04565b50929695505050505050565b5f60208284031215610f51575f5ffd5b5035919050565b634e487b7160e01b5f52601160045260245ffd5b5f6001600160401b0382166001600160401b038103610f8d57610f8d610f58565b60010192915050565b808201808211156103bc576103bc610f58565b818382375f9101908152919050565b83815260406020820152816040820152818360608301375f818301606090810191909152601f909201601f1916010192915050565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f191681016001600160401b038111828210171561102957611029610fed565b604052919050565b5f60208284031215611041575f5ffd5b81516001600160401b03811115611056575f5ffd5b8201601f81018413611066575f5ffd5b80516001600160401b0381111561107f5761107f610fed565b8060051b61108f60208201611001565b918252602081840181019290810190878411156110aa575f5ffd5b6020850192505b838310156111455782516001600160401b038111156110ce575f5ffd5b8501603f810189136110de575f5ffd5b60208101516001600160401b038111156110fa576110fa610fed565b61110d601f8201601f1916602001611001565b8181526040838301018b1015611121575f5ffd5b611132826020830160408601610d45565b84525050602092830192909101906110b1565b97965050505050505056

Deployed Bytecode

0x60806040526004361061009a575f3560e01c8063d60b347f11610062578063d60b347f14610148578063db5c7e8814610177578063ecd0596114610197578063f2c5d61c146101b7578063f2fde38b14610230578063fa5441611461024f575f5ffd5b80630bd28e3b1461009e57806335567e1a146100bf5780636d61fe70146100fb57806384b0196e1461010e5780638a91b0e314610135575b5f5ffd5b3480156100a9575f5ffd5b506100bd6100b8366004610c59565b61029e565b005b3480156100ca575f5ffd5b506100de6100d9366004610c90565b610385565b6040516001600160401b0390911681526020015b60405180910390f35b6100bd610109366004610d07565b6103c2565b348015610119575f5ffd5b50610122610479565b6040516100f29796959493929190610d92565b6100bd610143366004610d07565b6104d9565b348015610153575f5ffd5b50610167610162366004610e28565b61055b565b60405190151581526020016100f2565b61018a610185366004610e43565b610565565b6040516100f29190610ede565b3480156101a2575f5ffd5b506101676101b1366004610f41565b60021490565b3480156101c2575f5ffd5b506102186101d1366004610f41565b604080516c22a1a229a0a2bc32b1baba37b960991b6020820152602d81018390525f9190604d0160408051601f198184030181529190528051602090910120901c92915050565b6040516001600160c01b0390911681526020016100f2565b34801561023b575f5ffd5b506100bd61024a366004610e28565b610852565b34801561025a575f5ffd5b50610286610269366004610e28565b6001600160a01b039081165f908152602081905260409020541690565b6040516001600160a01b0390911681526020016100f2565b6102a7336108fe565b6102cb5760405163f91bd6f160e01b81523360048201526024015b60405180910390fd5b335f908152602081815260408083206001600160c01b0385168452600101909152812080546001600160401b03169161030383610f6c565b82546101009290920a6001600160401b03818102199093169183160217909155335f818152602081815260408083206001600160c01b0388168085526001919091018352928190205490519416845290935090917f3c4422e7013dcec53c75bc4a839337cac5ca09bbb2aea3b2950fc04491583dd0910160405180910390a350565b6001600160a01b0382165f908152602081815260408083206001600160c01b03851684526001019091529020546001600160401b03165b92915050565b6103cb336108fe565b156103eb576040516393360fbf60e01b81523360048201526024016102c2565b5f6103f882840184610e28565b90506001600160a01b038116610421576040516349e27cff60e01b815260040160405180910390fd5b335f8181526020819052604080822080546001600160a01b0319166001600160a01b03861690811790915590519092917fa5e1f8b4009110f5525798d04ae2125421a12d0590aa52c13682ff1bd3c492ca91a3505050565b600f60f81b6060805f8080836104c7604080518082018252600d81526c22a1a229a0a2bc32b1baba37b960991b602080830191909152825180840190935260018352603160f81b9083015291565b97989097965046955030945091925090565b6104e2336108fe565b6105015760405163f91bd6f160e01b81523360048201526024016102c2565b335f8181526020819052604080822080546001600160a01b0319811690915590516001600160a01b0390911692839290917f2b019c4af52bef00865aed6c1141af64d6b25de072499d6ca81d9afcb20e86c39190a3505050565b5f6103bc826108fe565b60603068929eee149b4bd2126854036105855763ab143c065f526004601cfd5b3068929eee149b4bd21268556001600160a01b0389166105b857604051630da30f6560e31b815260040160405180910390fd5b6105c1896108fe565b6105e95760405163f91bd6f160e01b81526001600160a01b038a1660048201526024016102c2565b6105f662278d0042610f96565b84111561061957604051635170da8360e11b8152600481018590526024016102c2565b8342111561064c57604051631872770b60e01b81526001600160a01b038a166004820152602481018590526044016102c2565b610656898661091d565b6106608383610a51565b5f7f57163c36638b3a6c9b132da8051a766c3e21740a25b58e5d1671652192034d548a8a8a8a604051610694929190610fa9565b6040519081900381206106dc949392918b908b906020019586526001600160a01b0394909416602086015260408501929092526060840152608083015260a082015260c00190565b6040516020818303038152906040528051906020012090505f6106fe82610a9c565b9050808b6001600160a01b03167f8a887d1329b2ab70e3590efa71d8d5608fd795ad7798f4062b7e92bc87ab720c60405160405180910390a36001600160a01b03808c165f9081526020818152604091829020548251601f89018390048302810183019093528783529092169161079091849189908990819084018382808284375f92019190915250610bb292505050565b6001600160a01b0316816001600160a01b0316146107c157604051638baa579f60e01b815260040160405180910390fd5b6040516335a4725960e21b81526001600160a01b038d169063d691c9649034906107f3908f908f908f90600401610fb8565b5f6040518083038185885af115801561080e573d5f5f3e3d5ffd5b50505050506040513d5f823e601f3d908101601f191682016040526108369190810190611031565b3868929eee149b4bd21268559c9b505050505050505050505050565b61085b336108fe565b61087a5760405163f91bd6f160e01b81523360048201526024016102c2565b6001600160a01b0381166108a1576040516349e27cff60e01b815260040160405180910390fd5b335f8181526020819052604080822080546001600160a01b038681166001600160a01b0319831681179093559251921693909284927f73faf16c17e916c6f8862265d5560f565ca298b3a2bb70da5c3f979342a980779190a45050565b6001600160a01b039081165f9081526020819052604090205416151590565b6001600160a01b0382165f9081526020818152604080832084821c8085526001909101909252909120546001600160401b0380841691168082146109a8578467ffffffffffffffff19604085901b166001600160401b03831617604051630ec5782360e11b81526001600160a01b0390921660048301526024820152604481018590526064016102c2565b6109b3826001610f96565b6001600160a01b0386165f818152602081815260408083206001600160c01b03891680855260019182019093529220805467ffffffffffffffff19166001600160401b0395909516949094179093557f3c4422e7013dcec53c75bc4a839337cac5ca09bbb2aea3b2950fc04491583dd090610a2f908690610f96565b6040516001600160401b03909116815260200160405180910390a35050505050565b60208201357f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0811115610a975760405163f0ad0d0960e01b815260040160405180910390fd5b505050565b7f35f09d4e7a5cd124b6336d354865df0da65e5eb3941da12db85907d845f36cb97f000000000000000000000000ec2c96e75b09e29b66bf2ee5c37fa749ef9aa7c730147f0000000000000000000000000000000000000000000000000000000000000001461416610b8f5750604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f81527f624e42c426ec632a75570ab2c59adc8c1a61766221f44c6808aa2e0f2d6d57d060208201527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc69181019190915246606082015230608082015260a090205b6719010000000000005f5280601a5281603a52604260182090505f603a52919050565b5f604051825160408114610bce5760418114610bef5750610c2a565b604084015160ff81901c601b016020526001600160ff1b0316606052610c02565b60608401515f1a60205260408401516060525b50835f5260208301516040526020600160805f60015afa5191505f606052806040523d610c37575b638baa579f5f526004601cfd5b5092915050565b80356001600160c01b0381168114610c54575f5ffd5b919050565b5f60208284031215610c69575f5ffd5b610c7282610c3e565b9392505050565b6001600160a01b0381168114610c8d575f5ffd5b50565b5f5f60408385031215610ca1575f5ffd5b8235610cac81610c79565b9150610cba60208401610c3e565b90509250929050565b5f5f83601f840112610cd3575f5ffd5b5081356001600160401b03811115610ce9575f5ffd5b602083019150836020828501011115610d00575f5ffd5b9250929050565b5f5f60208385031215610d18575f5ffd5b82356001600160401b03811115610d2d575f5ffd5b610d3985828601610cc3565b90969095509350505050565b5f5b83811015610d5f578181015183820152602001610d47565b50505f910152565b5f8151808452610d7e816020860160208601610d45565b601f01601f19169290920160200192915050565b60ff60f81b8816815260e060208201525f610db060e0830189610d67565b8281036040840152610dc28189610d67565b606084018890526001600160a01b038716608085015260a0840186905283810360c0850152845180825260208087019350909101905f5b81811015610e17578351835260209384019390920191600101610df9565b50909b9a5050505050505050505050565b5f60208284031215610e38575f5ffd5b8135610c7281610c79565b5f5f5f5f5f5f5f5f60c0898b031215610e5a575f5ffd5b8835610e6581610c79565b97506020890135965060408901356001600160401b03811115610e86575f5ffd5b610e928b828c01610cc3565b909750955050606089013593506080890135925060a08901356001600160401b03811115610ebe575f5ffd5b610eca8b828c01610cc3565b999c989b5096995094979396929594505050565b5f602082016020835280845180835260408501915060408160051b8601019250602086015f5b82811015610f3557603f19878603018452610f20858351610d67565b94506020938401939190910190600101610f04565b50929695505050505050565b5f60208284031215610f51575f5ffd5b5035919050565b634e487b7160e01b5f52601160045260245ffd5b5f6001600160401b0382166001600160401b038103610f8d57610f8d610f58565b60010192915050565b808201808211156103bc576103bc610f58565b818382375f9101908152919050565b83815260406020820152816040820152818360608301375f818301606090810191909152601f909201601f1916010192915050565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f191681016001600160401b038111828210171561102957611029610fed565b604052919050565b5f60208284031215611041575f5ffd5b81516001600160401b03811115611056575f5ffd5b8201601f81018413611066575f5ffd5b80516001600160401b0381111561107f5761107f610fed565b8060051b61108f60208201611001565b918252602081840181019290810190878411156110aa575f5ffd5b6020850192505b838310156111455782516001600160401b038111156110ce575f5ffd5b8501603f810189136110de575f5ffd5b60208101516001600160401b038111156110fa576110fa610fed565b61110d601f8201601f1916602001611001565b8181526040838301018b1015611121575f5ffd5b611132826020830160408601610d45565b84525050602092830192909101906110b1565b97965050505050505056

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export  ]
[ 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.