ETH Price: $2,344.54 (-1.43%)

Contract

0x8EF7DC88F3c48ac072479335179Ebe7b4DF44b2a

Overview

ETH Balance

0.00011 ETH

ETH Value

$0.26 (@ $2,344.54/ETH)

Token Holdings

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Block
From
To
Transfer Ownersh...317485292025-12-22 15:08:2741 days ago1766416107IN
0x8EF7DC88...b4DF44b2a
0 ETH0.000004880.05633
Withdraw317481882025-12-22 15:05:2641 days ago1766415926IN
0x8EF7DC88...b4DF44b2a
0 ETH0.000006750.05633
Request Ownershi...311222082025-12-17 18:18:1846 days ago1765995498IN
0x8EF7DC88...b4DF44b2a
0 ETH0.00000560.04525
Request Ownershi...311219282025-12-17 18:15:3046 days ago1765995330IN
0x8EF7DC88...b4DF44b2a
0.0001 ETH0.000004030.04525
Cancel Ownership...311218602025-12-17 18:14:5946 days ago1765995299IN
0x8EF7DC88...b4DF44b2a
0.00001 ETH0.00000450.04525
Cancel Ownership...311199872025-12-17 17:56:3446 days ago1765994194IN
0x8EF7DC88...b4DF44b2a
0 ETH0.000005570.04525
Withdraw310886522025-12-17 13:20:1646 days ago1765977616IN
0x8EF7DC88...b4DF44b2a
0 ETH0.000006750.05633

Latest 3 internal transactions

Advanced mode:
Parent Transaction Hash Block From To
311219282025-12-17 18:15:3046 days ago1765995330
0x8EF7DC88...b4DF44b2a
0.0001 ETH
311218602025-12-17 18:14:5946 days ago1765995299
0x8EF7DC88...b4DF44b2a
0.00001 ETH
190733982025-09-10 21:46:39143 days ago1757540799  Contract Creation0 ETH
Cross-Chain Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
OnchainPaymentHandler

Compiler Version
v0.8.28+commit.7893614a

ZkSolc Version
v1.5.11

Optimization Enabled:
Yes with Mode 3

Other Settings:
prague EvmVersion
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {EIP712} from "solady-0.1.24/src/utils/EIP712.sol";
import {Ownable} from "solady-0.1.24/src/auth/Ownable.sol";
import {SafeTransferLib} from "solady-0.1.24/src/utils/ext/zksync/SafeTransferLib.sol";
import {SignatureCheckerLib} from "solady-0.1.24/src/utils/SignatureCheckerLib.sol";
import {PurchaseReservation} from "./Structs.sol";

contract OnchainPaymentHandler is EIP712, Ownable {
    using SafeTransferLib for address;

    bytes32 public constant PURCHASE_TYPEHASH =
        keccak256("Purchase(bytes32 reservationId,uint256 maxQuantity,uint256 expiration,uint256 pricePerUnit)");

    address public signer;
    address public paymentToken;
    bool public isPaused;

    mapping(bytes32 => PurchaseReservation) public reservations;

    error ReservationUsed();
    error ReservationExpired();
    error ExceedsMaxQuantity();
    error Paused();

    event Purchase(address indexed buyer, bytes32 indexed reservationId, uint256 quantity, uint256 pricePerUnit);

    constructor(address owner, address _paymentToken, address _signer) {
        _initializeOwner(owner);
        paymentToken = _paymentToken;
        signer = _signer;
    }

    function processPayment(
        bytes32 reservationId,
        address buyer,
        uint256 quantity,
        uint256 maxQuantity,
        uint256 expiration,
        uint256 pricePerUnit,
        bytes calldata signature
    ) public {
        if (isPaused) revert Paused();

        _validatePurchase(reservationId, quantity, maxQuantity, expiration, pricePerUnit, signature);

        uint256 totalAmount = quantity * pricePerUnit;
        reservations[reservationId] =
            PurchaseReservation({buyer: buyer, quantity: uint16(quantity), amount: uint80(totalAmount)});

        paymentToken.safeTransferFrom(msg.sender, address(this), totalAmount);

        emit Purchase(buyer, reservationId, quantity, pricePerUnit);
    }

    function withdraw(address token) public onlyOwner {
        token.safeTransferAll(owner());
    }

    function setSigner(address _signer) public onlyOwner {
        signer = _signer;
    }

    function setPaused(bool _isPaused) public onlyOwner {
        isPaused = _isPaused;
    }

    function _validatePurchase(
        bytes32 reservationId,
        uint256 quantity,
        uint256 maxQuantity,
        uint256 expiration,
        uint256 pricePerUnit,
        bytes calldata signature
    ) internal virtual {
        require(reservations[reservationId].buyer == address(0), ReservationUsed());
        require(block.timestamp < expiration, ReservationExpired());
        require(quantity <= maxQuantity, ExceedsMaxQuantity());

        bytes32 digest = _hashTypedData(
            keccak256(abi.encode(PURCHASE_TYPEHASH, reservationId, maxQuantity, expiration, pricePerUnit))
        );

        require(SignatureCheckerLib.isValidSignatureNowCalldata(signer, digest, signature), "Invalid signature");
    }

    function _domainNameAndVersion() internal pure override returns (string memory, string memory) {
        return ("OnchainPaymentHandler", "1");
    }
}

// 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;

    /// @dev `keccak256("EIP712Domain(string name,string version,address verifyingContract)")`.
    /// This is only used in `_hashTypedDataSansChainId`.
    bytes32 internal constant _DOMAIN_TYPEHASH_SANS_CHAIN_ID =
        0x91ab3d17e3a50a9d89e63fd30b92be7f5336b03b287bb946787a83a9d62a2766;

    /// @dev `keccak256("EIP712Domain(string name,string version)")`.
    /// This is only used in `_hashTypedDataSansChainIdAndVerifyingContract`.
    bytes32 internal constant _DOMAIN_TYPEHASH_SANS_CHAIN_ID_AND_VERIFYING_CONTRACT =
        0xb03948446334eb9b2196d5eb166f69b9d49403eb4a12f36de8d3f9f3cb8e15c3;

    /// @dev `keccak256("EIP712Domain(string name,string version,uint256 chainId)")`.
    /// This is only used in `_hashTypedDataSansVerifyingContract`.
    bytes32 internal constant _DOMAIN_TYPEHASH_SANS_VERIFYING_CONTRACT =
        0xc2f8787176b8ac6bf7215b4adcc1e069bf4ab82d9ab1df05a57a91d425935b6e;

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

    /// @dev Variant of `_hashTypedData` that excludes the chain ID.
    /// Included for the niche use case of cross-chain workflows.
    function _hashTypedDataSansChainId(bytes32 structHash)
        internal
        view
        virtual
        returns (bytes32 digest)
    {
        (string memory name, string memory version) = _domainNameAndVersion();
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Load the free memory pointer.
            mstore(0x00, _DOMAIN_TYPEHASH_SANS_CHAIN_ID)
            mstore(0x20, keccak256(add(name, 0x20), mload(name)))
            mstore(0x40, keccak256(add(version, 0x20), mload(version)))
            mstore(0x60, address())
            // Compute the digest.
            mstore(0x20, keccak256(0x00, 0x80)) // Store the domain separator.
            mstore(0x00, 0x1901) // Store "\x19\x01".
            mstore(0x40, structHash) // Store the struct hash.
            digest := keccak256(0x1e, 0x42)
            mstore(0x40, m) // Restore the free memory pointer.
            mstore(0x60, 0) // Restore the zero pointer.
        }
    }

    /// @dev Variant of `_hashTypedData` that excludes the chain ID and verifying contract.
    /// Included for the niche use case of cross-chain and multi-verifier workflows.
    function _hashTypedDataSansChainIdAndVerifyingContract(bytes32 structHash)
        internal
        view
        virtual
        returns (bytes32 digest)
    {
        (string memory name, string memory version) = _domainNameAndVersion();
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Load the free memory pointer.
            mstore(0x00, _DOMAIN_TYPEHASH_SANS_CHAIN_ID_AND_VERIFYING_CONTRACT)
            mstore(0x20, keccak256(add(name, 0x20), mload(name)))
            mstore(0x40, keccak256(add(version, 0x20), mload(version)))
            // Compute the digest.
            mstore(0x20, keccak256(0x00, 0x60)) // Store the domain separator.
            mstore(0x00, 0x1901) // Store "\x19\x01".
            mstore(0x40, structHash) // Store the struct hash.
            digest := keccak256(0x1e, 0x42)
            mstore(0x40, m) // Restore the free memory pointer.
            mstore(0x60, 0) // Restore the zero pointer.
        }
    }

    /// @dev Variant of `_hashTypedData` that excludes the chain ID and verifying contract.
    /// Included for the niche use case of multi-verifier workflows.
    function _hashTypedDataSansVerifyingContract(bytes32 structHash)
        internal
        view
        virtual
        returns (bytes32 digest)
    {
        (string memory name, string memory version) = _domainNameAndVersion();
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Load the free memory pointer.
            mstore(0x00, _DOMAIN_TYPEHASH_SANS_VERIFYING_CONTRACT)
            mstore(0x20, keccak256(add(name, 0x20), mload(name)))
            mstore(0x40, keccak256(add(version, 0x20), mload(version)))
            mstore(0x60, chainid())
            // Compute the digest.
            mstore(0x20, keccak256(0x00, 0x80)) // Store the domain separator.
            mstore(0x00, 0x1901) // Store "\x19\x01".
            mstore(0x40, structHash) // Store the struct hash.
            digest := keccak256(0x1e, 0x42)
            mstore(0x40, m) // Restore the free memory pointer.
            mstore(0x60, 0) // Restore the zero pointer.
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                    EIP-5267 OPERATIONS                     */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev See: https://eips.ethereum.org/EIPS/eip-5267
    function eip712Domain()
        public
        view
        virtual
        returns (
            bytes1 fields,
            string memory name,
            string memory version,
            uint256 chainId,
            address verifyingContract,
            bytes32 salt,
            uint256[] memory extensions
        )
    {
        fields = hex"0f"; // `0b01111`.
        (name, version) = _domainNameAndVersion();
        chainId = block.chainid;
        verifyingContract = address(this);
        salt = salt; // `bytes32(0)`.
        extensions = extensions; // `new uint256[](0)`.
    }

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

    /// @dev Returns the EIP-712 domain separator.
    function _buildDomainSeparator() private view returns (bytes32 separator) {
        // We will use `separator` to store the name hash to save a bit of gas.
        bytes32 versionHash;
        if (_domainNameAndVersionMayChange()) {
            (string memory name, string memory version) = _domainNameAndVersion();
            separator = keccak256(bytes(name));
            versionHash = keccak256(bytes(version));
        } else {
            separator = _cachedNameHash;
            versionHash = _cachedVersionHash;
        }
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Load the free memory pointer.
            mstore(m, _DOMAIN_TYPEHASH)
            mstore(add(m, 0x20), separator) // Name hash.
            mstore(add(m, 0x40), versionHash)
            mstore(add(m, 0x60), chainid())
            mstore(add(m, 0x80), address())
            separator := keccak256(m, 0xa0)
        }
    }

    /// @dev Returns if the cached domain separator has been invalidated.
    function _cachedDomainSeparatorInvalidated() private view returns (bool result) {
        uint256 cachedChainId = _cachedChainId;
        uint256 cachedThis = _cachedThis;
        /// @solidity memory-safe-assembly
        assembly {
            result := iszero(and(eq(chainid(), cachedChainId), eq(address(), cachedThis)))
        }
    }
}

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

/// @notice Simple single owner authorization mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol)
///
/// @dev Note:
/// This implementation does NOT auto-initialize the owner to `msg.sender`.
/// You MUST call the `_initializeOwner` in the constructor / initializer.
///
/// While the ownable portion follows
/// [EIP-173](https://eips.ethereum.org/EIPS/eip-173) for compatibility,
/// the nomenclature for the 2-step ownership handover may be unique to this codebase.
abstract contract Ownable {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The caller is not authorized to call the function.
    error Unauthorized();

    /// @dev The `newOwner` cannot be the zero address.
    error NewOwnerIsZeroAddress();

    /// @dev The `pendingOwner` does not have a valid handover request.
    error NoHandoverRequest();

    /// @dev Cannot double-initialize.
    error AlreadyInitialized();

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

    /// @dev The ownership is transferred from `oldOwner` to `newOwner`.
    /// This event is intentionally kept the same as OpenZeppelin's Ownable to be
    /// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173),
    /// despite it not being as lightweight as a single argument event.
    event OwnershipTransferred(address indexed oldOwner, address indexed newOwner);

    /// @dev An ownership handover to `pendingOwner` has been requested.
    event OwnershipHandoverRequested(address indexed pendingOwner);

    /// @dev The ownership handover to `pendingOwner` has been canceled.
    event OwnershipHandoverCanceled(address indexed pendingOwner);

    /// @dev `keccak256(bytes("OwnershipTransferred(address,address)"))`.
    uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE =
        0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0;

    /// @dev `keccak256(bytes("OwnershipHandoverRequested(address)"))`.
    uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE =
        0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d;

    /// @dev `keccak256(bytes("OwnershipHandoverCanceled(address)"))`.
    uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE =
        0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92;

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

    /// @dev The owner slot is given by:
    /// `bytes32(~uint256(uint32(bytes4(keccak256("_OWNER_SLOT_NOT")))))`.
    /// It is intentionally chosen to be a high value
    /// to avoid collision with lower slots.
    /// The choice of manual storage layout is to enable compatibility
    /// with both regular and upgradeable contracts.
    bytes32 internal constant _OWNER_SLOT =
        0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927;

    /// The ownership handover slot of `newOwner` is given by:
    /// ```
    ///     mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED))
    ///     let handoverSlot := keccak256(0x00, 0x20)
    /// ```
    /// It stores the expiry timestamp of the two-step ownership handover.
    uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                     INTERNAL FUNCTIONS                     */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Override to return true to make `_initializeOwner` prevent double-initialization.
    function _guardInitializeOwner() internal pure virtual returns (bool guard) {}

    /// @dev Initializes the owner directly without authorization guard.
    /// This function must be called upon initialization,
    /// regardless of whether the contract is upgradeable or not.
    /// This is to enable generalization to both regular and upgradeable contracts,
    /// and to save gas in case the initial owner is not the caller.
    /// For performance reasons, this function will not check if there
    /// is an existing owner.
    function _initializeOwner(address newOwner) internal virtual {
        if (_guardInitializeOwner()) {
            /// @solidity memory-safe-assembly
            assembly {
                let ownerSlot := _OWNER_SLOT
                if sload(ownerSlot) {
                    mstore(0x00, 0x0dc149f0) // `AlreadyInitialized()`.
                    revert(0x1c, 0x04)
                }
                // Clean the upper 96 bits.
                newOwner := shr(96, shl(96, newOwner))
                // Store the new value.
                sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
            }
        } else {
            /// @solidity memory-safe-assembly
            assembly {
                // Clean the upper 96 bits.
                newOwner := shr(96, shl(96, newOwner))
                // Store the new value.
                sstore(_OWNER_SLOT, newOwner)
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
            }
        }
    }

    /// @dev Sets the owner directly without authorization guard.
    function _setOwner(address newOwner) internal virtual {
        if (_guardInitializeOwner()) {
            /// @solidity memory-safe-assembly
            assembly {
                let ownerSlot := _OWNER_SLOT
                // Clean the upper 96 bits.
                newOwner := shr(96, shl(96, newOwner))
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
                // Store the new value.
                sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
            }
        } else {
            /// @solidity memory-safe-assembly
            assembly {
                let ownerSlot := _OWNER_SLOT
                // Clean the upper 96 bits.
                newOwner := shr(96, shl(96, newOwner))
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
                // Store the new value.
                sstore(ownerSlot, newOwner)
            }
        }
    }

    /// @dev Throws if the sender is not the owner.
    function _checkOwner() internal view virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // If the caller is not the stored owner, revert.
            if iszero(eq(caller(), sload(_OWNER_SLOT))) {
                mstore(0x00, 0x82b42900) // `Unauthorized()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Returns how long a two-step ownership handover is valid for in seconds.
    /// Override to return a different value if needed.
    /// Made internal to conserve bytecode. Wrap it in a public function if needed.
    function _ownershipHandoverValidFor() internal view virtual returns (uint64) {
        return 48 * 3600;
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  PUBLIC UPDATE FUNCTIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Allows the owner to transfer the ownership to `newOwner`.
    function transferOwnership(address newOwner) public payable virtual onlyOwner {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(shl(96, newOwner)) {
                mstore(0x00, 0x7448fbae) // `NewOwnerIsZeroAddress()`.
                revert(0x1c, 0x04)
            }
        }
        _setOwner(newOwner);
    }

    /// @dev Allows the owner to renounce their ownership.
    function renounceOwnership() public payable virtual onlyOwner {
        _setOwner(address(0));
    }

    /// @dev Request a two-step ownership handover to the caller.
    /// The request will automatically expire in 48 hours (172800 seconds) by default.
    function requestOwnershipHandover() public payable virtual {
        unchecked {
            uint256 expires = block.timestamp + _ownershipHandoverValidFor();
            /// @solidity memory-safe-assembly
            assembly {
                // Compute and set the handover slot to `expires`.
                mstore(0x0c, _HANDOVER_SLOT_SEED)
                mstore(0x00, caller())
                sstore(keccak256(0x0c, 0x20), expires)
                // Emit the {OwnershipHandoverRequested} event.
                log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller())
            }
        }
    }

    /// @dev Cancels the two-step ownership handover to the caller, if any.
    function cancelOwnershipHandover() public payable virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute and set the handover slot to 0.
            mstore(0x0c, _HANDOVER_SLOT_SEED)
            mstore(0x00, caller())
            sstore(keccak256(0x0c, 0x20), 0)
            // Emit the {OwnershipHandoverCanceled} event.
            log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller())
        }
    }

    /// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`.
    /// Reverts if there is no existing ownership handover requested by `pendingOwner`.
    function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute and set the handover slot to 0.
            mstore(0x0c, _HANDOVER_SLOT_SEED)
            mstore(0x00, pendingOwner)
            let handoverSlot := keccak256(0x0c, 0x20)
            // If the handover does not exist, or has expired.
            if gt(timestamp(), sload(handoverSlot)) {
                mstore(0x00, 0x6f5e8818) // `NoHandoverRequest()`.
                revert(0x1c, 0x04)
            }
            // Set the handover slot to 0.
            sstore(handoverSlot, 0)
        }
        _setOwner(pendingOwner);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   PUBLIC READ FUNCTIONS                    */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the owner of the contract.
    function owner() public view virtual returns (address result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := sload(_OWNER_SLOT)
        }
    }

    /// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`.
    function ownershipHandoverExpiresAt(address pendingOwner)
        public
        view
        virtual
        returns (uint256 result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the handover slot.
            mstore(0x0c, _HANDOVER_SLOT_SEED)
            mstore(0x00, pendingOwner)
            // Load the handover slot.
            result := sload(keccak256(0x0c, 0x20))
        }
    }

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

    /// @dev Marks a function as only callable by the owner.
    modifier onlyOwner() virtual {
        _checkOwner();
        _;
    }
}

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

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

/// @notice Library for force safe transferring ETH and ERC20s in ZKsync.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/ext/zksync/SafeTransferLib.sol)
library SafeTransferLib {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                           EVENTS                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev A single use ETH vault has been created for `to`, with `amount`.
    event SingleUseETHVaultCreated(address indexed to, uint256 amount, address vault);

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

    /// @dev The ETH transfer has failed.
    error ETHTransferFailed();

    /// @dev The ERC20 `transferFrom` has failed.
    error TransferFromFailed();

    /// @dev The ERC20 `transfer` has failed.
    error TransferFailed();

    /// @dev The ERC20 `approve` has failed.
    error ApproveFailed();

    /// @dev The ERC20 `totalSupply` query has failed.
    error TotalSupplyQueryFailed();

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

    /// @dev Suggested gas stipend for contract receiving ETH to perform a few
    /// storage reads and writes, but low enough to prevent griefing.
    uint256 internal constant GAS_STIPEND_NO_GRIEF = 1000000;

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

    // If the ETH transfer MUST succeed with a reasonable gas budget, use the force variants.
    //
    // The regular variants:
    // - Forwards all remaining gas to the target.
    // - Reverts if the target reverts.
    // - Reverts if the current contract has insufficient balance.
    //
    // The force variants:
    // - Forwards with an optional gas stipend
    //   (defaults to `GAS_STIPEND_NO_GRIEF`, which is sufficient for most cases).
    // - If the target reverts, or if the gas stipend is exhausted,
    //   creates a temporary contract to force send the ETH via `SELFDESTRUCT`.
    //   Future compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758.
    // - Reverts if the current contract has insufficient balance.
    //
    // The try variants:
    // - Forwards with a mandatory gas stipend.
    // - Instead of reverting, returns whether the transfer succeeded.

    /// @dev Sends `amount` (in wei) ETH to `to`.
    function safeTransferETH(address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(call(gas(), to, amount, 0x00, 0x00, 0x00, 0x00)) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Sends all the ETH in the current contract to `to`.
    function safeTransferAllETH(address to) internal {
        /// @solidity memory-safe-assembly
        assembly {
            // Transfer all the ETH and check if it succeeded or not.
            if iszero(call(gas(), to, selfbalance(), 0x00, 0x00, 0x00, 0x00)) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Force sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
    /// If force transfer is used, returns the vault. Else returns `address(0)`.
    function forceSafeTransferETH(address to, uint256 amount, uint256 gasStipend)
        internal
        returns (address vault)
    {
        if (amount == uint256(0)) return address(0); // Early return if `amount` is zero.
        uint256 selfBalanceBefore = address(this).balance;
        /// @solidity memory-safe-assembly
        assembly {
            if lt(selfBalanceBefore, amount) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
            pop(call(gasStipend, to, amount, 0x00, 0x00, 0x00, 0x00))
        }
        if (address(this).balance == selfBalanceBefore) {
            vault = address(new SingleUseETHVault());
            /// @solidity memory-safe-assembly
            assembly {
                mstore(0x00, shr(96, shl(96, to)))
                if iszero(call(gas(), vault, amount, 0x00, 0x20, 0x00, 0x00)) { revert(0x00, 0x00) }
            }
            emit SingleUseETHVaultCreated(to, amount, vault);
        }
    }

    /// @dev Force sends all the ETH in the current contract to `to`, with a `gasStipend`.
    /// If force transfer is used, returns the vault. Else returns `address(0)`.
    function forceSafeTransferAllETH(address to, uint256 gasStipend)
        internal
        returns (address vault)
    {
        vault = forceSafeTransferETH(to, address(this).balance, gasStipend);
    }

    /// @dev Force sends `amount` (in wei) ETH to `to`, with `GAS_STIPEND_NO_GRIEF`.
    /// If force transfer is used, returns the vault. Else returns `address(0)`.
    function forceSafeTransferETH(address to, uint256 amount) internal returns (address vault) {
        vault = forceSafeTransferETH(to, amount, GAS_STIPEND_NO_GRIEF);
    }

    /// @dev Force sends all the ETH in the current contract to `to`, with `GAS_STIPEND_NO_GRIEF`.
    /// If force transfer is used, returns the vault. Else returns `address(0)`.
    function forceSafeTransferAllETH(address to) internal returns (address vault) {
        vault = forceSafeTransferETH(to, address(this).balance, GAS_STIPEND_NO_GRIEF);
    }

    /// @dev Sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
    function trySafeTransferETH(address to, uint256 amount, uint256 gasStipend)
        internal
        returns (bool success)
    {
        /// @solidity memory-safe-assembly
        assembly {
            success := call(gasStipend, to, amount, 0x00, 0x00, 0x00, 0x00)
        }
    }

    /// @dev Sends all the ETH in the current contract to `to`, with a `gasStipend`.
    function trySafeTransferAllETH(address to, uint256 gasStipend)
        internal
        returns (bool success)
    {
        /// @solidity memory-safe-assembly
        assembly {
            success := call(gasStipend, to, selfbalance(), 0x00, 0x00, 0x00, 0x00)
        }
    }

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

    /// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
    /// Reverts upon failure.
    ///
    /// The `from` account must have at least `amount` approved for
    /// the current contract to manage.
    function safeTransferFrom(address token, address from, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x60, amount) // Store the `amount` argument.
            mstore(0x40, to) // Store the `to` argument.
            mstore(0x2c, shl(96, from)) // Store the `from` argument.
            mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.
            let success := call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
            if iszero(and(eq(mload(0x00), 1), success)) {
                if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
                    mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                    revert(0x1c, 0x04)
                }
            }
            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
    ///
    /// The `from` account must have at least `amount` approved for the current contract to manage.
    function trySafeTransferFrom(address token, address from, address to, uint256 amount)
        internal
        returns (bool success)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x60, amount) // Store the `amount` argument.
            mstore(0x40, to) // Store the `to` argument.
            mstore(0x2c, shl(96, from)) // Store the `from` argument.
            mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.
            success := call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
            if iszero(and(eq(mload(0x00), 1), success)) {
                success := lt(or(iszero(extcodesize(token)), returndatasize()), success)
            }
            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Sends all of ERC20 `token` from `from` to `to`.
    /// Reverts upon failure.
    ///
    /// The `from` account must have their entire balance approved for the current contract to manage.
    function safeTransferAllFrom(address token, address from, address to)
        internal
        returns (uint256 amount)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x40, to) // Store the `to` argument.
            mstore(0x2c, shl(96, from)) // Store the `from` argument.
            mstore(0x0c, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
            // Read the balance, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                    staticcall(gas(), token, 0x1c, 0x24, 0x60, 0x20)
                )
            ) {
                mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x00, 0x23b872dd) // `transferFrom(address,address,uint256)`.
            amount := mload(0x60) // The `amount` is already at 0x60. We'll need to return it.
            // Perform the transfer, reverting upon failure.
            let success := call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
            if iszero(and(eq(mload(0x00), 1), success)) {
                if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
                    mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                    revert(0x1c, 0x04)
                }
            }
            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Sends `amount` of ERC20 `token` from the current contract to `to`.
    /// Reverts upon failure.
    function safeTransfer(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
            // Perform the transfer, reverting upon failure.
            let success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
            if iszero(and(eq(mload(0x00), 1), success)) {
                if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
                    mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                    revert(0x1c, 0x04)
                }
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Sends all of ERC20 `token` from the current contract to `to`.
    /// Reverts upon failure.
    function safeTransferAll(address token, address to) internal returns (uint256 amount) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`.
            mstore(0x20, address()) // Store the address of the current contract.
            // Read the balance, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                    staticcall(gas(), token, 0x1c, 0x24, 0x34, 0x20)
                )
            ) {
                mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x14, to) // Store the `to` argument.
            amount := mload(0x34) // The `amount` is already at 0x34. We'll need to return it.
            mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
            // Perform the transfer, reverting upon failure.
            let success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
            if iszero(and(eq(mload(0x00), 1), success)) {
                if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
                    mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                    revert(0x1c, 0x04)
                }
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
    /// Reverts upon failure.
    function safeApprove(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
            let success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
            if iszero(and(eq(mload(0x00), 1), success)) {
                if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
                    mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
                    revert(0x1c, 0x04)
                }
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
    /// If the initial attempt to approve fails, attempts to reset the approved amount to zero,
    /// then retries the approval again (some tokens, e.g. USDT, requires this).
    /// Reverts upon failure.
    function safeApproveWithRetry(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
            // Perform the approval, retrying upon failure.
            let success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
            if iszero(and(eq(mload(0x00), 1), success)) {
                if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
                    mstore(0x34, 0) // Store 0 for the `amount`.
                    mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
                    pop(call(gas(), token, 0, 0x10, 0x44, 0x00, 0x00)) // Reset the approval.
                    mstore(0x34, amount) // Store back the original `amount`.
                    // Retry the approval, reverting upon failure.
                    success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                    if iszero(and(eq(mload(0x00), 1), success)) {
                        // Check the `extcodesize` again just in case the token selfdestructs lol.
                        if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
                            mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
                            revert(0x1c, 0x04)
                        }
                    }
                }
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Returns the amount of ERC20 `token` owned by `account`.
    /// Returns zero if the `token` does not exist.
    function balanceOf(address token, address account) internal view returns (uint256 amount) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, account) // Store the `account` argument.
            mstore(0x00, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
            amount :=
                mul( // The arguments of `mul` are evaluated from right to left.
                    mload(0x20),
                    and( // The arguments of `and` are evaluated from right to left.
                        gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                        staticcall(gas(), token, 0x10, 0x24, 0x20, 0x20)
                    )
                )
        }
    }

    /// @dev Returns the total supply of the `token`.
    /// Reverts if the token does not exist or does not implement `totalSupply()`.
    function totalSupply(address token) internal view returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, 0x18160ddd) // `totalSupply()`.
            if iszero(
                and(gt(returndatasize(), 0x1f), staticcall(gas(), token, 0x1c, 0x04, 0x00, 0x20))
            ) {
                mstore(0x00, 0x54cd9435) // `TotalSupplyQueryFailed()`.
                revert(0x1c, 0x04)
            }
            result := mload(0x00)
        }
    }
}

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

/// @notice Signature verification helper that supports both ECDSA signatures from EOAs
/// and ERC1271 signatures from smart contract wallets like Argent and Gnosis safe.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SignatureCheckerLib.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/SignatureChecker.sol)
///
/// @dev Note:
/// - The signature checking functions use the ecrecover precompile (0x1).
/// - The `bytes memory signature` variants use the identity precompile (0x4)
///   to copy memory internally.
/// - Unlike ECDSA signatures, contract signatures are revocable.
/// - 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 use signatures as unique identifiers:
/// - 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.
/// This implementation does NOT check if a signature is non-malleable.
library SignatureCheckerLib {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*               SIGNATURE CHECKING OPERATIONS                */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns whether `signature` is valid for `signer` and `hash`.
    /// If `signer.code.length == 0`, then validate with `ecrecover`, else
    /// it will validate with ERC1271 on `signer`.
    function isValidSignatureNow(address signer, bytes32 hash, bytes memory signature)
        internal
        view
        returns (bool isValid)
    {
        if (signer == address(0)) return isValid;
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            for {} 1 {} {
                if iszero(extcodesize(signer)) {
                    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`.
                    let recovered := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20))
                    isValid := gt(returndatasize(), shl(96, xor(signer, recovered)))
                    mstore(0x60, 0) // Restore the zero slot.
                    mstore(0x40, m) // Restore the free memory pointer.
                    break
                }
                let f := shl(224, 0x1626ba7e)
                mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
                mstore(add(m, 0x04), hash)
                let d := add(m, 0x24)
                mstore(d, 0x40) // The offset of the `signature` in the calldata.
                // Copy the `signature` over.
                let n := add(0x20, mload(signature))
                let copied := staticcall(gas(), 4, signature, n, add(m, 0x44), n)
                isValid := staticcall(gas(), signer, m, add(returndatasize(), 0x44), d, 0x20)
                isValid := and(eq(mload(d), f), and(isValid, copied))
                break
            }
        }
    }

    /// @dev Returns whether `signature` is valid for `signer` and `hash`.
    /// If `signer.code.length == 0`, then validate with `ecrecover`, else
    /// it will validate with ERC1271 on `signer`.
    function isValidSignatureNowCalldata(address signer, bytes32 hash, bytes calldata signature)
        internal
        view
        returns (bool isValid)
    {
        if (signer == address(0)) return isValid;
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            for {} 1 {} {
                if iszero(extcodesize(signer)) {
                    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) // `r`, `s`.
                    }
                    default { break }
                    mstore(0x00, hash)
                    let recovered := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20))
                    isValid := gt(returndatasize(), shl(96, xor(signer, recovered)))
                    mstore(0x60, 0) // Restore the zero slot.
                    mstore(0x40, m) // Restore the free memory pointer.
                    break
                }
                let f := shl(224, 0x1626ba7e)
                mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
                mstore(add(m, 0x04), hash)
                let d := add(m, 0x24)
                mstore(d, 0x40) // The offset of the `signature` in the calldata.
                mstore(add(m, 0x44), signature.length)
                // Copy the `signature` over.
                calldatacopy(add(m, 0x64), signature.offset, signature.length)
                isValid := staticcall(gas(), signer, m, add(signature.length, 0x64), d, 0x20)
                isValid := and(eq(mload(d), f), isValid)
                break
            }
        }
    }

    /// @dev Returns whether the signature (`r`, `vs`) is valid for `signer` and `hash`.
    /// If `signer.code.length == 0`, then validate with `ecrecover`, else
    /// it will validate with ERC1271 on `signer`.
    function isValidSignatureNow(address signer, bytes32 hash, bytes32 r, bytes32 vs)
        internal
        view
        returns (bool isValid)
    {
        if (signer == address(0)) return isValid;
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            for {} 1 {} {
                if iszero(extcodesize(signer)) {
                    mstore(0x00, hash)
                    mstore(0x20, add(shr(255, vs), 27)) // `v`.
                    mstore(0x40, r) // `r`.
                    mstore(0x60, shr(1, shl(1, vs))) // `s`.
                    let recovered := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20))
                    isValid := gt(returndatasize(), shl(96, xor(signer, recovered)))
                    mstore(0x60, 0) // Restore the zero slot.
                    mstore(0x40, m) // Restore the free memory pointer.
                    break
                }
                let f := shl(224, 0x1626ba7e)
                mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
                mstore(add(m, 0x04), hash)
                let d := add(m, 0x24)
                mstore(d, 0x40) // The offset of the `signature` in the calldata.
                mstore(add(m, 0x44), 65) // Length of the signature.
                mstore(add(m, 0x64), r) // `r`.
                mstore(add(m, 0x84), shr(1, shl(1, vs))) // `s`.
                mstore8(add(m, 0xa4), add(shr(255, vs), 27)) // `v`.
                isValid := staticcall(gas(), signer, m, 0xa5, d, 0x20)
                isValid := and(eq(mload(d), f), isValid)
                break
            }
        }
    }

    /// @dev Returns whether the signature (`v`, `r`, `s`) is valid for `signer` and `hash`.
    /// If `signer.code.length == 0`, then validate with `ecrecover`, else
    /// it will validate with ERC1271 on `signer`.
    function isValidSignatureNow(address signer, bytes32 hash, uint8 v, bytes32 r, bytes32 s)
        internal
        view
        returns (bool isValid)
    {
        if (signer == address(0)) return isValid;
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            for {} 1 {} {
                if iszero(extcodesize(signer)) {
                    mstore(0x00, hash)
                    mstore(0x20, and(v, 0xff)) // `v`.
                    mstore(0x40, r) // `r`.
                    mstore(0x60, s) // `s`.
                    let recovered := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20))
                    isValid := gt(returndatasize(), shl(96, xor(signer, recovered)))
                    mstore(0x60, 0) // Restore the zero slot.
                    mstore(0x40, m) // Restore the free memory pointer.
                    break
                }
                let f := shl(224, 0x1626ba7e)
                mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
                mstore(add(m, 0x04), hash)
                let d := add(m, 0x24)
                mstore(d, 0x40) // The offset of the `signature` in the calldata.
                mstore(add(m, 0x44), 65) // Length of the signature.
                mstore(add(m, 0x64), r) // `r`.
                mstore(add(m, 0x84), s) // `s`.
                mstore8(add(m, 0xa4), v) // `v`.
                isValid := staticcall(gas(), signer, m, 0xa5, d, 0x20)
                isValid := and(eq(mload(d), f), isValid)
                break
            }
        }
    }

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

    // Note: These ERC1271 operations do NOT have an ECDSA fallback.

    /// @dev Returns whether `signature` is valid for `hash` for an ERC1271 `signer` contract.
    function isValidERC1271SignatureNow(address signer, bytes32 hash, bytes memory signature)
        internal
        view
        returns (bool isValid)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            let f := shl(224, 0x1626ba7e)
            mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
            mstore(add(m, 0x04), hash)
            let d := add(m, 0x24)
            mstore(d, 0x40) // The offset of the `signature` in the calldata.
            // Copy the `signature` over.
            let n := add(0x20, mload(signature))
            let copied := staticcall(gas(), 4, signature, n, add(m, 0x44), n)
            isValid := staticcall(gas(), signer, m, add(returndatasize(), 0x44), d, 0x20)
            isValid := and(eq(mload(d), f), and(isValid, copied))
        }
    }

    /// @dev Returns whether `signature` is valid for `hash` for an ERC1271 `signer` contract.
    function isValidERC1271SignatureNowCalldata(
        address signer,
        bytes32 hash,
        bytes calldata signature
    ) internal view returns (bool isValid) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            let f := shl(224, 0x1626ba7e)
            mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
            mstore(add(m, 0x04), hash)
            let d := add(m, 0x24)
            mstore(d, 0x40) // The offset of the `signature` in the calldata.
            mstore(add(m, 0x44), signature.length)
            // Copy the `signature` over.
            calldatacopy(add(m, 0x64), signature.offset, signature.length)
            isValid := staticcall(gas(), signer, m, add(signature.length, 0x64), d, 0x20)
            isValid := and(eq(mload(d), f), isValid)
        }
    }

    /// @dev Returns whether the signature (`r`, `vs`) is valid for `hash`
    /// for an ERC1271 `signer` contract.
    function isValidERC1271SignatureNow(address signer, bytes32 hash, bytes32 r, bytes32 vs)
        internal
        view
        returns (bool isValid)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            let f := shl(224, 0x1626ba7e)
            mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
            mstore(add(m, 0x04), hash)
            let d := add(m, 0x24)
            mstore(d, 0x40) // The offset of the `signature` in the calldata.
            mstore(add(m, 0x44), 65) // Length of the signature.
            mstore(add(m, 0x64), r) // `r`.
            mstore(add(m, 0x84), shr(1, shl(1, vs))) // `s`.
            mstore8(add(m, 0xa4), add(shr(255, vs), 27)) // `v`.
            isValid := staticcall(gas(), signer, m, 0xa5, d, 0x20)
            isValid := and(eq(mload(d), f), isValid)
        }
    }

    /// @dev Returns whether the signature (`v`, `r`, `s`) is valid for `hash`
    /// for an ERC1271 `signer` contract.
    function isValidERC1271SignatureNow(address signer, bytes32 hash, uint8 v, bytes32 r, bytes32 s)
        internal
        view
        returns (bool isValid)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            let f := shl(224, 0x1626ba7e)
            mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
            mstore(add(m, 0x04), hash)
            let d := add(m, 0x24)
            mstore(d, 0x40) // The offset of the `signature` in the calldata.
            mstore(add(m, 0x44), 65) // Length of the signature.
            mstore(add(m, 0x64), r) // `r`.
            mstore(add(m, 0x84), s) // `s`.
            mstore8(add(m, 0xa4), v) // `v`.
            isValid := staticcall(gas(), signer, m, 0xa5, d, 0x20)
            isValid := and(eq(mload(d), f), isValid)
        }
    }

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

    // Note: These ERC6492 operations now include an ECDSA fallback at the very end.
    // The calldata variants are excluded for brevity.

    /// @dev Returns whether `signature` is valid for `hash`.
    /// If the signature is postfixed with the ERC6492 magic number, it will attempt to
    /// deploy / prepare the `signer` smart account before doing a regular ERC1271 check.
    /// Note: This function is NOT reentrancy safe.
    /// The verifier must be deployed.
    /// Otherwise, the function will return false if `signer` is not yet deployed / prepared.
    /// See: https://gist.github.com/Vectorized/011d6becff6e0a73e42fe100f8d7ef04
    /// With a dedicated verifier, this function is safe to use in contracts
    /// that have been granted special permissions.
    function isValidERC6492SignatureNowAllowSideEffects(
        address signer,
        bytes32 hash,
        bytes memory signature
    ) internal returns (bool isValid) {
        /// @solidity memory-safe-assembly
        assembly {
            function callIsValidSignature(signer_, hash_, signature_) -> _isValid {
                let m_ := mload(0x40)
                let f_ := shl(224, 0x1626ba7e)
                mstore(m_, f_) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
                mstore(add(m_, 0x04), hash_)
                let d_ := add(m_, 0x24)
                mstore(d_, 0x40) // The offset of the `signature` in the calldata.
                let n_ := add(0x20, mload(signature_))
                let copied_ := staticcall(gas(), 4, signature_, n_, add(m_, 0x44), n_)
                _isValid := staticcall(gas(), signer_, m_, add(returndatasize(), 0x44), d_, 0x20)
                _isValid := and(eq(mload(d_), f_), and(_isValid, copied_))
            }
            let noCode := iszero(extcodesize(signer))
            let n := mload(signature)
            for {} 1 {} {
                if iszero(eq(mload(add(signature, n)), mul(0x6492, div(not(isValid), 0xffff)))) {
                    if iszero(noCode) { isValid := callIsValidSignature(signer, hash, signature) }
                    break
                }
                if iszero(noCode) {
                    let o := add(signature, 0x20) // Signature bytes.
                    isValid := callIsValidSignature(signer, hash, add(o, mload(add(o, 0x40))))
                    if isValid { break }
                }
                let m := mload(0x40)
                mstore(m, signer)
                mstore(add(m, 0x20), hash)
                pop(
                    call(
                        gas(), // Remaining gas.
                        0x0000bc370E4DC924F427d84e2f4B9Ec81626ba7E, // Non-reverting verifier.
                        0, // Send zero ETH.
                        m, // Start of memory.
                        add(returndatasize(), 0x40), // Length of calldata in memory.
                        staticcall(gas(), 4, add(signature, 0x20), n, add(m, 0x40), n), // 1.
                        0x00 // Length of returndata to write.
                    )
                )
                isValid := returndatasize()
                break
            }
            // Do `ecrecover` fallback if `noCode && !isValid`.
            for {} gt(noCode, isValid) {} {
                switch n
                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 }
                let m := mload(0x40)
                mstore(0x00, hash)
                mstore(0x40, mload(add(signature, 0x20))) // `r`.
                let recovered := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20))
                isValid := gt(returndatasize(), shl(96, xor(signer, recovered)))
                mstore(0x60, 0) // Restore the zero slot.
                mstore(0x40, m) // Restore the free memory pointer.
                break
            }
        }
    }

    /// @dev Returns whether `signature` is valid for `hash`.
    /// If the signature is postfixed with the ERC6492 magic number, it will attempt
    /// to use a reverting verifier to deploy / prepare the `signer` smart account
    /// and do a `isValidSignature` check via the reverting verifier.
    /// Note: This function is reentrancy safe.
    /// The reverting verifier must be deployed.
    /// Otherwise, the function will return false if `signer` is not yet deployed / prepared.
    /// See: https://gist.github.com/Vectorized/846a474c855eee9e441506676800a9ad
    function isValidERC6492SignatureNow(address signer, bytes32 hash, bytes memory signature)
        internal
        returns (bool isValid)
    {
        /// @solidity memory-safe-assembly
        assembly {
            function callIsValidSignature(signer_, hash_, signature_) -> _isValid {
                let m_ := mload(0x40)
                let f_ := shl(224, 0x1626ba7e)
                mstore(m_, f_) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
                mstore(add(m_, 0x04), hash_)
                let d_ := add(m_, 0x24)
                mstore(d_, 0x40) // The offset of the `signature` in the calldata.
                let n_ := add(0x20, mload(signature_))
                let copied_ := staticcall(gas(), 4, signature_, n_, add(m_, 0x44), n_)
                _isValid := staticcall(gas(), signer_, m_, add(returndatasize(), 0x44), d_, 0x20)
                _isValid := and(eq(mload(d_), f_), and(_isValid, copied_))
            }
            let noCode := iszero(extcodesize(signer))
            let n := mload(signature)
            for {} 1 {} {
                if iszero(eq(mload(add(signature, n)), mul(0x6492, div(not(isValid), 0xffff)))) {
                    if iszero(noCode) { isValid := callIsValidSignature(signer, hash, signature) }
                    break
                }
                if iszero(noCode) {
                    let o := add(signature, 0x20) // Signature bytes.
                    isValid := callIsValidSignature(signer, hash, add(o, mload(add(o, 0x40))))
                    if isValid { break }
                }
                let m := mload(0x40)
                mstore(m, signer)
                mstore(add(m, 0x20), hash)
                let willBeZeroIfRevertingVerifierExists :=
                    call(
                        gas(), // Remaining gas.
                        0x00007bd799e4A591FeA53f8A8a3E9f931626Ba7e, // Reverting verifier.
                        0, // Send zero ETH.
                        m, // Start of memory.
                        add(returndatasize(), 0x40), // Length of calldata in memory.
                        staticcall(gas(), 4, add(signature, 0x20), n, add(m, 0x40), n), // 1.
                        0x00 // Length of returndata to write.
                    )
                isValid := gt(returndatasize(), willBeZeroIfRevertingVerifierExists)
                break
            }
            // Do `ecrecover` fallback if `noCode && !isValid`.
            for {} gt(noCode, isValid) {} {
                switch n
                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 }
                let m := mload(0x40)
                mstore(0x00, hash)
                mstore(0x40, mload(add(signature, 0x20))) // `r`.
                let recovered := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20))
                isValid := gt(returndatasize(), shl(96, xor(signer, recovered)))
                mstore(0x60, 0) // Restore the zero slot.
                mstore(0x40, m) // Restore the free memory pointer.
                break
            }
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                     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://eth.wiki/json-rpc/API#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://eth.wiki/json-rpc/API#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.
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   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
        }
    }
}

File 6 of 7 : Structs.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

struct PackItem {
    address token;
    uint96 id;
}

struct CubeResult {
    BlisterPackResult[] blisterPacks;
    ToyResult[] looseToys;
    uint256 looseItems;
}

struct BlisterPackResult {
    ToyResult[] toys;
    uint256 looseItems;
}

struct ToyResult {
    uint256 equippedItems;
}

enum PurchaseType {
    TOY,
    ITEM,
    EQUIPPED_ITEM,
    TOY_WITH_ITEMS,
    BLISTER_PACK
}

struct CheckoutItem {
    PurchaseType purchaseType;
    bool enabled;
    bool quantityLimited;
    uint40 activeFrom;
    uint40 activeUntil;
    uint32 price;
    uint32 maxQuantityPerPurchase;
    uint32 maxQuantityPerAddress;
    uint32 totalAvailable;
    uint32 remainingAvailable;
    string name;
    bytes additionalData;
}

struct CreateCheckoutItem {
    PurchaseType purchaseType;
    bool quantityLimited;
    uint40 activeFrom;
    uint40 activeUntil;
    uint32 price;
    uint32 maxQuantityPerPurchase;
    uint32 maxQuantityPerAddress;
    uint32 totalAvailable;
    string name;
    bytes additionalData;
}

struct UpdateCheckoutItem {
    bool enabled;
    uint40 activeFrom;
    uint40 activeUntil;
    uint32 price;
    uint32 maxQuantityPerPurchase;
    uint32 maxQuantityPerAddress;
    uint32 totalAvailable;
    string name;
    bytes additionalData;
}

struct PurchaseReservation {
    address buyer;
    uint16 quantity;
    uint80 amount;
}

File 7 of 7 : SingleUseETHVault.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice A single-use vault that allows a designated caller to withdraw all ETH in it.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/ext/zksync/SingleUseETHVault.sol)
contract SingleUseETHVault {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Unable to withdraw all.
    error WithdrawAllFailed();

    /// @dev Not authorized.
    error Unauthorized();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                        WITHDRAW ALL                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    fallback() external payable virtual {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x40, 0) // Optimization trick to remove free memory pointer initialization.
            let owner := sload(0)
            // Initialization.
            if iszero(owner) {
                sstore(0, calldataload(0x00)) // Store the owner.
                return(0x00, 0x00) // Early return.
            }
            // Authorization check.
            if iszero(eq(caller(), owner)) {
                mstore(0x00, 0x82b42900) // `Unauthorized()`.
                revert(0x1c, 0x04)
            }
            let to := calldataload(0x00)
            // If the calldata is less than 32 bytes, zero-left-pad it to 32 bytes.
            // Then use the rightmost 20 bytes of the word as the `to` address.
            // This allows for the calldata to be `abi.encode(to)` or `abi.encodePacked(to)`.
            to := shr(mul(lt(calldatasize(), 0x20), shl(3, sub(0x20, calldatasize()))), to)
            // If `to` is `address(0)`, set it to `msg.sender`.
            to := xor(mul(xor(to, caller()), iszero(to)), to)
            // Transfers the whole balance to `to`.
            if iszero(call(gas(), to, selfbalance(), 0x00, 0x00, 0x00, 0x00)) {
                mstore(0x00, 0x651aee10) // `WithdrawAllFailed()`.
                revert(0x1c, 0x04)
            }
        }
    }
}

Settings
{
  "viaIR": false,
  "codegen": "yul",
  "remappings": [
    "@/=src/",
    "@openzeppelin-contracts-4.9.6/=dependencies/@openzeppelin-contracts-4.9.6/",
    "@openzeppelin-contracts-5.3.0/=dependencies/@openzeppelin-contracts-5.3.0/",
    "@openzeppelin/contracts-v4/=dependencies/openzeppelin-contracts/contracts/",
    "@zksync-contracts-28.0.1/=dependencies/@zksync-contracts-28.0.1/",
    "era-contracts/=dependencies/@zksync-contracts-28.0.1/contracts/",
    "forge-std-1.9.7/=dependencies/forge-std-1.9.7/",
    "solady-0.1.24/=dependencies/solady-0.1.24/",
    "@openzeppelin/contracts/=dependencies/@zksync-contracts-28.0.1/lib/openzeppelin-contracts/contracts/",
    "ds-test/=dependencies/openzeppelin-contracts/lib/forge-std/lib/ds-test/src/",
    "erc4626-tests/=dependencies/openzeppelin-contracts/lib/erc4626-tests/",
    "forge-std/=dependencies/openzeppelin-contracts/lib/forge-std/src/",
    "openzeppelin-contracts/=dependencies/openzeppelin-contracts/",
    "openzeppelin/=dependencies/openzeppelin-contracts/contracts/"
  ],
  "evmVersion": "prague",
  "outputSelection": {
    "*": {
      "*": [
        "abi"
      ]
    }
  },
  "optimizer": {
    "enabled": true,
    "mode": "3",
    "size_fallback": false,
    "disable_system_request_memoization": true
  },
  "metadata": {},
  "libraries": {},
  "enableEraVMExtensions": false,
  "forceEVMLA": false
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"_paymentToken","type":"address"},{"internalType":"address","name":"_signer","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[],"name":"ExceedsMaxQuantity","type":"error"},{"inputs":[],"name":"NewOwnerIsZeroAddress","type":"error"},{"inputs":[],"name":"NoHandoverRequest","type":"error"},{"inputs":[],"name":"Paused","type":"error"},{"inputs":[],"name":"ReservationExpired","type":"error"},{"inputs":[],"name":"ReservationUsed","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"buyer","type":"address"},{"indexed":true,"internalType":"bytes32","name":"reservationId","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"quantity","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"pricePerUnit","type":"uint256"}],"name":"Purchase","type":"event"},{"inputs":[],"name":"PURCHASE_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cancelOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"completeOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"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":[],"name":"isPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"ownershipHandoverExpiresAt","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"paymentToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"reservationId","type":"bytes32"},{"internalType":"address","name":"buyer","type":"address"},{"internalType":"uint256","name":"quantity","type":"uint256"},{"internalType":"uint256","name":"maxQuantity","type":"uint256"},{"internalType":"uint256","name":"expiration","type":"uint256"},{"internalType":"uint256","name":"pricePerUnit","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"processPayment","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"requestOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"reservations","outputs":[{"internalType":"address","name":"buyer","type":"address"},{"internalType":"uint16","name":"quantity","type":"uint16"},{"internalType":"uint80","name":"amount","type":"uint80"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"_isPaused","type":"bool"}],"name":"setPaused","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_signer","type":"address"}],"name":"setSigner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"signer","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]

9c4d535b0000000000000000000000000000000000000000000000000000000000000000010001d9a7a5aad1928ef41848b631575ae238c1ace425be128bcbd25e82cedc000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000600000000000000000000000003e18b7cfc3955f5edfeb03ba04d78b4c9c8050bb00000000000000000000000084a71ccd554cc1b02749b35d22f684cc8ec987e1000000000000000000000000f0b272184dab47945ec2ace865db66cd6ba20cb7

Deployed Bytecode

0x0001000000000002000d00000000000200000000000103550000006003100270000001770330019700000001002001900000002c0000c13d0000008002000039000000400020043f000000040030008c000005890000413d000000000201043b000000e002200270000001870020009c0000007a0000a13d000001880020009c000000870000a13d000001890020009c0000011f0000a13d0000018a0020009c0000013e0000613d0000018b0020009c0000016c0000613d0000018c0020009c000005890000c13d000000240030008c000005890000413d0000000002000416000000000002004b000005890000c13d0000000401100370000000000101043b0000017a0010009c000005890000213d0000019f020000410000000c0020043f000000000010043f0000000c01000039000000200200003905d605b70000040f000000000101041a000000800010043f000001a001000041000005d70001042e0000012004000039000000400040043f0000000002000416000000000002004b000005890000c13d0000001f0230003900000178022001970000012002200039000000400020043f0000001f0530018f000001790630019800000120026000390000003e0000613d000000000701034f000000007807043c0000000004840436000000000024004b0000003a0000c13d000000000005004b0000004b0000613d000000000161034f0000000304500210000000000502043300000000054501cf000000000545022f000000000101043b0000010004400089000000000141022f00000000014101cf000000000151019f0000000000120435000000600030008c000005890000413d000001200300043d0000017a0030009c000005890000213d000001400100043d0000017a0010009c000005890000213d000001600200043d0000017a0020009c000005890000213d000d00000003001d000b00000002001d000c00000001001d0000000001000410000000800010043f0000017b0100004100000000001004430000000001000414000001770010009c0000017701008041000000c0011002100000017c011001c70000800b0200003905d605d10000040f00000001002001900000058b0000613d000000000401043b000000a00040043f000000400100043d0000017d0010009c000000740000213d0000004002100039000000400020043f000000150200003900000000022104360000017e030000410000000000320435000000400500043d0000017d0050009c000002b10000a13d000001ba01000041000000000010043f0000004101000039000000040010043f000001bb01000041000005d800010430000001940020009c000000c40000213d0000019a0020009c000000fd0000213d0000019d0020009c0000017d0000613d0000019e0020009c000005890000c13d0000000001000416000000000001004b000005890000c13d000000000100041a0000013a0000013d0000018f0020009c000000e20000213d000001920020009c0000012e0000613d000001930020009c000005890000c13d0000000001000416000000000001004b000005890000c13d0000001501000039000000800010043f0000017e02000041000000a00020043f0000010002000039000000400020043f0000000102000039000000c00020043f0000017f03000041000000e00030043f000001c803000041000001000030043f000000e003000039000001200030043f000001e00010043f000002000100043d000001c9011001970000017e011001c7000002000010043f000002150000043f0000012001000039000001400010043f000002200020043f000002400100043d000001ca011001970000017f011001c7000002400010043f000002410000043f0000017b0100004100000000001004430000000001000414000001770010009c0000017701008041000000c0011002100000017c011001c70000800b0200003905d605d10000040f00000001002001900000058b0000613d000000000101043b000001600010043f0000000001000410000001800010043f000001a00000043f0000016001000039000001c00010043f000000600100043d000002600010043f000000000001004b000003340000c13d00000180010000390000033e0000013d000001950020009c000001060000213d000001980020009c000001950000613d000001990020009c000005890000c13d0000019f010000410000000c0010043f0000000001000411000000000010043f0000000001000414000001770010009c0000017701008041000000c001100210000001a3011001c7000080100200003905d605d10000040f0000000100200190000005890000613d000000000101043b000000000001041b0000000001000414000001770010009c0000017701008041000000c00110021000000180011001c70000800d020000390000000203000039000001cc04000041000002400000013d000001900020009c000001350000613d000001910020009c000005890000c13d000000240030008c000005890000413d0000000002000416000000000002004b000005890000c13d0000000401100370000000000101043b000000000010043f0000000201000039000000200010043f0000004002000039000000000100001905d605b70000040f000000000101041a0000017a02100197000000800020043f000000a0021002700000ffff0220018f000000a00020043f000000b001100270000000c00010043f000001c701000041000005d70001042e0000019b0020009c0000021a0000613d0000019c0020009c000005890000c13d0000000001000416000000000001004b000005890000c13d0000000101000039000001390000013d000001960020009c000002460000613d000001970020009c000005890000c13d0000018301000041000000000501041a0000000001000411000000000051004b000002ad0000c13d0000000001000414000001770010009c0000017701008041000000c00110021000000180011001c70000800d0200003900000003030000390000018404000041000000000600001905d605cc0000040f0000000100200190000005890000613d0000018301000041000000000001041b0000000001000019000005d70001042e0000018d0020009c000002570000613d0000018e0020009c000005890000c13d0000000001000416000000000001004b000005890000c13d0000000101000039000000000101041a000001a6001001980000000001000039000000010100c039000000800010043f000001a001000041000005d70001042e0000000001000416000000000001004b000005890000c13d000001ad01000041000000800010043f000001a001000041000005d70001042e0000000001000416000000000001004b000005890000c13d0000018301000041000000000101041a0000017a01100197000000800010043f000001a001000041000005d70001042e000000240030008c000005890000413d0000000401100370000000000301043b0000017a0030009c000005890000213d0000018301000041000000000101041a0000000002000411000000000012004b000002ad0000c13d0000019f010000410000000c0010043f000000000030043f0000000001000414000001770010009c0000017701008041000000c001100210000001a3011001c70000801002000039000d00000003001d05d605d10000040f0000000100200190000005890000613d000000000101043b000b00000001001d000000000101041a000c00000001001d000001a40100004100000000001004430000000001000414000001770010009c0000017701008041000000c0011002100000017c011001c70000800b0200003905d605d10000040f00000001002001900000058b0000613d000000000101043b0000000c0010006c000003470000a13d000001a501000041000000000010043f000001a201000041000005d800010430000000240030008c000005890000413d0000000401100370000000000101043b0000017a0010009c000005890000213d0000018302000041000000000202041a0000000003000411000000000023004b000002ad0000c13d000000000001004b0000034a0000c13d000001a101000041000000000010043f000001a201000041000005d800010430000000240030008c000005890000413d0000000002000416000000000002004b000005890000c13d0000000401100370000000000201043b000000000002004b0000000001000039000000010100c039000000000012004b000005890000c13d000d00000002001d05d605960000040f0000000d0000006b0000000001000019000001d50100c0410000000102000039000000000302041a000001d603300197000000000113019f000000000012041b0000000001000019000005d70001042e000000240030008c000005890000413d0000000002000416000000000002004b000005890000c13d0000000401100370000000000201043b0000017a0020009c000005890000213d0000018301000041000000000301041a0000000001000411000000000031004b000002ad0000c13d000c00000003001d000001ce01000041000000000010043f0000000001000410000000200010043f0000000001000414000001770010009c0000017701008041000000c001100210000001cf011001c7000d00000002001d05d605d10000040f00000060031002700000017703300197000000200030008c000000200400003900000000040340190000001f0540018f00000020064001900000003404600039000001be0000613d0000003407000039000000000801034f000000008908043c0000000007970436000000000047004b000001ba0000c13d000000000005004b000001cb0000613d000000000161034f0000000305500210000000000604043300000000065601cf000000000656022f000000000101043b0000010005500089000000000151022f00000000015101cf000000000161019f00000000001404350000001f0030008c00000000010000390000000101002039000000000112016f0000000100100190000003430000613d0000000c01000029000000140010043f000001d001000041000000000010043f0000000001000414000001770010009c0000017701008041000000c001100210000001d1011001c70000000d0200002905d605cc0000040f00000060031002700000017707300197000000200070008c000000200400003900000000040740190000001f0340018f0000002008400190000001ea0000613d000000000401034f0000000005000019000000004604043c0000000005650436000000000085004b000001e60000c13d000000000003004b000001f70000613d000000000181034f0000000303300210000000000408043300000000043401cf000000000434022f000000000101043b0000010003300089000000000131022f00000000013101cf000000000141019f0000000000180435000000000100043d000000010010008c00000000010000390000000101006039000000000112016f0000000100100190000002170000c13d000b00000002001d000c00000007001d000001b30100004100000000001004430000000d0100002900000004001004430000000001000414000001770010009c0000017701008041000000c001100210000001b4011001c7000080020200003905d605d10000040f00000001002001900000058b0000613d000000010200008a0000000b0220014f000000000101043b000000000001004b000000000100003900000001010060390000000c001001b0000000010220c1bf0000000100200190000003430000c13d000000340000043f0000000001000019000005d70001042e0000019f010000410000000c0010043f0000000001000411000000000010043f000001a40100004100000000001004430000000001000414000001770010009c0000017701008041000000c0011002100000017c011001c70000800b0200003905d605d10000040f00000001002001900000058b0000613d000000000101043b000d00000001001d0000000001000414000001770010009c0000017701008041000000c001100210000001a3011001c7000080100200003905d605d10000040f0000000100200190000005890000613d000000000101043b0000000d02000029000001d30220009a000000000021041b0000000001000414000001770010009c0000017701008041000000c00110021000000180011001c70000800d020000390000000203000039000001d404000041000000000500041105d605cc0000040f0000000100200190000005890000613d0000000001000019000005d70001042e000000240030008c000005890000413d0000000002000416000000000002004b000005890000c13d0000000401100370000000000101043b0000017a0010009c000005890000213d000d00000001001d05d605960000040f000000000100041a00000185011001970000000d011001af000000000010041b0000000001000019000005d70001042e000000e40030008c000005890000413d0000000002000416000000000002004b000005890000c13d0000000402100370000000000402043b0000002402100370000000000702043b0000017a0070009c000005890000213d000000a402100370000000000502043b0000008402100370000000000c02043b0000006402100370000000000b02043b0000004402100370000000000602043b000000c402100370000000000902043b000001a70090009c000005890000213d0000002302900039000000000032004b000005890000813d000000040a9000390000000001a1034f000000000201043b000001a70020009c000005890000213d00000024089000390000000001820019000000000031004b000005890000213d0000000101000039000000000101041a000001a6001001980000034d0000c13d000000000040043f0000000201000039000000200010043f0000000001000414000001770010009c0000017701008041000000c001100210000001aa011001c7000800000002001d0000801002000039000900000004001d000a00000005001d000b00000006001d000400000007001d000700000008001d000600000009001d00050000000a001d000c0000000b001d000d0000000c001d05d605d10000040f0000000100200190000005890000613d000000000101043b000000000101041a0000017a00100198000003510000c13d000001a40100004100000000001004430000000001000414000001770010009c0000017701008041000000c0011002100000017c011001c70000800b0200003905d605d10000040f00000001002001900000058b0000613d000000000101043b0000000d0010006c000003550000813d0000000c020000290000000b0020006b000003590000a13d000001c601000041000000000010043f000001a901000041000005d800010430000001cd01000041000000000010043f000001a201000041000005d800010430000800000004001d0000004003500039000000400030043f0000000103000039000a00000005001d00000000043504360000017f03000041000900000004001d0000000000340435000001770020009c000001770200804100000040022002100000000001010433000001770010009c00000177010080410000006001100210000000000121019f0000000002000414000001770020009c0000017702008041000000c002200210000000000112019f00000180011001c7000080100200003905d605d10000040f0000000100200190000005890000613d0000000902000029000001770020009c000001770200804100000040022002100000000a030000290000000003030433000001770030009c00000177030080410000006003300210000000000223019f000000000101043b000a00000001001d0000000001000414000001770010009c0000017701008041000000c001100210000000000121019f00000180011001c7000080100200003905d605d10000040f0000000100200190000005890000613d000000000101043b0000000a05000029000000c00050043f000000e00010043f000000400200043d000000800320003900000000040004100000000000430435000000600320003900000008040000290000000000430435000000400320003900000000001304350000002001200039000000000051043500000181010000410000000000120435000001770020009c000001770200804100000040012002100000000002000414000001770020009c0000017702008041000000c002200210000000000121019f00000182011001c7000080100200003905d605d10000040f0000000d060000290000000100200190000005890000613d000000000101043b000001000010043f0000018301000041000000000061041b0000000001000414000001770010009c0000017701008041000000c00110021000000180011001c70000800d0200003900000003030000390000018404000041000000000500001905d605cc0000040f0000000b0300002900000001002001900000000c02000029000005890000613d0000000104000039000000000104041a0000018501100197000000000121019f000000000014041b000000000100041a0000018501100197000000000131019f000000000010041b000000800100043d000001400000044300000160001004430000002001000039000000a00200043d0000018000100443000001a0002004430000004002000039000000c00300043d000001c000200443000001e0003004430000006002000039000000e00300043d000002000020044300000220003004430000008002000039000001000300043d000002400020044300000260003004430000010000100443000000050100003900000120001004430000018601000041000005d70001042e0000028003000039000000000200001900000080050000390000000004030019000000005305043400000000033404360000000102200039000000000012004b000003370000413d000000e00140008a000001770010009c00000177010080410000006001100210000001cb011001c7000005d70001042e000001d201000041000000000010043f000001a201000041000005d8000104300000000b01000029000000000001041b0000000d0100002905d605a00000040f0000000001000019000005d70001042e000001a801000041000000000010043f000001a901000041000005d800010430000001ab01000041000000000010043f000001a901000041000005d800010430000001ac01000041000000000010043f000001a901000041000005d800010430000000400100043d000000a0021000390000000a03000029000000000032043500000080021000390000000d03000029000000000032043500000060021000390000000c030000290000000000320435000000400210003900000009030000290000000000320435000000a0020000390000000002210436000001ad030000410000000000320435000001ae0010009c000000740000213d000000c003100039000000400030043f000001770020009c000001770200804100000040022002100000000001010433000001770010009c00000177010080410000006001100210000000000121019f0000000002000414000001770020009c0000017702008041000000c002200210000000000112019f00000180011001c7000080100200003905d605d10000040f00000001002001900000008002000039000005890000613d000000000101043b000d00000001001d000001af0100004100000000001004430000000001000412000000040010044300000024002004430000000001000414000001770010009c0000017701008041000000c001100210000001b0011001c7000080050200003905d605d10000040f00000001002001900000058b0000613d000000000101043b000c00000001001d000001af0100004100000000001004430000000001000412000000040010044300000024000004430000000001000414000001770010009c0000017701008041000000c001100210000001b0011001c7000080050200003905d605d10000040f00000001002001900000058b0000613d000000000101043b000300000001001d000001af01000041000000000010044300000000010004120000000400100443000000200100003900000024001004430000000001000414000001770010009c0000017701008041000000c001100210000001b0011001c7000080050200003905d605d10000040f00000001002001900000058b0000613d000000000101043b000200000001001d0000017b0100004100000000001004430000000001000414000001770010009c0000017701008041000000c0011002100000017c011001c70000800b0200003905d605d10000040f00000001002001900000058b0000613d0000000002000410000000000101043b000100000001001d000000030020006c000003ee0000c13d0000000202000029000000010020006b000003ee0000c13d000001b101000041000000000010043f0000000c010000290000001a0010043f0000000d010000290000003a0010043f0000000001000414000001770010009c0000017701008041000000c001100210000001b2011001c7000080100200003905d605d10000040f0000000100200190000005890000613d000000000101043b000c00000001001d0000003a0000043f000000000100041a000300000001001d000d017a0010019c0000042e0000c13d000000400100043d0000004402100039000001c3030000410000000000320435000000240210003900000011030000390000000000320435000001c4020000410000000000210435000000040210003900000020030000390000000000320435000001770010009c00000177010080410000004001100210000001c5011001c7000005d800010430000000400200043d0000018101000041000300000002001d0000000001120436000c00000001001d000001af01000041000000000010044300000000010004120000000400100443000000400100003900000024001004430000000001000414000001770010009c0000017701008041000000c001100210000001b0011001c7000080050200003905d605d10000040f00000001002001900000058b0000613d000000000101043b0000000c020000290000000000120435000001af01000041000000000010044300000000010004120000000400100443000000600100003900000024001004430000000001000414000001770010009c0000017701008041000000c001100210000001b0011001c7000080050200003905d605d10000040f00000001002001900000058b0000613d000000000101043b000000030400002900000080024000390000000003000410000000000032043500000060024000390000000103000029000000000032043500000040024000390000000000120435000001770040009c000001770400804100000040014002100000000002000414000001770020009c0000017702008041000000c002200210000000000121019f00000182011001c7000080100200003905d605d10000040f0000000100200190000005890000613d000000000101043b000c00000001001d000003c70000013d000000400100043d000200000001001d000001b30100004100000000001004430000000d0100002900000004001004430000000001000414000001770010009c0000017701008041000000c001100210000001b4011001c7000080020200003905d605d10000040f00000001002001900000058b0000613d000000000101043b000000000001004b000004540000c13d0000000801000029000000400010008c000004af0000613d0000000801000029000000410010008c000003dd0000c13d000000060100002900000064011000390000000001100367000000000101043b000000f801100270000000200010043f000000070100002900000000011003670000004002000039000000001301043c0000000002320436000000800020008c0000044f0000c13d000004bc0000013d00000002050000290000006402500039000000440150003900000024065000390000000403500039000001b50400004100000000004504350000000c0400002900000000004304350000004003000039000c00000006001d000000000036043500000008040000290000000000410435000001d7034001980000001f0440018f0000000001320019000000070500002900000000055003670000046d0000613d000000000605034f000000006706043c0000000002720436000000000012004b000004690000c13d000000000004004b0000047a0000613d000000000235034f0000000303400210000000000401043300000000043401cf000000000434022f000000000202043b0000010003300089000000000232022f00000000023201cf000000000242019f00000000002104350000000201000029000001770010009c000001770100804100000040011002100000000802000029000001b60020009c000001b6020080410000006002200210000000000112019f0000000002000414000001770020009c0000017702008041000000c002200210000000000121019f000001b70110009a0000000d0200002905d605d10000040f00000060031002700000017703300197000000200030008c00000020030080390000001f0430018f00000020053001900000000c03500029000004990000613d000000000601034f0000000c07000029000000006806043c0000000007870436000000000037004b000004950000c13d000000000004004b000004a60000613d000000000151034f0000000304400210000000000503043300000000054501cf000000000545022f000000000101043b0000010004400089000000000141022f00000000014101cf000000000151019f00000000001304350000000c010000290000000001010433000001b50010009c00000000010000390000000101006039000000000112016f0000000100100190000003dd0000613d000004ea0000013d000000050400002900000040014000390000000001100367000000000101043b000000ff031002700000001b03300039000000200030043f00000020034000390000000002300367000000000202043b000000400020043f000001b801100197000000600010043f0000000c01000029000000000010043f0000000001000414000001770010009c0000017701008041000000c001100210000001b9011001c7000000010200003905d605d10000040f000000010900003900000060031002700000017703300197000000200030008c000000200400003900000000040340190000001f0540018f000000200640019000000001046001bf000004d40000613d000000000701034f000000007807043c0000000009890436000000000049004b000004d00000c13d000000010220018f000000000005004b000004e20000613d000000000161034f0000000305500210000000000604043300000000065601cf000000000656022f000000000101043b0000010005500089000000000151022f00000000015101cf000000000161019f00000000001404350000000001020433000000600000043f0000000202000029000000400020043f000000030110014f0000006001100210000000000013004b000003dd0000a13d0000000b02000029000d000a002000bd000000000002004b000004f20000613d0000000d020000290000000b012000fa0000000a0010006b0000058c0000c13d000000400100043d000c00000001001d000001bc0010009c000000740000213d00000004010000290000017a021001970000000c030000290000006001300039000000400010043f0000000d01000029000001bd011001970000004004300039000800000004001d0000000000140435000600000002001d00000000022304360000000b010000290000ffff0110018f000700000002001d00000000001204350000000901000029000000000010043f0000000201000039000000200010043f0000000001000414000001770010009c0000017701008041000000c001100210000001aa011001c7000080100200003905d605d10000040f0000000100200190000005890000613d00000008020000290000000002020433000000b00220021000000007030000290000000003030433000000a003300210000001be03300197000000000223019f0000000c0300002900000000030304330000017a03300197000000000232019f000000000101043b000000000021041b0000000101000039000000000101041a000000400200043d000c00000002001d0000000d02000029000000600020043f0000000002000410000000400020043f000000000200041100000060022002100000002c0020043f000001bf020000410000000c0020043f00000000030004140000017a02100197000001770030009c0000017703008041000000c001300210000001c0011001c7000800000002001d05d605cc0000040f000d00000002001d00000060021002700000017702200197000000200020008c000700000002001d00000020020080390000001f0320018f0000002002200190000005450000613d000000000401034f0000000005000019000000004604043c0000000005650436000000000025004b000005410000c13d000000000003004b000005520000613d000000000121034f0000000303300210000000000402043300000000043401cf000000000434022f000000000101043b0000010003300089000000000131022f00000000013101cf000000000141019f0000000000120435000000000100043d000000010010008c000000000100003900000001010060390000000d0110017f0000000100100190000005700000c13d000001b3010000410000000000100443000000080100002900000004001004430000000001000414000001770010009c0000017701008041000000c001100210000001b4011001c7000080020200003905d605d10000040f00000001002001900000058b0000613d000000010200008a0000000d0220014f000000000101043b000000000001004b0000000001000039000000010100603900000007001001b0000000010220c1bf0000000100200190000005920000c13d000000600000043f0000000c03000029000000400030043f00000020013000390000000a0200002900000000002104350000000b010000290000000000130435000001770030009c000001770300804100000040013002100000000002000414000001770020009c0000017702008041000000c002200210000000000112019f000001aa011001c70000800d020000390000000303000039000001c2040000410000000605000029000000090600002905d605cc0000040f0000000100200190000002440000c13d0000000001000019000005d800010430000000000001042f000001ba01000041000000000010043f0000001101000039000000040010043f000001bb01000041000005d800010430000001c101000041000000000010043f000001a201000041000005d8000104300000018301000041000000000101041a0000000002000411000000000012004b0000059c0000c13d000000000001042d000001cd01000041000000000010043f000001a201000041000005d80001043000010000000000020000018302000041000000000502041a00000000020004140000017a06100197000001770020009c0000017702008041000000c00120021000000180011001c70000800d0200003900000003030000390000018404000041000100000006001d05d605cc0000040f0000000100200190000005b40000613d00000183010000410000000102000029000000000021041b000000000001042d0000000001000019000005d800010430000000000001042f000001770010009c00000177010080410000004001100210000001770020009c00000177020080410000006002200210000000000112019f0000000002000414000001770020009c0000017702008041000000c002200210000000000112019f00000180011001c7000080100200003905d605d10000040f0000000100200190000005ca0000613d000000000101043b000000000001042d0000000001000019000005d800010430000005cf002104210000000102000039000000000001042d0000000002000019000000000001042d000005d4002104230000000102000039000000000001042d0000000002000019000000000001042d000005d600000432000005d70001042e000005d80001043000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffff00000000000000000000000000000000000000000000000000000001ffffffe000000000000000000000000000000000000000000000000000000000ffffffe0000000000000000000000000ffffffffffffffffffffffffffffffffffffffff9a8a0592ac89c5ad3bc6df8224c17b485976f597df104ee20d0df415241f670b0200000200000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffbf4f6e636861696e5061796d656e7448616e646c65720000000000000000000000310000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000008b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f02000000000000000000000000000000000000a0000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739278be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0ffffffffffffffffffffffff00000000000000000000000000000000000000000000000200000000000000000000000000000180000001000000000000000000000000000000000000000000000000000000000000000000000000007eb6f6e000000000000000000000000000000000000000000000000000000000aa56cf0a00000000000000000000000000000000000000000000000000000000f04e283d00000000000000000000000000000000000000000000000000000000f04e283e00000000000000000000000000000000000000000000000000000000f2fde38b00000000000000000000000000000000000000000000000000000000fee81cf400000000000000000000000000000000000000000000000000000000aa56cf0b00000000000000000000000000000000000000000000000000000000b187bd26000000000000000000000000000000000000000000000000000000008da5cb5a000000000000000000000000000000000000000000000000000000008da5cb5b00000000000000000000000000000000000000000000000000000000a1b416f9000000000000000000000000000000000000000000000000000000007eb6f6e10000000000000000000000000000000000000000000000000000000084b0196e0000000000000000000000000000000000000000000000000000000051cff8d8000000000000000000000000000000000000000000000000000000006c19e782000000000000000000000000000000000000000000000000000000006c19e78300000000000000000000000000000000000000000000000000000000715018a60000000000000000000000000000000000000000000000000000000051cff8d90000000000000000000000000000000000000000000000000000000054d1f13d00000000000000000000000000000000000000000000000000000000256929610000000000000000000000000000000000000000000000000000000025692962000000000000000000000000000000000000000000000000000000003013ce290000000000000000000000000000000000000000000000000000000016c38b3c00000000000000000000000000000000000000000000000000000000238ac93300000000000000000000000000000000000000000000000000000000389a75e10000000000000000000000000000000000000020000000800000000000000000000000000000000000000000000000000000000000000000000000007448fbae00000000000000000000000000000000000000040000001c000000000000000002000000000000000000000000000000000000200000000c0000000000000000796b89b91644bc98cd93958e4c9038275d622183e25ac5af08cc6b5d95539132000000000000000000000000000000000000000000000000000000006f5e88180000000000000000000000ff0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffff9e87fac800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000002000000000000000000000000000000000000400000000000000000000000009788df3100000000000000000000000000000000000000000000000000000000443df96f000000000000000000000000000000000000000000000000000000005995564d962001b477987327e72c2ea2ece6e78d0f93891a33628569c90d8274000000000000000000000000000000000000000000000000ffffffffffffff3f310ab089e4439a4c15d089f94afb7896ff553aecb10793d0ab882de59d99a32e0200000200000000000000000000000000000044000000000000000000000000000000000000000000000000000000000000000000000000190100000000000002000000000000000000000000000000000000420000001800000000000000001806aa1896bbf26568e884a7374b41e002500962caba6a15023a8d90e8508b8302000002000000000000000000000000000000240000000000000000000000001626ba7e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffff9bffffffffffffffffffffffffffffffffffffff9c0000000000000000000000007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000800000000000000000000000004e487b71000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffff9f00000000000000000000000000000000000000000000ffffffffffffffffffff00000000000000000000ffff00000000000000000000000000000000000000000000000000000000000000000000000023b872dd00000000000000000000000000000000000000000000000000000000000000640000001c0000000000000000000000000000000000000000000000000000000000000000000000007939f4249b429a8fe2296e0c55b003fb026b35c55fc68cea615f6fb9d533ec800634dd4b496e76616c6964207369676e617475726500000000000000000000000000000008c379a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064000000000000000000000000d76288d70000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000008000000000000000000f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000001000000000000000000fa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c920000000000000000000000000000000000000000000000000000000082b429000000000000000000000000000000000000000000000000000000000070a0823100000000000000000000000000000000000000240000001c000000000000000000000000000000000000000000000000a9059cbb00000000000000000000000000000000000000000000000000000000000000440000001000000000000000000000000000000000000000000000000000000000000000000000000090b8ec18fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd5d00dbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d0000000000000000000000010000000000000000000000000000000000000000ffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0c0ec6c955ade696f2b6ecc0d74aec887fef8c100781c29c8521eeb58adda92df

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

0000000000000000000000003e18b7cfc3955f5edfeb03ba04d78b4c9c8050bb00000000000000000000000084a71ccd554cc1b02749b35d22f684cc8ec987e1000000000000000000000000f0b272184dab47945ec2ace865db66cd6ba20cb7

-----Decoded View---------------
Arg [0] : owner (address): 0x3e18b7CFc3955f5edfeB03Ba04D78b4c9C8050bb
Arg [1] : _paymentToken (address): 0x84A71ccD554Cc1b02749b35d22F684CC8ec987e1
Arg [2] : _signer (address): 0xF0b272184dAb47945eC2AcE865db66CD6ba20CB7

-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 0000000000000000000000003e18b7cfc3955f5edfeb03ba04d78b4c9c8050bb
Arg [1] : 00000000000000000000000084a71ccd554cc1b02749b35d22f684cc8ec987e1
Arg [2] : 000000000000000000000000f0b272184dab47945ec2ace865db66cd6ba20cb7


Block Transaction Gas Used Reward
view all blocks produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
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.