APE Price: $1.15 (-0.84%)

Contract

0x721C00D4FB075b22a5469e9CF2440697F729aA13

Overview

APE Balance

Apechain LogoApechain LogoApechain Logo0 APE

APE Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Set Transfer Sec...83245172025-01-17 16:23:1212 hrs ago1737130992IN
0x721C00D4...7F729aA13
0 APE0.0010319725.42069
Apply List To Co...83245152025-01-17 16:23:1212 hrs ago1737130992IN
0x721C00D4...7F729aA13
0 APE0.0014237625.42069
Set Transfer Sec...81457382025-01-14 6:01:593 days ago1736834519IN
0x721C00D4...7F729aA13
0 APE0.0010319725.42069
Apply List To Co...81457362025-01-14 6:01:593 days ago1736834519IN
0x721C00D4...7F729aA13
0 APE0.0014237625.42069
Add Accounts To ...81325892025-01-13 23:33:274 days ago1736811207IN
0x721C00D4...7F729aA13
0 APE0.0025162125.42069
Set Transfer Sec...81323442025-01-13 23:25:384 days ago1736810738IN
0x721C00D4...7F729aA13
0 APE0.0009636425.42069
Set Token Type O...81323192025-01-13 23:24:554 days ago1736810695IN
0x721C00D4...7F729aA13
0 APE0.0008627525.42069
Apply List To Co...81323022025-01-13 23:24:224 days ago1736810662IN
0x721C00D4...7F729aA13
0 APE0.0013554325.42069
Set Transfer Sec...81129812025-01-13 16:06:484 days ago1736784408IN
0x721C00D4...7F729aA13
0 APE0.0010319525.42069
Apply List To Co...81129792025-01-13 16:06:474 days ago1736784407IN
0x721C00D4...7F729aA13
0 APE0.0014237625.42069
Set Token Type O...80166562025-01-12 3:06:406 days ago1736651200IN
0x721C00D4...7F729aA13
0 APE0.0008644525.42069
Set Transfer Sec...80153682025-01-12 2:33:556 days ago1736649235IN
0x721C00D4...7F729aA13
0 APE0.0009653225.42069
Apply List To Co...80153372025-01-12 2:32:476 days ago1736649167IN
0x721C00D4...7F729aA13
0 APE0.0013571325.42069
Set Transfer Sec...79892972025-01-11 20:58:196 days ago1736629099IN
0x721C00D4...7F729aA13
0 APE0.0009647625.42069
Apply List To Co...79892352025-01-11 20:57:446 days ago1736629064IN
0x721C00D4...7F729aA13
0 APE0.0013565725.42069
Set Transfer Sec...78477342025-01-09 21:47:598 days ago1736459279IN
0x721C00D4...7F729aA13
0 APE0.0010319725.42069
Apply List To Co...78477332025-01-09 21:47:588 days ago1736459278IN
0x721C00D4...7F729aA13
0 APE0.0014237625.42069
Set Transfer Sec...78381062025-01-09 19:42:388 days ago1736451758IN
0x721C00D4...7F729aA13
0 APE0.0009652925.42069
Apply List To Co...78380942025-01-09 19:42:258 days ago1736451745IN
0x721C00D4...7F729aA13
0 APE0.0013570825.42069
Set Transfer Sec...78263492025-01-09 16:26:388 days ago1736439998IN
0x721C00D4...7F729aA13
0 APE0.0009652925.42069
Apply List To Co...78263322025-01-09 16:26:278 days ago1736439987IN
0x721C00D4...7F729aA13
0 APE0.0008512125.42069
Apply List To Co...78263152025-01-09 16:26:118 days ago1736439971IN
0x721C00D4...7F729aA13
0 APE0.0013570825.42069
Apply List To Co...77215882025-01-07 21:19:1410 days ago1736284754IN
0x721C00D4...7F729aA13
0 APE0.0009363425.42069
Set Transfer Sec...77215762025-01-07 21:18:5610 days ago1736284736IN
0x721C00D4...7F729aA13
0 APE0.001050425.42069
Apply List To Co...77204722025-01-07 20:56:2510 days ago1736283385IN
0x721C00D4...7F729aA13
0 APE0.0014421925.42069
View all transactions

Latest 2 internal transactions

Parent Transaction Hash Block From To
25875852024-10-29 0:39:3181 days ago1730162371
0x721C00D4...7F729aA13
 Contract Creation0 APE
25875852024-10-29 0:39:3181 days ago1730162371  Contract Creation0 APE

Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
CreatorTokenTransferValidator

Compiler Version
v0.8.24+commit.e11b9ed9

Optimization Enabled:
Yes with 777 runs

Other Settings:
cancun EvmVersion
File 1 of 31 : CreatorTokenTransferValidator.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "../Constants.sol";
import "../interfaces/IEOARegistry.sol";
import "../interfaces/ITransferValidator.sol";
import "./TransferPolicy.sol";
import {CreatorTokenTransferValidatorConfiguration} from "./CreatorTokenTransferValidatorConfiguration.sol";
import "@limitbreak/permit-c/PermitC.sol";
import "@openzeppelin/contracts/utils/introspection/ERC165.sol";
import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import "@opensea/tstorish/Tstorish.sol";

/**
 * @title  CreatorTokenTransferValidator
 * @author Limit Break, Inc.
 * @notice The CreatorTokenTransferValidator contract is designed to provide a customizable and secure transfer 
 *         validation mechanism for NFT collections. This contract allows the owner of an NFT collection to configure 
 *         the transfer security level, blacklisted accounts and codehashes, whitelisted accounts and codehashes, and
 *         authorized accounts and codehashes for each collection.
 *
 * @dev    <h4>Features</h4>
 *         - Transfer security levels: Provides different levels of transfer security, 
 *           from open transfers to completely restricted transfers.
 *         - Blacklist: Allows the owner of a collection to blacklist specific operator addresses or codehashes
 *           from executing transfers on behalf of others.
 *         - Whitelist: Allows the owner of a collection to whitelist specific operator addresses or codehashes
 *           permitted to execute transfers on behalf of others or send/receive tokens when otherwise disabled by 
 *           security policy.
 *         - Authorizers: Allows the owner of a collection to enable authorizer contracts, that can perform 
 *           authorization-based filtering of transfers.
 *
 * @dev    <h4>Benefits</h4>
 *         - Enhanced security: Allows creators to have more control over their NFT collections, ensuring the safety 
 *           and integrity of their assets.
 *         - Flexibility: Provides collection owners the ability to customize transfer rules as per their requirements.
 *         - Compliance: Facilitates compliance with regulations by enabling creators to restrict transfers based on 
 *           specific criteria.
 *
 * @dev    <h4>Intended Usage</h4>
 *         - The CreatorTokenTransferValidatorV3 contract is intended to be used by NFT collection owners to manage and 
 *           enforce transfer policies. This contract is integrated with the following varations of creator token 
 *           NFT contracts to validate transfers according to the defined security policies.
 *
 *           - ERC721-C:   Creator token implenting OpenZeppelin's ERC-721 standard.
 *           - ERC721-AC:  Creator token implenting Azuki's ERC-721A standard.
 *           - ERC721-CW:  Creator token implementing OpenZeppelin's ERC-721 standard with opt-in staking to 
 *                         wrap/upgrade a pre-existing ERC-721 collection.
 *           - ERC721-ACW: Creator token implementing Azuki's ERC721-A standard with opt-in staking to 
 *                         wrap/upgrade a pre-existing ERC-721 collection.
 *           - ERC1155-C:  Creator token implenting OpenZeppelin's ERC-1155 standard.
 *           - ERC1155-CW: Creator token implementing OpenZeppelin's ERC-1155 standard with opt-in staking to 
 *                         wrap/upgrade a pre-existing ERC-1155 collection.
 *
 *          <h4>Transfer Security Levels</h4>
 *          - Recommended: Recommended defaults are same as Level 3 (Whitelisting with OTC Enabled).
 *            - Caller Constraints: OperatorWhitelistEnableOTC
 *            - Receiver Constraints: None
 *          - Level 1: No transfer restrictions.
 *            - Caller Constraints: None
 *            - Receiver Constraints: None
 *          - Level 2: Only non-blacklisted operators can initiate transfers, over-the-counter (OTC) trading enabled.
 *            - Caller Constraints: OperatorBlacklistEnableOTC
 *            - Receiver Constraints: None
 *          - Level 3: Only whitelisted accounts can initiate transfers, over-the-counter (OTC) trading enabled.
 *            - Caller Constraints: OperatorWhitelistEnableOTC
 *            - Receiver Constraints: None
 *          - Level 4: Only whitelisted accounts can initiate transfers, over-the-counter (OTC) trading disabled.
 *            - Caller Constraints: OperatorWhitelistDisableOTC
 *            - Receiver Constraints: None
 *          - Level 5: Only whitelisted accounts can initiate transfers, over-the-counter (OTC) trading enabled. 
 *                     Transfers to contracts with code are not allowed, unless present on the whitelist.
 *            - Caller Constraints: OperatorWhitelistEnableOTC
 *            - Receiver Constraints: NoCode
 *          - Level 6: Only whitelisted accounts can initiate transfers, over-the-counter (OTC) trading enabled. 
 *                     Transfers are allowed only to Externally Owned Accounts (EOAs), unless present on the whitelist.
 *            - Caller Constraints: OperatorWhitelistEnableOTC
 *            - Receiver Constraints: EOA
 *          - Level 7: Only whitelisted accounts can initiate transfers, over-the-counter (OTC) trading disabled. 
 *                     Transfers to contracts with code are not allowed, unless present on the whitelist.
 *            - Caller Constraints: OperatorWhitelistDisableOTC
 *            - Receiver Constraints: NoCode
 *          - Level 8: Only whitelisted accounts can initiate transfers, over-the-counter (OTC) trading disabled. 
 *                     Transfers are allowed only to Externally Owned Accounts (EOAs), unless present on the whitelist.
 *            - Caller Constraints: OperatorWhitelistDisableOTC
 *            - Receiver Constraints: EOA
 */
contract CreatorTokenTransferValidator is IEOARegistry, ITransferValidator, ERC165, Tstorish, PermitC {
    using EnumerableSet for EnumerableSet.AddressSet;
    using EnumerableSet for EnumerableSet.Bytes32Set;

    /*************************************************************************/
    /*                             CUSTOM ERRORS                             */
    /*************************************************************************/

    /// @dev Thrown when attempting to set a list id that does not exist.
    error CreatorTokenTransferValidator__ListDoesNotExist();

    /// @dev Thrown when attempting to transfer the ownership of a list to the zero address.
    error CreatorTokenTransferValidator__ListOwnershipCannotBeTransferredToZeroAddress();

    /// @dev Thrown when attempting to call a function that requires the caller to be the list owner.
    error CreatorTokenTransferValidator__CallerDoesNotOwnList();

    /// @dev Thrown when validating a transfer for a collection using whitelists and the operator is not on the whitelist.
    error CreatorTokenTransferValidator__CallerMustBeWhitelisted();

    /// @dev Thrown when authorizing a transfer for a collection using authorizers and the msg.sender is not in the authorizer list.
    error CreatorTokenTransferValidator__CallerMustBeAnAuthorizer();

    /// @dev Thrown when attempting to call a function that requires owner or default admin role for a collection that the caller does not have.
    error CreatorTokenTransferValidator__CallerMustHaveElevatedPermissionsForSpecifiedNFT();

    /// @dev Thrown when constructor args are not valid
    error CreatorTokenTransferValidator__InvalidConstructorArgs();

    /// @dev Thrown when setting the transfer security level to an invalid value.
    error CreatorTokenTransferValidator__InvalidTransferSecurityLevel();

    /// @dev Thrown when validating a transfer for a collection using blacklists and the operator is on the blacklist.
    error CreatorTokenTransferValidator__OperatorIsBlacklisted();

    /// @dev Thrown when validating a transfer for a collection that does not allow receiver to have code and the receiver has code.
    error CreatorTokenTransferValidator__ReceiverMustNotHaveDeployedCode();

    /// @dev Thrown when validating a transfer for a collection that requires receivers be verified EOAs and the receiver is not verified.
    error CreatorTokenTransferValidator__ReceiverProofOfEOASignatureUnverified();

    /// @dev Thrown when a frozen account is the receiver of a transfer
    error CreatorTokenTransferValidator__ReceiverAccountIsFrozen();

    /// @dev Thrown when a frozen account is the sender of a transfer
    error CreatorTokenTransferValidator__SenderAccountIsFrozen();

    /// @dev Thrown when validating a transfer for a collection that is in soulbound token mode.
    error CreatorTokenTransferValidator__TokenIsSoulbound();

    /// @dev Thrown when an authorizer attempts to set a wildcard authorized operator on collections that don't allow wildcards
    error CreatorTokenTransferValidator__WildcardOperatorsCannotBeAuthorizedForCollection();

    /// @dev Thrown when attempting to set a authorized operator when authorization mode is disabled.
    error CreatorTokenTransferValidator__AuthorizationDisabledForCollection();

    /// @dev Thrown when attempting to validate a permitted transfer where the permit type does not match the collection-defined token type.
    error CreatorTokenTransferValidator__TokenTypesDoNotMatch();

    /*************************************************************************/
    /*                                EVENTS                                 */
    /*************************************************************************/

    /// @dev Emitted when a new list is created.
    event CreatedList(uint256 indexed id, string name);

    /// @dev Emitted when a list is applied to a collection.
    event AppliedListToCollection(address indexed collection, uint120 indexed id);

    /// @dev Emitted when the ownership of a list is transferred to a new owner.
    event ReassignedListOwnership(uint256 indexed id, address indexed newOwner);

    /// @dev Emitted when an account is added to the list of frozen accounts for a collection.
    event AccountFrozenForCollection(address indexed collection, address indexed account);

    /// @dev Emitted when an account is removed from the list of frozen accounts for a collection.
    event AccountUnfrozenForCollection(address indexed collection, address indexed account);

    /// @dev Emitted when an address is added to a list.
    event AddedAccountToList(uint8 indexed kind, uint256 indexed id, address indexed account);

    /// @dev Emitted when a codehash is added to a list.
    event AddedCodeHashToList(uint8 indexed kind, uint256 indexed id, bytes32 indexed codehash);

    /// @dev Emitted when an address is removed from a list.
    event RemovedAccountFromList(uint8 indexed kind, uint256 indexed id, address indexed account);

    /// @dev Emitted when a codehash is removed from a list.
    event RemovedCodeHashFromList(uint8 indexed kind, uint256 indexed id, bytes32 indexed codehash);

    /// @dev Emitted when the security level for a collection is updated.
    event SetTransferSecurityLevel(address indexed collection, uint8 level);

    /// @dev Emitted when a collection updates its authorization mode.
    event SetAuthorizationModeEnabled(address indexed collection, bool disabled, bool authorizersCannotSetWildcardOperators);

    /// @dev Emitted when a collection turns account freezing on or off.
    event SetAccountFreezingModeEnabled(address indexed collection, bool enabled);

    /// @dev Emitted when a collection's token type is updated.
    event SetTokenType(address indexed collection, uint16 tokenType);

    /*************************************************************************/
    /*                                STRUCTS                                */
    /*************************************************************************/

    /**
     * @dev This struct is internally for the storage of account and codehash lists.
     */
    struct List {
        EnumerableSet.AddressSet enumerableAccounts;
        EnumerableSet.Bytes32Set enumerableCodehashes;
        mapping (address => bool) nonEnumerableAccounts;
        mapping (bytes32 => bool) nonEnumerableCodehashes;
    }

    /**
     * @dev This struct is internally for the storage of account lists.
     */
    struct AccountList {
        EnumerableSet.AddressSet enumerableAccounts;
        mapping (address => bool) nonEnumerableAccounts;
    }

    /*************************************************************************/
    /*                               CONSTANTS                               */
    /*************************************************************************/

    /// @dev Immutable lookup table for constant gas determination of caller constraints by security level.
    /// @dev Created during contract construction using defined constants.
    uint256 private immutable _callerConstraintsLookup;

    /// @dev Immutable lookup table for constant gas determination of receiver constraints by security level.
    /// @dev Created during contract construction using defined constants.
    uint256 private immutable _receiverConstraintsLookup;

    /// @dev The address of the EOA Registry to use to validate an account is a verified EOA.
    address private immutable _eoaRegistry;

    /// @dev The legacy Creator Token Transfer Validator Interface
    bytes4 private constant LEGACY_TRANSFER_VALIDATOR_INTERFACE_ID = 0x00000000;

    /// @dev The default admin role value for contracts that implement access control.
    bytes32 private constant DEFAULT_ACCESS_CONTROL_ADMIN_ROLE = 0x00;
    /// @dev Value representing a zero value code hash.
    bytes32 private constant BYTES32_ZERO = 0x0000000000000000000000000000000000000000000000000000000000000000;

    address private constant WILDCARD_OPERATOR_ADDRESS = address(0x01);

    uint16 private constant DEFAULT_TOKEN_TYPE = 0;

    /*************************************************************************/
    /*                                STORAGE                                */
    /*************************************************************************/

    /// @notice Keeps track of the most recently created list id.
    uint120 public lastListId;

    /// @notice Mapping of list ids to list owners
    mapping (uint120 => address) public listOwners;

    /// @dev Mapping of collection addresses to their security policy settings
    mapping (address => CollectionSecurityPolicyV3) internal collectionSecurityPolicies;

    /// @dev Mapping of list ids to blacklist settings
    mapping (uint120 => List) internal blacklists;

    /// @dev Mapping of list ids to whitelist settings
    mapping (uint120 => List) internal whitelists;

    /// @dev Mapping of list ids to authorizers
    mapping (uint120 => List) internal authorizers;

    /// @dev Mapping of collections to accounts that are frozen for those collections
    mapping (address => AccountList) internal frozenAccounts;

    constructor(
        address defaultOwner,
        address eoaRegistry_,
        string memory name,
        string memory version,
        address validatorConfiguration
    ) 
    Tstorish()
    PermitC(
        name,
        version,
        defaultOwner,
        CreatorTokenTransferValidatorConfiguration(validatorConfiguration).getNativeValueToCheckPauseState()
    ) {
        if (defaultOwner == address(0) || eoaRegistry_ == address(0)) {
            revert CreatorTokenTransferValidator__InvalidConstructorArgs();
        }

        _createDefaultList(defaultOwner);

        _eoaRegistry = eoaRegistry_;

        _callerConstraintsLookup = _constructCallerConstraintsTable();

        _receiverConstraintsLookup = _constructReceiverConstraintsTable();
    }

    /**
     * @dev This function is only called during contract construction to create the default list.
     */
    function _createDefaultList(address defaultOwner) internal {
        uint120 id = 0;

        listOwners[id] = defaultOwner;

        emit CreatedList(id, "DEFAULT LIST");
        emit ReassignedListOwnership(id, defaultOwner);
    }

    /**
     * @dev This function is only called during contract construction to create the caller constraints
     * @dev lookup table.
     */
    function _constructCallerConstraintsTable() internal pure returns (uint256) {
        return 
        (CALLER_CONSTRAINTS_OPERATOR_WHITELIST_ENABLE_OTC << (TRANSFER_SECURITY_LEVEL_RECOMMENDED << 3))
            | (CALLER_CONSTRAINTS_NONE << (TRANSFER_SECURITY_LEVEL_ONE << 3))
            | (CALLER_CONSTRAINTS_OPERATOR_BLACKLIST_ENABLE_OTC << (TRANSFER_SECURITY_LEVEL_TWO << 3))
            | (CALLER_CONSTRAINTS_OPERATOR_WHITELIST_ENABLE_OTC << (TRANSFER_SECURITY_LEVEL_THREE << 3))
            | (CALLER_CONSTRAINTS_OPERATOR_WHITELIST_DISABLE_OTC << (TRANSFER_SECURITY_LEVEL_FOUR << 3))
            | (CALLER_CONSTRAINTS_OPERATOR_WHITELIST_ENABLE_OTC << (TRANSFER_SECURITY_LEVEL_FIVE << 3))
            | (CALLER_CONSTRAINTS_OPERATOR_WHITELIST_ENABLE_OTC << (TRANSFER_SECURITY_LEVEL_SIX << 3))
            | (CALLER_CONSTRAINTS_OPERATOR_WHITELIST_DISABLE_OTC << (TRANSFER_SECURITY_LEVEL_SEVEN << 3))
            | (CALLER_CONSTRAINTS_OPERATOR_WHITELIST_DISABLE_OTC << (TRANSFER_SECURITY_LEVEL_EIGHT << 3))
            | (CALLER_CONSTRAINTS_SBT << (TRANSFER_SECURITY_LEVEL_NINE << 3));
    }

    /**
     * @dev This function is only called during contract construction to create the receiver constraints
     * @dev lookup table.
     */
    function _constructReceiverConstraintsTable() internal pure returns (uint256) {
        return 
        (RECEIVER_CONSTRAINTS_NONE << (TRANSFER_SECURITY_LEVEL_RECOMMENDED << 3))
            | (RECEIVER_CONSTRAINTS_NONE << (TRANSFER_SECURITY_LEVEL_ONE << 3))
            | (RECEIVER_CONSTRAINTS_NONE << (TRANSFER_SECURITY_LEVEL_TWO << 3))
            | (RECEIVER_CONSTRAINTS_NONE << (TRANSFER_SECURITY_LEVEL_THREE << 3))
            | (RECEIVER_CONSTRAINTS_NONE << (TRANSFER_SECURITY_LEVEL_FOUR << 3))
            | (RECEIVER_CONSTRAINTS_NO_CODE << (TRANSFER_SECURITY_LEVEL_FIVE << 3))
            | (RECEIVER_CONSTRAINTS_EOA << (TRANSFER_SECURITY_LEVEL_SIX << 3))
            | (RECEIVER_CONSTRAINTS_NO_CODE << (TRANSFER_SECURITY_LEVEL_SEVEN << 3))
            | (RECEIVER_CONSTRAINTS_EOA << (TRANSFER_SECURITY_LEVEL_EIGHT << 3))
            | (RECEIVER_CONSTRAINTS_SBT << (TRANSFER_SECURITY_LEVEL_NINE << 3));
    }

    /*************************************************************************/
    /*                               MODIFIERS                               */
    /*************************************************************************/

    /**
     * @dev This modifier restricts a function call to the owner of the list `id`.
     * @dev Throws when the caller is not the list owner.
     */
    modifier onlyListOwner(uint120 id) {
        _requireCallerOwnsList(id);
        _;
    }

    /*************************************************************************/
    /*                          APPLY TRANSFER POLICIES                      */
    /*************************************************************************/

    /**
     * @notice Apply the collection transfer policy to a transfer operation of a creator token.
     *
     * @dev If the caller is self (Permit-C Processor) it means we have already applied operator validation in the 
     *      _beforeTransferFrom callback.  In this case, the security policy was already applied and the operator
     *      that used the Permit-C processor passed the security policy check and transfer can be safely allowed.
     *
     * @dev The order of checking whitelisted accounts, authorized operator check and whitelisted codehashes
     *      is very deliberate.  The order of operations is determined by the most frequently used settings that are
     *      expected in the wild.
     *
     * @dev Throws when the collection has enabled account freezing mode and either the `from` or `to` addresses
     *      are on the list of frozen accounts for the collection.
     * @dev Throws when the collection is set to Level 9 - Soulbound Token.
     * @dev Throws when the receiver has deployed code and isn't whitelisted, if ReceiverConstraints.NoCode is set
     *      and the transfer is not approved by an authorizer for the collection.
     * @dev Throws when the receiver has never verified a signature to prove they are an EOA and the receiver
     *      isn't whitelisted, if the ReceiverConstraints.EOA is set and the transfer is not approved by an 
     *      authorizer for the collection..
     * @dev Throws when `msg.sender` is blacklisted, if CallerConstraints.OperatorBlacklistEnableOTC is set, unless
     *      `msg.sender` is also the `from` address or the transfer is approved by an authorizer for the collection.
     * @dev Throws when `msg.sender` isn't whitelisted, if CallerConstraints.OperatorWhitelistEnableOTC is set, unless
     *      `msg.sender` is also the `from` address or the transfer is approved by an authorizer for the collection.
     * @dev Throws when neither `msg.sender` nor `from` are whitelisted, if 
     *      CallerConstraints.OperatorWhitelistDisableOTC is set and the transfer 
     *      is not approved by an authorizer for the collection.
     *
     * @dev <h4>Postconditions:</h4>
     *      1. Transfer is allowed or denied based on the applied transfer policy.
     *
     * @param caller      The address initiating the transfer.
     * @param from        The address of the token owner.
     * @param to          The address of the token receiver.
     */
    function validateTransfer(address caller, address from, address to) public view {
        (bytes4 errorSelector,) = _validateTransfer(_callerAuthorizedCheckCollection, msg.sender, caller, from, to, 0);
        if (errorSelector != SELECTOR_NO_ERROR) {
            _revertCustomErrorSelectorAsm(errorSelector);
        }
    }

    /**
     * @notice Apply the collection transfer policy to a transfer operation of a creator token.
     *
     * @dev If the caller is self (Permit-C Processor) it means we have already applied operator validation in the 
     *      _beforeTransferFrom callback.  In this case, the security policy was already applied and the operator
     *      that used the Permit-C processor passed the security policy check and transfer can be safely allowed.
     *
     * @dev The order of checking whitelisted accounts, authorized operator check and whitelisted codehashes
     *      is very deliberate.  The order of operations is determined by the most frequently used settings that are
     *      expected in the wild.
     *
     * @dev Throws when the collection has enabled account freezing mode and either the `from` or `to` addresses
     *      are on the list of frozen accounts for the collection.
     * @dev Throws when the collection is set to Level 9 - Soulbound Token.
     * @dev Throws when the receiver has deployed code and isn't whitelisted, if ReceiverConstraints.NoCode is set
     *      and the transfer is not approved by an authorizer for the collection.
     * @dev Throws when the receiver has never verified a signature to prove they are an EOA and the receiver
     *      isn't whitelisted, if the ReceiverConstraints.EOA is set and the transfer is not approved by an 
     *      authorizer for the collection..
     * @dev Throws when `msg.sender` is blacklisted, if CallerConstraints.OperatorBlacklistEnableOTC is set, unless
     *      `msg.sender` is also the `from` address or the transfer is approved by an authorizer for the collection.
     * @dev Throws when `msg.sender` isn't whitelisted, if CallerConstraints.OperatorWhitelistEnableOTC is set, unless
     *      `msg.sender` is also the `from` address or the transfer is approved by an authorizer for the collection.
     * @dev Throws when neither `msg.sender` nor `from` are whitelisted, if 
     *      CallerConstraints.OperatorWhitelistDisableOTC is set and the transfer 
     *      is not approved by an authorizer for the collection.
     *
     * @dev <h4>Postconditions:</h4>
     *      1. Transfer is allowed or denied based on the applied transfer policy.
     *
     * @param caller      The address initiating the transfer.
     * @param from        The address of the token owner.
     * @param to          The address of the token receiver.
     * @param tokenId     The token id being transferred.
     */
    function validateTransfer(address caller, address from, address to, uint256 tokenId) public view {
        (bytes4 errorSelector,) = _validateTransfer(_callerAuthorizedCheckToken, msg.sender, caller, from, to, tokenId);
        if (errorSelector != SELECTOR_NO_ERROR) {
            _revertCustomErrorSelectorAsm(errorSelector);
        }
    }

    /**
     * @notice Apply the collection transfer policy to a transfer operation of a creator token.
     *
     * @dev If the caller is self (Permit-C Processor) it means we have already applied operator validation in the 
     *      _beforeTransferFrom callback.  In this case, the security policy was already applied and the operator
     *      that used the Permit-C processor passed the security policy check and transfer can be safely allowed.
     *
     * @dev The order of checking whitelisted accounts, authorized operator check and whitelisted codehashes
     *      is very deliberate.  The order of operations is determined by the most frequently used settings that are
     *      expected in the wild.
     *
     * @dev Throws when the collection has enabled account freezing mode and either the `from` or `to` addresses
     *      are on the list of frozen accounts for the collection.
     * @dev Throws when the collection is set to Level 9 - Soulbound Token.
     * @dev Throws when the receiver has deployed code and isn't whitelisted, if ReceiverConstraints.NoCode is set
     *      and the transfer is not approved by an authorizer for the collection.
     * @dev Throws when the receiver has never verified a signature to prove they are an EOA and the receiver
     *      isn't whitelisted, if the ReceiverConstraints.EOA is set and the transfer is not approved by an 
     *      authorizer for the collection..
     * @dev Throws when `msg.sender` is blacklisted, if CallerConstraints.OperatorBlacklistEnableOTC is set, unless
     *      `msg.sender` is also the `from` address or the transfer is approved by an authorizer for the collection.
     * @dev Throws when `msg.sender` isn't whitelisted, if CallerConstraints.OperatorWhitelistEnableOTC is set, unless
     *      `msg.sender` is also the `from` address or the transfer is approved by an authorizer for the collection.
     * @dev Throws when neither `msg.sender` nor `from` are whitelisted, if 
     *      CallerConstraints.OperatorWhitelistDisableOTC is set and the transfer 
     *      is not approved by an authorizer for the collection.
     *
     * @dev <h4>Postconditions:</h4>
     *      1. Transfer is allowed or denied based on the applied transfer policy.
     *
     * @param caller      The address initiating the transfer.
     * @param from        The address of the token owner.
     * @param to          The address of the token receiver.
     * @param tokenId     The token id being transferred.
     */
    function validateTransfer(address caller, address from, address to, uint256 tokenId, uint256 /*amount*/) external {
        validateTransfer(caller, from, to, tokenId);
    }

    /**
     * @notice Apply the collection transfer policy to a transfer operation of a creator token.
     *
     * @dev If the caller is self (Permit-C Processor) it means we have already applied operator validation in the 
     *      _beforeTransferFrom callback.  In this case, the security policy was already applied and the operator
     *      that used the Permit-C processor passed the security policy check and transfer can be safely allowed.
     *
     * @dev The order of checking whitelisted accounts, authorized operator check and whitelisted codehashes
     *      is very deliberate.  The order of operations is determined by the most frequently used settings that are
     *      expected in the wild.
     *
     * @dev Throws when the collection has enabled account freezing mode and either the `from` or `to` addresses
     *      are on the list of frozen accounts for the collection.
     * @dev Throws when the collection is set to Level 9 - Soulbound Token.
     * @dev Throws when the receiver has deployed code and isn't whitelisted, if ReceiverConstraints.NoCode is set
     *      and the transfer is not approved by an authorizer for the collection.
     * @dev Throws when the receiver has never verified a signature to prove they are an EOA and the receiver
     *      isn't whitelisted, if the ReceiverConstraints.EOA is set and the transfer is not approved by an 
     *      authorizer for the collection..
     * @dev Throws when `msg.sender` is blacklisted, if CallerConstraints.OperatorBlacklistEnableOTC is set, unless
     *      `msg.sender` is also the `from` address or the transfer is approved by an authorizer for the collection.
     * @dev Throws when `msg.sender` isn't whitelisted, if CallerConstraints.OperatorWhitelistEnableOTC is set, unless
     *      `msg.sender` is also the `from` address or the transfer is approved by an authorizer for the collection.
     * @dev Throws when neither `msg.sender` nor `from` are whitelisted, if 
     *      CallerConstraints.OperatorWhitelistDisableOTC is set and the transfer 
     *      is not approved by an authorizer for the collection.
     *
     * @dev <h4>Postconditions:</h4>
     *      1. Transfer is allowed or denied based on the applied transfer policy.
     *
     * @param caller      The address initiating the transfer.
     * @param from        The address of the token owner.
     * @param to          The address of the token receiver.
     */
    function applyCollectionTransferPolicy(address caller, address from, address to) external view {
        validateTransfer(caller, from, to);
    }

    /**
     * @notice Returns the caller and receiver constraints for the specified transfer security level.
     * 
     * @param level The transfer security level to return the caller and receiver constraints for.
     * 
     * @return callerConstraints    The `CallerConstraints` value for the level.
     * @return receiverConstraints The `ReceiverConstraints` value for the level.
     */
    function transferSecurityPolicies(
        uint256 level
    ) public view returns (uint256 callerConstraints, uint256 receiverConstraints) {
        callerConstraints = uint8((_callerConstraintsLookup >> (level << 3)));
        receiverConstraints = uint8((_receiverConstraintsLookup >> (level << 3)));
    }

    /**
     * @notice Sets an operator for an authorized transfer that skips transfer security level
     *         validation for caller and receiver constraints.
     * 
     * @dev    An authorizer *MUST* clear the authorization with a call to `afterAuthorizedTransfer`
     *         to prevent unauthorized transfers of the token.
     * 
     * @dev    Throws when authorization mode is disabled for the collection.
     * @dev    Throws when using the wildcard operator address and the collection does not allow
     *         for wildcard authorized operators.
     * @dev    Throws when the caller is not an allowed authorizer for the collection.
     *
     * @dev <h4>Postconditions:</h4>
     *      1. The `operator` is stored as an authorized operator for transfers.
     * 
     * @param operator  The address of the operator to set as authorized for transfers.
     * @param token     The address of the token to authorize.
     * @param tokenId   The token id to set the authorized operator for.
     */
    function beforeAuthorizedTransfer(address operator, address token, uint256 tokenId) external {
        _setOperatorInTransientStorage(operator, token, tokenId, false);
    }

    /**
     * @notice Clears the authorized operator for a token to prevent additional transfers that
     *         do not conform to the transfer security level for the token.
     * 
     * @dev    Throws when authorization mode is disabled for the collection.
     * @dev    Throws when using the wildcard operator address and the collection does not allow
     *         for wildcard authorized operators.
     * @dev    Throws when the caller is not an allowed authorizer for the collection.
     *
     * @dev <h4>Postconditions:</h4>
     *      1. The authorized operator for the token is cleared from storage.
     * 
     * @param token     The address of the token to authorize.
     * @param tokenId   The token id to set the authorized operator for.
     */
    function afterAuthorizedTransfer(address token, uint256 tokenId) public {
        _setOperatorInTransientStorage(address(uint160(uint256(BYTES32_ZERO))), token, tokenId, false);
    }

    /**
     * @notice Sets an operator for an authorized transfer that skips transfer security level
     *         validation for caller and receiver constraints.
     * @notice This overload of `beforeAuthorizedTransfer` defaults to a tokenId of 0.
     * 
     * @dev    An authorizer *MUST* clear the authorization with a call to `afterAuthorizedTransfer`
     *         to prevent unauthorized transfers of the token.
     * 
     * @dev    Throws when authorization mode is disabled for the collection.
     * @dev    Throws when using the wildcard operator address and the collection does not allow
     *         for wildcard authorized operators.
     * @dev    Throws when the caller is not an allowed authorizer for the collection.
     *
     * @dev <h4>Postconditions:</h4>
     *      1. The `operator` is stored as an authorized operator for transfers.
     * 
     * @param operator  The address of the operator to set as authorized for transfers.
     * @param token     The address of the token to authorize.
     */
    function beforeAuthorizedTransfer(address operator, address token) external {
        _setOperatorInTransientStorage(operator, token, 0, true);
    }

    /**
     * @notice Clears the authorized operator for a token to prevent additional transfers that
     *         do not conform to the transfer security level for the token.
     * @notice This overload of `afterAuthorizedTransfer` defaults to a tokenId of 0.
     * 
     * @dev    Throws when authorization mode is disabled for the collection.
     * @dev    Throws when using the wildcard operator address and the collection does not allow
     *         for wildcard authorized operators.
     * @dev    Throws when the caller is not an allowed authorizer for the collection.
     *
     * @dev <h4>Postconditions:</h4>
     *      1. The authorized operator for the token is cleared from storage.
     * 
     * @param token     The address of the token to authorize.
     */
    function afterAuthorizedTransfer(address token) external {
        afterAuthorizedTransfer(token, 0);
    }

    /**
     * @notice Sets the wildcard operator to authorize any operator to transfer a token while
     *         skipping transfer security level validation for caller and receiver constraints.
     * 
     * @dev    An authorizer *MUST* clear the authorization with a call to `afterAuthorizedTransfer`
     *         to prevent unauthorized transfers of the token.
     * 
     * @dev    Throws when authorization mode is disabled for the collection.
     * @dev    Throws when the collection does not allow for wildcard authorized operators.
     * @dev    Throws when the caller is not an allowed authorizer for the collection.
     *
     * @dev <h4>Postconditions:</h4>
     *      1. The wildcard operator is stored as an authorized operator for transfers.
     * 
     * @param token     The address of the token to authorize.
     * @param tokenId   The token id to set the authorized operator for.
     */
    function beforeAuthorizedTransfer(address token, uint256 tokenId) external {
        _setOperatorInTransientStorage(WILDCARD_OPERATOR_ADDRESS, token, tokenId, false);
    }

    /**
     * @notice Sets the wildcard operator to authorize any operator to transfer a token while
     *         skipping transfer security level validation for caller and receiver constraints.
     * 
     * @dev    An authorizer *MUST* clear the authorization with a call to `afterAuthorizedTransfer`
     *         to prevent unauthorized transfers of the token.
     * 
     * @dev    Throws when authorization mode is disabled for the collection.
     * @dev    Throws when the collection does not allow for wildcard authorized operators.
     * @dev    Throws when the caller is not an allowed authorizer for the collection.
     *
     * @dev <h4>Postconditions:</h4>
     *      1. The wildcard operator is stored as an authorized operator for transfers.
     * 
     * @param token     The address of the token to authorize.
     * @param tokenId   The token id to set the authorized operator for.
     */
    function beforeAuthorizedTransferWithAmount(address token, uint256 tokenId, uint256 /*amount*/) external {
        _setOperatorInTransientStorage(WILDCARD_OPERATOR_ADDRESS, token, tokenId, false);
    }

    /**
     * @notice Clears the authorized operator for a token to prevent additional transfers that
     *         do not conform to the transfer security level for the token.
     * 
     * @dev    Throws when authorization mode is disabled for the collection.
     * @dev    Throws when using the wildcard operator address and the collection does not allow
     *         for wildcard authorized operators.
     * @dev    Throws when the caller is not an allowed authorizer for the collection.
     *
     * @dev <h4>Postconditions:</h4>
     *      1. The authorized operator for the token is cleared from storage.
     * 
     * @param token     The address of the token to authorize.
     * @param tokenId   The token id to set the authorized operator for.
     */
    function afterAuthorizedTransferWithAmount(address token, uint256 tokenId) external {
        afterAuthorizedTransfer(token, tokenId);
    }

    /*************************************************************************/
    /*                              LIST MANAGEMENT                          */
    /*************************************************************************/

    /**
     * @notice Creates a new list id.  The list id is a handle to allow editing of blacklisted and whitelisted accounts
     *         and codehashes.
     *
     * @dev <h4>Postconditions:</h4>
     *      1. A new list with the specified name is created.
     *      2. The caller is set as the owner of the new list.
     *      3. A `CreatedList` event is emitted.
     *      4. A `ReassignedListOwnership` event is emitted.
     *
     * @param  name The name of the new list.
     * @return id   The id of the new list.
     */
    function createList(string calldata name) public returns (uint120 id) {
        unchecked {
            id = ++lastListId;
        }

        listOwners[id] = msg.sender;

        emit CreatedList(id, name);
        emit ReassignedListOwnership(id, msg.sender);
    }

    /**
     * @notice Creates a new list id, and copies all blacklisted and whitelisted accounts and codehashes from the
     *         specified source list.
     *
     * @dev    <h4>Postconditions:</h4>
     *         1. A new list with the specified name is created.
     *         2. The caller is set as the owner of the new list.
     *         3. A `CreatedList` event is emitted.
     *         4. A `ReassignedListOwnership` event is emitted.
     *         5. All blacklisted and whitelisted accounts and codehashes from the specified source list are copied
     *            to the new list.
     *         6. An `AddedAccountToList` event is emitted for each blacklisted and whitelisted account copied.
     *         7. An `AddedCodeHashToList` event is emitted for each blacklisted and whitelisted codehash copied.
     *
     * @param  name         The name of the new list.
     * @param  sourceListId The id of the source list to copy from.
     * @return id           The id of the new list.
     */
    function createListCopy(string calldata name, uint120 sourceListId) external returns (uint120 id) {
        unchecked {
            id = ++lastListId;
        }

        unchecked {
            if (sourceListId > id - 1) {
                revert CreatorTokenTransferValidator__ListDoesNotExist();
            }
        }

        listOwners[id] = msg.sender;

        emit CreatedList(id, name);
        emit ReassignedListOwnership(id, msg.sender);

        List storage sourceBlacklist = blacklists[sourceListId];
        List storage sourceWhitelist = whitelists[sourceListId];
        List storage sourceAuthorizers = authorizers[sourceListId];
        List storage targetBlacklist = blacklists[id];
        List storage targetWhitelist = whitelists[id];
        List storage targetAuthorizers = authorizers[id];

        _copyAddressSet(LIST_TYPE_BLACKLIST, id, sourceBlacklist, targetBlacklist);
        _copyBytes32Set(LIST_TYPE_BLACKLIST, id, sourceBlacklist, targetBlacklist);
        _copyAddressSet(LIST_TYPE_WHITELIST, id, sourceWhitelist, targetWhitelist);
        _copyBytes32Set(LIST_TYPE_WHITELIST, id, sourceWhitelist, targetWhitelist);
        _copyAddressSet(LIST_TYPE_AUTHORIZERS, id, sourceAuthorizers, targetAuthorizers);
        _copyBytes32Set(LIST_TYPE_AUTHORIZERS, id, sourceAuthorizers, targetAuthorizers);
    }

    /**
     * @notice Transfer ownership of a list to a new owner.
     *
     * @dev Throws when the new owner is the zero address.
     * @dev Throws when the caller does not own the specified list.
     *
     * @dev <h4>Postconditions:</h4>
     *      1. The list ownership is transferred to the new owner.
     *      2. A `ReassignedListOwnership` event is emitted.
     *
     * @param id       The id of the list.
     * @param newOwner The address of the new owner.
     */
    function reassignOwnershipOfList(uint120 id, address newOwner) public {
        if(newOwner == address(0)) {
            revert CreatorTokenTransferValidator__ListOwnershipCannotBeTransferredToZeroAddress();
        }

        _reassignOwnershipOfList(id, newOwner);
    }

    /**
     * @notice Renounce the ownership of a list, rendering the list immutable.
     *
     * @dev Throws when the caller does not own the specified list.
     *
     * @dev <h4>Postconditions:</h4>
     *      1. The ownership of the specified list is renounced.
     *      2. A `ReassignedListOwnership` event is emitted.
     *
     * @param id The id of the list.
     */
    function renounceOwnershipOfList(uint120 id) public {
        _reassignOwnershipOfList(id, address(0));
    }

    /**
     * @notice Set the transfer security level, authorization mode and account freezing mode settings of a collection.
     *
     * @dev Throws when the caller is neither collection contract, nor the owner or admin of the specified collection.
     *
     * @dev <h4>Postconditions:</h4>
     *      1. The transfer security level of the specified collection is set to the new value.
     *      2. The authorization mode setting of the specified collection is set to the new value.
     *      3. The authorization wildcard operator mode setting of the specified collection is set to the new value.
     *      4. The account freezing mode setting of the specified collection is set to the new value.
     *      5. A `SetTransferSecurityLevel` event is emitted.
     *      6. A `SetAuthorizationModeEnabled` event is emitted.
     *      7. A `SetAccountFreezingModeEnabled` event is emitted.
     *
     * @param collection                 The address of the collection.
     * @param level                      The new transfer security level to apply.
     * @param disableAuthorizationMode   Flag if the collection allows for authorizer mode.
     * @param disableWildcardOperators   Flag if the authorizer can set wildcard operators.
     * @param enableAccountFreezingMode  Flag if the collection is using account freezing.
     */
    function setTransferSecurityLevelOfCollection(
        address collection, 
        uint8 level,
        bool disableAuthorizationMode,
        bool disableWildcardOperators,
        bool enableAccountFreezingMode) external {

        if (level > TRANSFER_SECURITY_LEVEL_NINE) {
            revert CreatorTokenTransferValidator__InvalidTransferSecurityLevel();
        }

        _requireCallerIsNFTOrContractOwnerOrAdmin(collection);
        collectionSecurityPolicies[collection].transferSecurityLevel = level;
        collectionSecurityPolicies[collection].disableAuthorizationMode = disableAuthorizationMode;
        collectionSecurityPolicies[collection].authorizersCannotSetWildcardOperators = disableWildcardOperators;
        collectionSecurityPolicies[collection].enableAccountFreezingMode = enableAccountFreezingMode;
        emit SetTransferSecurityLevel(collection, level);
        emit SetAuthorizationModeEnabled(collection, disableAuthorizationMode, disableWildcardOperators);
        emit SetAccountFreezingModeEnabled(collection, enableAccountFreezingMode);
    }

    /**
     * @notice Set the token type setting of a collection.
     *
     * @dev Throws when the caller is neither collection contract, nor the owner or admin of the specified collection.
     *
     * @dev <h4>Postconditions:</h4>
     *      1. The token type of the specified collection is set to the new value.
     *      2. A `SetTokenType` event is emitted.
     *
     * @param collection  The address of the collection.
     * @param tokenType   The new transfer security level to apply.
     */
    function setTokenTypeOfCollection(
        address collection, 
        uint16 tokenType
    ) external {
        _requireCallerIsNFTOrContractOwnerOrAdmin(collection);
        collectionSecurityPolicies[collection].tokenType = tokenType;
        emit SetTokenType(collection, tokenType);
    }

    /**
     * @notice Applies the specified list to a collection.
     * 
     * @dev Throws when the caller is neither collection contract, nor the owner or admin of the specified collection.
     * @dev Throws when the specified list id does not exist.
     *
     * @dev <h4>Postconditions:</h4>
     *      1. The list of the specified collection is set to the new value.
     *      2. An `AppliedListToCollection` event is emitted.
     *
     * @param collection The address of the collection.
     * @param id         The id of the operator whitelist.
     */
    function applyListToCollection(address collection, uint120 id) public {
        _requireCallerIsNFTOrContractOwnerOrAdmin(collection);

        if (id > lastListId) {
            revert CreatorTokenTransferValidator__ListDoesNotExist();
        }

        collectionSecurityPolicies[collection].listId = id;
        emit AppliedListToCollection(collection, id);
    }

    /**
     * @notice Adds accounts to the frozen accounts list of a collection.
     * 
     * @dev Throws when the caller is neither collection contract, nor the owner or admin of the specified collection.
     *
     * @dev <h4>Postconditions:</h4>
     *      1. The accounts are added to the list of frozen accounts for a collection.
     *      2. A `AccountFrozenForCollection` event is emitted for each account added to the list.
     *
     * @param collection        The address of the collection.
     * @param accountsToFreeze  The list of accounts to added to frozen accounts.
     */
    function freezeAccountsForCollection(address collection, address[] calldata accountsToFreeze) external {
        _requireCallerIsNFTOrContractOwnerOrAdmin(collection);

        AccountList storage accounts = frozenAccounts[collection];

        for (uint256 i = 0; i < accountsToFreeze.length;) {
            address accountToFreeze = accountsToFreeze[i];

            if (accounts.enumerableAccounts.add(accountToFreeze)) {
                emit AccountFrozenForCollection(collection, accountToFreeze);
                accounts.nonEnumerableAccounts[accountToFreeze] = true;
            }

            unchecked {
                ++i;
            }
        }
    }

    /**
     * @notice Removes accounts to the frozen accounts list of a collection.
     * 
     * @dev Throws when the caller is neither collection contract, nor the owner or admin of the specified collection.
     *
     * @dev <h4>Postconditions:</h4>
     *      1. The accounts are removed from the list of frozen accounts for a collection.
     *      2. A `AccountUnfrozenForCollection` event is emitted for each account removed from the list.
     *
     * @param collection          The address of the collection.
     * @param accountsToUnfreeze  The list of accounts to remove from frozen accounts.
     */
    function unfreezeAccountsForCollection(address collection, address[] calldata accountsToUnfreeze) external {
        _requireCallerIsNFTOrContractOwnerOrAdmin(collection);

        AccountList storage accounts = frozenAccounts[collection];

        for (uint256 i = 0; i < accountsToUnfreeze.length;) {
            address accountToUnfreeze = accountsToUnfreeze[i];

            if (accounts.enumerableAccounts.remove(accountToUnfreeze)) {
                emit AccountUnfrozenForCollection(collection, accountToUnfreeze);
                accounts.nonEnumerableAccounts[accountToUnfreeze] = false;
            }

            unchecked {
                ++i;
            }
        }
    }

    /**
     * @notice Get the security policy of the specified collection.
     * @param collection The address of the collection.
     * @return           The security policy of the specified collection, which includes:
     *                   Transfer security level, operator whitelist id, permitted contract receiver allowlist id,
     *                   authorizer mode, if authorizer can set a wildcard operator, and if account freezing is
     *                   enabled.
     */
    function getCollectionSecurityPolicy(
        address collection
    ) external view returns (CollectionSecurityPolicyV3 memory) {
        return collectionSecurityPolicies[collection];
    }

    /**
     * @notice Adds one or more accounts to a blacklist.
     *
     * @dev Throws when the caller does not own the specified list.
     * @dev Throws when the accounts array is empty.
     *
     * @dev <h4>Postconditions:</h4>
     *      1. Accounts not previously in the list are added.
     *      2. An `AddedAccountToList` event is emitted for each account that is newly added to the list.
     *
     * @param id       The id of the list.
     * @param accounts The addresses of the accounts to add.
     */
    function addAccountsToBlacklist(
        uint120 id, 
        address[] calldata accounts
    ) external {
        _addAccountsToList(blacklists[id], LIST_TYPE_BLACKLIST, id, accounts);
    }

    /**
     * @notice Adds one or more accounts to a whitelist.
     *
     * @dev Throws when the caller does not own the specified list.
     * @dev Throws when the accounts array is empty.
     *
     * @dev <h4>Postconditions:</h4>
     *      1. Accounts not previously in the list are added.
     *      2. An `AddedAccountToList` event is emitted for each account that is newly added to the list.
     *
     * @param id       The id of the list.
     * @param accounts The addresses of the accounts to add.
     */
    function addAccountsToWhitelist(
        uint120 id, 
        address[] calldata accounts
    ) external {
        _addAccountsToList(whitelists[id], LIST_TYPE_WHITELIST, id, accounts);
    }

    /**
     * @notice Adds one or more accounts to authorizers.
     *
     * @dev Throws when the caller does not own the specified list.
     * @dev Throws when the accounts array is empty.
     *
     * @dev <h4>Postconditions:</h4>
     *      1. Accounts not previously in the list are added.
     *      2. An `AddedAccountToList` event is emitted for each account that is newly added to the list.
     *
     * @param id       The id of the list.
     * @param accounts The addresses of the accounts to add.
     */
    function addAccountsToAuthorizers(
        uint120 id, 
        address[] calldata accounts
    ) external {
        _addAccountsToList(authorizers[id], LIST_TYPE_AUTHORIZERS, id, accounts);
    }

    /**
     * @notice Adds one or more codehashes to a blacklist.
     *
     * @dev Throws when the caller does not own the specified list.
     * @dev Throws when the codehashes array is empty.
     * @dev Throws when a codehash is zero.
     *
     * @dev <h4>Postconditions:</h4>
     *      1. Codehashes not previously in the list are added.
     *      2. An `AddedCodeHashToList` event is emitted for each codehash that is newly added to the list.
     *
     * @param id         The id of the list.
     * @param codehashes The codehashes to add.
     */
    function addCodeHashesToBlacklist(
        uint120 id, 
        bytes32[] calldata codehashes
    ) external {
        _addCodeHashesToList(blacklists[id], LIST_TYPE_BLACKLIST, id, codehashes);
    }

    /**
     * @notice Adds one or more codehashes to a whitelist.
     *
     * @dev Throws when the caller does not own the specified list.
     * @dev Throws when the codehashes array is empty.
     * @dev Throws when a codehash is zero.
     *
     * @dev <h4>Postconditions:</h4>
     *      1. Codehashes not previously in the list are added.
     *      2. An `AddedCodeHashToList` event is emitted for each codehash that is newly added to the list.
     *
     * @param id         The id of the list.
     * @param codehashes The codehashes to add.
     */
    function addCodeHashesToWhitelist(
        uint120 id, 
        bytes32[] calldata codehashes
    ) external {
        _addCodeHashesToList(whitelists[id], LIST_TYPE_WHITELIST, id, codehashes);
    }

    /**
     * @notice Removes one or more accounts from a blacklist.
     *
     * @dev Throws when the caller does not own the specified list.
     * @dev Throws when the accounts array is empty.
     *
     * @dev <h4>Postconditions:</h4>
     *      1. Accounts previously in the list are removed.
     *      2. A `RemovedAccountFromList` event is emitted for each account that is removed from the list.
     *
     * @param id       The id of the list.
     * @param accounts The addresses of the accounts to remove.
     */
    function removeAccountsFromBlacklist(
        uint120 id, 
        address[] calldata accounts
    ) external {
        _removeAccountsFromList(blacklists[id], LIST_TYPE_BLACKLIST, id, accounts);
    }

    /**
     * @notice Removes one or more accounts from a whitelist.
     *
     * @dev Throws when the caller does not own the specified list.
     * @dev Throws when the accounts array is empty.
     *
     * @dev <h4>Postconditions:</h4>
     *      1. Accounts previously in the list are removed.
     *      2. A `RemovedAccountFromList` event is emitted for each account that is removed from the list.
     *
     * @param id       The id of the list.
     * @param accounts The addresses of the accounts to remove.
     */
    function removeAccountsFromWhitelist(
        uint120 id, 
        address[] calldata accounts
    ) external {
        _removeAccountsFromList(whitelists[id], LIST_TYPE_WHITELIST, id, accounts);
    }

    /**
     * @notice Removes one or more accounts from authorizers.
     *
     * @dev Throws when the caller does not own the specified list.
     * @dev Throws when the accounts array is empty.
     *
     * @dev <h4>Postconditions:</h4>
     *      1. Accounts previously in the list are removed.
     *      2. A `RemovedAccountFromList` event is emitted for each account that is removed from the list.
     *
     * @param id       The id of the list.
     * @param accounts The addresses of the accounts to remove.
     */
    function removeAccountsFromAuthorizers(
        uint120 id, 
        address[] calldata accounts
    ) external {
        _removeAccountsFromList(authorizers[id], LIST_TYPE_AUTHORIZERS, id, accounts);
    }

    /**
     * @notice Removes one or more codehashes from a blacklist.
     *
     * @dev Throws when the caller does not own the specified list.
     * @dev Throws when the codehashes array is empty.
     *
     * @dev <h4>Postconditions:</h4>
     *      1. Codehashes previously in the list are removed.
     *      2. A `RemovedCodeHashFromList` event is emitted for each codehash that is removed from the list.
     *
     * @param id         The id of the list.
     * @param codehashes The codehashes to remove.
     */
    function removeCodeHashesFromBlacklist(
        uint120 id, 
        bytes32[] calldata codehashes
    ) external {
        _removeCodeHashesFromList(blacklists[id], LIST_TYPE_BLACKLIST, id, codehashes);
    }

    /**
     * @notice Removes one or more codehashes from a whitelist.
     *
     * @dev Throws when the caller does not own the specified list.
     * @dev Throws when the codehashes array is empty.
     *
     * @dev <h4>Postconditions:</h4>
     *      1. Codehashes previously in the list are removed.
     *      2. A `RemovedCodeHashFromList` event is emitted for each codehash that is removed from the list.
     *
     * @param id         The id of the list.
     * @param codehashes The codehashes to remove.
     */
    function removeCodeHashesFromWhitelist(
        uint120 id, 
        bytes32[] calldata codehashes
    ) external {
        _removeCodeHashesFromList(whitelists[id], LIST_TYPE_WHITELIST, id, codehashes);
    }

    /**
     * @notice Get blacklisted accounts by list id.
     * @param  id The id of the list.
     * @return An array of blacklisted accounts.
     */
    function getBlacklistedAccounts(uint120 id) public view returns (address[] memory) {
        return blacklists[id].enumerableAccounts.values();
    }

    /**
     * @notice Get whitelisted accounts by list id.
     * @param  id The id of the list.
     * @return An array of whitelisted accounts.
     */
    function getWhitelistedAccounts(uint120 id) public view returns (address[] memory) {
        return whitelists[id].enumerableAccounts.values();
    }

    /**
     * @notice Get authorizor accounts by list id.
     * @param  id The id of the list.
     * @return An array of authorizer accounts.
     */
    function getAuthorizerAccounts(uint120 id) public view returns (address[] memory) {
        return authorizers[id].enumerableAccounts.values();
    }

    /**
     * @notice Get blacklisted codehashes by list id.
     * @param id The id of the list.
     * @return   An array of blacklisted codehashes.
     */
    function getBlacklistedCodeHashes(uint120 id) public view returns (bytes32[] memory) {
        return blacklists[id].enumerableCodehashes.values();
    }

    /**
     * @notice Get whitelisted codehashes by list id.
     * @param id The id of the list.
     * @return   An array of whitelisted codehashes.
     */
    function getWhitelistedCodeHashes(uint120 id) public view returns (bytes32[] memory) {
        return whitelists[id].enumerableCodehashes.values();
    }

    /**
     * @notice Check if an account is blacklisted in a specified list.
     * @param id       The id of the list.
     * @param account  The address of the account to check.
     * @return         True if the account is blacklisted in the specified list, false otherwise.
     */
    function isAccountBlacklisted(uint120 id, address account) public view returns (bool) {
        return blacklists[id].nonEnumerableAccounts[account];
    }

    /**
     * @notice Check if an account is whitelisted in a specified list.
     * @param id       The id of the list.
     * @param account  The address of the account to check.
     * @return         True if the account is whitelisted in the specified list, false otherwise.
     */
    function isAccountWhitelisted(uint120 id, address account) public view returns (bool) {
        return whitelists[id].nonEnumerableAccounts[account];
    }

    /**
     * @notice Check if an account is an authorizer in a specified list.
     * @param id       The id of the list.
     * @param account  The address of the account to check.
     * @return         True if the account is an authorizer in the specified list, false otherwise.
     */
    function isAccountAuthorizer(uint120 id, address account) public view returns (bool) {
        return authorizers[id].nonEnumerableAccounts[account];
    }

    /**
     * @notice Check if a codehash is blacklisted in a specified list.
     * @param id       The id of the list.
     * @param codehash  The codehash to check.
     * @return         True if the codehash is blacklisted in the specified list, false otherwise.
     */
    function isCodeHashBlacklisted(uint120 id, bytes32 codehash) public view returns (bool) {
        return blacklists[id].nonEnumerableCodehashes[codehash];
    }

    /**
     * @notice Check if a codehash is whitelisted in a specified list.
     * @param id       The id of the list.
     * @param codehash  The codehash to check.
     * @return         True if the codehash is whitelisted in the specified list, false otherwise.
     */
    function isCodeHashWhitelisted(uint120 id, bytes32 codehash) public view returns (bool) {
        return whitelists[id].nonEnumerableCodehashes[codehash];
    }

    /**
     * @notice Get blacklisted accounts by collection.
     * @param collection The address of the collection.
     * @return           An array of blacklisted accounts.
     */
    function getBlacklistedAccountsByCollection(address collection) external view returns (address[] memory) {
        return getBlacklistedAccounts(collectionSecurityPolicies[collection].listId);
    }

    /**
     * @notice Get whitelisted accounts by collection.
     * @param collection The address of the collection.
     * @return           An array of whitelisted accounts.
     */
    function getWhitelistedAccountsByCollection(address collection) external view returns (address[] memory) {
        return getWhitelistedAccounts(collectionSecurityPolicies[collection].listId);
    }

    /**
     * @notice Get authorizer accounts by collection.
     * @param collection The address of the collection.
     * @return           An array of authorizer accounts.
     */
    function getAuthorizerAccountsByCollection(address collection) external view returns (address[] memory) {
        return getAuthorizerAccounts(collectionSecurityPolicies[collection].listId);
    }

    /**
     * @notice Get frozen accounts by collection.
     * @param collection The address of the collection.
     * @return           An array of frozen accounts.
     */
    function getFrozenAccountsByCollection(address collection) external view returns (address[] memory) {
        return frozenAccounts[collection].enumerableAccounts.values();
    }

    /**
     * @notice Get blacklisted codehashes by collection.
     * @param collection The address of the collection.
     * @return           An array of blacklisted codehashes.
     */
    function getBlacklistedCodeHashesByCollection(address collection) external view returns (bytes32[] memory) {
        return getBlacklistedCodeHashes(collectionSecurityPolicies[collection].listId);
    }

    /**
     * @notice Get whitelisted codehashes by collection.
     * @param collection The address of the collection.
     * @return           An array of whitelisted codehashes.
     */
    function getWhitelistedCodeHashesByCollection(address collection) external view returns (bytes32[] memory) {
        return getWhitelistedCodeHashes(collectionSecurityPolicies[collection].listId);
    }

    /**
     * @notice Check if an account is blacklisted by a specified collection.
     * @param collection The address of the collection.
     * @param account    The address of the account to check.
     * @return           True if the account is blacklisted by the specified collection, false otherwise.
     */
    function isAccountBlacklistedByCollection(address collection, address account) external view returns (bool) {
        return isAccountBlacklisted(collectionSecurityPolicies[collection].listId, account);
    }

    /**
     * @notice Check if an account is whitelisted by a specified collection.
     * @param collection The address of the collection.
     * @param account    The address of the account to check.
     * @return           True if the account is whitelisted by the specified collection, false otherwise.
     */
    function isAccountWhitelistedByCollection(address collection, address account) external view returns (bool) {
        return isAccountWhitelisted(collectionSecurityPolicies[collection].listId, account);
    }

    /**
     * @notice Check if an account is an authorizer of a specified collection.
     * @param collection The address of the collection.
     * @param account    The address of the account to check.
     * @return           True if the account is an authorizer by the specified collection, false otherwise.
     */
    function isAccountAuthorizerOfCollection(address collection, address account) external view returns (bool) {
        return isAccountAuthorizer(collectionSecurityPolicies[collection].listId, account);
    }

    /**
     * @notice Check if an account is frozen for a specified collection.
     * @param collection The address of the collection.
     * @param account    The address of the account to check.
     * @return           True if the account is frozen by the specified collection, false otherwise.
     */
    function isAccountFrozenForCollection(address collection, address account) external view returns (bool) {
        return frozenAccounts[collection].nonEnumerableAccounts[account];
    }

    /**
     * @notice Check if a codehash is blacklisted by a specified collection.
     * @param collection The address of the collection.
     * @param codehash   The codehash to check.
     * @return           True if the codehash is blacklisted by the specified collection, false otherwise.
     */
    function isCodeHashBlacklistedByCollection(address collection, bytes32 codehash) external view returns (bool) {
        return isCodeHashBlacklisted(collectionSecurityPolicies[collection].listId, codehash);
    }

    /**
     * @notice Check if a codehash is whitelisted by a specified collection.
     * @param collection The address of the collection.
     * @param codehash   The codehash to check.
     * @return           True if the codehash is whitelisted by the specified collection, false otherwise.
     */
    function isCodeHashWhitelistedByCollection(address collection, bytes32 codehash) external view returns (bool) {
        return isCodeHashWhitelisted(collectionSecurityPolicies[collection].listId, codehash);
    }

    /// @notice Returns true if the specified account has verified a signature on the registry, false otherwise.
    function isVerifiedEOA(address account) public view returns (bool) {
        return IEOARegistry(_eoaRegistry).isVerifiedEOA(account);
    }

    /// @notice ERC-165 Interface Support
    /// @dev    Do not remove LEGACY from this contract or future contracts.  
    ///         Doing so will break backwards compatibility with V1 and V2 creator tokens.
    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
        return
            interfaceId == LEGACY_TRANSFER_VALIDATOR_INTERFACE_ID ||
            interfaceId == type(ITransferValidator).interfaceId ||
            interfaceId == type(IPermitC).interfaceId ||
            interfaceId == type(IEOARegistry).interfaceId ||
            super.supportsInterface(interfaceId);
    }

    /*************************************************************************/
    /*                                HELPERS                                */
    /*************************************************************************/

    /**
     * @notice Reverts the transaction if the caller is not the owner or assigned the default
     * @notice admin role of the contract at `tokenAddress`.
     *
     * @dev    Throws when the caller is neither owner nor assigned the default admin role.
     * 
     * @param tokenAddress The contract address of the token to check permissions for.
     */
    function _requireCallerIsNFTOrContractOwnerOrAdmin(address tokenAddress) internal view {
        address caller = msg.sender;
        
        if(caller == tokenAddress) {
            return;
        }

        (address contractOwner,) = _safeOwner(tokenAddress);
        if(caller == contractOwner) {
            return;
        }

        (bool callerIsContractAdmin,) = _safeHasRole(tokenAddress, DEFAULT_ACCESS_CONTROL_ADMIN_ROLE, caller);
        if(callerIsContractAdmin) {
            return;
        }

        revert CreatorTokenTransferValidator__CallerMustHaveElevatedPermissionsForSpecifiedNFT();
    }

    /**
     * @notice Copies all addresses in `ptrFromList` to `ptrToList`.
     * 
     * @dev    This function will copy all addresses from one list to another list.
     * @dev    Note: If used to copy adddresses to an existing list the current list contents will not be
     * @dev    deleted before copying. New addresses will be appeneded to the end of the list and the
     * @dev    non-enumerable mapping key value will be set to true.
     * 
     * @dev <h4>Postconditions:</h4>
     *      1. Addresses in from list that are not already present in to list are added to the to list.
     *      2. Emits an `AddedAccountToList` event for each address copied to the list.
     * 
     * @param  listType          The type of list addresses are being copied from and to.
     * @param  destinationListId The id of the list being copied to.
     * @param  ptrFromList       The storage pointer for the list being copied from.
     * @param  ptrToList         The storage pointer for the list being copied to.
     */
    function _copyAddressSet(
        uint8 listType,
        uint120 destinationListId,
        List storage ptrFromList,
        List storage ptrToList
    ) private {
        EnumerableSet.AddressSet storage ptrFromSet = ptrFromList.enumerableAccounts;
        EnumerableSet.AddressSet storage ptrToSet = ptrToList.enumerableAccounts;
        mapping (address => bool) storage ptrToNonEnumerableSet = ptrToList.nonEnumerableAccounts;
        uint256 sourceLength = ptrFromSet.length();
        address account;
        for (uint256 i = 0; i < sourceLength;) {
            account = ptrFromSet.at(i); 
            if (ptrToSet.add(account)) {
                emit AddedAccountToList(listType, destinationListId, account);
                ptrToNonEnumerableSet[account] = true;
            }

            unchecked {
                ++i;
            }
        }
    }

    /**
     * @notice Copies all codehashes in `ptrFromList` to `ptrToList`.
     * 
     * @dev    This function will copy all codehashes from one list to another list.
     * @dev    Note: If used to copy codehashes to an existing list the current list contents will not be
     * @dev    deleted before copying. New codehashes will be appeneded to the end of the list and the
     * @dev    non-enumerable mapping key value will be set to true.
     * 
     * @dev <h4>Postconditions:</h4>
     *      1. Codehashes in from list that are not already present in to list are added to the to list.
     *      2. Emits an `AddedCodeHashToList` event for each codehash copied to the list.
     * 
     * @param  listType          The type of list codehashes are being copied from and to.
     * @param  destinationListId The id of the list being copied to.
     * @param  ptrFromList       The storage pointer for the list being copied from.
     * @param  ptrToList         The storage pointer for the list being copied to.
     */
    function _copyBytes32Set(
        uint8 listType,
        uint120 destinationListId,
        List storage ptrFromList,
        List storage ptrToList
    ) private {
        EnumerableSet.Bytes32Set storage ptrFromSet = ptrFromList.enumerableCodehashes;
        EnumerableSet.Bytes32Set storage ptrToSet = ptrToList.enumerableCodehashes;
        mapping (bytes32 => bool) storage ptrToNonEnumerableSet = ptrToList.nonEnumerableCodehashes;
        uint256 sourceLength = ptrFromSet.length();
        bytes32 codehash;
        for (uint256 i = 0; i < sourceLength;) {
            codehash = ptrFromSet.at(i);
            if (ptrToSet.add(codehash)) {
                emit AddedCodeHashToList(listType, destinationListId, codehash);
                ptrToNonEnumerableSet[codehash] = true;
            }

            unchecked {
                ++i;
            }
        }
    }

    /**
     * @notice Adds one or more accounts to a list.
     * 
     * @dev <h4>Postconditions:</h4>
     *      1. Accounts that were not previously in the list are added to the list.
     *      2. An `AddedAccountToList` event is emitted for each account that was not
     *         previously on the list.
     * 
     * @param list     The storage pointer for the list to add accounts to.
     * @param listType The type of list the accounts are being added to.
     * @param id       The id of the list the accounts are being added to.
     * @param accounts An array of accounts to add to the list.
     */
    function _addAccountsToList(
        List storage list,
        uint8 listType,
        uint120 id,
        address[] calldata accounts
    ) internal onlyListOwner(id) {
        address account;
        for (uint256 i = 0; i < accounts.length;) {
            account = accounts[i];

            if (list.enumerableAccounts.add(account)) {
                emit AddedAccountToList(listType, id, account);
                list.nonEnumerableAccounts[account] = true;
            }

            unchecked {
                ++i;
            }
        }
    }

    /**
     * @notice Adds one or more codehashes to a list.
     * 
     * @dev <h4>Postconditions:</h4>
     *      1. Codehashes that were not previously in the list are added to the list.
     *      2. An `AddedCodeHashToList` event is emitted for each codehash that was not
     *         previously on the list.
     * 
     * @param list        The storage pointer for the list to add codehashes to.
     * @param listType    The type of list the codehashes are being added to.
     * @param id          The id of the list the codehashes are being added to.
     * @param codehashes  An array of codehashes to add to the list.
     */
    function _addCodeHashesToList(
        List storage list,
        uint8 listType,
        uint120 id,
        bytes32[] calldata codehashes
    ) internal onlyListOwner(id) {
        bytes32 codehash;
        for (uint256 i = 0; i < codehashes.length;) {
            codehash = codehashes[i];

            if (list.enumerableCodehashes.add(codehash)) {
                emit AddedCodeHashToList(listType, id, codehash);
                list.nonEnumerableCodehashes[codehash] = true;
            }

            unchecked {
                ++i;
            }
        }
    }

    /**
     * @notice Removes one or more accounts from a list.
     * 
     * @dev <h4>Postconditions:</h4>
     *      1. Accounts that were previously in the list are removed from the list.
     *      2. An `RemovedAccountFromList` event is emitted for each account that was
     *         previously on the list.
     * 
     * @param list        The storage pointer for the list to remove accounts from.
     * @param listType    The type of list the accounts are being removed from.
     * @param id          The id of the list the accounts are being removed from.
     * @param accounts    An array of accounts to remove from the list.
     */
    function _removeAccountsFromList(
        List storage list, 
        uint8 listType,
        uint120 id, 
        address[] memory accounts
    ) internal onlyListOwner(id) {
        address account;
        for (uint256 i = 0; i < accounts.length;) {
            account = accounts[i];
            if (list.enumerableAccounts.remove(account)) {
                emit RemovedAccountFromList(listType, id, account);
                delete list.nonEnumerableAccounts[account];
            }

            unchecked {
                ++i;
            }
        }
    }

    /**
     * @notice Removes one or more codehashes from a list.
     * 
     * @dev <h4>Postconditions:</h4>
     *      1. Codehashes that were previously in the list are removed from the list.
     *      2. An `RemovedCodeHashFromList` event is emitted for each codehash that was
     *         previously on the list.
     * 
     * @param list        The storage pointer for the list to remove codehashes from.
     * @param listType    The type of list the codehashes are being removed from.
     * @param id          The id of the list the codehashes are being removed from.
     * @param codehashes  An array of codehashes to remove from the list.
     */
    function _removeCodeHashesFromList(
        List storage list, 
        uint8 listType, 
        uint120 id, 
        bytes32[] calldata codehashes
    ) internal onlyListOwner(id) {
        bytes32 codehash;
        for (uint256 i = 0; i < codehashes.length;) {
            codehash = codehashes[i];
            if (list.enumerableCodehashes.remove(codehash)) {
                emit RemovedCodeHashFromList(listType, id, codehash);
                delete list.nonEnumerableCodehashes[codehash];
            }

            unchecked {
                ++i;
            }
        }
    }

    /**
     * @notice Sets the owner of list `id` to `newOwner`.
     * 
     * @dev    Throws when the caller is not the owner of the list.
     * 
     * @dev    <h4>Postconditions:</h4>
     *         1. The owner of list `id` is set to `newOwner`.
     *         2. Emits a `ReassignedListOwnership` event.
     * 
     * @param id       The id of the list to reassign ownership of.
     * @param newOwner The account to assign ownership of the list to.
     */
    function _reassignOwnershipOfList(uint120 id, address newOwner) private {
        _requireCallerOwnsList(id);
        listOwners[id] = newOwner;
        emit ReassignedListOwnership(id, newOwner);
    }

    /**
     * @notice Requires the caller to be the owner of list `id`.
     * 
     * @dev    Throws when the caller is not the owner of the list.
     * 
     * @param id  The id of the list to check ownership of.
     */
    function _requireCallerOwnsList(uint120 id) private view {
        if (msg.sender != listOwners[id]) {
            revert CreatorTokenTransferValidator__CallerDoesNotOwnList();
        }
    }

    /**
     * @dev Internal function used to efficiently retrieve the code length of `account`.
     * 
     * @param account The address to get the deployed code length for.
     * 
     * @return length The length of deployed code at the address.
     */
    function _getCodeLengthAsm(address account) internal view returns (uint256 length) {
        assembly { length := extcodesize(account) }
    }

    /**
     * @dev Internal function used to efficiently retrieve the codehash of `account`.
     * 
     * @param account The address to get the deployed codehash for.
     * 
     * @return codehash The codehash of the deployed code at the address.
     */
    function _getCodeHashAsm(address account) internal view returns (bytes32 codehash) {
        assembly { codehash := extcodehash(account) }
    }

    /**
     * @dev Hook that is called before any permitted token transfer that goes through Permit-C.
     *      Applies the collection transfer policy, using the operator that called Permit-C as the caller.
     *      This allows creator token standard protections to extend to permitted transfers.
     * 
     * @param token  The collection address of the token being transferred.
     * @param from   The address of the token owner.
     * @param to     The address of the token receiver.
     * @param id     The token id being transferred.
     */
    function _beforeTransferFrom(
        uint256 tokenType,
        address token, 
        address from, 
        address to, 
        uint256 id, 
        uint256 /*amount*/
    ) internal override returns (bool isError) {
        (bytes4 selector, uint16 collectionTokenType) = _validateTransfer(_callerAuthorizedCheckToken, token, msg.sender, from, to, id);
        if (collectionTokenType == DEFAULT_TOKEN_TYPE || collectionTokenType == tokenType) {
            isError = SELECTOR_NO_ERROR != selector;
        } else {
            revert CreatorTokenTransferValidator__TokenTypesDoNotMatch();
        }
    }

    /**
     * @notice Apply the collection transfer policy to a transfer operation of a creator token.
     *
     * @dev If the caller is self (Permit-C Processor) it means we have already applied operator validation in the 
     *      _beforeTransferFrom callback.  In this case, the security policy was already applied and the operator
     *      that used the Permit-C processor passed the security policy check and transfer can be safely allowed.
     *
     * @dev The order of checking whitelisted accounts, authorized operator check and whitelisted codehashes
     *      is very deliberate.  The order of operations is determined by the most frequently used settings that are
     *      expected in the wild.
     *
     * @dev Throws when the collection has enabled account freezing mode and either the `from` or `to` addresses
     *      are on the list of frozen accounts for the collection.
     * @dev Throws when the collection is set to Level 9 - Soulbound Token.
     * @dev Throws when the receiver has deployed code and isn't whitelisted, if ReceiverConstraints.NoCode is set
     *      and the transfer is not approved by an authorizer for the collection.
     * @dev Throws when the receiver has never verified a signature to prove they are an EOA and the receiver
     *      isn't whitelisted, if the ReceiverConstraints.EOA is set and the transfer is not approved by an 
     *      authorizer for the collection..
     * @dev Throws when `msg.sender` is blacklisted, if CallerConstraints.OperatorBlacklistEnableOTC is set, unless
     *      `msg.sender` is also the `from` address or the transfer is approved by an authorizer for the collection.
     * @dev Throws when `msg.sender` isn't whitelisted, if CallerConstraints.OperatorWhitelistEnableOTC is set, unless
     *      `msg.sender` is also the `from` address or the transfer is approved by an authorizer for the collection.
     * @dev Throws when neither `msg.sender` nor `from` are whitelisted, if 
     *      CallerConstraints.OperatorWhitelistDisableOTC is set and the transfer 
     *      is not approved by an authorizer for the collection.
     *
     * @dev <h4>Postconditions:</h4>
     *      1. Transfer is allowed or denied based on the applied transfer policy.
     *
     * @param collection  The collection address of the token being transferred.
     * @param caller      The address initiating the transfer.
     * @param from        The address of the token owner.
     * @param to          The address of the token receiver.
     * @param tokenId     The token id being transferred.
     * 
     * @return The selector value for an error if the transfer is not allowed, `SELECTOR_NO_ERROR` if the transfer is allowed.
     */
    function _validateTransfer(
        function(address,address,uint256) internal view returns(bool) _callerAuthorizedParam,
        address collection, 
        address caller, 
        address from, 
        address to,
        uint256 tokenId
    ) internal view returns (bytes4,uint16) {
        if (caller == address(this)) { 
            // If the caller is self (Permit-C Processor) it means we have already applied operator validation in the 
            // _beforeTransferFrom callback.  In this case, the security policy was already applied and the operator
            // that used the Permit-C processor passed the security policy check and transfer can be safely allowed.
            return (SELECTOR_NO_ERROR, DEFAULT_TOKEN_TYPE);
        }

        CollectionSecurityPolicyV3 storage collectionSecurityPolicy = collectionSecurityPolicies[collection];

        uint120 listId = collectionSecurityPolicy.listId;

        (uint256 callerConstraints, uint256 receiverConstraints) = 
            transferSecurityPolicies(collectionSecurityPolicy.transferSecurityLevel);

        if (collectionSecurityPolicy.enableAccountFreezingMode) {
            AccountList storage frozenAccountList = frozenAccounts[collection];
            
            if (frozenAccountList.nonEnumerableAccounts[from]) {
                return (CreatorTokenTransferValidator__SenderAccountIsFrozen.selector, DEFAULT_TOKEN_TYPE);
            }

            if (frozenAccountList.nonEnumerableAccounts[to]) {
                return (CreatorTokenTransferValidator__ReceiverAccountIsFrozen.selector, DEFAULT_TOKEN_TYPE);
            }
        }

        if (callerConstraints == CALLER_CONSTRAINTS_SBT) {
            return (CreatorTokenTransferValidator__TokenIsSoulbound.selector, DEFAULT_TOKEN_TYPE);
        }

        List storage whitelist = whitelists[listId];

        if (receiverConstraints == RECEIVER_CONSTRAINTS_NO_CODE) {
            if (_getCodeLengthAsm(to) > 0) {
                if (!whitelist.nonEnumerableAccounts[to]) {
                    // Cache _callerAuthorizedParam on stack to avoid stack too deep
                    function(address,address,uint256) internal view returns(bool) _callerAuthorized = _callerAuthorizedParam;
                    if(!_callerAuthorized(collection, caller, tokenId)) {
                        if (!whitelist.nonEnumerableCodehashes[_getCodeHashAsm(to)]) {
                            return (CreatorTokenTransferValidator__ReceiverMustNotHaveDeployedCode.selector, DEFAULT_TOKEN_TYPE);
                        }
                    }
                }
            }
        } else if (receiverConstraints == RECEIVER_CONSTRAINTS_EOA) {
            if (!isVerifiedEOA(to)) {
                if (!whitelist.nonEnumerableAccounts[to]) {
                    // Cache _callerAuthorizedParam on stack to avoid stack too deep
                    function(address,address,uint256) internal view returns(bool) _callerAuthorized = _callerAuthorizedParam;
                    if(!_callerAuthorized(collection, caller, tokenId)) {
                        if (!whitelist.nonEnumerableCodehashes[_getCodeHashAsm(to)]) {
                            return (CreatorTokenTransferValidator__ReceiverProofOfEOASignatureUnverified.selector, DEFAULT_TOKEN_TYPE);
                        }
                    }
                }
            }
        }

        if (caller == from) {
            if (callerConstraints != CALLER_CONSTRAINTS_OPERATOR_WHITELIST_DISABLE_OTC) {
                return (SELECTOR_NO_ERROR, collectionSecurityPolicy.tokenType);
            }
        }

        if (callerConstraints == CALLER_CONSTRAINTS_OPERATOR_BLACKLIST_ENABLE_OTC) {
            // Cache _callerAuthorizedParam on stack to avoid stack too deep
            function(address,address,uint256) internal view returns(bool) _callerAuthorized = _callerAuthorizedParam;
            if(_callerAuthorized(collection, caller, tokenId)) {
                return (SELECTOR_NO_ERROR, collectionSecurityPolicy.tokenType);
            }

            List storage blacklist = blacklists[listId];
            if (blacklist.nonEnumerableAccounts[caller]) {
                return (CreatorTokenTransferValidator__OperatorIsBlacklisted.selector, DEFAULT_TOKEN_TYPE);
            }

            if (blacklist.nonEnumerableCodehashes[_getCodeHashAsm(caller)]) {
                return (CreatorTokenTransferValidator__OperatorIsBlacklisted.selector, DEFAULT_TOKEN_TYPE);
            }
        } else if (callerConstraints == CALLER_CONSTRAINTS_OPERATOR_WHITELIST_ENABLE_OTC) {
            if (whitelist.nonEnumerableAccounts[caller]) {
                return (SELECTOR_NO_ERROR, collectionSecurityPolicy.tokenType);
            }

            // Cache _callerAuthorizedParam on stack to avoid stack too deep
            function(address,address,uint256) internal view returns(bool) _callerAuthorized = _callerAuthorizedParam;
            if( _callerAuthorized(collection, caller, tokenId)) {
                return (SELECTOR_NO_ERROR, collectionSecurityPolicy.tokenType);
            }

            if (whitelist.nonEnumerableCodehashes[_getCodeHashAsm(caller)]) {
                return (SELECTOR_NO_ERROR, collectionSecurityPolicy.tokenType);
            }

            return (CreatorTokenTransferValidator__CallerMustBeWhitelisted.selector, DEFAULT_TOKEN_TYPE);
        } else if (callerConstraints == CALLER_CONSTRAINTS_OPERATOR_WHITELIST_DISABLE_OTC) {
            mapping(address => bool) storage accountWhitelist = whitelist.nonEnumerableAccounts;

            if (accountWhitelist[caller]) {
                return (SELECTOR_NO_ERROR, collectionSecurityPolicy.tokenType);
            }

            if (accountWhitelist[from]) {
                return (SELECTOR_NO_ERROR, collectionSecurityPolicy.tokenType);
            }

            // Cache _callerAuthorizedParam on stack to avoid stack too deep
            function(address,address,uint256) internal view returns(bool) _callerAuthorized = _callerAuthorizedParam;
            if(_callerAuthorized(collection, caller, tokenId)) {
                return (SELECTOR_NO_ERROR, collectionSecurityPolicy.tokenType);
            }

            mapping(bytes32 => bool) storage codehashWhitelist = whitelist.nonEnumerableCodehashes;

            // Cache caller on stack to avoid stack too deep
            address tmpAddress = caller;
            if (codehashWhitelist[_getCodeHashAsm(tmpAddress)]) {
                return (SELECTOR_NO_ERROR, collectionSecurityPolicy.tokenType);
            }

            // Cache from on stack to avoid stack too deep
            tmpAddress = from;
            if (codehashWhitelist[_getCodeHashAsm(tmpAddress)]) {
                return (SELECTOR_NO_ERROR, collectionSecurityPolicy.tokenType);
            }

            return (CreatorTokenTransferValidator__CallerMustBeWhitelisted.selector, DEFAULT_TOKEN_TYPE);
        }

        return (SELECTOR_NO_ERROR, collectionSecurityPolicy.tokenType);
    }

    /**
     * @dev Internal function used to efficiently revert with a custom error selector.
     *
     * @param errorSelector The error selector to revert with.
     */
    function _revertCustomErrorSelectorAsm(bytes4 errorSelector) internal pure {
        assembly {
            mstore(0x00, errorSelector)
            revert(0x00, 0x04)
        }
    }

    /**
     * @dev Internal function used to check if authorization mode can be activated for a transfer.
     * 
     * @dev Throws when the collection has not enabled authorization mode.
     * @dev Throws when the wildcard operator is being set for a collection that does not
     *      allow wildcard operators.
     * @dev Throws when the authorizer is not in the list of approved authorizers for
     *      the collection.
     * 
     * @param collection  The collection address to activate authorization mode for a transfer.
     * @param operator    The operator specified by the authorizer to allow transfers.
     * @param authorizer  The address of the authorizer making the call.
     */
    function _checkCollectionAllowsAuthorizerAndOperator(
        address collection, 
        address operator, 
        address authorizer
    ) internal view {
        CollectionSecurityPolicyV3 storage collectionSecurityPolicy = collectionSecurityPolicies[collection];

        if (collectionSecurityPolicy.disableAuthorizationMode) {
            revert CreatorTokenTransferValidator__AuthorizationDisabledForCollection();
        }

        if (collectionSecurityPolicy.authorizersCannotSetWildcardOperators) {
            if (operator == WILDCARD_OPERATOR_ADDRESS) {
                revert CreatorTokenTransferValidator__WildcardOperatorsCannotBeAuthorizedForCollection();
            }
        }

        if (!authorizers[collectionSecurityPolicy.listId].nonEnumerableAccounts[authorizer]) {
            revert CreatorTokenTransferValidator__CallerMustBeAnAuthorizer();
        }
    }

    /**
     * @dev Modifier to apply the allowed authorizer and operator for collection checks.
     * 
     * @dev Throws when the collection has not enabled authorization mode.
     * @dev Throws when the wildcard operator is being set for a collection that does not
     *      allow wildcard operators.
     * @dev Throws when the authorizer is not in the list of approved authorizers for
     *      the collection.
     * 
     * @param collection  The collection address to activate authorization mode for a transfer.
     * @param operator    The operator specified by the authorizer to allow transfers.
     * @param authorizer  The address of the authorizer making the call.
     */
    modifier whenAuthorizerAndOperatorEnabledForCollection(
        address collection, 
        address operator, 
        address authorizer
    ) {
        _checkCollectionAllowsAuthorizerAndOperator(collection, operator, authorizer);
        _;
    }

    /**
     * @dev Internal function for setting the authorized operator in storage for a token and collection.
     * 
     * @param operator         The allowed operator for an authorized transfer.
     * @param collection       The address of the collection that the operator is authorized for.
     * @param tokenId          The id of the token that is authorized.
     * @param allowAnyTokenId  Flag if the authorizer is enabling transfers for any token id
     */
    function _setOperatorInTransientStorage(
        address operator,
        address collection, 
        uint256 tokenId,
        bool allowAnyTokenId
    ) internal whenAuthorizerAndOperatorEnabledForCollection(collection, operator, msg.sender) {
        _setTstorish(_getTransientOperatorSlot(collection), (allowAnyTokenId ? 1 << 255 : 0) | uint256(uint160(operator)));
        _setTstorish(_getTransientOperatorSlot(collection, tokenId), uint256(uint160(operator)));
    }

    /**
     * @dev Internal function to check if a caller is an authorized operator for the token being transferred.
     * 
     * @param caller     The caller of the token transfer.
     * @param collection The collection address of the token being transferred.
     * @param tokenId    The id of the token being transferred.
     * 
     * @return isAuthorized  True if the caller is authorized to transfer the token, false otherwise.
     */
    function _callerAuthorizedCheckToken(
      address collection,
        address caller,
        uint256 tokenId
    ) internal view returns (bool isAuthorized) {
        uint256 slotValue;
        (isAuthorized, ) = _callerAuthorized(caller, _getTransientOperatorSlot(collection, tokenId));
        if (isAuthorized) return true;
        (isAuthorized, slotValue) = _callerAuthorized(caller, _getTransientOperatorSlot(collection));
        isAuthorized = isAuthorized && slotValue >> 255 == 1;
    }

    /**
     * @dev Internal function to check if a caller is an authorized operator for the collection being transferred.
     * 
     * @param caller     The caller of the token transfer.
     * @param collection The collection address of the token being transferred.
     * 
     * @return isAuthorized  True if the caller is authorized to transfer the collection, false otherwise.
     */
    function _callerAuthorizedCheckCollection(
      address collection,
        address caller,
        uint256 /*tokenId*/
    ) internal view returns (bool isAuthorized) {
        (isAuthorized, ) = _callerAuthorized(caller, _getTransientOperatorSlot(collection));
    }

    /**
     * @dev Internal function to check if a caller is an authorized operator. 
     * @dev This overload of `_callerAuthorized` checks a specific storage slot for the caller address.
     * 
     * @param caller     The caller of the token transfer.
     * @param slot       The storage slot to check for the caller address.
     * 
     * @return isAuthorized  True if the caller is authorized to transfer the token, false otherwise.
     * @return slotValue     The transient storage value in `slot`, used to check for allow any token id flag if necessary.
     */
    function _callerAuthorized(address caller, uint256 slot) internal view returns (bool isAuthorized, uint256 slotValue) {
        slotValue = _getTstorish(slot);
        address authorizedOperator = address(uint160(slotValue));
        isAuthorized = authorizedOperator == WILDCARD_OPERATOR_ADDRESS || authorizedOperator == caller;
    }

    /**
     * @dev Internal function used to compute the transient storage slot for the authorized 
     *      operator of a token in a collection.
     * 
     * @param collection The collection address of the token being transferred.
     * @param tokenId    The id of the token being transferred.
     * 
     * @return operatorSlot The storage slot location for the authorized operator value.
     */
    function _getTransientOperatorSlot(
        address collection, 
        uint256 tokenId
    ) internal pure returns (uint256 operatorSlot) {
        assembly {
            mstore(0x00, collection)
            mstore(0x20, tokenId)
            operatorSlot := shr(4, keccak256(0x00, 0x40))
       }
    }

    /**
     * @dev Internal function used to compute the transient storage slot for the authorized operator of a collection.
     * 
     * @param collection The collection address of the token being transferred.
     * 
     * @return operatorSlot The storage slot location for the authorized operator value.
     */
    function _getTransientOperatorSlot(address collection) internal pure returns (uint256 operatorSlot) {
        return uint256(uint160(collection));
    }

    /**
     * @dev A gas efficient, and fallback-safe way to call the owner function on a token contract.
     *      This will get the owner if it exists - and when the function is unimplemented, the
     *      presence of a fallback function will not result in halted execution.
     * 
     * @param tokenAddress  The address of the token collection to get the owner of.
     * 
     * @return owner   The owner of the token collection contract.
     * @return isError True if there was an error in retrieving the owner, false if the call was successful.
     */
    function _safeOwner(
        address tokenAddress
    ) internal view returns(address owner, bool isError) {
        assembly {
            function _callOwner(_tokenAddress) -> _owner, _isError {
                mstore(0x00, 0x8da5cb5b)
                if and(iszero(lt(returndatasize(), 0x20)), staticcall(gas(), _tokenAddress, 0x1C, 0x04, 0x00, 0x20)) {
                    _owner := mload(0x00)
                    leave
                }
                _isError := true
            }
            owner, isError := _callOwner(tokenAddress)
        }
    }
    
    /**
     * @dev A gas efficient, and fallback-safe way to call the hasRole function on a token contract.
     *      This will check if the account `hasRole` if `hasRole` exists - and when the function is unimplemented, the
     *      presence of a fallback function will not result in halted execution.
     * 
     * @param tokenAddress  The address of the token collection to call hasRole on.
     * @param role          The role to check if the account has on the collection.
     * @param account       The address of the account to check if they have a specified role.
     * 
     * @return hasRole The owner of the token collection contract.
     * @return isError True if there was an error in retrieving the owner, false if the call was successful.
     */
    function _safeHasRole(
        address tokenAddress,
        bytes32 role,
        address account
    ) internal view returns(bool hasRole, bool isError) {
        assembly {
            function _callHasRole(_tokenAddress, _role, _account) -> _hasRole, _isError {
                let ptr := mload(0x40)
                mstore(0x40, add(ptr, 0x60))
                mstore(ptr, 0x91d14854)
                mstore(add(0x20, ptr), _role)
                mstore(add(0x40, ptr), _account)
                if and(iszero(lt(returndatasize(), 0x20)), staticcall(gas(), _tokenAddress, add(ptr, 0x1C), 0x44, 0x00, 0x20)) {
                    _hasRole := mload(0x00)
                    leave
                }
                _isError := true
            }
            hasRole, isError := _callHasRole(tokenAddress, role, account)
        }
    }
}

File 2 of 31 : Constants.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/**
 * @dev Constant definitions for receiver constraints used by the transfer validator.
 */
/// @dev No constraints on the receiver of a token.
uint256 constant RECEIVER_CONSTRAINTS_NONE = 0;

/// @dev Token receiver cannot have deployed code.
uint256 constant RECEIVER_CONSTRAINTS_NO_CODE = 1;

/// @dev Token receiver must be a verified EOA with the EOA Registry.
uint256 constant RECEIVER_CONSTRAINTS_EOA = 2;

/// @dev Token is a soulbound token and cannot be transferred.
uint256 constant RECEIVER_CONSTRAINTS_SBT = 3;

/**
 * @dev Constant definitions for caller constraints used by the transfer validator.
 */
/// @dev No constraints on the caller of a token transfer.
uint256 constant CALLER_CONSTRAINTS_NONE = 0;

/// @dev Caller of a token transfer must not be on the blacklist unless it is an OTC transfer.
uint256 constant CALLER_CONSTRAINTS_OPERATOR_BLACKLIST_ENABLE_OTC = 1;

/// @dev Caller of a token transfer must be on the whitelist unless it is an OTC transfer.
uint256 constant CALLER_CONSTRAINTS_OPERATOR_WHITELIST_ENABLE_OTC = 2;

/// @dev Caller of a token transfer must be on the whitelist.
uint256 constant CALLER_CONSTRAINTS_OPERATOR_WHITELIST_DISABLE_OTC = 3;

/// @dev Token is a soulbound token and cannot be transferred.
uint256 constant CALLER_CONSTRAINTS_SBT = 4;


/**
 * @dev Constant definitions for transfer security levels used by the transfer validator
 *      to define what receiver and caller constraints are applied to a transfer.
 */

/// @dev Recommend Security Level -
///        Caller Constraints: Operator Whitelist
///        Receiver Constraints: None
///        OTC: Allowed
uint8 constant TRANSFER_SECURITY_LEVEL_RECOMMENDED = 0;

/// @dev Security Level One -
///        Caller Constraints: None
///        Receiver Constraints: None
///        OTC: Allowed
uint8 constant TRANSFER_SECURITY_LEVEL_ONE = 1;

/// @dev Security Level Two -
///        Caller Constraints: Operator Blacklist
///        Receiver Constraints: None
///        OTC: Allowed
uint8 constant TRANSFER_SECURITY_LEVEL_TWO = 2;

/// @dev Security Level Three -
///        Caller Constraints: Operator Whitelist
///        Receiver Constraints: None
///        OTC: Allowed
uint8 constant TRANSFER_SECURITY_LEVEL_THREE = 3;

/// @dev Security Level Four -
///        Caller Constraints: Operator Whitelist
///        Receiver Constraints: None
///        OTC: Not Allowed
uint8 constant TRANSFER_SECURITY_LEVEL_FOUR = 4;

/// @dev Security Level Five -
///        Caller Constraints: Operator Whitelist
///        Receiver Constraints: No Code
///        OTC: Allowed
uint8 constant TRANSFER_SECURITY_LEVEL_FIVE = 5;

/// @dev Security Level Six -
///        Caller Constraints: Operator Whitelist
///        Receiver Constraints: Verified EOA
///        OTC: Allowed
uint8 constant TRANSFER_SECURITY_LEVEL_SIX = 6;

/// @dev Security Level Seven -
///        Caller Constraints: Operator Whitelist
///        Receiver Constraints: No Code
///        OTC: Not Allowed
uint8 constant TRANSFER_SECURITY_LEVEL_SEVEN = 7;

/// @dev Security Level Eight -
///        Caller Constraints: Operator Whitelist
///        Receiver Constraints: Verified EOA
///        OTC: Not Allowed
uint8 constant TRANSFER_SECURITY_LEVEL_EIGHT = 8;

/// @dev Security Level Nine -
///        Soulbound Token, No Transfers Allowed
uint8 constant TRANSFER_SECURITY_LEVEL_NINE = 9;

/// @dev List type is a blacklist.
uint8 constant LIST_TYPE_BLACKLIST = 0;

/// @dev List type is a whitelist.
uint8 constant LIST_TYPE_WHITELIST = 1;

/// @dev List type is authorizers.
uint8 constant LIST_TYPE_AUTHORIZERS = 2;

/// @dev Constant value for the no error selector.
bytes4 constant SELECTOR_NO_ERROR = bytes4(0x00000000);

File 3 of 31 : IEOARegistry.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "@openzeppelin/contracts/utils/introspection/IERC165.sol";

interface IEOARegistry is IERC165 {
    function isVerifiedEOA(address account) external view returns (bool);
}

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

interface ITransferValidator {
    function applyCollectionTransferPolicy(address caller, address from, address to) external view;
    function validateTransfer(address caller, address from, address to) external view;
    function validateTransfer(address caller, address from, address to, uint256 tokenId) external view;
    function validateTransfer(address caller, address from, address to, uint256 tokenId, uint256 amount) external;

    function beforeAuthorizedTransfer(address operator, address token, uint256 tokenId) external;
    function afterAuthorizedTransfer(address token, uint256 tokenId) external;
    function beforeAuthorizedTransfer(address operator, address token) external;
    function afterAuthorizedTransfer(address token) external;
    function beforeAuthorizedTransfer(address token, uint256 tokenId) external;
    function beforeAuthorizedTransferWithAmount(address token, uint256 tokenId, uint256 amount) external;
    function afterAuthorizedTransferWithAmount(address token, uint256 tokenId) external;
}

File 5 of 31 : TransferPolicy.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/**
 * @dev Defines constraints for staking tokens in token wrapper contracts.
 */
enum StakerConstraints {
    // 0: No constraints applied to staker.
    None,

    // 1: Transaction originator must be the address that will receive the wrapped tokens.
    CallerIsTxOrigin,

    // 2: Address that will receive the wrapped tokens must be a verified EOA.
    EOA
}

/**
 * @dev Defines the security policy for a token collection in Creator Token Standards V2.
 * 
 * @dev **transferSecurityLevel**: The transfer security level set for the collection.
 * @dev **listId**: The list id that contains the blacklist and whitelist to apply to the collection.
 * @dev **enableGraylisting**: If true, graylisting will be enabled for the collection.
 */
struct CollectionSecurityPolicyV3 {
    bool disableAuthorizationMode;
    bool authorizersCannotSetWildcardOperators;
    uint8 transferSecurityLevel;
    uint120 listId;
    bool enableAccountFreezingMode;
    uint16 tokenType;
}

File 6 of 31 : CreatorTokenTransferValidatorConfiguration.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "@openzeppelin/contracts/access/Ownable.sol";

contract CreatorTokenTransferValidatorConfiguration is Ownable {

    error CreatorTokenTransferValidatorConfiguration__ConfigurationNotInitialized();

    bool configurationInitialized;
    uint256 private nativeValueToCheckPauseState;

    constructor(address defaultOwner) {
        _transferOwnership(defaultOwner);
    }

    function setNativeValueToCheckPauseState(uint256 _nativeValueToCheckPauseState) external onlyOwner {
        nativeValueToCheckPauseState = _nativeValueToCheckPauseState;
        configurationInitialized = true;
    }

    function getNativeValueToCheckPauseState() external view returns(uint256 _nativeValueToCheckPauseState) {
        if (!configurationInitialized) {
            revert CreatorTokenTransferValidatorConfiguration__ConfigurationNotInitialized();
        }

        _nativeValueToCheckPauseState = nativeValueToCheckPauseState;
    }
}

File 7 of 31 : PermitC.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "./Errors.sol";
import {IERC20} from "@openzeppelin/contracts/interfaces/IERC20.sol";
import {IERC721} from "@openzeppelin/contracts/interfaces/IERC721.sol";
import {IERC1155} from "@openzeppelin/contracts/interfaces/IERC1155.sol";
import {IERC1271} from "@openzeppelin/contracts/interfaces/IERC1271.sol";
import {Ownable} from "./openzeppelin-optimized/Ownable.sol";
import {EIP712} from "./openzeppelin-optimized/EIP712.sol";
import {
    ZERO_BYTES32,
    ZERO, 
    ONE, 
    ORDER_STATE_OPEN,
    ORDER_STATE_FILLED,
    ORDER_STATE_CANCELLED,
    SINGLE_USE_PERMIT_TRANSFER_ADVANCED_TYPEHASH_STUB,
    PERMIT_ORDER_ADVANCED_TYPEHASH_STUB,
    UPPER_BIT_MASK,
    TOKEN_TYPE_ERC1155,
    TOKEN_TYPE_ERC20,
    TOKEN_TYPE_ERC721,
    PAUSABLE_APPROVAL_TRANSFER_FROM_ERC721,
    PAUSABLE_APPROVAL_TRANSFER_FROM_ERC1155,
    PAUSABLE_APPROVAL_TRANSFER_FROM_ERC20,
    PAUSABLE_PERMITTED_TRANSFER_FROM_ERC721,
    PAUSABLE_PERMITTED_TRANSFER_FROM_ERC1155,
    PAUSABLE_PERMITTED_TRANSFER_FROM_ERC20,
    PAUSABLE_ORDER_TRANSFER_FROM_ERC1155,
    PAUSABLE_ORDER_TRANSFER_FROM_ERC20
} from "./Constants.sol";
import {PackedApproval, OrderFillAmounts} from "./DataTypes.sol";
import {PermitHash} from './libraries/PermitHash.sol';
import {IPermitC} from './interfaces/IPermitC.sol';
import {CollateralizedPausableFlags} from './CollateralizedPausableFlags.sol';

/*
                                                     @@@@@@@@@@@@@@             
                                                    @@@@@@@@@@@@@@@@@@(         
                                                   @@@@@@@@@@@@@@@@@@@@@        
                                                  @@@@@@@@@@@@@@@@@@@@@@@@      
                                                           #@@@@@@@@@@@@@@      
                                                               @@@@@@@@@@@@     
                            @@@@@@@@@@@@@@*                    @@@@@@@@@@@@     
                           @@@@@@@@@@@@@@@     @               @@@@@@@@@@@@     
                          @@@@@@@@@@@@@@@     @                @@@@@@@@@@@      
                         @@@@@@@@@@@@@@@     @@               @@@@@@@@@@@@      
                        @@@@@@@@@@@@@@@     #@@             @@@@@@@@@@@@/       
                        @@@@@@@@@@@@@@.     @@@@@@@@@@@@@@@@@@@@@@@@@@@         
                       @@@@@@@@@@@@@@@     @@@@@@@@@@@@@@@@@@@@@@@@@            
                      @@@@@@@@@@@@@@@     @@@@@@@@@@@@@@@@@@@@@@@@@             
                     @@@@@@@@@@@@@@@     @@@@@@@@@@@@@@@@@@@@@@@@@@@@           
                    @@@@@@@@@@@@@@@     @@@@@&%%%%%%%%&&@@@@@@@@@@@@@@          
                    @@@@@@@@@@@@@@      @@@@@               @@@@@@@@@@@         
                   @@@@@@@@@@@@@@@     @@@@@                 @@@@@@@@@@@        
                  @@@@@@@@@@@@@@@     @@@@@@                 @@@@@@@@@@@        
                 @@@@@@@@@@@@@@@     @@@@@@@                 @@@@@@@@@@@        
                @@@@@@@@@@@@@@@     @@@@@@@                 @@@@@@@@@@@&        
                @@@@@@@@@@@@@@     *@@@@@@@               (@@@@@@@@@@@@         
               @@@@@@@@@@@@@@@     @@@@@@@@             @@@@@@@@@@@@@@          
              @@@@@@@@@@@@@@@     @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@           
             @@@@@@@@@@@@@@@     @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@            
            @@@@@@@@@@@@@@@     @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@              
           .@@@@@@@@@@@@@@     @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                 
           @@@@@@@@@@@@@@%     @@@@@@@@@@@@@@@@@@@@@@@@(                        
          @@@@@@@@@@@@@@@                                                       
         @@@@@@@@@@@@@@@                                                        
        @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                                         
       @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                                          
       @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&                                          
      @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                                           
 
* @title PermitC
* @custom:version 1.0.0
* @author Limit Break, Inc.
* @description Advanced approval management for ERC20, ERC721 and ERC1155 tokens
*              allowing for single use permit transfers, time-bound approvals
*              and order ID based transfers.
*/
contract PermitC is Ownable, CollateralizedPausableFlags, EIP712, IPermitC {

    /**
     * @notice Map of approval details for the provided bytes32 hash to allow for multiple accessors
     *
     * @dev    keccak256(abi.encode(owner, tokenType, token, id, orderId, masterNonce)) => 
     * @dev        operator => (state, amount, expiration)
     * @dev    Utilized for stored approvals by an owner's direct call to `approve` and  
     * @dev    approvals by signature in `updateApprovalBySignature`. Both methods use a
     * @dev    bytes32(0) value for the `orderId`.
     */
    mapping(bytes32 => mapping(address => PackedApproval)) private _transferApprovals;

    /**
     * @notice Map of approval details for the provided bytes32 hash to allow for multiple accessors
     *
     * @dev    keccak256(abi.encode(owner, tokenType, token, id, orderId, masterNonce)) => 
     * @dev        operator => (state, amount, expiration)
     * @dev    Utilized for order approvals by `fillPermittedOrderERC20` and `fillPermittedOrderERC1155`
     * @dev    with the `orderId` provided by the sender.
     */
    mapping(bytes32 => mapping(address => PackedApproval)) private _orderApprovals;

    /**
     * @notice Map of registered additional data hashes for transfer permits.
     *
     * @dev    This is used to prevent someone from providing an invalid EIP712 envelope label
     * @dev    and tricking a user into signing a different message than they expect.
     */
    mapping(bytes32 => bool) private _registeredTransferHashes;

    /**
     * @notice Map of registered additional data hashes for order permits.
     *
     * @dev    This is used to prevent someone from providing an invalid EIP712 envelope label
     * @dev    and tricking a user into signing a different message than they expect.
     */
    mapping(bytes32 => bool) private _registeredOrderHashes;

    /// @dev Map of an address to a bitmap (slot => status)
    mapping(address => mapping(uint256 => uint256)) private _unorderedNonces;

    /**
     * @notice Master nonce used to invalidate all outstanding approvals for an owner
     *
     * @dev    owner => masterNonce
     * @dev    This is incremented when the owner calls lockdown()
     */
    mapping(address => uint256) private _masterNonces;

    constructor(
        string memory name,
        string memory version,
        address _defaultContractOwner,
        uint256 _nativeValueToCheckPauseState
    ) CollateralizedPausableFlags(_nativeValueToCheckPauseState) EIP712(name, version) {
        _transferOwnership(_defaultContractOwner);
    }

    /**
     * =================================================
     * ================= Modifiers =====================
     * =================================================
     */

    modifier onlyRegisteredTransferAdvancedTypeHash(bytes32 advancedPermitHash) {
        _requireTransferAdvancedPermitHashIsRegistered(advancedPermitHash);
        _;
    }

    modifier onlyRegisteredOrderAdvancedTypeHash(bytes32 advancedPermitHash) {
        _requireOrderAdvancedPermitHashIsRegistered(advancedPermitHash);
        _;
    }

    /**
     * =================================================
     * ============== Approval Transfers ===============
     * =================================================
     */

    /**
     * @notice Approve an operator to spend a specific token / ID combination
     * @notice This function is compatible with ERC20, ERC721 and ERC1155
     * @notice To give unlimited approval for ERC20 and ERC1155, set amount to type(uint200).max
     * @notice When approving an ERC721, you MUST set amount to `1`
     * @notice When approving an ERC20, you MUST set id to `0`
     *
     * @dev    <h4>Postconditions:</h4>
     * @dev    1. Updates the approval for an operator to use an amount of a specific token / ID combination
     * @dev    2. If the expiration is 0, the approval is valid only in the context of the current block
     * @dev    3. If the expiration is not 0, the approval is valid until the expiration timestamp
     * @dev    4. If the provided amount is type(uint200).max, the approval is unlimited
     *
     * @param  tokenType  The type of token being approved - must be 20, 721 or 1155.
     * @param  token      The address of the token contract
     * @param  id         The token ID
     * @param  operator   The address of the operator
     * @param  amount     The amount of tokens to approve
     * @param  expiration The expiration timestamp of the approval
     */
    function approve(
        uint256 tokenType,
        address token, 
        uint256 id, 
        address operator, 
        uint200 amount, 
        uint48 expiration
    ) external {
        _requireValidTokenType(tokenType);
        _storeApproval(tokenType, token, id, amount, expiration, msg.sender, operator);
    }

    /**
     * @notice Use a signed permit to increase the allowance for a provided operator
     * @notice This function is compatible with ERC20, ERC721 and ERC1155
     * @notice To give unlimited approval for ERC20 and ERC1155, set amount to type(uint200).max
     * @notice When approving an ERC721, you MUST set amount to `1`
     * @notice When approving an ERC20, you MUST set id to `0`
     * @notice An `approvalExpiration` of zero is considered an atomic permit which will use the 
     * @notice current block time as the expiration time when storing the permit data.
     *
     * @dev    - Throws if the permit has expired
     * @dev    - Throws if the permit's nonce has already been used
     * @dev    - Throws if the permit signature is does not recover to the provided owner
     *
     * @dev    <h4>Postconditions:</h4>
     * @dev    1. Updates the approval for an operator to use an amount of a specific token / ID combination
     * @dev    3. Sets the expiration of the approval to the expiration timestamp of the permit
     * @dev    4. If the provided amount is type(uint200).max, the approval is unlimited
     *
     * @param  tokenType            The type of token being approved - must be 20, 721 or 1155.
     * @param  token                Address of the token to approve
     * @param  id                   The token ID
     * @param  nonce                The nonce of the permit
     * @param  amount               The amount of tokens to approve
     * @param  operator             The address of the operator
     * @param  approvalExpiration   The expiration timestamp of the approval
     * @param  sigDeadline          The deadline timestamp for the permit signature
     * @param  owner                The owner of the tokens
     * @param  signedPermit         The permit signature, signed by the owner
     */
    function updateApprovalBySignature(
        uint256 tokenType,
        address token,
        uint256 id,
        uint256 nonce,
        uint200 amount,
        address operator,
        uint48 approvalExpiration,
        uint48 sigDeadline,
        address owner,
        bytes calldata signedPermit
    ) external {
        if (block.timestamp > sigDeadline) {
            revert PermitC__ApprovalTransferPermitExpiredOrUnset();
        }
        _requireValidTokenType(tokenType);
        _checkAndInvalidateNonce(owner, nonce);
        _verifyPermitSignature(
            _hashTypedDataV4(
                PermitHash.hashOnChainApproval(
                    tokenType,
                    token,
                    id,
                    amount,
                    nonce,
                    operator,
                    approvalExpiration,
                    sigDeadline,
                    _masterNonces[owner]
                )
            ),
            signedPermit, 
            owner
        );

        // Expiration of zero is considered an atomic permit which is only valid in the 
        // current block.
        approvalExpiration = approvalExpiration == 0 ? uint48(block.timestamp) : approvalExpiration;

        _storeApproval(tokenType, token, id, amount, approvalExpiration, owner, operator);
    }

    /**
     * @notice Returns the amount of allowance an operator has and it's expiration for a specific token and id
     * @notice If the expiration on the allowance has expired, returns 0
     * @notice To retrieve allowance for ERC20, set id to `0`
     * 
     * @param  owner     The owner of the token
     * @param  operator  The operator of the token
     * @param  tokenType The type of token the allowance is for
     * @param  token     The address of the token contract
     * @param  id        The token ID
     *
     * @return allowedAmount The amount of allowance the operator has
     * @return expiration    The expiration timestamp of the allowance
     */
    function allowance(
        address owner, 
        address operator, 
        uint256 tokenType,
        address token, 
        uint256 id
    ) external view returns (uint256 allowedAmount, uint256 expiration) {
        return _allowance(_transferApprovals, owner, operator, tokenType, token, id, ZERO_BYTES32);
    }

    /**
     * =================================================
     * ================ Signed Transfers ===============
     * =================================================
     */

    /**
     * @notice Registers the combination of a provided string with the `SINGLE_USE_PERMIT_TRANSFER_ADVANCED_TYPEHASH_STUB` 
     * @notice and `PERMIT_ORDER_ADVANCED_TYPEHASH_STUB` to create valid additional data hashes
     *
     * @dev    This function prevents malicious actors from changing the label of the EIP712 hash
     * @dev    to a value that would fool an external user into signing a different message.
     *
     * @dev    <h4>Postconditions:</h4>
     * @dev    1. The provided string is combined with the `SINGLE_USE_PERMIT_TRANSFER_ADVANCED_TYPEHASH_STUB` string
     * @dev    2. The combined string is hashed using keccak256
     * @dev    3. The resulting hash is added to the `_registeredTransferHashes` mapping
     * @dev    4. The provided string is combined with the `PERMIT_ORDER_ADVANCED_TYPEHASH_STUB` string
     * @dev    5. The combined string is hashed using keccak256
     * @dev    6. The resulting hash is added to the `_registeredOrderHashes` mapping
     *
     * @param  additionalDataTypeString The string to register as a valid additional data hash
     */
    function registerAdditionalDataHash(string calldata additionalDataTypeString) external {
        _registeredTransferHashes[
            keccak256(
                bytes(
                    string.concat(
                        SINGLE_USE_PERMIT_TRANSFER_ADVANCED_TYPEHASH_STUB, 
                        additionalDataTypeString
                    )
                )
            )
        ] = true;

        _registeredOrderHashes[
            keccak256(
                bytes(
                    string.concat(
                        PERMIT_ORDER_ADVANCED_TYPEHASH_STUB, 
                        additionalDataTypeString
                    )
                )
            )
        ] = true;
    }

    /**
     * @notice Transfer an ERC721 token from the owner to the recipient using a permit signature.
     *
     * @dev    Be advised that the permitted amount for ERC721 is always inferred to be 1, so signed permitted amount
     * @dev    MUST always be set to 1.
     *
     * @dev    - Throws if the permit is expired
     * @dev    - Throws if the nonce has already been used
     * @dev    - Throws if the permit is not signed by the owner
     * @dev    - Throws if the requested amount exceeds the permitted amount
     * @dev    - Throws if the provided token address does not implement ERC721 transferFrom function
     * @dev    - Returns `false` if the transfer fails
     *
     * @dev    <h4>Postconditions:</h4>
     * @dev    1. Transfers the token from the owner to the recipient
     * @dev    2. The nonce of the permit is marked as used
     * @dev    3. Performs any additional checks in the before and after hooks
     *
     * @param token         The address of the token
     * @param id            The ID of the token
     * @param nonce         The nonce of the permit
     * @param expiration    The expiration timestamp of the permit
     * @param owner         The owner of the token
     * @param to            The address to transfer the tokens to
     * @param signedPermit  The permit signature, signed by the owner
     *
     * @return isError      True if the transfer failed, false otherwise
     */
    function permitTransferFromERC721(
        address token,
        uint256 id,
        uint256 nonce,
        uint256 expiration,
        address owner,
        address to,
        bytes calldata signedPermit
    ) external returns (bool isError) {
        _requireNotPaused(PAUSABLE_PERMITTED_TRANSFER_FROM_ERC721);

        _checkPermitApproval(TOKEN_TYPE_ERC721, token, id, ONE, nonce, expiration, owner, ONE, signedPermit);
        isError = _transferFromERC721(owner, to, token, id);

        if (isError) {
            _restoreNonce(owner, nonce);
        }
    }


    /**
     * @notice Transfers an ERC721 token from the owner to the recipient using a permit signature
     * @notice This function includes additional data to verify on the signature, allowing
     * @notice protocols to extend the validation in one function call. NOTE: before calling this 
     * @notice function you MUST register the stub end of the additional data typestring using
     * @notice the `registerAdditionalDataHash` function.
     *
     * @dev    Be advised that the permitted amount for ERC721 is always inferred to be 1, so signed permitted amount
     * @dev    MUST always be set to 1.
     *
     * @dev    - Throws for any reason permitTransferFromERC721 would.
     * @dev    - Throws if the additional data does not match the signature
     * @dev    - Throws if the provided hash has not been registered as a valid additional data hash
     * @dev    - Throws if the provided hash does not match the provided additional data
     *
     * @dev    <h4>Postconditions:</h4>
     * @dev    1. Transfers the token from the owner to the recipient
     * @dev    2. Performs any additional checks in the before and after hooks
     * @dev    3. The nonce of the permit is marked as used
     * 
     * @param  token                    The address of the token
     * @param  id                       The ID of the token
     * @param  nonce                    The nonce of the permit
     * @param  expiration               The expiration timestamp of the permit
     * @param  owner                    The owner of the token
     * @param  to                       The address to transfer the tokens to
     * @param  additionalData           The additional data to verify on the signature
     * @param  advancedPermitHash       The hash of the additional data
     * @param  signedPermit             The permit signature, signed by the owner
     *
     * @return isError                  True if the transfer failed, false otherwise
     */
    function permitTransferFromWithAdditionalDataERC721(
        address token,
        uint256 id,
        uint256 nonce,
        uint256 expiration,
        address owner,
        address to,
        bytes32 additionalData,
        bytes32 advancedPermitHash,
        bytes calldata signedPermit
   ) external onlyRegisteredTransferAdvancedTypeHash(advancedPermitHash) returns (bool isError) {
        _requireNotPaused(PAUSABLE_PERMITTED_TRANSFER_FROM_ERC721);

        _checkPermitApprovalWithAdditionalDataERC721(
            token,
            id,
            ONE,
            nonce,
            expiration,
            owner,
            ONE,
            signedPermit,
            additionalData,
            advancedPermitHash
        );
        isError = _transferFromERC721(owner, to, token, id);

        if (isError) {
            _restoreNonce(owner, nonce);
        }
    }

    /**
     * @notice Transfer an ERC1155 token from the owner to the recipient using a permit signature
     *
     * @dev    - Throws if the permit is expired
     * @dev    - Throws if the nonce has already been used
     * @dev    - Throws if the permit is not signed by the owner
     * @dev    - Throws if the requested amount exceeds the permitted amount
     * @dev    - Throws if the provided token address does not implement ERC1155 safeTransferFrom function
     * @dev    - Returns `false` if the transfer fails
     *
     * @dev    <h4>Postconditions:</h4>
     * @dev    1. Transfers the token (in the requested amount) from the owner to the recipient
     * @dev    2. The nonce of the permit is marked as used
     * @dev    3. Performs any additional checks in the before and after hooks
     *
     * @param token           The address of the token
     * @param id              The ID of the token
     * @param nonce           The nonce of the permit
     * @param permitAmount    The amount of tokens permitted by the owner
     * @param expiration      The expiration timestamp of the permit
     * @param owner           The owner of the token
     * @param to              The address to transfer the tokens to
     * @param transferAmount  The amount of tokens to transfer
     * @param signedPermit    The permit signature, signed by the owner
     *
     * @return isError        True if the transfer failed, false otherwise
     */
    function permitTransferFromERC1155(
        address token,
        uint256 id,
        uint256 nonce,
        uint256 permitAmount,
        uint256 expiration,
        address owner,
        address to,
        uint256 transferAmount,
        bytes calldata signedPermit
    ) external returns (bool isError) {
        _requireNotPaused(PAUSABLE_PERMITTED_TRANSFER_FROM_ERC1155);

        _checkPermitApproval(TOKEN_TYPE_ERC1155, token, id, permitAmount, nonce, expiration, owner, transferAmount, signedPermit);
        isError = _transferFromERC1155(token, owner, to, id, transferAmount);

        if (isError) {
            _restoreNonce(owner, nonce);
        }
    }

    /**
     * @notice Transfers a token from the owner to the recipient using a permit signature
     * @notice This function includes additional data to verify on the signature, allowing
     * @notice protocols to extend the validation in one function call. NOTE: before calling this 
     * @notice function you MUST register the stub end of the additional data typestring using
     * @notice the `registerAdditionalDataHash` function.
     *
     * @dev    - Throws for any reason permitTransferFrom would.
     * @dev    - Throws if the additional data does not match the signature
     * @dev    - Throws if the provided hash has not been registered as a valid additional data hash
     * @dev    - Throws if the provided hash does not match the provided additional data
     * @dev    - Throws if the provided hash has not been registered as a valid additional data hash
     * @dev    - Returns `false` if the transfer fails
     *
     * @dev    <h4>Postconditions:</h4>
     * @dev    1. Transfers the token (in the requested amount) from the owner to the recipient
     * @dev    2. Performs any additional checks in the before and after hooks
     * @dev    3. The nonce of the permit is marked as used
     *
     * @param  token                    The address of the token
     * @param  id                       The ID of the token
     * @param  nonce                    The nonce of the permit
     * @param  permitAmount             The amount of tokens permitted by the owner
     * @param  expiration               The expiration timestamp of the permit
     * @param  owner                    The owner of the token
     * @param  to                       The address to transfer the tokens to
     * @param  transferAmount           The amount of tokens to transfer
     * @param  additionalData           The additional data to verify on the signature
     * @param  advancedPermitHash       The hash of the additional data
     * @param  signedPermit             The permit signature, signed by the owner
     *
     * @return isError                  True if the transfer failed, false otherwise
     */
    function permitTransferFromWithAdditionalDataERC1155(
        address token,
        uint256 id,
        uint256 nonce,
        uint256 permitAmount,
        uint256 expiration,
        address owner,
        address to,
        uint256 transferAmount,
        bytes32 additionalData,
        bytes32 advancedPermitHash,
        bytes calldata signedPermit
    ) external onlyRegisteredTransferAdvancedTypeHash(advancedPermitHash) returns (bool isError) {
        _requireNotPaused(PAUSABLE_PERMITTED_TRANSFER_FROM_ERC1155);

        _checkPermitApprovalWithAdditionalDataERC1155(
            token,
            id,
            permitAmount,
            nonce,
            expiration,
            owner,
            transferAmount,
            signedPermit,
            additionalData,
            advancedPermitHash
        );
        
        // copy id to top of stack to avoid stack too deep
        uint256 tmpId = id;
        isError = _transferFromERC1155(token, owner, to, tmpId, transferAmount);

        if (isError) {
            _restoreNonce(owner, nonce);
        }
    }

    /**
     * @notice Transfer an ERC20 token from the owner to the recipient using a permit signature.
     *
     * @dev    Be advised that the token ID for ERC20 is always inferred to be 0, so signed token ID
     * @dev    MUST always be set to 0.
     *
     * @dev    - Throws if the permit is expired
     * @dev    - Throws if the nonce has already been used
     * @dev    - Throws if the permit is not signed by the owner
     * @dev    - Throws if the requested amount exceeds the permitted amount
     * @dev    - Throws if the provided token address does not implement ERC20 transferFrom function
     * @dev    - Returns `false` if the transfer fails
     *
     * @dev    <h4>Postconditions:</h4>
     * @dev    1. Transfers the token in the requested amount from the owner to the recipient
     * @dev    2. The nonce of the permit is marked as used
     * @dev    3. Performs any additional checks in the before and after hooks
     *
     * @param token         The address of the token
     * @param nonce         The nonce of the permit
     * @param permitAmount  The amount of tokens permitted by the owner
     * @param expiration    The expiration timestamp of the permit
     * @param owner         The owner of the token
     * @param to            The address to transfer the tokens to
     * @param signedPermit  The permit signature, signed by the owner
     *
     * @return isError      True if the transfer failed, false otherwise
     */
    function permitTransferFromERC20(
        address token,
        uint256 nonce,
        uint256 permitAmount,
        uint256 expiration,
        address owner,
        address to,
        uint256 transferAmount,
        bytes calldata signedPermit
    ) external returns (bool isError) {
        _requireNotPaused(PAUSABLE_PERMITTED_TRANSFER_FROM_ERC20);

        _checkPermitApproval(TOKEN_TYPE_ERC20, token, ZERO, permitAmount, nonce, expiration, owner, transferAmount, signedPermit);
        isError = _transferFromERC20(token, owner, to, ZERO, transferAmount);

        if (isError) {
            _restoreNonce(owner, nonce);
        }
    }

    /**
     * @notice Transfers an ERC20 token from the owner to the recipient using a permit signature
     * @notice This function includes additional data to verify on the signature, allowing
     * @notice protocols to extend the validation in one function call. NOTE: before calling this 
     * @notice function you MUST register the stub end of the additional data typestring using
     * @notice the `registerAdditionalDataHash` function.
     *
     * @dev    Be advised that the token ID for ERC20 is always inferred to be 0, so signed token ID
     * @dev    MUST always be set to 0.
     *
     * @dev    - Throws for any reason permitTransferFromERC20 would.
     * @dev    - Throws if the additional data does not match the signature
     * @dev    - Throws if the provided hash has not been registered as a valid additional data hash
     * @dev    - Throws if the provided hash does not match the provided additional data
     * @dev    - Returns `false` if the transfer fails
     *
     * @dev    <h4>Postconditions:</h4>
     * @dev    1. Transfers the token (in the requested amount) from the owner to the recipient
     * @dev    2. Performs any additional checks in the before and after hooks
     * @dev    3. The nonce of the permit is marked as used
     *
     * @param  token                    The address of the token
     * @param  nonce                    The nonce of the permit
     * @param  permitAmount             The amount of tokens permitted by the owner
     * @param  expiration               The expiration timestamp of the permit
     * @param  owner                    The owner of the token
     * @param  to                       The address to transfer the tokens to
     * @param  transferAmount           The amount of tokens to transfer
     * @param  additionalData           The additional data to verify on the signature
     * @param  advancedPermitHash       The hash of the additional data
     * @param  signedPermit             The permit signature, signed by the owner
     *
     * @return isError                  True if the transfer failed, false otherwise
     */
    function permitTransferFromWithAdditionalDataERC20(
        address token,
        uint256 nonce,
        uint256 permitAmount,
        uint256 expiration,
        address owner,
        address to,
        uint256 transferAmount,
        bytes32 additionalData,
        bytes32 advancedPermitHash,
        bytes calldata signedPermit
    ) external onlyRegisteredTransferAdvancedTypeHash(advancedPermitHash) returns (bool isError) {
        _requireNotPaused(PAUSABLE_PERMITTED_TRANSFER_FROM_ERC20);

        _checkPermitApprovalWithAdditionalDataERC20(
            token,
            ZERO,
            permitAmount,
            nonce,
            expiration,
            owner,
            transferAmount,
            signedPermit,
            additionalData,
            advancedPermitHash
        );
        isError = _transferFromERC20(token, owner, to, ZERO, transferAmount);

        if (isError) {
            _restoreNonce(owner, nonce);
        }
    }

    /**
     * @notice Returns true if the provided hash has been registered as a valid additional data hash for transfers.
     *
     * @param  hash The hash to check
     *
     * @return isRegistered true if the hash is valid, false otherwise
     */
    function isRegisteredTransferAdditionalDataHash(bytes32 hash) external view returns (bool isRegistered) {
        isRegistered = _registeredTransferHashes[hash];
    }

    /**
     * @notice Returns true if the provided hash has been registered as a valid additional data hash for orders.
     *
     * @param  hash The hash to check
     *
     * @return isRegistered true if the hash is valid, false otherwise
     */
    function isRegisteredOrderAdditionalDataHash(bytes32 hash) external view returns (bool isRegistered) {
        isRegistered = _registeredOrderHashes[hash];
    }

    /**
     * =================================================
     * =============== Order Transfers =================
     * =================================================
     */

    /**
     * @notice Transfers an ERC1155 token from the owner to the recipient using a permit signature
     * @notice Order transfers are used to transfer a specific amount of a token from a specific order
     * @notice and allow for multiple uses of the same permit up to the allocated amount. NOTE: before calling this 
     * @notice function you MUST register the stub end of the additional data typestring using
     * @notice the `registerAdditionalDataHash` function.
     *
     * @dev    - Throws if the permit is expired
     * @dev    - Throws if the permit is not signed by the owner
     * @dev    - Throws if the requested amount + amount already filled exceeds the permitted amount
     * @dev    - Throws if the requested amount is less than the minimum fill amount
     * @dev    - Throws if the provided token address does not implement ERC1155 safeTransferFrom function
     * @dev    - Throws if the provided advanced permit hash has not been registered
     * @dev    - Returns `false` if the transfer fails
     *
     * @dev    <h4>Postconditions:</h4>
     * @dev    1. Transfers the token (in the requested amount) from the owner to the recipient
     * @dev    2. Updates the amount filled for the order ID
     * @dev    3. If completely filled, marks the order as filled
     * 
     * @param  signedPermit         The permit signature, signed by the owner
     * @param  orderFillAmounts     The amount of tokens to transfer
     * @param  token                The address of the token
     * @param  id                   The ID of the token
     * @param  owner                The owner of the token
     * @param  to                   The address to transfer the tokens to
     * @param  salt                 The salt of the permit
     * @param  expiration           The expiration timestamp of the permit
     * @param  orderId              The order ID
     * @param  advancedPermitHash   The hash of the additional data
     *
     * @return quantityFilled       The amount of tokens filled
     * @return isError              True if the transfer failed, false otherwise
     */
    function fillPermittedOrderERC1155(
        bytes calldata signedPermit,
        OrderFillAmounts calldata orderFillAmounts,
        address token,
        uint256 id,
        address owner,
        address to,
        uint256 salt,
        uint48 expiration,
        bytes32 orderId,
        bytes32 advancedPermitHash
    ) external onlyRegisteredOrderAdvancedTypeHash(advancedPermitHash) returns (uint256 quantityFilled, bool isError) {
        _requireNotPaused(PAUSABLE_ORDER_TRANSFER_FROM_ERC1155);

        PackedApproval storage orderStatus = _checkOrderTransferERC1155(
            signedPermit,
            orderFillAmounts,
            token,
            id,
            owner,
            salt,
            expiration,
            orderId,
            advancedPermitHash
        );

        (
            quantityFilled,
            isError
        ) = _orderTransfer(
                orderStatus,
                orderFillAmounts,
                token, 
                id, 
                owner, 
                to, 
                orderId,
                _transferFromERC1155
        );

        if (isError) {
            _restoreFillableItems(orderStatus, owner, orderId, quantityFilled, true);
        }
    }

    /**
     * @notice Transfers an ERC20 token from the owner to the recipient using a permit signature
     * @notice Order transfers are used to transfer a specific amount of a token from a specific order
     * @notice and allow for multiple uses of the same permit up to the allocated amount. NOTE: before calling this
     * @notice function you MUST register the stub end of the additional data typestring using
     * @notice the `registerAdditionalDataHash` function.
     *
     * @dev    - Throws if the permit is expired
     * @dev    - Throws if the permit is not signed by the owner
     * @dev    - Throws if the requested amount + amount already filled exceeds the permitted amount
     * @dev    - Throws if the requested amount is less than the minimum fill amount
     * @dev    - Throws if the provided token address does not implement ERC20 transferFrom function
     * @dev    - Throws if the provided advanced permit hash has not been registered
     * @dev    - Returns `false` if the transfer fails
     *
     * @dev    <h4>Postconditions:</h4>
     * @dev    1. Transfers the token (in the requested amount) from the owner to the recipient
     * @dev    2. Updates the amount filled for the order ID
     * @dev    3. If completely filled, marks the order as filled
     *
     * @param  signedPermit         The permit signature, signed by the owner
     * @param  orderFillAmounts     The amount of tokens to transfer
     * @param  token                The address of the token
     * @param  owner                The owner of the token
     * @param  to                   The address to transfer the tokens to
     * @param  salt                 The salt of the permit
     * @param  expiration           The expiration timestamp of the permit
     * @param  orderId              The order ID
     * @param  advancedPermitHash   The hash of the additional data
     *
     * @return quantityFilled       The amount of tokens filled
     * @return isError              True if the transfer failed, false otherwise
     */
    function fillPermittedOrderERC20(
        bytes calldata signedPermit,
        OrderFillAmounts calldata orderFillAmounts,
        address token,
        address owner,
        address to,
        uint256 salt,
        uint48 expiration,
        bytes32 orderId,
        bytes32 advancedPermitHash
    ) external onlyRegisteredOrderAdvancedTypeHash(advancedPermitHash) returns (uint256 quantityFilled, bool isError) {
        _requireNotPaused(PAUSABLE_ORDER_TRANSFER_FROM_ERC20);

        PackedApproval storage orderStatus = _checkOrderTransferERC20(
            signedPermit,
            orderFillAmounts,
            token,
            ZERO,
            owner,
            salt,
            expiration,
            orderId,
            advancedPermitHash
        );

        (
            quantityFilled,
            isError
        ) = _orderTransfer(
                orderStatus,
                orderFillAmounts,
                token, 
                ZERO, 
                owner, 
                to, 
                orderId,
                _transferFromERC20
        );

        if (isError) {
            _restoreFillableItems(orderStatus, owner, orderId, quantityFilled, true);
        }
    }

    /**
     * @notice Closes an outstanding order to prevent further execution of transfers.
     *
     * @dev    - Throws if the order is not in the open state
     *
     * @dev    <h4>Postconditions:</h4>
     * @dev    1. Marks the order as cancelled
     * @dev    2. Sets the order amount to 0
     * @dev    3. Sets the order expiration to 0
     * @dev    4. Emits a OrderClosed event
     *
     * @param  owner      The owner of the token
     * @param  operator   The operator allowed to transfer the token
     * @param  tokenType  The type of token the order is for - must be 20, 721 or 1155.
     * @param  token      The address of the token contract
     * @param  id         The token ID
     * @param  orderId    The order ID
     */
    function closePermittedOrder(
        address owner,
        address operator,
        uint256 tokenType,
        address token,
        uint256 id,
        bytes32 orderId
    ) external {
        if(!(msg.sender == owner || msg.sender == operator)) {
            revert PermitC__CallerMustBeOwnerOrOperator();
        }
        _requireValidTokenType(tokenType);
        PackedApproval storage orderStatus = _getPackedApprovalPtr(_orderApprovals, owner, tokenType, token, id, orderId, operator);
    
        if (orderStatus.state == ORDER_STATE_OPEN) {
            orderStatus.state = ORDER_STATE_CANCELLED;
            orderStatus.amount = 0;
            orderStatus.expiration = 0;
            emit OrderClosed(orderId, owner, operator, true);
        } else {
            revert PermitC__OrderIsEitherCancelledOrFilled();
        }
    }

    /**
     * @notice Returns the amount of allowance an operator has for a specific token and id
     * @notice If the expiration on the allowance has expired, returns 0
     *
     * @dev    Overload of the on chain allowance function for approvals with a specified order ID
     * 
     * @param  owner    The owner of the token
     * @param  operator The operator of the token
     * @param  token    The address of the token contract
     * @param  id       The token ID
     *
     * @return allowedAmount The amount of allowance the operator has
     */
    function allowance(
        address owner, 
        address operator, 
        uint256 tokenType,
        address token, 
        uint256 id, 
        bytes32 orderId
    ) external view returns (uint256 allowedAmount, uint256 expiration) {
        return _allowance(_orderApprovals, owner, operator, tokenType, token, id, orderId);
    }

    /**
     * =================================================
     * ================ Nonce Management ===============
     * =================================================
     */

    /**
     * @notice Invalidates the provided nonce
     *
     * @dev    - Throws if the provided nonce has already been used
     *
     * @dev    <h4>Postconditions:</h4>
     * @dev    1. Sets the provided nonce as used for the sender
     *
     * @param  nonce Nonce to invalidate
     */
    function invalidateUnorderedNonce(uint256 nonce) external {
        _checkAndInvalidateNonce(msg.sender, nonce);
    }

    /**
     * @notice Returns if the provided nonce has been used
     *
     * @param  owner The owner of the token
     * @param  nonce The nonce to check
     *
     * @return isValid true if the nonce is valid, false otherwise
     */
    function isValidUnorderedNonce(address owner, uint256 nonce) external view returns (bool isValid) {
        isValid = ((_unorderedNonces[owner][uint248(nonce >> 8)] >> uint8(nonce)) & ONE) == ZERO;
    }

    /**
     * @notice Revokes all outstanding approvals for the sender
     *
     * @dev    <h4>Postconditions:</h4>
     * @dev    1. Increments the master nonce for the sender
     * @dev    2. All outstanding approvals for the sender are invalidated
     */
    function lockdown() external {
        unchecked {
            _masterNonces[msg.sender]++;
        }

        emit Lockdown(msg.sender);
    }

    /**
     * @notice Returns the master nonce for the provided owner address
     *
     * @param  owner The owner address
     *
     * @return The master nonce
     */
    function masterNonce(address owner) external view returns (uint256) {
        return _masterNonces[owner];
    }

    /**
     * =================================================
     * ============== Transfer Functions ===============
     * =================================================
     */

    /**
     * @notice Transfer an ERC721 token from the owner to the recipient using on chain approvals
     *
     * @dev    Public transfer function overload for approval transfers
     * @dev    - Throws if the provided token address does not implement ERC721 transferFrom function
     * @dev    - Throws if the requested amount exceeds the approved amount
     * @dev    - Throws if the approval is expired
     * @dev    - Returns `false` if the transfer fails
     *
     * @dev    <h4>Postconditions:</h4>
     * @dev    1. Transfers the token (in the requested amount) from the owner to the recipient
     * @dev    2. Decrements the approval amount by the requested amount
     * @dev    3. Performs any additional checks in the before and after hooks
     *
     * @param  owner    The owner of the token
     * @param  to       The recipient of the token
     * @param  token    The address of the token
     * @param  id       The id of the token
     *
     * @return isError  True if the transfer failed, false otherwise
     */
    function transferFromERC721(
        address owner,
        address to,
        address token,
        uint256 id
    ) external returns (bool isError) {
        _requireNotPaused(PAUSABLE_APPROVAL_TRANSFER_FROM_ERC721);

        PackedApproval storage approval = _checkAndUpdateApproval(owner, TOKEN_TYPE_ERC721, token, id, ONE, true);
        isError = _transferFromERC721(owner, to, token, id);

        if (isError) {
            _restoreFillableItems(approval, owner, ZERO_BYTES32, ONE, false);
        }
    }

    /**
     * @notice Transfer an ERC1155 token from the owner to the recipient using on chain approvals
     *
     * @dev    Public transfer function overload for approval transfers
     * @dev    - Throws if the provided token address does not implement ERC1155 safeTransferFrom function
     * @dev    - Throws if the requested amount exceeds the approved amount
     * @dev    - Throws if the approval is expired
     * @dev    - Returns `false` if the transfer fails
     *
     * @dev    <h4>Postconditions:</h4>
     * @dev    1. Transfers the token (in the requested amount) from the owner to the recipient
     * @dev    2. Decrements the approval amount by the requested amount
     * @dev    3. Performs any additional checks in the before and after hooks
     *
     * @param  owner     The owner of the token
     * @param  to       The recipient of the token
     * @param  amount   The amount of the token to transfer
     * @param  token    The address of the token
     * @param  id       The id of the token
     *
     * @return isError  True if the transfer failed, false otherwise
     */
    function transferFromERC1155(
        address owner,
        address to,
        address token,
        uint256 id,
        uint256 amount
    ) external returns (bool isError) {
        _requireNotPaused(PAUSABLE_APPROVAL_TRANSFER_FROM_ERC1155);

        PackedApproval storage approval = _checkAndUpdateApproval(owner, TOKEN_TYPE_ERC1155, token, id, amount, false);
        isError = _transferFromERC1155(token, owner, to, id, amount);

        if (isError) {
            _restoreFillableItems(approval, owner, ZERO_BYTES32, amount, false);
        }
    }

    /**
     * @notice Transfer an ERC20 token from the owner to the recipient using on chain approvals
     *
     * @dev    Public transfer function overload for approval transfers
     * @dev    - Throws if the provided token address does not implement ERC20 transferFrom function
     * @dev    - Throws if the requested amount exceeds the approved amount
     * @dev    - Throws if the approval is expired
     * @dev    - Returns `false` if the transfer fails
     *
     * @dev    <h4>Postconditions:</h4>
     * @dev    1. Transfers the token (in the requested amount) from the owner to the recipient
     * @dev    2. Decrements the approval amount by the requested amount
     * @dev    3. Performs any additional checks in the before and after hooks
     *
     * @param  owner     The owner of the token
     * @param  to       The recipient of the token
     * @param  amount   The amount of the token to transfer
     * @param  token    The address of the token
     *
     * @return isError  True if the transfer failed, false otherwise
     */
    function transferFromERC20(
        address owner,
        address to,
        address token,
        uint256 amount
    ) external returns (bool isError) {
        _requireNotPaused(PAUSABLE_APPROVAL_TRANSFER_FROM_ERC20);

        PackedApproval storage approval = _checkAndUpdateApproval(owner, TOKEN_TYPE_ERC20, token, ZERO, amount, false);
        isError = _transferFromERC20(token, owner, to, ZERO, amount);

        if (isError) {
            _restoreFillableItems(approval, owner, ZERO_BYTES32, amount, false);
        }
    }

    /**
     * @notice  Performs a transfer of an ERC721 token.
     * 
     * @dev     Will **NOT** attempt transfer if `_beforeTransferFrom` hook returns false.
     * @dev     Will **NOT** revert if the transfer is unsucessful.
     * @dev     Invokers **MUST** check `isError` return value to determine success.
     * 
     * @param owner  The owner of the token being transferred
     * @param to     The address to transfer the token to
     * @param token  The token address of the token being transferred
     * @param id     The token id being transferred
     * 
     * @return isError True if the token was not transferred, false if token was transferred
     */
    function _transferFromERC721(
        address owner,
        address to,
        address token,
        uint256 id
    ) private returns (bool isError) {
        isError = _beforeTransferFrom(TOKEN_TYPE_ERC721, token, owner, to, id, ONE);

        if (!isError) {
            try IERC721(token).transferFrom(owner, to, id) { } 
            catch {
                isError = true;
            }
        }
    }

    /**
     * @notice  Performs a transfer of an ERC1155 token.
     * 
     * @dev     Will **NOT** attempt transfer if `_beforeTransferFrom` hook returns false.
     * @dev     Will **NOT** revert if the transfer is unsucessful.
     * @dev     Invokers **MUST** check `isError` return value to determine success.
     * 
     * @param token  The token address of the token being transferred
     * @param owner  The owner of the token being transferred
     * @param to     The address to transfer the token to
     * @param id     The token id being transferred
     * @param amount The quantity of token id to transfer
     * 
     * @return isError True if the token was not transferred, false if token was transferred
     */
    function _transferFromERC1155(
        address token,
        address owner,
        address to,
        uint256 id,
        uint256 amount
    ) private returns (bool isError) {
        isError = _beforeTransferFrom(TOKEN_TYPE_ERC1155, token, owner, to, id, amount);

        if (!isError) {
            try IERC1155(token).safeTransferFrom(owner, to, id, amount, "") { } catch {
                isError = true;
            }
        }
    }

    /**
     * @notice  Performs a transfer of an ERC20 token.
     * 
     * @dev     Will **NOT** attempt transfer if `_beforeTransferFrom` hook returns false.
     * @dev     Will **NOT** revert if the transfer is unsucessful.
     * @dev     Invokers **MUST** check `isError` return value to determine success.
     * 
     * @param token  The token address of the token being transferred
     * @param owner  The owner of the token being transferred
     * @param to     The address to transfer the token to
     * @param amount The quantity of token id to transfer
     * 
     * @return isError True if the token was not transferred, false if token was transferred
     */
    function _transferFromERC20(
        address token,
        address owner,
        address to,
        uint256 /*id*/,
        uint256 amount
      ) private returns (bool isError) {
        isError = _beforeTransferFrom(TOKEN_TYPE_ERC20, token, owner, to, ZERO, amount);

        if (!isError) {
            (bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.transferFrom.selector, owner, to, amount));
            if (!success) {
                isError = true;
            } else if (data.length > 0) {
                isError = !abi.decode(data, (bool));
            }
        }
    }

    /**
     * =================================================
     * ============ Signature Verification =============
     * =================================================
     */

    /**
     * @notice Returns the domain separator used in the permit signature
     *
     * @return domainSeparator The domain separator
     */
    function domainSeparatorV4() external view returns (bytes32 domainSeparator) {
        domainSeparator = _domainSeparatorV4();
    }

    /**
     * @notice  Verifies a permit signature based on the bytes length of the signature provided.
     * 
     * @dev     Throws when -
     * @dev         The bytes signature length is 64 or 65 bytes AND
     * @dev         The ECDSA recovered signer is not the owner AND
     * @dev         The owner's code length is zero OR the owner does not return a valid EIP-1271 response
     * @dev 
     * @dev         OR
     * @dev
     * @dev         The bytes signature length is not 64 or 65 bytes AND
     * @dev         The owner's code length is zero OR the owner does not return a valid EIP-1271 response
     */
    function _verifyPermitSignature(bytes32 digest, bytes calldata signature, address owner) private view {
        if (signature.length == 65) {
            bytes32 r;
            bytes32 s;
            uint8 v;
            // Divide the signature in r, s and v variables
            /// @solidity memory-safe-assembly
            assembly {
                r := calldataload(signature.offset)
                s := calldataload(add(signature.offset, 32))
                v := byte(0, calldataload(add(signature.offset, 64)))
            }
            (bool isError, address signer) = _ecdsaRecover(digest, v, r, s);
            if (owner != signer || isError) {
                _verifyEIP1271Signature(owner, digest, signature);
            }
        } else if (signature.length == 64) {
            bytes32 r;
            bytes32 vs;
            // Divide the signature in r and vs variables
            /// @solidity memory-safe-assembly
            assembly {
                r := calldataload(signature.offset)
                vs := calldataload(add(signature.offset, 32))
            }
            (bool isError, address signer) = _ecdsaRecover(digest, r, vs);
            if (owner != signer || isError) {
                _verifyEIP1271Signature(owner, digest, signature);
            }
        } else {
            _verifyEIP1271Signature(owner, digest, signature);
        }
    }

    /**
     * @notice Verifies an EIP-1271 signature.
     * 
     * @dev    Throws when `signer` code length is zero OR the EIP-1271 call does not
     * @dev    return the correct magic value.
     * 
     * @param signer     The signer address to verify a signature with
     * @param hash       The hash digest to verify with the signer
     * @param signature  The signature to verify
     */
    function _verifyEIP1271Signature(address signer, bytes32 hash, bytes calldata signature) private view {
        if(signer.code.length == 0) {
            revert PermitC__SignatureTransferInvalidSignature();
        }

        if (!_safeIsValidSignature(signer, hash, signature)) {
            revert PermitC__SignatureTransferInvalidSignature();
        }
    }

    /**
     * @notice  Overload of the `_ecdsaRecover` function to unpack the `v` and `s` values
     * 
     * @param digest    The hash digest that was signed
     * @param r         The `r` value of the signature
     * @param vs        The packed `v` and `s` values of the signature
     * 
     * @return isError  True if the ECDSA function is provided invalid inputs
     * @return signer   The recovered address from ECDSA
     */
    function _ecdsaRecover(bytes32 digest, bytes32 r, bytes32 vs) private pure returns (bool isError, address signer) {
        unchecked {
            bytes32 s = vs & UPPER_BIT_MASK;
            uint8 v = uint8(uint256(vs >> 255)) + 27;

            (isError, signer) = _ecdsaRecover(digest, v, r, s);
        }
    }

    /**
     * @notice  Recovers the signer address using ECDSA
     * 
     * @dev     Does **NOT** revert if invalid input values are provided or `signer` is recovered as address(0)
     * @dev     Returns an `isError` value in those conditions that is handled upstream
     * 
     * @param digest    The hash digest that was signed
     * @param v         The `v` value of the signature
     * @param r         The `r` value of the signature
     * @param s         The `s` value of the signature
     * 
     * @return isError  True if the ECDSA function is provided invalid inputs
     * @return signer   The recovered address from ECDSA
     */
    function _ecdsaRecover(bytes32 digest, uint8 v, bytes32 r, bytes32 s) private pure returns (bool isError, address signer) {
        if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
            // Invalid signature `s` value - return isError = true and signer = address(0) to check EIP-1271
            return (true, address(0));
        }

        signer = ecrecover(digest, v, r, s);
        isError = (signer == address(0));
    }

    /**
     * @notice A gas efficient, and fallback-safe way to call the isValidSignature function for EIP-1271.
     *
     * @param signer     The EIP-1271 signer to call to check for a valid signature.
     * @param hash       The hash digest to verify with the EIP-1271 signer.
     * @param signature  The supplied signature to verify.
     * 
     * @return isValid   True if the EIP-1271 signer returns the EIP-1271 magic value.
     */
    function _safeIsValidSignature(
        address signer,
        bytes32 hash,
        bytes calldata signature
    ) private view returns(bool isValid) {
        assembly {
            function _callIsValidSignature(_signer, _hash, _signatureOffset, _signatureLength) -> _isValid {
                let ptr := mload(0x40)
                // store isValidSignature(bytes32,bytes) selector
                mstore(ptr, hex"1626ba7e")
                // store bytes32 hash value in abi encoded location
                mstore(add(ptr, 0x04), _hash)
                // store abi encoded location of the bytes signature data
                mstore(add(ptr, 0x24), 0x40)
                // store bytes signature length
                mstore(add(ptr, 0x44), _signatureLength)
                // copy calldata bytes signature to memory
                calldatacopy(add(ptr, 0x64), _signatureOffset, _signatureLength)
                // calculate data length based on abi encoded data with rounded up signature length
                let dataLength := add(0x64, and(add(_signatureLength, 0x1F), not(0x1F)))
                // update free memory pointer
                mstore(0x40, add(ptr, dataLength))

                // static call _signer with abi encoded data
                // skip return data check if call failed or return data size is not at least 32 bytes
                if and(iszero(lt(returndatasize(), 0x20)), staticcall(gas(), _signer, ptr, dataLength, 0x00, 0x20)) {
                    // check if return data is equal to isValidSignature magic value
                    _isValid := eq(mload(0x00), hex"1626ba7e")
                    leave
                }
            }
            isValid := _callIsValidSignature(signer, hash, signature.offset, signature.length)
        }
    }

    /**
     * =================================================
     * ===================== Hooks =====================
     * =================================================
     */

    /**
     * @dev    This function is empty by default. Override it to add additional logic after the approval transfer.
     * @dev    The function returns a boolean value instead of reverting to indicate if there is an error for more granular control in inheriting protocols.
     */
    function _beforeTransferFrom(uint256 tokenType, address token, address owner, address to, uint256 id, uint256 amount) internal virtual returns (bool isError) {}

    /**
     * =================================================
     * ==================== Internal ===================
     * =================================================
     */

    /**
     * @notice Checks if an advanced permit typehash has been registered with PermitC
     * 
     * @dev    Throws when the typehash has not been registered
     * 
     * @param advancedPermitHash  The permit typehash to check
     */
    function _requireTransferAdvancedPermitHashIsRegistered(bytes32 advancedPermitHash) private view {
        if (!_registeredTransferHashes[advancedPermitHash]) {
            revert PermitC__SignatureTransferPermitHashNotRegistered();
        }
    }

    /**
     * @notice Checks if an advanced permit typehash has been registered with PermitC
     * 
     * @dev    Throws when the typehash has not been registered
     * 
     * @param advancedPermitHash  The permit typehash to check
     */
    function _requireOrderAdvancedPermitHashIsRegistered(bytes32 advancedPermitHash) private view {
        if (!_registeredOrderHashes[advancedPermitHash]) {
            revert PermitC__SignatureTransferPermitHashNotRegistered();
        }
    }

    /**
     * @notice  Invalidates an account nonce if it has not been previously used
     * 
     * @dev     Throws when the nonce was previously used
     * 
     * @param account  The account to invalidate the nonce of
     * @param nonce    The nonce to invalidate
     */
    function _checkAndInvalidateNonce(address account, uint256 nonce) private {
        unchecked {
            if (uint256(_unorderedNonces[account][uint248(nonce >> 8)] ^= (ONE << uint8(nonce))) & 
                (ONE << uint8(nonce)) == ZERO) {
                revert PermitC__NonceAlreadyUsedOrRevoked();
            }
        }
    }

    /**
     * @notice Checks an approval to ensure it is sufficient for the `amount` to send
     * 
     * @dev    Throws when the approval is expired
     * @dev    Throws when the approved amount is insufficient
     * 
     * @param owner            The owner of the token
     * @param tokenType        The type of token
     * @param token            The address of the token
     * @param id               The id of the token
     * @param amount           The amount to deduct from the approval
     * @param zeroOutApproval  True if the approval should be set to zero
     * 
     * @return approval  Storage pointer for the approval data
     */
    function _checkAndUpdateApproval(
        address owner,
        uint256 tokenType,
        address token,
        uint256 id,
        uint256 amount,
        bool zeroOutApproval
    ) private returns (PackedApproval storage approval) {
        approval = _getPackedApprovalPtr(_transferApprovals, owner, tokenType, token, id, ZERO_BYTES32, msg.sender);
        
        if (approval.expiration < block.timestamp) {
            revert PermitC__ApprovalTransferPermitExpiredOrUnset();
        }
        if (approval.amount < amount) {
            revert PermitC__ApprovalTransferExceededPermittedAmount();
        }

        if(zeroOutApproval) {
            approval.amount = 0;
        } else if (approval.amount < type(uint200).max) {
            unchecked {
                approval.amount -= uint200(amount);
            }
        }
    }

    /**
     * @notice  Gets the storage pointer for an approval
     * 
     * @param _approvals  The mapping to retrieve the approval from
     * @param account     The account the approval is from
     * @param tokenType   The type of token the approval is for
     * @param token       The address of the token
     * @param id          The id of the token
     * @param orderId     The order id for the approval
     * @param operator    The operator for the approval
     * 
     * @return approval  Storage pointer for the approval data
     */
    function _getPackedApprovalPtr(
        mapping(bytes32 => mapping(address => PackedApproval)) storage _approvals,
        address account, 
        uint256 tokenType,
        address token, 
        uint256 id,
        bytes32 orderId,
        address operator
    ) private view returns (PackedApproval storage approval) {
        approval = _approvals[_getPackedApprovalKey(account, tokenType, token, id, orderId)][operator];
    }

    /**
     * @notice  Gets the storage key for the mapping for a specific approval
     * 
     * @param owner      The owner of the token
     * @param tokenType  The type of token
     * @param token      The address of the token
     * @param id         The id of the token
     * @param orderId    The order id of the approval
     * 
     * @return key  The key value to use to access the approval in the mapping
     */
    function _getPackedApprovalKey(address owner, uint256 tokenType, address token, uint256 id, bytes32 orderId) private view returns (bytes32 key) {
        key = keccak256(abi.encode(owner, tokenType, token, id, orderId, _masterNonces[owner]));
    }

    /**
     * @notice Checks the permit approval for a single use permit without additional data
     * 
     * @dev    Throws when the `nonce` has already been consumed
     * @dev    Throws when the permit amount is less than the transfer amount
     * @dev    Throws when the permit is expired
     * @dev    Throws when the signature is invalid
     * 
     * @param tokenType       The type of token
     * @param token           The address of the token
     * @param id              The id of the token
     * @param permitAmount    The amount authorized by the owner signature
     * @param nonce           The nonce of the permit
     * @param expiration      The time the permit expires
     * @param owner           The owner of the token
     * @param transferAmount  The amount of tokens requested to transfer
     * @param signedPermit    The signature for the permit
     */
    function _checkPermitApproval(
        uint256 tokenType,
        address token,
        uint256 id,
        uint256 permitAmount,
        uint256 nonce,
        uint256 expiration,
        address owner,
        uint256 transferAmount,
        bytes calldata signedPermit
    ) private {
        bytes32 digest = _hashTypedDataV4(
            PermitHash.hashSingleUsePermit(
                tokenType,
                token,
                id,
                permitAmount,
                nonce,
                expiration,
                _masterNonces[owner]
            )
        );

        _checkPermitData(
            nonce,
            expiration,
            transferAmount,
            permitAmount,
            owner,
            digest,
            signedPermit
        );
    }

    /**
     * @notice  Overload of `_checkPermitApprovalWithAdditionalData` to supply TOKEN_TYPE_ERC1155
     * 
     * @dev     Prevents stack too deep in `permitTransferFromWithAdditionalDataERC1155`
     * @dev     Throws when the `nonce` has already been consumed
     * @dev     Throws when the permit amount is less than the transfer amount
     * @dev     Throws when the permit is expired
     * @dev     Throws when the signature is invalid
     * 
     * @param token               The address of the token
     * @param id                  The id of the token
     * @param permitAmount        The amount authorized by the owner signature
     * @param nonce               The nonce of the permit
     * @param expiration          The time the permit expires
     * @param owner               The owner of the token
     * @param transferAmount      The amount of tokens requested to transfer
     * @param signedPermit        The signature for the permit
     * @param additionalData      The additional data to validate with the permit signature
     * @param advancedPermitHash  The typehash of the permit to use for validating the signature
     */
    function _checkPermitApprovalWithAdditionalDataERC1155(
        address token,
        uint256 id,
        uint256 permitAmount,
        uint256 nonce,
        uint256 expiration,
        address owner,
        uint256 transferAmount,
        bytes calldata signedPermit,
        bytes32 additionalData,
        bytes32 advancedPermitHash
    ) private {
        _checkPermitApprovalWithAdditionalData(
            TOKEN_TYPE_ERC1155,
            token,
            id,
            permitAmount,
            nonce,
            expiration,
            owner,
            transferAmount,
            signedPermit,
            additionalData,
            advancedPermitHash
        );
    }

    /**
     * @notice  Overload of `_checkPermitApprovalWithAdditionalData` to supply TOKEN_TYPE_ERC20
     * 
     * @dev     Prevents stack too deep in `permitTransferFromWithAdditionalDataERC220`
     * @dev     Throws when the `nonce` has already been consumed
     * @dev     Throws when the permit amount is less than the transfer amount
     * @dev     Throws when the permit is expired
     * @dev     Throws when the signature is invalid
     * 
     * @param token               The address of the token
     * @param id                  The id of the token
     * @param permitAmount        The amount authorized by the owner signature
     * @param nonce               The nonce of the permit
     * @param expiration          The time the permit expires
     * @param owner               The owner of the token
     * @param transferAmount      The amount of tokens requested to transfer
     * @param signedPermit        The signature for the permit
     * @param additionalData      The additional data to validate with the permit signature
     * @param advancedPermitHash  The typehash of the permit to use for validating the signature
     */
    function _checkPermitApprovalWithAdditionalDataERC20(
        address token,
        uint256 id,
        uint256 permitAmount,
        uint256 nonce,
        uint256 expiration,
        address owner,
        uint256 transferAmount,
        bytes calldata signedPermit,
        bytes32 additionalData,
        bytes32 advancedPermitHash
    ) private {
        _checkPermitApprovalWithAdditionalData(
            TOKEN_TYPE_ERC20,
            token,
            id,
            permitAmount,
            nonce,
            expiration,
            owner,
            transferAmount,
            signedPermit,
            additionalData,
            advancedPermitHash
        );
    }

    /**
     * @notice  Overload of `_checkPermitApprovalWithAdditionalData` to supply TOKEN_TYPE_ERC721
     * 
     * @dev     Prevents stack too deep in `permitTransferFromWithAdditionalDataERC721`
     * @dev     Throws when the `nonce` has already been consumed
     * @dev     Throws when the permit amount is less than the transfer amount
     * @dev     Throws when the permit is expired
     * @dev     Throws when the signature is invalid
     * 
     * @param token               The address of the token
     * @param id                  The id of the token
     * @param permitAmount        The amount authorized by the owner signature
     * @param nonce               The nonce of the permit
     * @param expiration          The time the permit expires
     * @param owner               The owner of the token
     * @param transferAmount      The amount of tokens requested to transfer
     * @param signedPermit        The signature for the permit
     * @param additionalData      The additional data to validate with the permit signature
     * @param advancedPermitHash  The typehash of the permit to use for validating the signature
     */
    function _checkPermitApprovalWithAdditionalDataERC721(
        address token,
        uint256 id,
        uint256 permitAmount,
        uint256 nonce,
        uint256 expiration,
        address owner,
        uint256 transferAmount,
        bytes calldata signedPermit,
        bytes32 additionalData,
        bytes32 advancedPermitHash
    ) private {
        _checkPermitApprovalWithAdditionalData(
            TOKEN_TYPE_ERC721,
            token,
            id,
            permitAmount,
            nonce,
            expiration,
            owner,
            transferAmount,
            signedPermit,
            additionalData,
            advancedPermitHash
        );
    }

    /**
     * @notice Checks the permit approval for a single use permit with additional data
     * 
     * @dev    Throws when the `nonce` has already been consumed
     * @dev    Throws when the permit amount is less than the transfer amount
     * @dev    Throws when the permit is expired
     * @dev    Throws when the signature is invalid
     * 
     * @param tokenType           The type of token
     * @param token               The address of the token
     * @param id                  The id of the token
     * @param permitAmount        The amount authorized by the owner signature
     * @param nonce               The nonce of the permit
     * @param expiration          The time the permit expires
     * @param owner               The owner of the token
     * @param transferAmount      The amount of tokens requested to transfer
     * @param signedPermit        The signature for the permit
     * @param additionalData      The additional data to validate with the permit signature
     * @param advancedPermitHash  The typehash of the permit to use for validating the signature
     */
    function _checkPermitApprovalWithAdditionalData(
        uint256 tokenType,
        address token,
        uint256 id,
        uint256 permitAmount,
        uint256 nonce,
        uint256 expiration,
        address owner,
        uint256 transferAmount,
        bytes calldata signedPermit,
        bytes32 additionalData,
        bytes32 advancedPermitHash
    ) private {
        bytes32 digest = _getAdvancedTypedDataV4PermitHash(
            tokenType,
            token, 
            id, 
            permitAmount, 
            owner,
            nonce, 
            expiration, 
            additionalData, 
            advancedPermitHash
        );        

        _checkPermitData(
            nonce,
            expiration,
            transferAmount,
            permitAmount,
            owner,
            digest,
            signedPermit
        );
    }

    /**
     * @notice  Checks that a single use permit has not expired, was authorized for the amount
     * @notice  being transferred, has a valid nonce and has a valid signature.
     * 
     * @dev    Throws when the `nonce` has already been consumed
     * @dev    Throws when the permit amount is less than the transfer amount
     * @dev    Throws when the permit is expired
     * @dev    Throws when the signature is invalid
     * 
     * @param nonce           The nonce of the permit
     * @param expiration      The time the permit expires
     * @param transferAmount  The amount of tokens requested to transfer
     * @param permitAmount    The amount authorized by the owner signature
     * @param owner           The owner of the token
     * @param digest          The digest that was signed by the owner
     * @param signedPermit    The signature for the permit
     */
    function _checkPermitData(
        uint256 nonce,
        uint256 expiration, 
        uint256 transferAmount, 
        uint256 permitAmount, 
        address owner, 
        bytes32 digest,
        bytes calldata signedPermit
    ) private {
        if (block.timestamp > expiration) {
            revert PermitC__SignatureTransferExceededPermitExpired();
        }

        if (transferAmount > permitAmount) {
            revert PermitC__SignatureTransferExceededPermittedAmount();
        }

        _checkAndInvalidateNonce(owner, nonce);
        _verifyPermitSignature(digest, signedPermit, owner);
    }

    /**
     * @notice  Stores an approval for future use by `operator` to move tokens on behalf of `owner`
     * 
     * @param tokenType           The type of token
     * @param token               The address of the token
     * @param id                  The id of the token
     * @param amount              The amount authorized by the owner
     * @param expiration          The time the permit expires
     * @param owner               The owner of the token
     * @param operator            The account allowed to transfer the tokens
     */
    function _storeApproval(
        uint256 tokenType,
        address token,
        uint256 id,
        uint200 amount,
        uint48 expiration,
        address owner,
        address operator
    ) private {
        PackedApproval storage approval = _getPackedApprovalPtr(_transferApprovals, owner, tokenType, token, id, ZERO_BYTES32, operator);
        
        approval.expiration = expiration;
        approval.amount = amount;

        emit Approval(owner, token, operator, id, amount, expiration);
    }

    /**
     * @notice  Overload of `_checkOrderTransfer` to supply TOKEN_TYPE_ERC1155
     * 
     * @dev     Prevents stack too deep in `fillPermittedOrderERC1155`
     * @dev     Throws when the order start amount is greater than type(uint200).max
     * @dev     Throws when the order status is not open
     * @dev     Throws when the signature is invalid
     * @dev     Throws when the permit is expired
     * 
     * @param signedPermit        The signature for the permit
     * @param orderFillAmounts    A struct containing the order start, requested fill and minimum fill amounts
     * @param token               The address of the token
     * @param id                  The id of the token
     * @param owner               The owner of the token
     * @param salt                The salt value for the permit
     * @param expiration          The time the permit expires
     * @param orderId             The order id for the permit
     * @param advancedPermitHash  The typehash of the permit to use for validating the signature
     * 
     * @return orderStatus  Storage pointer for the approval data
     */
    function _checkOrderTransferERC1155(
        bytes calldata signedPermit,
        OrderFillAmounts calldata orderFillAmounts,
        address token,
        uint256 id,
        address owner,
        uint256 salt,
        uint48 expiration,
        bytes32 orderId,
        bytes32 advancedPermitHash
    ) private returns (PackedApproval storage orderStatus) {
        orderStatus = _checkOrderTransfer(
            signedPermit,
            orderFillAmounts,
            TOKEN_TYPE_ERC1155,
            token,
            id,
            owner,
            salt,
            expiration,
            orderId,
            advancedPermitHash
        );
    }

    /**
     * @notice  Overload of `_checkOrderTransfer` to supply TOKEN_TYPE_ERC20
     * 
     * @dev     Prevents stack too deep in `fillPermittedOrderERC20`
     * @dev     Throws when the order start amount is greater than type(uint200).max
     * @dev     Throws when the order status is not open
     * @dev     Throws when the signature is invalid
     * @dev     Throws when the permit is expired
     * 
     * @param signedPermit        The signature for the permit
     * @param orderFillAmounts    A struct containing the order start, requested fill and minimum fill amounts
     * @param token               The address of the token
     * @param id                  The id of the token
     * @param owner               The owner of the token
     * @param salt                The salt value for the permit
     * @param expiration          The time the permit expires
     * @param orderId             The order id for the permit
     * @param advancedPermitHash  The typehash of the permit to use for validating the signature
     * 
     * @return orderStatus  Storage pointer for the approval data
     */
    function _checkOrderTransferERC20(
        bytes calldata signedPermit,
        OrderFillAmounts calldata orderFillAmounts,
        address token,
        uint256 id,
        address owner,
        uint256 salt,
        uint48 expiration,
        bytes32 orderId,
        bytes32 advancedPermitHash
    ) private returns (PackedApproval storage orderStatus) {
        orderStatus = _checkOrderTransfer(
            signedPermit,
            orderFillAmounts,
            TOKEN_TYPE_ERC20,
            token,
            id,
            owner,
            salt,
            expiration,
            orderId,
            advancedPermitHash
        );
    }

    /**
     * @notice  Validates an order transfer to check order start amount, status, signature if not previously
     * @notice  opened, and expiration.
     * 
     * @dev     Throws when the order start amount is greater than type(uint200).max
     * @dev     Throws when the order status is not open
     * @dev     Throws when the signature is invalid
     * @dev     Throws when the permit is expired
     * 
     * @param signedPermit        The signature for the permit
     * @param orderFillAmounts    A struct containing the order start, requested fill and minimum fill amounts
     * @param tokenType           The type of token
     * @param token               The address of the token
     * @param id                  The id of the token
     * @param owner               The owner of the token
     * @param salt                The salt value for the permit
     * @param expiration          The time the permit expires
     * @param orderId             The order id for the permit
     * @param advancedPermitHash  The typehash of the permit to use for validating the signature
     * 
     * @return orderStatus  Storage pointer for the approval data
     */
    function _checkOrderTransfer(
        bytes calldata signedPermit,
        OrderFillAmounts calldata orderFillAmounts,
        uint256 tokenType,
        address token,
        uint256 id,
        address owner,
        uint256 salt,
        uint48 expiration,
        bytes32 orderId,
        bytes32 advancedPermitHash
    ) private returns (PackedApproval storage orderStatus) {
        if (orderFillAmounts.orderStartAmount > type(uint200).max) {
            revert PermitC__AmountExceedsStorageMaximum();
        }

        orderStatus = _getPackedApprovalPtr(_orderApprovals, owner, tokenType, token, id, orderId, msg.sender);

        if (orderStatus.state == ORDER_STATE_OPEN) {
            if (orderStatus.amount == 0) {
                _verifyPermitSignature(
                    _getAdvancedTypedDataV4PermitHash(
                        tokenType,
                        token, 
                        id, 
                        orderFillAmounts.orderStartAmount,
                        owner,
                        salt, 
                        expiration, 
                        orderId, 
                        advancedPermitHash
                    ), 
                    signedPermit, 
                    owner
                );

                orderStatus.amount = uint200(orderFillAmounts.orderStartAmount);
                orderStatus.expiration = expiration;   
                emit OrderOpened(orderId, owner, msg.sender, orderFillAmounts.orderStartAmount);
            }

            if (block.timestamp > orderStatus.expiration) {
                revert PermitC__SignatureTransferExceededPermitExpired();
            }
        } else {
            revert PermitC__OrderIsEitherCancelledOrFilled();
        }
    }

    /**
     * @notice  Checks the order fill amounts against approval data and transfers tokens, updates
     * @notice  approval if the fill results in the order being closed.
     * 
     * @dev     Throws when the amount to fill is less than the minimum fill amount
     * 
     * @param orderStatus         Storage pointer for the approval data
     * @param orderFillAmounts    A struct containing the order start, requested fill and minimum fill amounts
     * @param token               The address of the token
     * @param id                  The id of the token
     * @param owner               The owner of the token
     * @param to                  The address to send the tokens to
     * @param orderId             The order id for the permit
     * @param _transferFrom       Function pointer of the transfer function to send tokens with
     * 
     * @return quantityFilled     The number of tokens filled in the order
     * @return isError            True if there was an error transferring tokens, false otherwise
     */
    function _orderTransfer(
        PackedApproval storage orderStatus,
        OrderFillAmounts calldata orderFillAmounts,
        address token,
        uint256 id,
        address owner,
        address to,
        bytes32 orderId,
        function (address, address, address, uint256, uint256) internal returns (bool) _transferFrom
    ) private returns (uint256 quantityFilled, bool isError) {
        quantityFilled = orderFillAmounts.requestedFillAmount;
        
        if (quantityFilled > orderStatus.amount) {
            quantityFilled = orderStatus.amount;
        }

        if (quantityFilled < orderFillAmounts.minimumFillAmount) {
            revert PermitC__UnableToFillMinimumRequestedQuantity();
        }

        unchecked {
            orderStatus.amount -= uint200(quantityFilled);
            emit OrderFilled(orderId, owner, msg.sender, quantityFilled);
        }

        if (orderStatus.amount == 0) {
            orderStatus.state = ORDER_STATE_FILLED;
            emit OrderClosed(orderId, owner, msg.sender, false);
        }

        isError = _transferFrom(token, owner, to, id, quantityFilled);
    }

    /**
     * @notice  Restores an account's nonce when a transfer was not successful
     * 
     * @dev     Throws when the nonce was not already consumed
     * 
     * @param account  The account to restore the nonce of
     * @param nonce    The nonce to restore
     */
    function _restoreNonce(address account, uint256 nonce) private {
        unchecked {
            if (uint256(_unorderedNonces[account][uint248(nonce >> 8)] ^= (ONE << uint8(nonce))) & 
                (ONE << uint8(nonce)) != ZERO) {
                revert PermitC__NonceNotUsedOrRevoked();
            }
        }
    }

    /**
     * @notice  Restores an approval amount when a transfer was not successful
     * 
     * @param approval        Storage pointer for the approval data
     * @param owner           The owner of the tokens
     * @param orderId         The order id to restore approval amount on
     * @param unfilledAmount  The amount that was not filled on the order
     * @param isOrderPermit   True if the fill restoration is for an permit order
     */
    function _restoreFillableItems(
        PackedApproval storage approval,
        address owner,
        bytes32 orderId,
        uint256 unfilledAmount,
        bool isOrderPermit
    ) private {
        if (unfilledAmount > 0) {
            if (isOrderPermit) {
                // Order permits always deduct amount and must be restored
                unchecked {
                    approval.amount += uint200(unfilledAmount);
                }

                approval.state = ORDER_STATE_OPEN;
                emit OrderRestored(orderId, owner, unfilledAmount);
            } else if (approval.amount < type(uint200).max) {
                // Stored approvals only deduct amount 
                unchecked {
                    approval.amount += uint200(unfilledAmount);
                }
            }
        }
    }

    function _requireValidTokenType(uint256 tokenType) private pure {
        if(!(
            tokenType == TOKEN_TYPE_ERC721 || 
            tokenType == TOKEN_TYPE_ERC1155 || 
            tokenType == TOKEN_TYPE_ERC20
            )
        ) {
            revert PermitC__InvalidTokenType();
        }
    }

    /**
     * @notice  Generates an EIP-712 digest for a permit
     * 
     * @param tokenType           The type of token
     * @param token               The address of the token
     * @param id                  The id of the token
     * @param amount              The amount authorized by the owner signature
     * @param owner               The owner of the token
     * @param nonce               The nonce for the permit
     * @param expiration          The time the permit expires
     * @param additionalData      The additional data to validate with the permit signature
     * @param advancedPermitHash  The typehash of the permit to use for validating the signature
     * 
     * @return digest  The EIP-712 digest of the permit data
     */
    function _getAdvancedTypedDataV4PermitHash(
        uint256 tokenType,
        address token,
        uint256 id,
        uint256 amount,
        address owner,
        uint256 nonce,
        uint256 expiration,
        bytes32 additionalData,
        bytes32 advancedPermitHash
    ) private view returns (bytes32 digest) {
        // cache masterNonce on stack to avoid stack too deep
        uint256 masterNonce_ = _masterNonces[owner];
        digest = 
            _hashTypedDataV4(
                PermitHash.hashSingleUsePermitWithAdditionalData(
                    tokenType,
                    token, 
                    id, 
                    amount, 
                    nonce, 
                    expiration, 
                    additionalData, 
                    advancedPermitHash, 
                    masterNonce_
                )
            );
    }

    /**
     * @notice  Returns the current allowed amount and expiration for a stored permit
     * 
     * @dev     Returns zero allowed if the permit has expired
     * 
     * @param _approvals  The mapping to retrieve the approval from
     * @param owner       The account the approval is from
     * @param operator    The operator for the approval
     * @param tokenType   The type of token the approval is for
     * @param token       The address of the token
     * @param id          The id of the token
     * @param orderId     The order id for the approval
     * 
     * @return allowedAmount  The amount authorized by the approval, zero if the permit has expired
     * @return expiration     The expiration of the approval
     */
    function _allowance(
        mapping(bytes32 => mapping(address => PackedApproval)) storage _approvals,
        address owner, 
        address operator, 
        uint256 tokenType, 
        address token, 
        uint256 id, 
        bytes32 orderId
    ) private view returns (uint256 allowedAmount, uint256 expiration) {
        PackedApproval storage allowed = _getPackedApprovalPtr(_approvals, owner, tokenType, token, id, orderId, operator);
        allowedAmount = allowed.expiration < block.timestamp ? 0 : allowed.amount;
        expiration = allowed.expiration;
    }

    /**
     * @notice  Allows the owner of the PermitC contract to access pausable admin functions
     * 
     * @dev     May be overriden by an inheriting contract to provide alternative permission structure
     */
    function _requireCallerHasPausePermissions() internal view virtual override {
        _checkOwner();
    }
}

File 8 of 31 : ERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)

pragma solidity ^0.8.0;

import "./IERC165.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 *
 * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

File 9 of 31 : EnumerableSet.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.

pragma solidity ^0.8.0;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 *
 * [WARNING]
 * ====
 * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
 * unusable.
 * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
 *
 * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
 * array of EnumerableSet.
 * ====
 */
library EnumerableSet {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

    struct Set {
        // Storage of set values
        bytes32[] _values;
        // Position of the value in the `values` array, plus 1 because index 0
        // means a value is not in the set.
        mapping(bytes32 => uint256) _indexes;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._indexes[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function _remove(Set storage set, bytes32 value) private returns (bool) {
        // We read and store the value's index to prevent multiple reads from the same storage slot
        uint256 valueIndex = set._indexes[value];

        if (valueIndex != 0) {
            // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 toDeleteIndex = valueIndex - 1;
            uint256 lastIndex = set._values.length - 1;

            if (lastIndex != toDeleteIndex) {
                bytes32 lastValue = set._values[lastIndex];

                // Move the last value to the index where the value to delete is
                set._values[toDeleteIndex] = lastValue;
                // Update the index for the moved value
                set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
            }

            // Delete the slot where the moved value was stored
            set._values.pop();

            // Delete the index for the deleted slot
            delete set._indexes[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function _contains(Set storage set, bytes32 value) private view returns (bool) {
        return set._indexes[value] != 0;
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function _at(Set storage set, uint256 index) private view returns (bytes32) {
        return set._values[index];
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function _values(Set storage set) private view returns (bytes32[] memory) {
        return set._values;
    }

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _add(set._inner, value);
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _remove(set._inner, value);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
        return _contains(set._inner, value);
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
        return _at(set._inner, index);
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
        bytes32[] memory store = _values(set._inner);
        bytes32[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(AddressSet storage set, address value) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(AddressSet storage set, address value) internal returns (bool) {
        return _remove(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(AddressSet storage set, address value) internal view returns (bool) {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(AddressSet storage set, uint256 index) internal view returns (address) {
        return address(uint160(uint256(_at(set._inner, index))));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(AddressSet storage set) internal view returns (address[] memory) {
        bytes32[] memory store = _values(set._inner);
        address[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }

    // UintSet

    struct UintSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(UintSet storage set, uint256 value) internal returns (bool) {
        return _remove(set._inner, bytes32(value));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(UintSet storage set, uint256 value) internal view returns (bool) {
        return _contains(set._inner, bytes32(value));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(UintSet storage set, uint256 index) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(UintSet storage set) internal view returns (uint256[] memory) {
        bytes32[] memory store = _values(set._inner);
        uint256[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }
}

File 10 of 31 : Tstorish.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

contract Tstorish {
    // Declare a storage variable indicating if TSTORE support has been
    // activated post-deployment.
    bool private _tstoreSupport;

    /*
     * ------------------------------------------------------------------------+
     * Opcode      | Mnemonic         | Stack              | Memory            |
     * ------------------------------------------------------------------------|
     * 60 0x02     | PUSH1 0x02       | 0x02               |                   |
     * 60 0x1e     | PUSH1 0x1e       | 0x1e 0x02          |                   |
     * 61 0x3d5c   | PUSH2 0x3d5c     | 0x3d5c 0x1e 0x02   |                   |
     * 3d          | RETURNDATASIZE   | 0 0x3d5c 0x1e 0x02 |                   |
     *                                                                         |
     * :: store deployed bytecode in memory: (3d) RETURNDATASIZE (5c) TLOAD :: |
     * 52          | MSTORE           | 0x1e 0x02          | [0..0x20): 0x3d5c |
     * f3          | RETURN           |                    | [0..0x20): 0x3d5c |
     * ------------------------------------------------------------------------+
     */
    uint256 constant _TLOAD_TEST_PAYLOAD = 0x6002_601e_613d5c_3d_52_f3;
    uint256 constant _TLOAD_TEST_PAYLOAD_LENGTH = 0x0a;
    uint256 constant _TLOAD_TEST_PAYLOAD_OFFSET = 0x16;

    // Declare an immutable variable to store the initial TSTORE support status.
    bool private immutable _tstoreInitialSupport;

    // Declare an immutable variable to store the tstore test contract address.
    address private immutable _tloadTestContract;

    // Declare a few custom revert error types.
    error TStoreAlreadyActivated();
    error TStoreNotSupported();
    error TloadTestContractDeploymentFailed();
    error OnlyDirectCalls();

    /**
     * @dev Determine TSTORE availability during deployment. This involves
     *      attempting to deploy a contract that utilizes TLOAD as part of the
     *      contract construction bytecode, and configuring initial support for
     *      using TSTORE in place of SSTORE based on the result.
     */
    constructor() {
        // Deploy the contract testing TLOAD support and store the address.
        address tloadTestContract = _prepareTloadTest();

        // Ensure the deployment was successful.
        if (tloadTestContract == address(0)) {
            revert TloadTestContractDeploymentFailed();
        }

        // Determine if TSTORE is supported.
        bool tstoreInitialSupport = _testTload(tloadTestContract);

        // Store the result as an immutable.
        _tstoreInitialSupport = tstoreInitialSupport;

        // Set the address of the deployed TLOAD test contract as an immutable.
        _tloadTestContract = tloadTestContract;
    }

    /**
     * @dev External function to activate TSTORE usage. Does not need to be
     *      called if TSTORE is supported from deployment, and only needs to be
     *      called once. Reverts if TSTORE has already been activated or if the
     *      opcode is not available. Note that this must be called directly from
     *      an externally-owned account to avoid potential reentrancy issues.
     */
    function __activateTstore() external {
        // Ensure this function is triggered from an externally-owned account.
        if (msg.sender != tx.origin) {
            revert OnlyDirectCalls();
        }

        // Determine if TSTORE can potentially be activated.
        if (_tstoreInitialSupport || _tstoreSupport) {
            revert TStoreAlreadyActivated();
        }

        // Determine if TSTORE can be activated and revert if not.
        if (!_testTload(_tloadTestContract)) {
            revert TStoreNotSupported();
        }

        // Mark TSTORE as activated.
        _tstoreSupport = true;
    }

    /**
     * @dev Internal function to set a TSTORISH value.
     *
     * @param storageSlot The slot to write the TSTORISH value to.
     * @param value       The value to write to the given storage slot.
     */
    function _setTstorish(uint256 storageSlot, uint256 value) internal {
        if (_tstoreInitialSupport) {
            assembly {
                tstore(storageSlot, value)
            }
        } else if (_tstoreSupport) {
            assembly {
                tstore(storageSlot, value)
            }
        } else {
            assembly {
                sstore(storageSlot, value)
            }
        }
    }

    /**
     * @dev Internal function to read a TSTORISH value.
     *
     * @param storageSlot The slot to read the TSTORISH value from.
     *
     * @return value The TSTORISH value at the given storage slot.
     */
    function _getTstorish(
        uint256 storageSlot
    ) internal view returns (uint256 value) {
        if (_tstoreInitialSupport) {
            assembly {
                value := tload(storageSlot)
            }
        } else if (_tstoreSupport) {
            assembly {
                value := tload(storageSlot)
            }
        } else {
            assembly {
                value := sload(storageSlot)
            }
        }
    }

    /**
     * @dev Internal function to clear a TSTORISH value.
     *
     * @param storageSlot The slot to clear the TSTORISH value for.
     */
    function _clearTstorish(uint256 storageSlot) internal {
        if (_tstoreInitialSupport) {
            assembly {
                tstore(storageSlot, 0)
            }
        } else if (_tstoreSupport) {
            assembly {
                tstore(storageSlot, 0)
            }
        } else {
            assembly {
                sstore(storageSlot, 0)
            }
        }
    }

    /**
     * @dev Private function to deploy a test contract that utilizes TLOAD as
     *      part of its fallback logic.
     */
    function _prepareTloadTest() private returns (address contractAddress) {
        // Utilize assembly to deploy a contract testing TLOAD support.
        assembly {
            // Write the contract deployment code payload to scratch space.
            mstore(0, _TLOAD_TEST_PAYLOAD)

            // Deploy the contract.
            contractAddress := create(
                0,
                _TLOAD_TEST_PAYLOAD_OFFSET,
                _TLOAD_TEST_PAYLOAD_LENGTH
            )
        }
    }

    /**
     * @dev Private view function to determine if TSTORE/TLOAD are supported by
     *      the current EVM implementation by attempting to call the test
     *      contract, which utilizes TLOAD as part of its fallback logic.
     */
    function _testTload(
        address tloadTestContract
    ) private view returns (bool ok) {
        // Call the test contract, which will perform a TLOAD test. If the call
        // does not revert, then TLOAD/TSTORE is supported. Do not forward all
        // available gas, as all forwarded gas will be consumed on revert.
        (ok, ) = tloadTestContract.staticcall{ gas: gasleft() / 10 }("");
    }
}

File 11 of 31 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

File 12 of 31 : Ownable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _transferOwnership(_msgSender());
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

File 13 of 31 : Errors.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @dev Thrown when a stored approval exceeds type(uint200).max
error PermitC__AmountExceedsStorageMaximum();

/// @dev Thrown when a transfer amount requested exceeds the permitted amount
error PermitC__ApprovalTransferExceededPermittedAmount();

/// @dev Thrown when a transfer is requested after the permit has expired
error PermitC__ApprovalTransferPermitExpiredOrUnset();

/// @dev Thrown when attempting to close an order by an account that is not the owner or operator
error PermitC__CallerMustBeOwnerOrOperator();

/// @dev Thrown when attempting to approve a token type that is not valid for PermitC
error PermitC__InvalidTokenType();

/// @dev Thrown when attempting to invalidate a nonce that has already been used
error PermitC__NonceAlreadyUsedOrRevoked();

/// @dev Thrown when attempting to restore a nonce that has not been used
error PermitC__NonceNotUsedOrRevoked();

/// @dev Thrown when attempting to fill an order that has already been filled or cancelled
error PermitC__OrderIsEitherCancelledOrFilled();

/// @dev Thrown when a transfer amount requested exceeds the permitted amount
error PermitC__SignatureTransferExceededPermittedAmount();

/// @dev Thrown when a transfer is requested after the permit has expired
error PermitC__SignatureTransferExceededPermitExpired();

/// @dev Thrown when attempting to use an advanced permit typehash that is not registered
error PermitC__SignatureTransferPermitHashNotRegistered();

/// @dev Thrown when a permit signature is invalid
error PermitC__SignatureTransferInvalidSignature();

/// @dev Thrown when the remaining fill amount is less than the requested minimum fill
error PermitC__UnableToFillMinimumRequestedQuantity();

File 14 of 31 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC20.sol)

pragma solidity ^0.8.0;

import "../token/ERC20/IERC20.sol";

File 15 of 31 : IERC721.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC721.sol)

pragma solidity ^0.8.0;

import "../token/ERC721/IERC721.sol";

File 16 of 31 : IERC1155.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC1155.sol)

pragma solidity ^0.8.0;

import "../token/ERC1155/IERC1155.sol";

File 17 of 31 : IERC1271.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC1271.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC1271 standard signature validation method for
 * contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271].
 *
 * _Available since v4.1._
 */
interface IERC1271 {
    /**
     * @dev Should return whether the signature provided is valid for the provided data
     * @param hash      Hash of the data to be signed
     * @param signature Signature byte array associated with _data
     */
    function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue);
}

File 18 of 31 : Ownable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

import {Context} from "@openzeppelin/contracts/utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    error Ownable__CallerIsNotOwner();
    error Ownable__NewOwnerIsZeroAddress();

    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _transferOwnership(_msgSender());
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        if(owner() != _msgSender()) revert Ownable__CallerIsNotOwner();
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        if(newOwner == address(0)) revert Ownable__NewOwnerIsZeroAddress();
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

File 19 of 31 : EIP712.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/EIP712.sol)

pragma solidity ^0.8.8;

import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";

/**
 * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
 *
 * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
 * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
 * they need in their contracts using a combination of `abi.encode` and `keccak256`.
 *
 * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
 * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
 * ({_hashTypedDataV4}).
 *
 * The implementation of the domain separator was designed to be as efficient as possible while still properly updating
 * the chain id to protect against replay attacks on an eventual fork of the chain.
 *
 * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
 * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
 *
 * NOTE: In the upgradeable version of this contract, the cached values will correspond to the address, and the domain
 * separator of the implementation contract. This will cause the `_domainSeparatorV4` function to always rebuild the
 * separator from the immutable values, which is cheaper than accessing a cached version in cold storage.
 *
 * _Available since v3.4._
 *
 * @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment
 */
abstract contract EIP712 {
    bytes32 private constant _TYPE_HASH =
        keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");

    // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
    // invalidate the cached domain separator if the chain id changes.
    bytes32 private immutable _cachedDomainSeparator;
    uint256 private immutable _cachedChainId;

    bytes32 private immutable _hashedName;
    bytes32 private immutable _hashedVersion;

    /**
     * @dev Initializes the domain separator and parameter caches.
     *
     * The meaning of `name` and `version` is specified in
     * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
     *
     * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
     * - `version`: the current major version of the signing domain.
     *
     * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
     * contract upgrade].
     */
    constructor(string memory name, string memory version) {
        _hashedName = keccak256(bytes(name));
        _hashedVersion = keccak256(bytes(version));

        _cachedChainId = block.chainid;
        _cachedDomainSeparator = _buildDomainSeparator();
    }

    /**
     * @dev Returns the domain separator for the current chain.
     */
    function _domainSeparatorV4() internal view returns (bytes32) {
        if (block.chainid == _cachedChainId) {
            return _cachedDomainSeparator;
        } else {
            return _buildDomainSeparator();
        }
    }

    function _buildDomainSeparator() private view returns (bytes32) {
        return keccak256(abi.encode(_TYPE_HASH, _hashedName, _hashedVersion, block.chainid, address(this)));
    }

    /**
     * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
     * function returns the hash of the fully encoded EIP712 message for this domain.
     *
     * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
     *
     * ```solidity
     * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
     *     keccak256("Mail(address to,string contents)"),
     *     mailTo,
     *     keccak256(bytes(mailContents))
     * )));
     * address signer = ECDSA.recover(digest, signature);
     * ```
     */
    function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
        return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash);
    }
}

File 20 of 31 : Constants.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @dev Constant bytes32 value of 0x000...000
bytes32 constant ZERO_BYTES32 = bytes32(0);

/// @dev Constant value of 0
uint256 constant ZERO = 0;
/// @dev Constant value of 1
uint256 constant ONE = 1;

/// @dev Constant value representing an open order in storage
uint8 constant ORDER_STATE_OPEN = 0;
/// @dev Constant value representing a filled order in storage
uint8 constant ORDER_STATE_FILLED = 1;
/// @dev Constant value representing a cancelled order in storage
uint8 constant ORDER_STATE_CANCELLED = 2;

/// @dev Constant value representing the ERC721 token type for signatures and transfer hooks
uint256 constant TOKEN_TYPE_ERC721 = 721;
/// @dev Constant value representing the ERC1155 token type for signatures and transfer hooks
uint256 constant TOKEN_TYPE_ERC1155 = 1155;
/// @dev Constant value representing the ERC20 token type for signatures and transfer hooks
uint256 constant TOKEN_TYPE_ERC20 = 20;

/// @dev Constant value to mask the upper bits of a signature that uses a packed `vs` value to extract `s`
bytes32 constant UPPER_BIT_MASK = 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;

/// @dev EIP-712 typehash used for validating signature based stored approvals
bytes32 constant UPDATE_APPROVAL_TYPEHASH =
    keccak256("UpdateApprovalBySignature(uint256 tokenType,address token,uint256 id,uint256 amount,uint256 nonce,address operator,uint256 approvalExpiration,uint256 sigDeadline,uint256 masterNonce)");

/// @dev EIP-712 typehash used for validating a single use permit without additional data
bytes32 constant SINGLE_USE_PERMIT_TYPEHASH =
    keccak256("PermitTransferFrom(uint256 tokenType,address token,uint256 id,uint256 amount,uint256 nonce,address operator,uint256 expiration,uint256 masterNonce)");

/// @dev EIP-712 typehash used for validating a single use permit with additional data
string constant SINGLE_USE_PERMIT_TRANSFER_ADVANCED_TYPEHASH_STUB =
    "PermitTransferFromWithAdditionalData(uint256 tokenType,address token,uint256 id,uint256 amount,uint256 nonce,address operator,uint256 expiration,uint256 masterNonce,";

/// @dev EIP-712 typehash used for validating an order permit that updates storage as it fills
string constant PERMIT_ORDER_ADVANCED_TYPEHASH_STUB =
    "PermitOrderWithAdditionalData(uint256 tokenType,address token,uint256 id,uint256 amount,uint256 salt,address operator,uint256 expiration,uint256 masterNonce,";

/// @dev Pausable flag for stored approval transfers of ERC721 assets
uint256 constant PAUSABLE_APPROVAL_TRANSFER_FROM_ERC721 = 1 << 0;
/// @dev Pausable flag for stored approval transfers of ERC1155 assets
uint256 constant PAUSABLE_APPROVAL_TRANSFER_FROM_ERC1155 = 1 << 1;
/// @dev Pausable flag for stored approval transfers of ERC20 assets
uint256 constant PAUSABLE_APPROVAL_TRANSFER_FROM_ERC20 = 1 << 2;

/// @dev Pausable flag for single use permit transfers of ERC721 assets
uint256 constant PAUSABLE_PERMITTED_TRANSFER_FROM_ERC721 = 1 << 3;
/// @dev Pausable flag for single use permit transfers of ERC1155 assets
uint256 constant PAUSABLE_PERMITTED_TRANSFER_FROM_ERC1155 = 1 << 4;
/// @dev Pausable flag for single use permit transfers of ERC20 assets
uint256 constant PAUSABLE_PERMITTED_TRANSFER_FROM_ERC20 = 1 << 5;

/// @dev Pausable flag for order fill transfers of ERC1155 assets
uint256 constant PAUSABLE_ORDER_TRANSFER_FROM_ERC1155 = 1 << 6;
/// @dev Pausable flag for order fill transfers of ERC20 assets
uint256 constant PAUSABLE_ORDER_TRANSFER_FROM_ERC20 = 1 << 7;

File 21 of 31 : DataTypes.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @dev Storage data struct for stored approvals and order approvals
struct PackedApproval {
    // Only used for partial fill position 1155 transfers
    uint8 state;
    // Amount allowed
    uint200 amount;
    // Permission expiry
    uint48 expiration;
}

/// @dev Calldata data struct for order fill amounts
struct OrderFillAmounts {
    uint256 orderStartAmount;
    uint256 requestedFillAmount;
    uint256 minimumFillAmount;
}

File 22 of 31 : PermitHash.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import {SINGLE_USE_PERMIT_TYPEHASH, UPDATE_APPROVAL_TYPEHASH} from "../Constants.sol";

library PermitHash {

    /**
     * @notice  Hashes the permit data for a stored approval
     * 
     * @param tokenType           The type of token
     * @param token               The address of the token
     * @param id                  The id of the token
     * @param amount              The amount authorized by the owner signature
     * @param nonce               The nonce for the permit
     * @param operator            The account that is allowed to use the permit
     * @param approvalExpiration  The time the permit approval expires
     * @param sigDeadline         The deadline for submitting the permit onchain
     * @param masterNonce         The signers master nonce
     * 
     * @return hash  The hash of the permit data
     */
    function hashOnChainApproval(
        uint256 tokenType,
        address token,
        uint256 id,
        uint256 amount,
        uint256 nonce,
        address operator, 
        uint256 approvalExpiration,
        uint256 sigDeadline,
        uint256 masterNonce
    ) internal pure returns (bytes32 hash) {
        hash = keccak256(
            abi.encode(
                UPDATE_APPROVAL_TYPEHASH,
                tokenType,
                token,
                id,
                amount,
                nonce,
                operator,
                approvalExpiration,
                sigDeadline,
                masterNonce
            )
        );
    }

    /**
     * @notice  Hashes the permit data with the single user permit without additional data typehash
     * 
     * @param tokenType               The type of token
     * @param token                   The address of the token
     * @param id                      The id of the token
     * @param amount                  The amount authorized by the owner signature
     * @param nonce                   The nonce for the permit
     * @param expiration              The time the permit expires
     * @param masterNonce             The signers master nonce
     * 
     * @return hash  The hash of the permit data
     */
    function hashSingleUsePermit(
        uint256 tokenType,
        address token,
        uint256 id,
        uint256 amount,
        uint256 nonce,
        uint256 expiration,
        uint256 masterNonce
    ) internal view returns (bytes32 hash) {
        hash = keccak256(
            abi.encode(
                SINGLE_USE_PERMIT_TYPEHASH,
                tokenType,
                token,
                id,
                amount,
                nonce,
                msg.sender,
                expiration,
                masterNonce
            )
        );
    }

    /**
     * @notice  Hashes the permit data with the supplied typehash
     * 
     * @param tokenType               The type of token
     * @param token                   The address of the token
     * @param id                      The id of the token
     * @param amount                  The amount authorized by the owner signature
     * @param nonce                   The nonce for the permit
     * @param expiration              The time the permit expires
     * @param additionalData          The additional data to validate with the permit signature
     * @param additionalDataTypeHash  The typehash of the permit to use for validating the signature
     * @param masterNonce             The signers master nonce
     * 
     * @return hash  The hash of the permit data with the supplied typehash
     */
    function hashSingleUsePermitWithAdditionalData(
        uint256 tokenType,
        address token,
        uint256 id,
        uint256 amount,
        uint256 nonce,
        uint256 expiration,
        bytes32 additionalData,
        bytes32 additionalDataTypeHash,
        uint256 masterNonce
    ) internal view returns (bytes32 hash) {
        hash = keccak256(
            abi.encode(
                additionalDataTypeHash,
                tokenType,
                token,
                id,
                amount,
                nonce,
                msg.sender,
                expiration,
                masterNonce,
                additionalData
            )
        );
    }
}

File 23 of 31 : IPermitC.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import {OrderFillAmounts} from "../DataTypes.sol";

interface IPermitC {

    /**
     * =================================================
     * ==================== Events =====================
     * =================================================
     */

    /// @dev Emitted when an approval is stored
    event Approval(
        address indexed owner,
        address indexed token,
        address indexed operator,
        uint256 id,
        uint200 amount,
        uint48 expiration
    );

    /// @dev Emitted when a user increases their master nonce
    event Lockdown(address indexed owner);

    /// @dev Emitted when an order is opened
    event OrderOpened(
        bytes32 indexed orderId,
        address indexed owner,
        address indexed operator,
        uint256 fillableQuantity
    );

    /// @dev Emitted when an order has a fill
    event OrderFilled(
        bytes32 indexed orderId,
        address indexed owner,
        address indexed operator,
        uint256 amount
    );

    /// @dev Emitted when an order has been fully filled or cancelled
    event OrderClosed(
        bytes32 indexed orderId, 
        address indexed owner, 
        address indexed operator, 
        bool wasCancellation);

    /// @dev Emitted when an order has an amount restored due to a failed transfer
    event OrderRestored(
        bytes32 indexed orderId,
        address indexed owner,
        uint256 amountRestoredToOrder
    );

    /**
     * =================================================
     * ============== Approval Transfers ===============
     * =================================================
     */
    function approve(uint256 tokenType, address token, uint256 id, address operator, uint200 amount, uint48 expiration) external;

    function updateApprovalBySignature(
        uint256 tokenType,
        address token,
        uint256 id,
        uint256 nonce,
        uint200 amount,
        address operator,
        uint48 approvalExpiration,
        uint48 sigDeadline,
        address owner,
        bytes calldata signedPermit
    ) external;

    function allowance(
        address owner, 
        address operator, 
        uint256 tokenType,
        address token, 
        uint256 id
    ) external view returns (uint256 amount, uint256 expiration);

    /**
     * =================================================
     * ================ Signed Transfers ===============
     * =================================================
     */
    function registerAdditionalDataHash(string memory additionalDataTypeString) external;

    function permitTransferFromERC721(
        address token,
        uint256 id,
        uint256 nonce,
        uint256 expiration,
        address owner,
        address to,
        bytes calldata signedPermit
    ) external returns (bool isError);

    function permitTransferFromWithAdditionalDataERC721(
        address token,
        uint256 id,
        uint256 nonce,
        uint256 expiration,
        address owner,
        address to,
        bytes32 additionalData,
        bytes32 advancedPermitHash,
        bytes calldata signedPermit
    ) external returns (bool isError);

    function permitTransferFromERC1155(
        address token,
        uint256 id,
        uint256 nonce,
        uint256 permitAmount,
        uint256 expiration,
        address owner,
        address to,
        uint256 transferAmount,
        bytes calldata signedPermit
    ) external returns (bool isError);

    function permitTransferFromWithAdditionalDataERC1155(
        address token,
        uint256 id,
        uint256 nonce,
        uint256 permitAmount,
        uint256 expiration,
        address owner,
        address to,
        uint256 transferAmount,
        bytes32 additionalData,
        bytes32 advancedPermitHash,
        bytes calldata signedPermit
    ) external returns (bool isError);

    function permitTransferFromERC20(
        address token,
        uint256 nonce,
        uint256 permitAmount,
        uint256 expiration,
        address owner,
        address to,
        uint256 transferAmount,
        bytes calldata signedPermit
    ) external returns (bool isError);

    function permitTransferFromWithAdditionalDataERC20(
        address token,
        uint256 nonce,
        uint256 permitAmount,
        uint256 expiration,
        address owner,
        address to,
        uint256 transferAmount,
        bytes32 additionalData,
        bytes32 advancedPermitHash,
        bytes calldata signedPermit
    ) external returns (bool isError);

    function isRegisteredTransferAdditionalDataHash(bytes32 hash) external view returns (bool isRegistered);

    function isRegisteredOrderAdditionalDataHash(bytes32 hash) external view returns (bool isRegistered);

    /**
     * =================================================
     * =============== Order Transfers =================
     * =================================================
     */
    function fillPermittedOrderERC1155(
        bytes calldata signedPermit,
        OrderFillAmounts calldata orderFillAmounts,
        address token,
        uint256 id,
        address owner,
        address to,
        uint256 nonce,
        uint48 expiration,
        bytes32 orderId,
        bytes32 advancedPermitHash
    ) external returns (uint256 quantityFilled, bool isError);

    function fillPermittedOrderERC20(
        bytes calldata signedPermit,
        OrderFillAmounts calldata orderFillAmounts,
        address token,
        address owner,
        address to,
        uint256 nonce,
        uint48 expiration,
        bytes32 orderId,
        bytes32 advancedPermitHash
    ) external returns (uint256 quantityFilled, bool isError);

    function closePermittedOrder(
        address owner,
        address operator,
        uint256 tokenType,
        address token,
        uint256 id,
        bytes32 orderId
    ) external;

    function allowance(
        address owner, 
        address operator, 
        uint256 tokenType,
        address token, 
        uint256 id,
        bytes32 orderId
    ) external view returns (uint256 amount, uint256 expiration);


    /**
     * =================================================
     * ================ Nonce Management ===============
     * =================================================
     */
    function invalidateUnorderedNonce(uint256 nonce) external;

    function isValidUnorderedNonce(address owner, uint256 nonce) external view returns (bool isValid);

    function lockdown() external;

    function masterNonce(address owner) external view returns (uint256);

    /**
     * =================================================
     * ============== Transfer Functions ===============
     * =================================================
     */
    function transferFromERC721(
        address from,
        address to,
        address token,
        uint256 id
    ) external returns (bool isError);

    function transferFromERC1155(
        address from,
        address to,
        address token,
        uint256 id,
        uint256 amount
    ) external returns (bool isError);

    function transferFromERC20(
        address from,
        address to,
        address token,
        uint256 amount
    ) external returns (bool isError);

    /**
     * =================================================
     * ============ Signature Verification =============
     * =================================================
     */
    function domainSeparatorV4() external view returns (bytes32);
}

File 24 of 31 : CollateralizedPausableFlags.sol
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

/*
                                                     @@@@@@@@@@@@@@             
                                                    @@@@@@@@@@@@@@@@@@(         
                                                   @@@@@@@@@@@@@@@@@@@@@        
                                                  @@@@@@@@@@@@@@@@@@@@@@@@      
                                                           #@@@@@@@@@@@@@@      
                                                               @@@@@@@@@@@@     
                            @@@@@@@@@@@@@@*                    @@@@@@@@@@@@     
                           @@@@@@@@@@@@@@@     @               @@@@@@@@@@@@     
                          @@@@@@@@@@@@@@@     @                @@@@@@@@@@@      
                         @@@@@@@@@@@@@@@     @@               @@@@@@@@@@@@      
                        @@@@@@@@@@@@@@@     #@@             @@@@@@@@@@@@/       
                        @@@@@@@@@@@@@@.     @@@@@@@@@@@@@@@@@@@@@@@@@@@         
                       @@@@@@@@@@@@@@@     @@@@@@@@@@@@@@@@@@@@@@@@@            
                      @@@@@@@@@@@@@@@     @@@@@@@@@@@@@@@@@@@@@@@@@             
                     @@@@@@@@@@@@@@@     @@@@@@@@@@@@@@@@@@@@@@@@@@@@           
                    @@@@@@@@@@@@@@@     @@@@@&%%%%%%%%&&@@@@@@@@@@@@@@          
                    @@@@@@@@@@@@@@      @@@@@               @@@@@@@@@@@         
                   @@@@@@@@@@@@@@@     @@@@@                 @@@@@@@@@@@        
                  @@@@@@@@@@@@@@@     @@@@@@                 @@@@@@@@@@@        
                 @@@@@@@@@@@@@@@     @@@@@@@                 @@@@@@@@@@@        
                @@@@@@@@@@@@@@@     @@@@@@@                 @@@@@@@@@@@&        
                @@@@@@@@@@@@@@     *@@@@@@@               (@@@@@@@@@@@@         
               @@@@@@@@@@@@@@@     @@@@@@@@             @@@@@@@@@@@@@@          
              @@@@@@@@@@@@@@@     @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@           
             @@@@@@@@@@@@@@@     @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@            
            @@@@@@@@@@@@@@@     @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@              
           .@@@@@@@@@@@@@@     @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                 
           @@@@@@@@@@@@@@%     @@@@@@@@@@@@@@@@@@@@@@@@(                        
          @@@@@@@@@@@@@@@                                                       
         @@@@@@@@@@@@@@@                                                        
        @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                                         
       @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                                          
       @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&                                          
      @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                                           
 
* @title CollateralizedPausableFlags
* @custom:version 1.0.0
* @author Limit Break, Inc.
* @description Collateralized Pausable Flags is an extension for contracts
*              that require features to be pausable in the event of potential
*              or actual threats without incurring a storage read overhead cost
*              during normal operations by using contract starting balance as
*              a signal for checking the paused state.
*
*              Using contract balance to enable checking paused state creates an
*              economic penalty for developers that deploy code that can be 
*              exploited as well as an economic incentive (recovery of collateral)
*              for them to mitigate the threat.
*
*              Developers implementing Collateralized Pausable Flags should consider
*              their risk mitigation strategy and ensure funds are readily available
*              for pausing if ever necessary by setting an appropriate threshold 
*              value and considering use of an escrow contract that can initiate the
*              pause with funds.
*
*              There is no restriction on the depositor as this can be easily 
*              circumvented through a `SELFDESTRUCT` opcode.
*
*              Developers must be aware of potential outflows from the contract that
*              could reduce collateral below the pausable check threshold and protect
*              against those methods when pausing is required.
*/
abstract contract CollateralizedPausableFlags {

    /// @dev Emitted when the pausable flags are updated
    event PausableFlagsUpdated(uint256 previousFlags, uint256 newFlags);

    /// @dev Thrown when an execution path requires a flag to not be paused but it is paused
    error CollateralizedPausableFlags__Paused();
    /// @dev Thrown when an executin path requires a flag to be paused but it is not paused
    error CollateralizedPausableFlags__NotPaused();
    /// @dev Thrown when a call to withdraw funds fails
    error CollateralizedPausableFlags__WithdrawFailed();

    /// @dev Immutable variable that defines the native funds threshold before flags are checked
    uint256 private immutable nativeValueToCheckPauseState;
    /// @dev Flags for current pausable state, each bit is considered a separate flag
    uint256 private pausableFlags;

    /// @dev Immutable pointer for the _requireNotPaused function to use based on value threshold
    function(uint256) internal view immutable _requireNotPaused;
    /// @dev Immutable pointer for the _requirePaused function to use based on value threshold
    function(uint256) internal view immutable _requirePaused;
    /// @dev Immutable pointer for the _getPausableFlags function to use based on value threshold
    function() internal view returns (uint256) immutable _getPausableFlags;

    constructor(uint256 _nativeValueToCheckPauseState) {
        // Optimizes value check at runtime by reducing the stored immutable 
        // value by 1 so that greater than can be used instead of greater 
        // than or equal while allowing the deployment parameter to reflect 
        // the value at which the deployer wants to trigger pause checking.
        // Example: 
        //     Constructed with a value of 1000
        //     Immutable value stored is 999
        //     State checking enabled at 1000 units deposited because
        //     1000 > 999 evaluates true
        if (_nativeValueToCheckPauseState > 0) {
            unchecked {
                _nativeValueToCheckPauseState -= 1;
            }
            _requireNotPaused = _requireNotPausedWithCollateralCheck;
            _requirePaused = _requirePausedWithCollateralCheck;
            _getPausableFlags = _getPausableFlagsWithCollateralCheck;
        } else {
            _requireNotPaused = _requireNotPausedWithoutCollateralCheck;
            _requirePaused = _requirePausedWithoutCollateralCheck;
            _getPausableFlags = _getPausableFlagsWithoutCollateralCheck;
        }

        nativeValueToCheckPauseState = _nativeValueToCheckPauseState;
    }

    /**
     * @dev  Modifier to make a function callable only when the specified flags are not paused
     * @dev  Throws when any of the flags specified are paused
     * 
     * @param _flags  The flags to check for pause state
     */
    modifier whenNotPaused(uint256 _flags) {
        _requireNotPaused(_flags);
        _;
    }

    /**
     * @dev  Modifier to make a function callable only when the specified flags are paused
     * @dev  Throws when any of the flags specified are not paused
     * 
     * @param _flags  The flags to check for pause state
     */
    modifier whenPaused(uint256 _flags) {
        _requirePaused(_flags);
        _;
    }

    /**
     * @dev  Modifier to make a function callable only by a permissioned account
     * @dev  Throws when the caller does not have permission
     */
    modifier onlyPausePermissionedCaller() {
        _requireCallerHasPausePermissions();
        _;
    }

    /**
     * @notice  Updates the pausable flags settings
     * 
     * @dev     Throws when the caller does not have permission
     * @dev     **NOTE:** Pausable flag settings will only take effect if contract balance exceeds 
     * @dev     `nativeValueToPause`
     * 
     * @dev     <h4>Postconditions:</h4>
     * @dev     1. address(this).balance increases by msg.value
     * @dev     2. `pausableFlags` is set to the new value
     * @dev     3. Emits a PausableFlagsUpdated event
     * 
     * @param _pausableFlags  The new pausable flags to set
     */
    function pause(uint256 _pausableFlags) external payable onlyPausePermissionedCaller {
        _setPausableFlags(_pausableFlags);
    }

    /**
     * @notice  Allows any account to supply funds for enabling the pausable checks
     * 
     * @dev     **NOTE:** The threshold check for pausable collateral does not pause
     * @dev     any functions unless the associated pausable flag is set.
     */
    function pausableDepositCollateral() external payable {
        // thank you for your contribution to safety
    }

    /**
     * @notice  Resets all pausable flags to unpaused and withdraws funds
     * 
     * @dev     Throws when the caller does not have permission
     * 
     * @dev     <h4>Postconditions:</h4>
     * @dev     1. `pausableFlags` is set to zero
     * @dev     2. Emits a PausableFlagsUpdated event
     * @dev     3. Transfers `withdrawAmount` of native funds to `withdrawTo` if non-zero
     * 
     * @param withdrawTo      The address to withdraw the collateral to
     * @param withdrawAmount  The amount of collateral to withdraw
     */
    function unpause(address withdrawTo, uint256 withdrawAmount) external onlyPausePermissionedCaller {
        _setPausableFlags(0);

        if (withdrawAmount > 0) {
            (bool success, ) = withdrawTo.call{value: withdrawAmount}("");
            if(!success) revert CollateralizedPausableFlags__WithdrawFailed();
        }
    }

    /**
     * @notice  Returns collateralized pausable configuration information
     * 
     * @return _nativeValueToCheckPauseState  The collateral required to enable pause state checking
     * @return _pausableFlags                 The current pausable flags set, only checked when collateral met
     */
    function pausableConfigurationSettings() external view returns(
        uint256 _nativeValueToCheckPauseState, 
        uint256 _pausableFlags
    ) {
        unchecked {
            _nativeValueToCheckPauseState = nativeValueToCheckPauseState + 1;
            _pausableFlags = pausableFlags;
        }
    }

    /**
     * @notice  Updates the `pausableFlags` variable and emits a PausableFlagsUpdated event
     * 
     * @param _pausableFlags  The new pausable flags to set
     */
    function _setPausableFlags(uint256 _pausableFlags) internal {
        uint256 previousFlags = pausableFlags;

        pausableFlags = _pausableFlags;

        emit PausableFlagsUpdated(previousFlags, _pausableFlags);
    }

    /**
     * @notice  Checks the current pause state of the supplied flags and reverts if any are paused
     * 
     * @dev     *Should* be called prior to any transfers of native funds out of the contract for efficiency
     * @dev     Throws when the native funds balance is greater than the value to enable pausing AND
     * @dev     one or more of the supplied `_flags` is paused.
     * 
     * @param _flags  The flags to check for pause state
     */
    function _requireNotPausedWithCollateralCheck(uint256 _flags) private view {
        if (_nativeBalanceSubMsgValue() > nativeValueToCheckPauseState) {
            if (pausableFlags & _flags > 0) {
                revert CollateralizedPausableFlags__Paused();
            }
        }
    }

    /**
     * @notice  Checks the current pause state of the supplied flags and reverts if any are paused
     * 
     * @dev     Throws when one or more of the supplied `_flags` is paused.
     * 
     * @param _flags  The flags to check for pause state
     */
    function _requireNotPausedWithoutCollateralCheck(uint256 _flags) private view {
        if (pausableFlags & _flags > 0) {
            revert CollateralizedPausableFlags__Paused();
        }
    }

    /**
     * @notice  Checks the current pause state of the supplied flags and reverts if none are paused
     * 
     * @dev     *Should* be called prior to any transfers of native funds out of the contract for efficiency
     * @dev     Throws when the native funds balance is not greater than the value to enable pausing OR
     * @dev     none of the supplied `_flags` are paused.
     * 
     * @param _flags  The flags to check for pause state
     */
    function _requirePausedWithCollateralCheck(uint256 _flags) private view {
        if (_nativeBalanceSubMsgValue() <= nativeValueToCheckPauseState) {
            revert CollateralizedPausableFlags__NotPaused();
        } else if (pausableFlags & _flags == 0) {
            revert CollateralizedPausableFlags__NotPaused();
        }
    }

    /**
     * @notice  Checks the current pause state of the supplied flags and reverts if none are paused
     * 
     * @dev     Throws when none of the supplied `_flags` are paused.
     * 
     * @param _flags  The flags to check for pause state
     */
    function _requirePausedWithoutCollateralCheck(uint256 _flags) private view {
        if (pausableFlags & _flags == 0) {
            revert CollateralizedPausableFlags__NotPaused();
        }
    }

    /**
     * @notice  Returns the current state of the pausable flags
     * 
     * @dev     Will return zero if the native funds balance is not greater than the value to enable pausing
     * 
     * @return _pausableFlags  The current state of the pausable flags
     */
    function _getPausableFlagsWithCollateralCheck() private view returns(uint256 _pausableFlags) {
        if (_nativeBalanceSubMsgValue() > nativeValueToCheckPauseState) {
            _pausableFlags = pausableFlags;
        }
    }

    /**
     * @notice  Returns the current state of the pausable flags
     * 
     * @return _pausableFlags  The current state of the pausable flags
     */
    function _getPausableFlagsWithoutCollateralCheck() private view returns(uint256 _pausableFlags) {
        _pausableFlags = pausableFlags;
    }

    /**
     * @notice  Returns the current contract balance minus the value sent with the call
     * 
     * @dev     This is expected to be the contract balance at the beginning of a function call
     * @dev     to efficiently determine whether a contract has the necessary collateral to enable
     * @dev     the pausable flags checking for contracts that hold native token funds.
     * @dev     This should **NOT** be used in any way to determine current balance for contract logic
     * @dev     other than its intended purpose for pause state checking activation.
     */
    function _nativeBalanceSubMsgValue() private view returns (uint256 _value) {
        unchecked {
            _value = address(this).balance - msg.value;
        }
    }

    /**
     * @dev  To be implemented by an inheriting contract for authorization to `pause` and `unpause` 
     * @dev  functions as well as any functions in the inheriting contract that utilize the
     * @dev  `onlyPausePermissionedCaller` modifier.
     * 
     * @dev  Implementing contract function **MUST** throw when the caller is not permissioned
     */
    function _requireCallerHasPausePermissions() internal view virtual;
}

File 25 of 31 : Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}

File 26 of 31 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) external returns (bool);
}

File 27 of 31 : IERC721.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/IERC721.sol)

pragma solidity ^0.8.0;

import "../../utils/introspection/IERC165.sol";

/**
 * @dev Required interface of an ERC721 compliant contract.
 */
interface IERC721 is IERC165 {
    /**
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
     */
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
     */
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /**
     * @dev Returns the number of tokens in ``owner``'s account.
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes calldata data
    ) external;

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
     * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
     * understand this adds an external call which potentially creates a reentrancy vulnerability.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

    /**
     * @dev Gives permission to `to` to transfer `tokenId` token to another account.
     * The approval is cleared when the token is transferred.
     *
     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
     *
     * Requirements:
     *
     * - The caller must own the token or be an approved operator.
     * - `tokenId` must exist.
     *
     * Emits an {Approval} event.
     */
    function approve(address to, uint256 tokenId) external;

    /**
     * @dev Approve or remove `operator` as an operator for the caller.
     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
     *
     * Requirements:
     *
     * - The `operator` cannot be the caller.
     *
     * Emits an {ApprovalForAll} event.
     */
    function setApprovalForAll(address operator, bool _approved) external;

    /**
     * @dev Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
     *
     * See {setApprovalForAll}
     */
    function isApprovedForAll(address owner, address operator) external view returns (bool);
}

File 28 of 31 : IERC1155.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC1155/IERC1155.sol)

pragma solidity ^0.8.0;

import "../../utils/introspection/IERC165.sol";

/**
 * @dev Required interface of an ERC1155 compliant contract, as defined in the
 * https://eips.ethereum.org/EIPS/eip-1155[EIP].
 *
 * _Available since v3.1._
 */
interface IERC1155 is IERC165 {
    /**
     * @dev Emitted when `value` tokens of token type `id` are transferred from `from` to `to` by `operator`.
     */
    event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);

    /**
     * @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all
     * transfers.
     */
    event TransferBatch(
        address indexed operator,
        address indexed from,
        address indexed to,
        uint256[] ids,
        uint256[] values
    );

    /**
     * @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to
     * `approved`.
     */
    event ApprovalForAll(address indexed account, address indexed operator, bool approved);

    /**
     * @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI.
     *
     * If an {URI} event was emitted for `id`, the standard
     * https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value
     * returned by {IERC1155MetadataURI-uri}.
     */
    event URI(string value, uint256 indexed id);

    /**
     * @dev Returns the amount of tokens of token type `id` owned by `account`.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     */
    function balanceOf(address account, uint256 id) external view returns (uint256);

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}.
     *
     * Requirements:
     *
     * - `accounts` and `ids` must have the same length.
     */
    function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids)
        external
        view
        returns (uint256[] memory);

    /**
     * @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`,
     *
     * Emits an {ApprovalForAll} event.
     *
     * Requirements:
     *
     * - `operator` cannot be the caller.
     */
    function setApprovalForAll(address operator, bool approved) external;

    /**
     * @dev Returns true if `operator` is approved to transfer ``account``'s tokens.
     *
     * See {setApprovalForAll}.
     */
    function isApprovedForAll(address account, address operator) external view returns (bool);

    /**
     * @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
     *
     * Emits a {TransferSingle} event.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - If the caller is not `from`, it must have been approved to spend ``from``'s tokens via {setApprovalForAll}.
     * - `from` must have a balance of tokens of type `id` of at least `amount`.
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
     * acceptance magic value.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes calldata data
    ) external;

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.
     *
     * Emits a {TransferBatch} event.
     *
     * Requirements:
     *
     * - `ids` and `amounts` must have the same length.
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
     * acceptance magic value.
     */
    function safeBatchTransferFrom(
        address from,
        address to,
        uint256[] calldata ids,
        uint256[] calldata amounts,
        bytes calldata data
    ) external;
}

File 29 of 31 : ECDSA.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/ECDSA.sol)

pragma solidity ^0.8.0;

import "../Strings.sol";

/**
 * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
 *
 * These functions can be used to verify that a message was signed by the holder
 * of the private keys of a given address.
 */
library ECDSA {
    enum RecoverError {
        NoError,
        InvalidSignature,
        InvalidSignatureLength,
        InvalidSignatureS,
        InvalidSignatureV // Deprecated in v4.8
    }

    function _throwError(RecoverError error) private pure {
        if (error == RecoverError.NoError) {
            return; // no error: do nothing
        } else if (error == RecoverError.InvalidSignature) {
            revert("ECDSA: invalid signature");
        } else if (error == RecoverError.InvalidSignatureLength) {
            revert("ECDSA: invalid signature length");
        } else if (error == RecoverError.InvalidSignatureS) {
            revert("ECDSA: invalid signature 's' value");
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature` or error string. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     *
     * Documentation for signature generation:
     * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
     * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
     *
     * _Available since v4.3._
     */
    function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
        if (signature.length == 65) {
            bytes32 r;
            bytes32 s;
            uint8 v;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            /// @solidity memory-safe-assembly
            assembly {
                r := mload(add(signature, 0x20))
                s := mload(add(signature, 0x40))
                v := byte(0, mload(add(signature, 0x60)))
            }
            return tryRecover(hash, v, r, s);
        } else {
            return (address(0), RecoverError.InvalidSignatureLength);
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature`. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, signature);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
     *
     * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
     *
     * _Available since v4.3._
     */
    function tryRecover(
        bytes32 hash,
        bytes32 r,
        bytes32 vs
    ) internal pure returns (address, RecoverError) {
        bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
        uint8 v = uint8((uint256(vs) >> 255) + 27);
        return tryRecover(hash, v, r, s);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
     *
     * _Available since v4.2._
     */
    function recover(
        bytes32 hash,
        bytes32 r,
        bytes32 vs
    ) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, r, vs);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
     * `r` and `s` signature fields separately.
     *
     * _Available since v4.3._
     */
    function tryRecover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address, RecoverError) {
        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
        // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
        // signatures from current libraries generate a unique signature with an s-value in the lower half order.
        //
        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
        // these malleable signatures as well.
        if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
            return (address(0), RecoverError.InvalidSignatureS);
        }

        // If the signature is valid (and not malleable), return the signer address
        address signer = ecrecover(hash, v, r, s);
        if (signer == address(0)) {
            return (address(0), RecoverError.InvalidSignature);
        }

        return (signer, RecoverError.NoError);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function recover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from a `hash`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
        // 32 is the length in bytes of hash,
        // enforced by the type signature above
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from `s`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s));
    }

    /**
     * @dev Returns an Ethereum Signed Typed Data, created from a
     * `domainSeparator` and a `structHash`. This produces hash corresponding
     * to the one signed with the
     * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
     * JSON-RPC method as part of EIP-712.
     *
     * See {recover}.
     */
    function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
    }
}

File 30 of 31 : Strings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol)

pragma solidity ^0.8.0;

import "./math/Math.sol";

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant _SYMBOLS = "0123456789abcdef";
    uint8 private constant _ADDRESS_LENGTH = 20;

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        unchecked {
            uint256 length = Math.log10(value) + 1;
            string memory buffer = new string(length);
            uint256 ptr;
            /// @solidity memory-safe-assembly
            assembly {
                ptr := add(buffer, add(32, length))
            }
            while (true) {
                ptr--;
                /// @solidity memory-safe-assembly
                assembly {
                    mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        unchecked {
            return toHexString(value, Math.log256(value) + 1);
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
    }
}

File 31 of 31 : Math.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    enum Rounding {
        Down, // Toward negative infinity
        Up, // Toward infinity
        Zero // Toward zero
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
     * with further edits by Uniswap Labs also under MIT license.
     */
    function mulDiv(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod0 := mul(x, y)
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1);

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
            // See https://cs.stackexchange.com/q/138556/92363.

            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 twos = denominator & (~denominator + 1);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
            // in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(
        uint256 x,
        uint256 y,
        uint256 denominator,
        Rounding rounding
    ) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10**64) {
                value /= 10**64;
                result += 64;
            }
            if (value >= 10**32) {
                value /= 10**32;
                result += 32;
            }
            if (value >= 10**16) {
                value /= 10**16;
                result += 16;
            }
            if (value >= 10**8) {
                value /= 10**8;
                result += 8;
            }
            if (value >= 10**4) {
                value /= 10**4;
                result += 4;
            }
            if (value >= 10**2) {
                value /= 10**2;
                result += 2;
            }
            if (value >= 10**1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256, rounded down, of a positive value.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0);
        }
    }
}

Settings
{
  "remappings": [
    "@openzeppelin/=lib/openzeppelin-contracts/",
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "forge-std/=lib/forge-std/src/",
    "murky/=lib/murky/src/",
    "erc721a/=lib/ERC721A/",
    "@limitbreak/permit-c/=lib/PermitC/src/",
    "@opensea/tstorish/=lib/tstorish/src/",
    "@rari-capital/solmate/=lib/PermitC/lib/solmate/",
    "ERC721A/=lib/ERC721A/contracts/",
    "PermitC/=lib/PermitC/",
    "erc4626-tests/=lib/PermitC/lib/openzeppelin-contracts/lib/erc4626-tests/",
    "forge-gas-metering/=lib/PermitC/lib/forge-gas-metering/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "openzeppelin/=lib/PermitC/lib/openzeppelin-contracts/contracts/",
    "solady/=lib/PermitC/lib/forge-gas-metering/lib/solady/",
    "solmate/=lib/PermitC/lib/solmate/src/",
    "tstorish/=lib/tstorish/src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 777
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "cancun",
  "viaIR": false,
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"defaultOwner","type":"address"},{"internalType":"address","name":"eoaRegistry_","type":"address"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"version","type":"string"},{"internalType":"address","name":"validatorConfiguration","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"CollateralizedPausableFlags__NotPaused","type":"error"},{"inputs":[],"name":"CollateralizedPausableFlags__Paused","type":"error"},{"inputs":[],"name":"CollateralizedPausableFlags__WithdrawFailed","type":"error"},{"inputs":[],"name":"CreatorTokenTransferValidator__AuthorizationDisabledForCollection","type":"error"},{"inputs":[],"name":"CreatorTokenTransferValidator__CallerDoesNotOwnList","type":"error"},{"inputs":[],"name":"CreatorTokenTransferValidator__CallerMustBeAnAuthorizer","type":"error"},{"inputs":[],"name":"CreatorTokenTransferValidator__CallerMustBeWhitelisted","type":"error"},{"inputs":[],"name":"CreatorTokenTransferValidator__CallerMustHaveElevatedPermissionsForSpecifiedNFT","type":"error"},{"inputs":[],"name":"CreatorTokenTransferValidator__InvalidConstructorArgs","type":"error"},{"inputs":[],"name":"CreatorTokenTransferValidator__InvalidTransferSecurityLevel","type":"error"},{"inputs":[],"name":"CreatorTokenTransferValidator__ListDoesNotExist","type":"error"},{"inputs":[],"name":"CreatorTokenTransferValidator__ListOwnershipCannotBeTransferredToZeroAddress","type":"error"},{"inputs":[],"name":"CreatorTokenTransferValidator__OperatorIsBlacklisted","type":"error"},{"inputs":[],"name":"CreatorTokenTransferValidator__ReceiverAccountIsFrozen","type":"error"},{"inputs":[],"name":"CreatorTokenTransferValidator__ReceiverMustNotHaveDeployedCode","type":"error"},{"inputs":[],"name":"CreatorTokenTransferValidator__ReceiverProofOfEOASignatureUnverified","type":"error"},{"inputs":[],"name":"CreatorTokenTransferValidator__SenderAccountIsFrozen","type":"error"},{"inputs":[],"name":"CreatorTokenTransferValidator__TokenIsSoulbound","type":"error"},{"inputs":[],"name":"CreatorTokenTransferValidator__TokenTypesDoNotMatch","type":"error"},{"inputs":[],"name":"CreatorTokenTransferValidator__WildcardOperatorsCannotBeAuthorizedForCollection","type":"error"},{"inputs":[],"name":"OnlyDirectCalls","type":"error"},{"inputs":[],"name":"Ownable__CallerIsNotOwner","type":"error"},{"inputs":[],"name":"Ownable__NewOwnerIsZeroAddress","type":"error"},{"inputs":[],"name":"PermitC__AmountExceedsStorageMaximum","type":"error"},{"inputs":[],"name":"PermitC__ApprovalTransferExceededPermittedAmount","type":"error"},{"inputs":[],"name":"PermitC__ApprovalTransferPermitExpiredOrUnset","type":"error"},{"inputs":[],"name":"PermitC__CallerMustBeOwnerOrOperator","type":"error"},{"inputs":[],"name":"PermitC__InvalidTokenType","type":"error"},{"inputs":[],"name":"PermitC__NonceAlreadyUsedOrRevoked","type":"error"},{"inputs":[],"name":"PermitC__NonceNotUsedOrRevoked","type":"error"},{"inputs":[],"name":"PermitC__OrderIsEitherCancelledOrFilled","type":"error"},{"inputs":[],"name":"PermitC__SignatureTransferExceededPermitExpired","type":"error"},{"inputs":[],"name":"PermitC__SignatureTransferExceededPermittedAmount","type":"error"},{"inputs":[],"name":"PermitC__SignatureTransferInvalidSignature","type":"error"},{"inputs":[],"name":"PermitC__SignatureTransferPermitHashNotRegistered","type":"error"},{"inputs":[],"name":"PermitC__UnableToFillMinimumRequestedQuantity","type":"error"},{"inputs":[],"name":"TStoreAlreadyActivated","type":"error"},{"inputs":[],"name":"TStoreNotSupported","type":"error"},{"inputs":[],"name":"TloadTestContractDeploymentFailed","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"collection","type":"address"},{"indexed":true,"internalType":"address","name":"account","type":"address"}],"name":"AccountFrozenForCollection","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"collection","type":"address"},{"indexed":true,"internalType":"address","name":"account","type":"address"}],"name":"AccountUnfrozenForCollection","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint8","name":"kind","type":"uint8"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"address","name":"account","type":"address"}],"name":"AddedAccountToList","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint8","name":"kind","type":"uint8"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"bytes32","name":"codehash","type":"bytes32"}],"name":"AddedCodeHashToList","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"collection","type":"address"},{"indexed":true,"internalType":"uint120","name":"id","type":"uint120"}],"name":"AppliedListToCollection","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint200","name":"amount","type":"uint200"},{"indexed":false,"internalType":"uint48","name":"expiration","type":"uint48"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"string","name":"name","type":"string"}],"name":"CreatedList","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"}],"name":"Lockdown","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"orderId","type":"bytes32"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"wasCancellation","type":"bool"}],"name":"OrderClosed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"orderId","type":"bytes32"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"OrderFilled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"orderId","type":"bytes32"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"uint256","name":"fillableQuantity","type":"uint256"}],"name":"OrderOpened","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"orderId","type":"bytes32"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountRestoredToOrder","type":"uint256"}],"name":"OrderRestored","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"previousFlags","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newFlags","type":"uint256"}],"name":"PausableFlagsUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"ReassignedListOwnership","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint8","name":"kind","type":"uint8"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"address","name":"account","type":"address"}],"name":"RemovedAccountFromList","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint8","name":"kind","type":"uint8"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"bytes32","name":"codehash","type":"bytes32"}],"name":"RemovedCodeHashFromList","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"collection","type":"address"},{"indexed":false,"internalType":"bool","name":"enabled","type":"bool"}],"name":"SetAccountFreezingModeEnabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"collection","type":"address"},{"indexed":false,"internalType":"bool","name":"disabled","type":"bool"},{"indexed":false,"internalType":"bool","name":"authorizersCannotSetWildcardOperators","type":"bool"}],"name":"SetAuthorizationModeEnabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"collection","type":"address"},{"indexed":false,"internalType":"uint16","name":"tokenType","type":"uint16"}],"name":"SetTokenType","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"collection","type":"address"},{"indexed":false,"internalType":"uint8","name":"level","type":"uint8"}],"name":"SetTransferSecurityLevel","type":"event"},{"inputs":[],"name":"__activateTstore","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint120","name":"id","type":"uint120"},{"internalType":"address[]","name":"accounts","type":"address[]"}],"name":"addAccountsToAuthorizers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint120","name":"id","type":"uint120"},{"internalType":"address[]","name":"accounts","type":"address[]"}],"name":"addAccountsToBlacklist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint120","name":"id","type":"uint120"},{"internalType":"address[]","name":"accounts","type":"address[]"}],"name":"addAccountsToWhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint120","name":"id","type":"uint120"},{"internalType":"bytes32[]","name":"codehashes","type":"bytes32[]"}],"name":"addCodeHashesToBlacklist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint120","name":"id","type":"uint120"},{"internalType":"bytes32[]","name":"codehashes","type":"bytes32[]"}],"name":"addCodeHashesToWhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"afterAuthorizedTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"afterAuthorizedTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"afterAuthorizedTransferWithAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"},{"internalType":"uint256","name":"tokenType","type":"uint256"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"allowedAmount","type":"uint256"},{"internalType":"uint256","name":"expiration","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"},{"internalType":"uint256","name":"tokenType","type":"uint256"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes32","name":"orderId","type":"bytes32"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"allowedAmount","type":"uint256"},{"internalType":"uint256","name":"expiration","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"caller","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"}],"name":"applyCollectionTransferPolicy","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"collection","type":"address"},{"internalType":"uint120","name":"id","type":"uint120"}],"name":"applyListToCollection","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenType","type":"uint256"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"address","name":"operator","type":"address"},{"internalType":"uint200","name":"amount","type":"uint200"},{"internalType":"uint48","name":"expiration","type":"uint48"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"beforeAuthorizedTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"address","name":"token","type":"address"}],"name":"beforeAuthorizedTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"beforeAuthorizedTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"beforeAuthorizedTransferWithAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"},{"internalType":"uint256","name":"tokenType","type":"uint256"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes32","name":"orderId","type":"bytes32"}],"name":"closePermittedOrder","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"name","type":"string"}],"name":"createList","outputs":[{"internalType":"uint120","name":"id","type":"uint120"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"uint120","name":"sourceListId","type":"uint120"}],"name":"createListCopy","outputs":[{"internalType":"uint120","name":"id","type":"uint120"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"domainSeparatorV4","outputs":[{"internalType":"bytes32","name":"domainSeparator","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"signedPermit","type":"bytes"},{"components":[{"internalType":"uint256","name":"orderStartAmount","type":"uint256"},{"internalType":"uint256","name":"requestedFillAmount","type":"uint256"},{"internalType":"uint256","name":"minimumFillAmount","type":"uint256"}],"internalType":"struct OrderFillAmounts","name":"orderFillAmounts","type":"tuple"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"salt","type":"uint256"},{"internalType":"uint48","name":"expiration","type":"uint48"},{"internalType":"bytes32","name":"orderId","type":"bytes32"},{"internalType":"bytes32","name":"advancedPermitHash","type":"bytes32"}],"name":"fillPermittedOrderERC1155","outputs":[{"internalType":"uint256","name":"quantityFilled","type":"uint256"},{"internalType":"bool","name":"isError","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"signedPermit","type":"bytes"},{"components":[{"internalType":"uint256","name":"orderStartAmount","type":"uint256"},{"internalType":"uint256","name":"requestedFillAmount","type":"uint256"},{"internalType":"uint256","name":"minimumFillAmount","type":"uint256"}],"internalType":"struct OrderFillAmounts","name":"orderFillAmounts","type":"tuple"},{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"salt","type":"uint256"},{"internalType":"uint48","name":"expiration","type":"uint48"},{"internalType":"bytes32","name":"orderId","type":"bytes32"},{"internalType":"bytes32","name":"advancedPermitHash","type":"bytes32"}],"name":"fillPermittedOrderERC20","outputs":[{"internalType":"uint256","name":"quantityFilled","type":"uint256"},{"internalType":"bool","name":"isError","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"collection","type":"address"},{"internalType":"address[]","name":"accountsToFreeze","type":"address[]"}],"name":"freezeAccountsForCollection","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint120","name":"id","type":"uint120"}],"name":"getAuthorizerAccounts","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"collection","type":"address"}],"name":"getAuthorizerAccountsByCollection","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint120","name":"id","type":"uint120"}],"name":"getBlacklistedAccounts","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"collection","type":"address"}],"name":"getBlacklistedAccountsByCollection","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint120","name":"id","type":"uint120"}],"name":"getBlacklistedCodeHashes","outputs":[{"internalType":"bytes32[]","name":"","type":"bytes32[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"collection","type":"address"}],"name":"getBlacklistedCodeHashesByCollection","outputs":[{"internalType":"bytes32[]","name":"","type":"bytes32[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"collection","type":"address"}],"name":"getCollectionSecurityPolicy","outputs":[{"components":[{"internalType":"bool","name":"disableAuthorizationMode","type":"bool"},{"internalType":"bool","name":"authorizersCannotSetWildcardOperators","type":"bool"},{"internalType":"uint8","name":"transferSecurityLevel","type":"uint8"},{"internalType":"uint120","name":"listId","type":"uint120"},{"internalType":"bool","name":"enableAccountFreezingMode","type":"bool"},{"internalType":"uint16","name":"tokenType","type":"uint16"}],"internalType":"struct CollectionSecurityPolicyV3","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"collection","type":"address"}],"name":"getFrozenAccountsByCollection","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint120","name":"id","type":"uint120"}],"name":"getWhitelistedAccounts","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"collection","type":"address"}],"name":"getWhitelistedAccountsByCollection","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint120","name":"id","type":"uint120"}],"name":"getWhitelistedCodeHashes","outputs":[{"internalType":"bytes32[]","name":"","type":"bytes32[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"collection","type":"address"}],"name":"getWhitelistedCodeHashesByCollection","outputs":[{"internalType":"bytes32[]","name":"","type":"bytes32[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"nonce","type":"uint256"}],"name":"invalidateUnorderedNonce","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint120","name":"id","type":"uint120"},{"internalType":"address","name":"account","type":"address"}],"name":"isAccountAuthorizer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"collection","type":"address"},{"internalType":"address","name":"account","type":"address"}],"name":"isAccountAuthorizerOfCollection","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint120","name":"id","type":"uint120"},{"internalType":"address","name":"account","type":"address"}],"name":"isAccountBlacklisted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"collection","type":"address"},{"internalType":"address","name":"account","type":"address"}],"name":"isAccountBlacklistedByCollection","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"collection","type":"address"},{"internalType":"address","name":"account","type":"address"}],"name":"isAccountFrozenForCollection","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint120","name":"id","type":"uint120"},{"internalType":"address","name":"account","type":"address"}],"name":"isAccountWhitelisted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"collection","type":"address"},{"internalType":"address","name":"account","type":"address"}],"name":"isAccountWhitelistedByCollection","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint120","name":"id","type":"uint120"},{"internalType":"bytes32","name":"codehash","type":"bytes32"}],"name":"isCodeHashBlacklisted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"collection","type":"address"},{"internalType":"bytes32","name":"codehash","type":"bytes32"}],"name":"isCodeHashBlacklistedByCollection","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint120","name":"id","type":"uint120"},{"internalType":"bytes32","name":"codehash","type":"bytes32"}],"name":"isCodeHashWhitelisted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"collection","type":"address"},{"internalType":"bytes32","name":"codehash","type":"bytes32"}],"name":"isCodeHashWhitelistedByCollection","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"hash","type":"bytes32"}],"name":"isRegisteredOrderAdditionalDataHash","outputs":[{"internalType":"bool","name":"isRegistered","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"hash","type":"bytes32"}],"name":"isRegisteredTransferAdditionalDataHash","outputs":[{"internalType":"bool","name":"isRegistered","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"}],"name":"isValidUnorderedNonce","outputs":[{"internalType":"bool","name":"isValid","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"isVerifiedEOA","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastListId","outputs":[{"internalType":"uint120","name":"","type":"uint120"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint120","name":"","type":"uint120"}],"name":"listOwners","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lockdown","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"masterNonce","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pausableConfigurationSettings","outputs":[{"internalType":"uint256","name":"_nativeValueToCheckPauseState","type":"uint256"},{"internalType":"uint256","name":"_pausableFlags","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pausableDepositCollateral","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_pausableFlags","type":"uint256"}],"name":"pause","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"permitAmount","type":"uint256"},{"internalType":"uint256","name":"expiration","type":"uint256"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"transferAmount","type":"uint256"},{"internalType":"bytes","name":"signedPermit","type":"bytes"}],"name":"permitTransferFromERC1155","outputs":[{"internalType":"bool","name":"isError","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"permitAmount","type":"uint256"},{"internalType":"uint256","name":"expiration","type":"uint256"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"transferAmount","type":"uint256"},{"internalType":"bytes","name":"signedPermit","type":"bytes"}],"name":"permitTransferFromERC20","outputs":[{"internalType":"bool","name":"isError","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"expiration","type":"uint256"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"bytes","name":"signedPermit","type":"bytes"}],"name":"permitTransferFromERC721","outputs":[{"internalType":"bool","name":"isError","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"permitAmount","type":"uint256"},{"internalType":"uint256","name":"expiration","type":"uint256"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"transferAmount","type":"uint256"},{"internalType":"bytes32","name":"additionalData","type":"bytes32"},{"internalType":"bytes32","name":"advancedPermitHash","type":"bytes32"},{"internalType":"bytes","name":"signedPermit","type":"bytes"}],"name":"permitTransferFromWithAdditionalDataERC1155","outputs":[{"internalType":"bool","name":"isError","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"permitAmount","type":"uint256"},{"internalType":"uint256","name":"expiration","type":"uint256"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"transferAmount","type":"uint256"},{"internalType":"bytes32","name":"additionalData","type":"bytes32"},{"internalType":"bytes32","name":"advancedPermitHash","type":"bytes32"},{"internalType":"bytes","name":"signedPermit","type":"bytes"}],"name":"permitTransferFromWithAdditionalDataERC20","outputs":[{"internalType":"bool","name":"isError","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"expiration","type":"uint256"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"bytes32","name":"additionalData","type":"bytes32"},{"internalType":"bytes32","name":"advancedPermitHash","type":"bytes32"},{"internalType":"bytes","name":"signedPermit","type":"bytes"}],"name":"permitTransferFromWithAdditionalDataERC721","outputs":[{"internalType":"bool","name":"isError","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint120","name":"id","type":"uint120"},{"internalType":"address","name":"newOwner","type":"address"}],"name":"reassignOwnershipOfList","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"additionalDataTypeString","type":"string"}],"name":"registerAdditionalDataHash","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint120","name":"id","type":"uint120"},{"internalType":"address[]","name":"accounts","type":"address[]"}],"name":"removeAccountsFromAuthorizers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint120","name":"id","type":"uint120"},{"internalType":"address[]","name":"accounts","type":"address[]"}],"name":"removeAccountsFromBlacklist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint120","name":"id","type":"uint120"},{"internalType":"address[]","name":"accounts","type":"address[]"}],"name":"removeAccountsFromWhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint120","name":"id","type":"uint120"},{"internalType":"bytes32[]","name":"codehashes","type":"bytes32[]"}],"name":"removeCodeHashesFromBlacklist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint120","name":"id","type":"uint120"},{"internalType":"bytes32[]","name":"codehashes","type":"bytes32[]"}],"name":"removeCodeHashesFromWhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint120","name":"id","type":"uint120"}],"name":"renounceOwnershipOfList","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"collection","type":"address"},{"internalType":"uint16","name":"tokenType","type":"uint16"}],"name":"setTokenTypeOfCollection","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"collection","type":"address"},{"internalType":"uint8","name":"level","type":"uint8"},{"internalType":"bool","name":"disableAuthorizationMode","type":"bool"},{"internalType":"bool","name":"disableWildcardOperators","type":"bool"},{"internalType":"bool","name":"enableAccountFreezingMode","type":"bool"}],"name":"setTransferSecurityLevelOfCollection","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFromERC1155","outputs":[{"internalType":"bool","name":"isError","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFromERC20","outputs":[{"internalType":"bool","name":"isError","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"transferFromERC721","outputs":[{"internalType":"bool","name":"isError","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"level","type":"uint256"}],"name":"transferSecurityPolicies","outputs":[{"internalType":"uint256","name":"callerConstraints","type":"uint256"},{"internalType":"uint256","name":"receiverConstraints","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"collection","type":"address"},{"internalType":"address[]","name":"accountsToUnfreeze","type":"address[]"}],"name":"unfreezeAccountsForCollection","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"withdrawTo","type":"address"},{"internalType":"uint256","name":"withdrawAmount","type":"uint256"}],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenType","type":"uint256"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint200","name":"amount","type":"uint200"},{"internalType":"address","name":"operator","type":"address"},{"internalType":"uint48","name":"approvalExpiration","type":"uint48"},{"internalType":"uint48","name":"sigDeadline","type":"uint48"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"bytes","name":"signedPermit","type":"bytes"}],"name":"updateApprovalBySignature","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"caller","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"validateTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"caller","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"}],"name":"validateTransfer","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"caller","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"validateTransfer","outputs":[],"stateMutability":"view","type":"function"}]

61022060405234801562000011575f80fd5b50604051620065fe380380620065fe8339810160408190526200003491620005fa565b828286836001600160a01b031663c8dc9bc46040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000074573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906200009a919062000697565b8383825f620000a8620002aa565b90506001600160a01b038116620000d257604051632aea588760e01b815260040160405180910390fd5b5f620000de82620002c3565b1515608052506001600160a01b031660a052620000fb3362000325565b801562000149576200037d602090811b62002c26176001600160401b0390811660e052620003b1821b62002c7317811661010052620003fb90911b62002cd91716610120525f190162000188565b62000412602090811b62002d0c176001600160401b0390811660e052620003d6821b62002cb5178116610100526200043690911b62002d2f1716610120525b60c0528151602080840191909120610180528151908201206101a052466101605262000217610180516101a051604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201529081019290925260608201524660808201523060a08201525f9060c00160405160208183030381529060405280519060200120905090565b61014052506200022990508262000325565b505050506001600160a01b03851615806200024b57506001600160a01b038416155b156200026a5760405163c56e5d7360e01b815260040160405180910390fd5b62000275856200043c565b5050506001600160a01b0316610200525069040303020203020100026101c05269030201020100000000006101e052620006cf565b5f696002601e613d5c3d52f35f52600a60165ff0905090565b5f816001600160a01b0316600a5a620002dd9190620006af565b6040515f8181818686fa925050503d805f811462000317576040519150601f19603f3d011682016040523d82523d5f602084013e6200031c565b606091505b50909392505050565b5f80546001600160a01b03838116610100818102610100600160a81b0319851617855560405193049190911692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a35050565b60c0513447031115620003ae57600154811615620003ae57604051636623b92d60e11b815260040160405180910390fd5b50565b60c05134470311620003d65760405163fd2c901360e01b815260040160405180910390fd5b80600154165f03620003ae5760405163fd2c901360e01b815260040160405180910390fd5b60c0515f9034470311156200040f57506001545b90565b600154811615620003ae57604051636623b92d60e11b815260040160405180910390fd5b60015490565b5f80805260096020527fec8156718a8372b1db44bb411437d0870f3e3790d4a08526d024ce1b0b668f6b80546001600160a01b0384166001600160a01b031990911617905560405181907f5cc365f89543268cb9f25c255f7f610e9147e733c589bc2732279575f125be1490620004d3906020808252600c908201526b1111519055531508131254d560a21b604082015260600190565b60405180910390a26040516001600160a01b038316906001600160781b038316907f9b0894203394c3cbb23140db7a23b224d3e18e0366e9f65bd9c8402650e27367905f90a35050565b80516001600160a01b038116811462000534575f80fd5b919050565b634e487b7160e01b5f52604160045260245ffd5b5f82601f8301126200055d575f80fd5b81516001600160401b03808211156200057a576200057a62000539565b604051601f8301601f19908116603f01168101908282118183101715620005a557620005a562000539565b8160405283815260209250866020858801011115620005c2575f80fd5b5f91505b83821015620005e55785820183015181830184015290820190620005c6565b5f602085830101528094505050505092915050565b5f805f805f60a086880312156200060f575f80fd5b6200061a866200051d565b94506200062a602087016200051d565b60408701519094506001600160401b038082111562000647575f80fd5b6200065589838a016200054d565b945060608801519150808211156200066b575f80fd5b506200067a888289016200054d565b9250506200068b608087016200051d565b90509295509295909350565b5f60208284031215620006a8575f80fd5b5051919050565b5f82620006ca57634e487b7160e01b5f52601260045260245ffd5b500490565b60805160a05160c05160e05161010051610120516101405161016051610180516101a0516101c0516101e05161020051615e30620007ce5f395f6120ce01525f8181610dc70152613e9f01525f8181610d9a0152613e6801525f613db701525f613d8f01525f613d1601525f613d3e01525f50505f50505f81816114b701528181611531015281816115c8015281816118c901528181611a6801528181611c0101528181611f9d01528181612325015281816125cc0152818161264c015261279a01525f818161138601528181612c2801528181612c750152612cdc01525f611eb801525f8181611e6a015281816148e10152614e9c0152615e305ff3fe6080604052600436106105c1575f3560e01c80638254fcb7116102f0578063b97f6f8b11610191578063ddae38f2116100e7578063e34fda8511610092578063f7a2000a1161006d578063f7a2000a14611374578063fb2de5d7146113ae578063ffbcb4aa146113cd575f80fd5b8063e34fda85146105e4578063e991dc3014611336578063f2fde38b14611355575f80fd5b8063df21dc1d116100c2578063df21dc1d146112c4578063df5fd29a146112f8578063e07cb24014611317575f80fd5b8063ddae38f214611267578063de02cbb114611286578063def0125b146112a5575f80fd5b8063d415f62f11610147578063db5f30a811610122578063db5f30a81461120a578063dd71105d14611229578063dda964e314611248575f80fd5b8063d415f62f14611184578063d5dc239a146111cc578063db188e63146111eb575f80fd5b8063c435f43511610177578063c435f43514611127578063caee23ea14611146578063cfea7ecf14611165575f80fd5b8063b97f6f8b146110e9578063bf7bfd7e14611108575f80fd5b8063a5d56b4611610246578063b67d8f99116101fc578063b89c4b0d116101d7578063b89c4b0d14610f78578063b8dcc68f14610f97578063b955455214610fb6575f80fd5b8063b67d8f9914610ee8578063b6e39ba114610f07578063b70510f514610f26575f80fd5b8063ad1ff6851161022c578063ad1ff68514610e76578063ae602f4414610e95578063b3992ab114610ec9575f80fd5b8063a5d56b4614610e0c578063a87b03b614610e2b575f80fd5b80638da5cb5b116102a6578063982d03c011610281578063982d03c014610d5d5780639c2a9c6f14610d7c578063a1cc5cc114610ded575f80fd5b80638da5cb5b14610cea5780638e28800f14610d1f5780639340a7cc14610d3e575f80fd5b806389631626116102d65780638963162614610c8d57806389a9c85514610cac5780638b6ee86514610ccb575f80fd5b80638254fcb714610c4057806386e1177414610c6e575f80fd5b80633e5c139d116104655780636498c045116103bb578063725d07c5116103665780637bac97de116103415780637bac97de14610be35780637c1e14b414610c025780637df81b9014610c21575f80fd5b8063725d07c514610b8e5780637423eb3c14610bad57806378e890ba14610bc1575f80fd5b8063715018a611610396578063715018a614610b3c5780637161ac8d14610b5057806371be859d14610b6f575f80fd5b80636498c04514610adf5780636971082814610afe5780636bfab91d14610b1d575f80fd5b80634c9d0b451161041b578063539d2602116103f6578063539d260214610a595780635e17263d14610a785780635ed5917f14610ac0575f80fd5b80634c9d0b45146109fc5780635079331514610a1b578063515f7b2814610a3a575f80fd5b80633f6560ee1161044b5780633f6560ee1461098a578063409dc573146109be5780634be52a89146109dd575f80fd5b80633e5c139d146109575780633e8a0bc914610976575f80fd5b80631854b2411161051a5780632c7fe70a116104d05780633779e6fd116104ab5780633779e6fd146108e55780633a0e3160146109195780633cda743a14610938575f80fd5b80632c7fe70a146108885780632eb0b98a146108a7578063317e3e8d146108c6575f80fd5b806323c992621161050057806323c9926214610813578063285fb8c81461084a57806328cc113114610869575f80fd5b80631854b241146107a55780631f2fdc79146107c4575f80fd5b80630f59197d1161057a578063136439dd11610555578063136439dd1461071457806316a17ce01461072757806316f18d7414610779575f80fd5b80630f59197d146106aa57806310b5c6a0146106c957806312d3848a146106f5575f80fd5b8063057497cb116105aa578063057497cb1461061a5780630ad388991461066c5780630e14021a1461068b575f80fd5b806301549930146105c557806301ffc9a7146105e6575b5f80fd5b3480156105d0575f80fd5b506105e46105df366004614fbf565b6113fb565b005b3480156105f1575f80fd5b5061060561060036600461500e565b611423565b60405190151581526020015b60405180910390f35b348015610625575f80fd5b5061060561063436600461504b565b6001600160781b0382165f908152600c602090815260408083206001600160a01b038516845260040190915290205460ff1692915050565b348015610677575f80fd5b506105e461068636600461507c565b6114a2565b348015610696575f80fd5b506106056106a53660046150d3565b6114af565b3480156106b5575f80fd5b506106056106c4366004615176565b61151f565b3480156106d4575f80fd5b506106e86106e336600461522e565b61159d565b6040516106119190615247565b348015610700575f80fd5b5061060561070f366004615293565b6115c0565b6105e46107223660046152e4565b61162e565b348015610732575f80fd5b5061060561074136600461504b565b6001600160781b0382165f908152600b602090815260408083206001600160a01b038516845260040190915290205460ff1692915050565b348015610784575f80fd5b5061079861079336600461507c565b61163f565b60405161061191906152fb565b3480156107b0575f80fd5b506105e46107bf366004615293565b611673565b3480156107cf575f80fd5b506106056107de366004615332565b6001600160a01b039091165f908152600660209081526040808320600885901c845290915290205460ff9091161c6001161590565b34801561081e575f80fd5b50600854610832906001600160781b031681565b6040516001600160781b039091168152602001610611565b348015610855575f80fd5b506105e461086436600461535a565b611686565b348015610874575f80fd5b506105e461088336600461539a565b611691565b348015610893575f80fd5b506106056108a2366004615332565b61169e565b3480156108b2575f80fd5b506105e46108c1366004614fbf565b6116e9565b3480156108d1575f80fd5b506105e46108e03660046153d7565b61174a565b3480156108f0575f80fd5b506109046108ff366004615472565b6118b6565b60408051928352901515602083015201610611565b348015610924575f80fd5b506105e4610933366004615332565b611942565b348015610943575f80fd5b5061060561095236600461552d565b611953565b348015610962575f80fd5b506106e861097136600461507c565b6119a0565b348015610981575f80fd5b506105e46119d4565b348015610995575f80fd5b506109a96109a4366004615547565b611a11565b60408051928352602083019190915201610611565b3480156109c9575f80fd5b506105e46109d8366004614fbf565b611a31565b3480156109e8575f80fd5b506106056109f7366004615598565b611a56565b348015610a07575f80fd5b50610798610a1636600461522e565b611ad1565b348015610a26575f80fd5b506105e4610a3536600461552d565b611af7565b348015610a45575f80fd5b506105e4610a54366004614fbf565b611b04565b348015610a64575f80fd5b506105e4610a7336600461565b565b611b29565b348015610a83575f80fd5b50610605610a923660046156f7565b6001600160781b03919091165f908152600c6020908152604080832093835260059093019052205460ff1690565b348015610acb575f80fd5b50610605610ada366004615711565b611bf9565b348015610aea575f80fd5b506109a9610af936600461579d565b611c68565b348015610b09575f80fd5b506105e4610b18366004614fbf565b611c8a565b348015610b28575f80fd5b506105e4610b3736600461504b565b611cea565b348015610b47575f80fd5b506105e4611d1b565b348015610b5b575f80fd5b50610798610b6a36600461522e565b611d2e565b348015610b7a575f80fd5b506105e4610b893660046157f8565b611d54565b348015610b99575f80fd5b506106e8610ba836600461507c565b611e25565b348015610bb8575f80fd5b506105e4611e48565b348015610bcc575f80fd5b50610bd5611f07565b604051908152602001610611565b348015610bee575f80fd5b50610605610bfd36600461552d565b611f15565b348015610c0d575f80fd5b506105e4610c1c36600461535a565b611f62565b348015610c2c575f80fd5b50610605610c3b366004615813565b611f95565b348015610c4b575f80fd5b50610605610c5a3660046152e4565b5f9081526004602052604090205460ff1690565b348015610c79575f80fd5b506105e4610c88366004615332565b612003565b348015610c98575f80fd5b506106e8610ca736600461522e565b61208a565b348015610cb7575f80fd5b50610605610cc636600461507c565b6120ad565b348015610cd6575f80fd5b506105e4610ce536600461522e565b612139565b348015610cf5575f80fd5b505f5461010090046001600160a01b03165b6040516001600160a01b039091168152602001610611565b348015610d2a575f80fd5b50610605610d3936600461552d565b612143565b348015610d49575f80fd5b50610798610d5836600461507c565b612190565b348015610d68575f80fd5b506106e8610d7736600461507c565b6121c4565b348015610d87575f80fd5b506109a9610d963660046152e4565b60ff7f000000000000000000000000000000000000000000000000000000000000000060039290921b91821c8116927f000000000000000000000000000000000000000000000000000000000000000090921c1690565b348015610df8575f80fd5b506105e4610e07366004614fbf565b6121f8565b348015610e17575f80fd5b506105e4610e26366004614fbf565b61221d565b348015610e36575f80fd5b50610605610e4536600461552d565b6001600160a01b039182165f908152600e602090815260408083209390941682526002909201909152205460ff1690565b348015610e81575f80fd5b506105e4610e9036600461579d565b612240565b348015610ea0575f80fd5b50610d07610eaf36600461522e565b60096020525f90815260409020546001600160a01b031681565b348015610ed4575f80fd5b50610605610ee336600461585b565b612313565b348015610ef3575f80fd5b50610605610f02366004615332565b61238e565b348015610f12575f80fd5b506105e4610f21366004615332565b6123d6565b348015610f31575f80fd5b50610605610f4036600461504b565b6001600160781b0382165f908152600d602090815260408083206001600160a01b038516845260040190915290205460ff1692915050565b348015610f83575f80fd5b506105e4610f92366004615332565b6123dc565b348015610fa2575f80fd5b50610832610fb13660046158d9565b6123e8565b348015610fc1575f80fd5b50611088610fd036600461507c565b6040805160c0810182525f80825260208201819052918101829052606081018290526080810182905260a0810191909152506001600160a01b03165f908152600a6020908152604091829020825160c081018452905460ff8082161515835261010082048116151593830193909352620100008104831693820193909352630100000083046001600160781b03166060820152600160901b830490911615156080820152600160981b90910461ffff1660a082015290565b60405161061191905f60c08201905082511515825260208301511515602083015260ff60408401511660408301526001600160781b03606084015116606083015260808301511515608083015261ffff60a08401511660a083015292915050565b3480156110f4575f80fd5b506105e4611103366004615918565b6124b5565b348015611113575f80fd5b506105e461112236600461597a565b6124d5565b348015611132575f80fd5b506105e46111413660046152e4565b612582565b348015611151575f80fd5b506105e4611160366004615813565b61258c565b348015611170575f80fd5b5061090461117f3660046159a2565b6125b9565b34801561118f575f80fd5b5061060561119e3660046156f7565b6001600160781b03919091165f908152600b6020908152604080832093835260059093019052205460ff1690565b3480156111d7575f80fd5b506106056111e6366004615a53565b612644565b3480156111f6575f80fd5b506105e46112053660046158d9565b6126b2565b348015611215575f80fd5b506105e4611224366004615aeb565b612786565b348015611234575f80fd5b50610605611243366004615813565b612792565b348015611253575f80fd5b506106e861126236600461522e565b6127f5565b348015611272575f80fd5b506105e4611281366004614fbf565b612818565b348015611291575f80fd5b506108326112a0366004615b24565b61283d565b3480156112b0575f80fd5b506105e46112bf366004614fbf565b6129d8565b3480156112cf575f80fd5b50610bd56112de36600461507c565b6001600160a01b03165f9081526007602052604090205490565b348015611303575f80fd5b506105e4611312366004614fbf565b6129fb565b348015611322575f80fd5b506105e46113313660046157f8565b612a5c565b348015611341575f80fd5b506106e861135036600461507c565b612b2a565b348015611360575f80fd5b506105e461136f36600461507c565b612b5e565b34801561137f575f80fd5b50600180547f0000000000000000000000000000000000000000000000000000000000000000909101906109a9565b3480156113b9575f80fd5b506105e46113c8366004615b6b565b612b96565b3480156113d8575f80fd5b506106056113e73660046152e4565b5f9081526005602052604090205460ff1690565b6001600160781b0383165f908152600b6020526040812061141e91858585612d35565b505050565b5f6001600160e01b03198216158061144b57506001600160e01b03198216630dd9a30760e11b145b8061146657506001600160e01b03198216633016020d60e21b145b8061148157506001600160e01b031982166389a9c85560e01b145b8061149c57506301ffc9a760e01b6001600160e01b03198316145b92915050565b6114ac815f6123dc565b50565b5f6114de60107f000000000000000000000000000000000000000000000000000000000000000063ffffffff16565b6114f26104838c8c8b8d8c8c8b8b8b612dfd565b6114ff8b87878d88612ece565b9050801561151157611511868a612f6b565b9a9950505050505050505050565b5f8361152a81612fc0565b61155860107f000000000000000000000000000000000000000000000000000000000000000063ffffffff16565b61156b8e8e8d8f8e8e8d8b8b8f8f612fee565b8c6115798f8b8b848c612ece565b9250821561158b5761158b8a8e612f6b565b50509c9b505050505050505050505050565b6001600160781b0381165f908152600b6020526040902060609061149c90613004565b5f6115ef60027f000000000000000000000000000000000000000000000000000000000000000063ffffffff16565b5f611600876104838787875f613010565b905061160f8588888787612ece565b915081156116245761162481885f86816130ec565b5095945050505050565b6116366131c7565b6114ac816131cf565b6001600160a01b0381165f908152600a602052604090205460609061149c90630100000090046001600160781b0316611d2e565b61167f8585858561258c565b5050505050565b61141e838383611f62565b61141e600184845f613214565b6001600160a01b0382165f908152600a6020908152604080832054630100000090046001600160781b03168352600b825280832084845260050190915281205460ff165b9392505050565b61141e600c5f856001600160781b03166001600160781b031681526020019081526020015f206001858585808060200260200160405190810160405280939291908181526020018383602002808284375f9201919091525061327892505050565b600960ff8516111561176f576040516317c5702360e01b815260040160405180910390fd5b6117788561332a565b6001600160a01b0385165f818152600a6020908152604091829020805462ff00ff19166201000060ff8a1690810260ff1916919091178815151772ff00000000000000000000000000000000ff0019166101008815150272ff000000000000000000000000000000000000191617600160901b871515021790915591519182527fb39d8f1e6f05413a407e46fc950eb92e9f5b3d65a47c3f0bdc7a2741a6ec0f7d910160405180910390a260408051841515815283151560208201526001600160a01b038716917f9c615afab54584e53810beb24cced6ca36919dfc62bff2d4a0d244906c41c2ac910160405180910390a2846001600160a01b03167f9c6e8620d0004b9dd8b49560e2de8ced6f409f529cc23d0bb9e5a106d5c43242826040516118a7911515815260200190565b60405180910390a25050505050565b5f80826118c28161339e565b6118f060407f000000000000000000000000000000000000000000000000000000000000000063ffffffff16565b5f6119038f8f8f8f8f8f8e8e8e8e6133cc565b9050611917818e8e8e8e8e8c612ece6133f1565b9094509250821561193057611930818b888760016130ec565b50509b509b9950505050505050505050565b61194f600183835f613214565b5050565b6001600160a01b038281165f908152600a6020908152604080832054630100000090046001600160781b03168352600d82528083209385168352600490930190529081205460ff166116e2565b6001600160a01b0381165f908152600a602052604090205460609061149c90630100000090046001600160781b03166127f5565b335f8181526007602052604080822080546001019055517f8e8cebe67607ce50a14a2e3261437f641a7b33ecc053e3d9c90b25ae5e66c6569190a2565b5f80611a236002888888888887613537565b915091509550959350505050565b6001600160781b0383165f908152600c6020526040902061141e9060018585856135a6565b5f83611a6181612fc0565b611a8f60207f000000000000000000000000000000000000000000000000000000000000000063ffffffff16565b611aa28d5f8d8f8e8e8d8b8b8f8f61364f565b611aaf8d8a8a5f8b613664565b91508115611ac157611ac1898d612f6b565b509b9a5050505050505050505050565b6001600160781b0381165f908152600c6020526040902060609061149c90600201613004565b61194f82825f6001613214565b6001600160781b0383165f908152600c6020526040902061141e90600185858561376e565b8365ffffffffffff16421115611b5257604051630104f5db60e71b815260040160405180910390fd5b611b5b8b613814565b611b65838961384d565b611bc5611bbd611bb88d8d8d8c6001600160c81b03168e8d8d65ffffffffffff168d65ffffffffffff1660075f8f6001600160a01b03166001600160a01b031681526020019081526020015f20546138a1565b613941565b83838661398d565b65ffffffffffff851615611bd95784611bdb565b425b9450611bec8b8b8b8a89888c613a56565b5050505050505050505050565b5f611c2860087f000000000000000000000000000000000000000000000000000000000000000063ffffffff16565b611c3e6102d18a8a60018b8b8b60018b8b612dfd565b611c4a85858b8b613b04565b90508015611c5c57611c5c8588612f6b565b98975050505050505050565b5f80611c7a6003898989898989613537565b915091505b965096945050505050565b61141e600b5f856001600160781b03166001600160781b031681526020019081526020015f205f858585808060200260200160405190810160405280939291908181526020018383602002808284375f9201919091525061327892505050565b6001600160a01b038116611d11576040516303e9dcb360e31b815260040160405180910390fd5b61194f8282613b8d565b611d23613c03565b611d2c5f613c32565b565b6001600160781b0381165f908152600b6020526040902060609061149c90600201613004565b611d5d8361332a565b6001600160a01b0383165f908152600e60205260408120905b8281101561167f575f848483818110611d9157611d91615ba6565b9050602002016020810190611da6919061507c565b9050611db28382613ca1565b15611e1c57806001600160a01b0316866001600160a01b03167f1138edbff75f319641eb242dbcacee294ebc2a473eb6aa1454a7706da5bf96b060405160405180910390a36001600160a01b0381165f9081526002840160205260409020805460ff191660011790555b50600101611d76565b6001600160a01b0381165f908152600e6020526040902060609061149c90613004565b333214611e685760405163096650c560e21b815260040160405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000080611e9557505f5460ff165b15611eb357604051630f45b98b60e41b815260040160405180910390fd5b611edc7f0000000000000000000000000000000000000000000000000000000000000000613cb5565b611ef9576040516370a4078f60e01b815260040160405180910390fd5b5f805460ff19166001179055565b5f611f10613d13565b905090565b6001600160a01b038281165f908152600a6020908152604080832054630100000090046001600160781b03168352600b82528083209385168352600490930190529081205460ff166116e2565b5f611f73613e07338686865f613e20565b5090506001600160e01b0319811615611f8f57611f8f816143e9565b50505050565b5f611fc460017f000000000000000000000000000000000000000000000000000000000000000063ffffffff16565b5f611fd6866102d18686600180613010565b9050611fe486868686613b04565b91508115611ffa57611ffa81875f6001816130ec565b50949350505050565b61200b6131c7565b6120145f6131cf565b801561194f575f826001600160a01b0316826040515f6040518083038185875af1925050503d805f8114612063576040519150601f19603f3d011682016040523d82523d5f602084013e612068565b606091505b505090508061141e57604051630fc77c5160e21b815260040160405180910390fd5b6001600160781b0381165f908152600c6020526040902060609061149c90613004565b6040516389a9c85560e01b81526001600160a01b0382811660048301525f917f0000000000000000000000000000000000000000000000000000000000000000909116906389a9c85590602401602060405180830381865afa158015612115573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061149c9190615bba565b6114ac815f613b8d565b6001600160a01b038281165f908152600a6020908152604080832054630100000090046001600160781b03168352600c82528083209385168352600490930190529081205460ff166116e2565b6001600160a01b0381165f908152600a602052604090205460609061149c90630100000090046001600160781b0316611ad1565b6001600160a01b0381165f908152600a602052604090205460609061149c90630100000090046001600160781b031661159d565b6001600160781b0383165f908152600c6020526040902061141e906001858585612d35565b6001600160781b0383165f908152600b6020526040812061141e918585856135a6565b336001600160a01b038716148061225f5750336001600160a01b038616145b61227c5760405163eda7110360e01b815260040160405180910390fd5b61228584613814565b5f612296600388878787878c6143f1565b805490915060ff166122f15760028155604051600181526001600160a01b03878116919089169084907f705db7ac401a8091bb37a7838ad73d1fa8e1c663cb345f347fefe71280e3f03b9060200160405180910390a461230a565b60405163465fc3a960e11b815260040160405180910390fd5b50505050505050565b5f8361231e81612fc0565b61234c60087f000000000000000000000000000000000000000000000000000000000000000063ffffffff16565b6123618c8c60018d8d8d60018b8b8f8f61449b565b61236d88888e8e613b04565b9150811561237f5761237f888b612f6b565b509a9950505050505050505050565b6001600160a01b0382165f908152600a6020908152604080832054630100000090046001600160781b03168352600c825280832084845260050190915281205460ff166116e2565b61194f82825b61194f5f838382613214565b600880546001600160781b038082166001019081166effffffffffffffffffffffffffffff1990921682179092555f8181526009602052604090819020805473ffffffffffffffffffffffffffffffffffffffff191633179055517f5cc365f89543268cb9f25c255f7f610e9147e733c589bc2732279575f125be14906124729086908690615bd5565b60405180910390a260405133906001600160781b038316907f9b0894203394c3cbb23140db7a23b224d3e18e0366e9f65bd9c8402650e27367905f90a392915050565b6124be86613814565b6124cd86868685853389613a56565b505050505050565b6124de8261332a565b6008546001600160781b03908116908216111561250e57604051637b8b439360e11b815260040160405180910390fd5b6001600160a01b0382165f818152600a6020526040808220805471ffffffffffffffffffffffffffffff000000191663010000006001600160781b0387169081029190911790915590519092917fa66ff5557b7dc1562bb5e83306e15b513a25aa7537369bce38fc29c20847a79191a35050565b6114ac338261384d565b5f61259d6144b13387878787613e20565b5090506001600160e01b031981161561167f5761167f816143e9565b5f80826125c58161339e565b6125f360807f000000000000000000000000000000000000000000000000000000000000000063ffffffff16565b5f6126068e8e8e8e5f8f8e8e8e8e614511565b905061261a818d8d5f8e8e8c6136646133f1565b9094509250821561263357612633818b888760016130ec565b50509a509a98505050505050505050565b5f61267360207f000000000000000000000000000000000000000000000000000000000000000063ffffffff16565b61268660148b5f8b8d8c8c8b8b8b612dfd565b6126938a87875f88613664565b905080156126a5576126a5868a612f6b565b9998505050505050505050565b600160045f6040518060e0016040528060a58152602001615d5660a5913985856040516020016126e493929190615c25565b6040516020818303038152906040528051906020012081526020019081526020015f205f6101000a81548160ff021916908315150217905550600160055f6040518060c00160405280609d8152602001615cb9609d9139858560405160200161274f93929190615c25565b60408051808303601f190181529181528151602092830120835290820192909252015f20805460ff19169115159190911790555050565b61141e8383835f613214565b5f6127c160047f000000000000000000000000000000000000000000000000000000000000000063ffffffff16565b5f6127d1866014865f875f613010565b90506127e08487875f87613664565b91508115611ffa57611ffa81875f86816130ec565b6001600160781b0381165f908152600d6020526040902060609061149c90613004565b6001600160781b0383165f908152600d6020526040902061141e906002858585612d35565b600880546effffffffffffffffffffffffffffff19811660016001600160781b039283169081018084169290921790935591908316111561289157604051637b8b439360e11b815260040160405180910390fd5b6001600160781b0381165f8181526009602052604090819020805473ffffffffffffffffffffffffffffffffffffffff191633179055517f5cc365f89543268cb9f25c255f7f610e9147e733c589bc2732279575f125be14906128f79087908790615bd5565b60405180910390a260405133906001600160781b038316907f9b0894203394c3cbb23140db7a23b224d3e18e0366e9f65bd9c8402650e27367905f90a36001600160781b038083165f908152600b60208181526040808420600c808452828620600d808652848820988a16885295855283872091855283872095909452918520909592949293919261298b90888886614526565b6129975f8888866145d9565b6129a46001888785614526565b6129b160018887856145d9565b6129be6002888684614526565b6129cb60028886846145d9565b5050505050509392505050565b6001600160781b0383165f908152600b6020526040812061141e9185858561376e565b61141e600d5f856001600160781b03166001600160781b031681526020019081526020015f206002858585808060200260200160405190810160405280939291908181526020018383602002808284375f9201919091525061327892505050565b612a658361332a565b6001600160a01b0383165f908152600e60205260408120905b8281101561167f575f848483818110612a9957612a99615ba6565b9050602002016020810190612aae919061507c565b9050612aba8382614673565b15612b2157806001600160a01b0316866001600160a01b03167f208795fcac393398e42038456348398d8cac9067232f671ab240444cb51b1d2060405160405180910390a36001600160a01b0381165f9081526002840160205260409020805460ff191690555b50600101612a7e565b6001600160a01b0381165f908152600a602052604090205460609061149c90630100000090046001600160781b031661208a565b612b66613c03565b6001600160a01b038116612b8d57604051633e58254b60e01b815260040160405180910390fd5b6114ac81613c32565b612b9f8261332a565b6001600160a01b0382165f818152600a602090815260409182902080547fffffffffffffffffffffff0000ffffffffffffffffffffffffffffffffffffff16600160981b61ffff87169081029190911790915591519182527fa5c37ab91519073edd58e608f19f7ce383fd171f4f22c3612a1d0a7c1047794a910160405180910390a25050565b7f000000000000000000000000000000000000000000000000000000000000000034470311156114ac576001548116156114ac57604051636623b92d60e11b815260040160405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000034470311612cb55760405163fd2c901360e01b815260040160405180910390fd5b80600154165f036114ac5760405163fd2c901360e01b815260040160405180910390fd5b5f7f00000000000000000000000000000000000000000000000000000000000000003447031115612d0957506001545b90565b6001548116156114ac57604051636623b92d60e11b815260040160405180910390fd5b60015490565b82612d3f81614687565b5f805b83811015612df357848482818110612d5c57612d5c615ba6565b9050602002016020810190612d71919061507c565b9150612d7d8883613ca1565b15612deb57816001600160a01b0316866001600160781b03168860ff167fda8f3bd170446760f0f965a9b52bf271cb9679b5e0a70059eff2d49425229d1760405160405180910390a46001600160a01b0382165f9081526004890160205260409020805460ff191660011790555b600101612d42565b5050505050505050565b5f612ebc611bb88c8c8c8c8c8c60075f8e6001600160a01b03166001600160a01b031681526020019081526020015f2054604080517f932b8553b8e35bbee682d275cbe1cf115e14a777e2ca3266b4797369fb6317d3602080830191909152818301999099526001600160a01b03979097166060880152608087019590955260a086019390935260c08501919091523360e085015261010084015261012080840191909152815180840390910181526101409092019052805191012090565b9050611bec8787868b898689896146c7565b5f612edf610483878787878761471f565b905080612f6257604051637921219560e11b81526001600160a01b0386811660048301528581166024830152604482018590526064820184905260a060848301525f60a483015287169063f242432a9060c4015f604051808303815f87803b158015612f49575f80fd5b505af1925050508015612f5a575060015b612f62575060015b95945050505050565b6001600160a01b0382165f908152600660209081526040808320600885901c845290915290208054600160ff84161b90811891829055161561194f5760405163e4adc0bf60e01b815260040160405180910390fd5b5f8181526004602052604090205460ff166114ac57604051632391283760e11b815260040160405180910390fd5b611bec6104838c8c8c8c8c8c8c8c8c8c8c614789565b60605f6116e2836147bc565b5f61302160028888888886336143f1565b805490915042600160d01b90910465ffffffffffff16101561305657604051630104f5db60e71b815260040160405180910390fd5b805461010090046001600160c81b03168311156130865760405163fee142c560e01b815260040160405180910390fd5b81156130a0578054610100600160d01b03191681556130e2565b80546001600160c81b03610100909104811610156130e25780546001600160c81b03610100808304821686900390911602610100600160d01b03199091161781555b9695505050505050565b811561167f57801561318057845460ff196001600160c81b036101008084048216860190911602167fffffffffffff00000000000000000000000000000000000000000000000000009091161785556040518281526001600160a01b0385169084907f83e0ca2c1392f14286fa1e41c797789d48c5827572e8bcc352d8943c1961eaf09060200160405180910390a361167f565b84546001600160c81b036101009091048116101561167f5784546001600160c81b036101008083048216850190911602610100600160d01b03199091161785555050505050565b611d2c613c03565b600180549082905560408051828152602081018490527fe0d8d9ad73c586e8cf60ffd390b6f3654200a2d8857eb6abba4f6842a1210aae910160405180910390a15050565b828433613222838383614815565b6132506001600160a01b038716886001600160a01b031686613244575f61324a565b600160ff1b5b176148df565b61230a61326987875f9182526020526040902060041c90565b886001600160a01b03166148df565b8161328281614687565b5f805b835181101561230a578381815181106132a0576132a0615ba6565b602090810291909101015191506132b78783614673565b1561332257816001600160a01b0316856001600160781b03168760ff167f503012490a650739416858609e898957b874d17415a062945179c5735797884060405160405180910390a46001600160a01b0382165f9081526004880160205260409020805460ff191690555b600101613285565b336001600160a01b038216810361333f575050565b5f61334983614921565b509050806001600160a01b0316826001600160a01b03160361336a57505050565b5f613376848285614969565b50905080156133855750505050565b604051637f954ba160e01b815260040160405180910390fd5b5f8181526005602052604090205460ff166114ac57604051632391283760e11b815260040160405180910390fd5b5f6133e28b8b8b6104838c8c8c8c8c8c8c6149d5565b9b9a5050505050505050505050565b87546020880135905f9061010090046001600160c81b031682111561342357895461010090046001600160c81b031691505b88604001358210156134485760405163b9ff981560e01b815260040160405180910390fd5b89546001600160c81b03610100808304821685900390911602610100600160d01b0319909116178a5560405182815233906001600160a01b0388169086907f2203cb053e6b01ec07e87d67d288d360ae164171185684936663b7d8fa9c534c9060200160405180910390a4895461010090046001600160c81b03165f0361351757895460ff19166001178a556040515f815233906001600160a01b0388169086907f705db7ac401a8091bb37a7838ad73d1fa8e1c663cb345f347fefe71280e3f03b9060200160405180910390a45b6135288887878a868863ffffffff16565b90509850989650505050505050565b5f805f6135498a8a898989898e6143f1565b805490915042600160d01b90910465ffffffffffff161061357957805461010090046001600160c81b031661357b565b5f5b90546001600160c81b039091169a600160d01b90910465ffffffffffff169950975050505050505050565b826135b081614687565b5f805b83811015612df3578484828181106135cd576135cd615ba6565b9050602002013591506135ec8289600201614b1490919063ffffffff16565b156136475781866001600160781b03168860ff167fc8615322788d404dfe307db9eef031bc148d1cec5e270a1fd6528a02b445d44560405160405180910390a45f8281526005890160205260409020805460ff191660011790555b6001016135b3565b611bec60148c8c8c8c8c8c8c8c8c8c8c614789565b5f61367460148787875f8761471f565b905080612f6257604080516001600160a01b0387811660248301528681166044830152606480830186905283518084039091018152608490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166323b872dd60e01b17905291515f928392908a16916136f39190615c4b565b5f604051808303815f865af19150503d805f811461372c576040519150601f19603f3d011682016040523d82523d5f602084013e613731565b606091505b5091509150816137445760019250613763565b805115613763578080602001905181019061375f9190615bba565b1592505b505095945050505050565b8261377881614687565b5f805b83811015612df35784848281811061379557613795615ba6565b9050602002013591506137b48289600201614b1f90919063ffffffff16565b1561380c5781866001600160781b03168860ff167f061d78094976b1d9ae7bb858f141c915b46152756409caadb07482983c2ca30160405160405180910390a45f8281526005890160205260409020805460ff191690555b60010161377b565b6102d1811480613825575061048381145b806138305750601481145b6114ac57604051639d36a97960e01b815260040160405180910390fd5b6001600160a01b0382165f908152600660209081526040808320600885901c845290915290208054600160ff84161b908118918290551661194f5760405163d979627360e01b815260040160405180910390fd5b604080517ff9c04f8b028fcfa3315ea5accaee4589194a685f07cda0392e6ba9550706111960208201529081018a90526001600160a01b03808a1660608301526080820189905260a0820188905260c08201879052851660e08201526101008101849052610120810183905261014081018290525f90610160015b6040516020818303038152906040528051906020012090509998505050505050505050565b5f61149c61394d613d13565b8360405161190160f01b602082015260228101839052604281018290525f9060620160405160208183030381529060405280519060200120905092915050565b60418290036139f1578235602084013560408501355f90811a90806139b489848787614b2a565b91509150806001600160a01b0316866001600160a01b03161415806139d65750815b156139e7576139e7868a8a8a614bd3565b5050505050611f8f565b6040829003613a4a57823560208401355f80613a0e888585614c26565b91509150806001600160a01b0316856001600160a01b0316141580613a305750815b15613a4157613a4185898989614bd3565b50505050611f8f565b611f8f81858585614bd3565b5f613a676002848a8a8a86886143f1565b805460ff16600160d01b65ffffffffffff8716908102610100600160d01b031916919091176101006001600160c81b038916908102919091178355604080518a815260208101929092528101919091529091506001600160a01b0383811691898216918616907f0ec867d4f1b037422566cd0248bae620e6c142dcf5631948271916e8ca8dd2639060600160405180910390a45050505050505050565b5f613b166102d184878786600161471f565b905080613b85576040516323b872dd60e01b81526001600160a01b0386811660048301528581166024830152604482018490528416906323b872dd906064015f604051808303815f87803b158015613b6c575f80fd5b505af1925050508015613b7d575060015b613b85575060015b949350505050565b613b9682614687565b6001600160781b0382165f81815260096020526040808220805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b03861690811790915590519092917f9b0894203394c3cbb23140db7a23b224d3e18e0366e9f65bd9c8402650e2736791a35050565b5f546001600160a01b03610100909104163314611d2c576040516208650f60e61b815260040160405180910390fd5b5f80546001600160a01b038381166101008181027fffffffffffffffffffffff0000000000000000000000000000000000000000ff851617855560405193049190911692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a35050565b5f6116e2836001600160a01b038416614c6d565b5f816001600160a01b0316600a5a613ccd9190615c66565b6040515f8181818686fa925050503d805f8114613d05576040519150601f19603f3d011682016040523d82523d5f602084013e613d0a565b606091505b50909392505050565b5f7f00000000000000000000000000000000000000000000000000000000000000004603613d6057507f000000000000000000000000000000000000000000000000000000000000000090565b611f10604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201527f0000000000000000000000000000000000000000000000000000000000000000918101919091527f000000000000000000000000000000000000000000000000000000000000000060608201524660808201523060a08201525f9060c00160405160208183030381529060405280519060200120905090565b5f611ffa836001600160a01b038616614cb9565b614cb9565b5f80306001600160a01b03871603613e3c57505f905080611c7f565b6001600160a01b0387165f908152600a602052604090208054630100000081046001600160781b0316907f00000000000000000000000000000000000000000000000000000000000000006107f862010000830460031b1690811c60ff908116927f000000000000000000000000000000000000000000000000000000000000000090921c811691600160901b90041615613f57576001600160a01b03808c165f908152600e60209081526040808320938d1683526002840190915290205460ff1615613f1b575063056c6b9160e31b95505f9450611c7f9350505050565b6001600160a01b0389165f90815260028201602052604090205460ff1615613f555750632a5cb1c360e01b95505f9450611c7f9350505050565b505b60048203613f76575063d73e63af60e01b94505f9350611c7f92505050565b6001600160781b0383165f908152600c602052604090205f19820161400a57883b15614005576001600160a01b0389165f90815260048201602052604090205460ff16614005578c613fcd8d8d8b63ffffffff8516565b61400357893f5f90815260058301602052604090205460ff1661400357506305652c5560e51b96505f9550611c7f945050505050565b505b614088565b600282036140885761401b896120ad565b614088576001600160a01b0389165f90815260048201602052604090205460ff16614088578c6140508d8d8b63ffffffff8516565b61408657893f5f90815260058301602052604090205460ff166140865750636719795560e11b96505f9550611c7f945050505050565b505b896001600160a01b03168b6001600160a01b0316036140c557600383146140c557505091545f9450600160981b900461ffff169250611c7f915050565b600183036141b1578c6140dd8d8d8b63ffffffff8516565b156140ff57505092545f9550600160981b900461ffff169350611c7f92505050565b5f600b5f876001600160781b03166001600160781b031681526020019081526020015f209050806004015f8e6001600160a01b03166001600160a01b031681526020019081526020015f205f9054906101000a900460ff1615614176575063204f370f60e11b97505f9650611c7f95505050505050565b8c3f5f90815260058201602052604090205460ff16156141aa575063204f370f60e11b97505f9650611c7f95505050505050565b50506143c9565b60028303614279576001600160a01b038b165f90815260048201602052604090205460ff16156141f757505091545f9450600160981b900461ffff169250611c7f915050565b8c6142078d8d8b63ffffffff8516565b1561422957505092545f9550600160981b900461ffff169350611c7f92505050565b8b3f5f90815260058301602052604090205460ff161561426057505092545f9550600160981b900461ffff169350611c7f92505050565b5063ef28f90160e01b96505f9550611c7f945050505050565b600383036143c9576001600160a01b038b165f90815260048201602081905260409091205460ff16156142c357505092545f9550600160981b900461ffff169350611c7f92505050565b6001600160a01b038b165f9081526020829052604090205460ff161561430057505092545f9550600160981b900461ffff169350611c7f92505050565b8d6143108e8e8c63ffffffff8516565b1561433357505093545f9650600160981b900461ffff169450611c7f9350505050565b8c3f5f9081526005840160208190526040909120548e9060ff161561437257505095545f9850600160981b900461ffff169650611c7f95505050505050565b508c3f5f908152602082905260409020548d9060ff16156143ad57505095545f9850600160981b900461ffff169650611c7f95505050505050565b5063ef28f90160e01b99505f9850611c7f975050505050505050565b505091545f9b600160981b90910461ffff169a5098505050505050505050565b805f5260045ffd5b5f875f61446089898989896001600160a01b039485165f818152600760209081526040918290205482518083019490945283830197909752949096166060820152608081019290925260a082015260c0808201939093528351808203909301835260e001909252805191012090565b81526020019081526020015f205f836001600160a01b03166001600160a01b031681526020019081526020015f209050979650505050505050565b611bec6102d18c8c8c8c8c8c8c8c8c8c8c614789565b5f806144cd84613e1b87865f9182526020526040902060041c90565b50915081156144e05760019150506116e2565b6144f3846001600160a01b038716614cb9565b9092509050818015612f62575060ff81901c60011495945050505050565b5f6133e28b8b8b60148c8c8c8c8c8c8c6149d5565b8181600481015f61453684614cfa565b90505f805b828110156145cd5761454d8682614d03565b91506145598583613ca1565b156145c557816001600160a01b0316896001600160781b03168b60ff167fda8f3bd170446760f0f965a9b52bf271cb9679b5e0a70059eff2d49425229d1760405160405180910390a46001600160a01b0382165f908152602085905260409020805460ff191660011790555b60010161453b565b50505050505050505050565b6002808301908201600583015f6145ef84614cfa565b90505f805b828110156145cd576146068682614d03565b91506146128583614b14565b1561466b5781896001600160781b03168b60ff167fc8615322788d404dfe307db9eef031bc148d1cec5e270a1fd6528a02b445d44560405160405180910390a45f828152602085905260409020805460ff191660011790555b6001016145f4565b5f6116e2836001600160a01b038416614d0e565b6001600160781b0381165f908152600960205260409020546001600160a01b031633146114ac576040516304e680cb60e41b815260040160405180910390fd5b864211156146e85760405163e3fd7ac360e01b815260040160405180910390fd5b848611156147095760405163de7fafeb60e01b815260040160405180910390fd5b614713848961384d565b612df38383838761398d565b5f805f6147326144b189338a8a8a613e20565b909250905061ffff8116158061474b5750888161ffff16145b15614764576001600160e01b031982161515925061477d565b604051633d29aed360e11b815260040160405180910390fd5b50509695505050505050565b5f61479b8d8d8d8d8b8e8e8a8a614df1565b90506147ad8989888d8b868b8b6146c7565b50505050505050505050505050565b6060815f0180548060200260200160405190810160405280929190818152602001828054801561480957602002820191905f5260205f20905b8154815260200190600101908083116147f5575b50505050509050919050565b6001600160a01b0383165f908152600a60205260409020805460ff161561484f5760405163b1ae736760e01b815260040160405180910390fd5b8054610100900460ff1615614888575f196001600160a01b038416016148885760405163bf729bb160e01b815260040160405180910390fd5b8054630100000090046001600160781b03165f908152600d602090815260408083206001600160a01b038616845260040190915290205460ff16611f8f57604051635e47503160e11b815260040160405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000001561490c5780825d5050565b5f5460ff161561491d5780825d5050565b9055565b5f80614957565b638da5cb5b5f525f8060205f6004601c865afa60203d1015161561494f575f519150915091565b509160019150565b61496083614928565b91509150915091565b5f806149be565b5f80604051606081016040526391d14854815284816020015285816040015260205f6044601c8401875afa60203d101516156149b0575f519250506149b6565b50600190505b935093915050565b6149c9838587614970565b91509150935093915050565b5f6001600160c81b038a3511156149ff57604051633b0a334360e01b815260040160405180910390fd5b614a0f6003878b8b8b88336143f1565b805490915060ff166122f157805461010090046001600160c81b03165f03614ae357614a56614a4e8a8a8a8e358b8b65ffffffffffff8c168b8b614df1565b8d8d8961398d565b805460ff166101008b356001600160c81b0381169190910279ffffffffffffffffffffffffffffffffffffffffffffffffffff1691909117600160d01b65ffffffffffff87160217825560405190815233906001600160a01b0388169085907f257001e1f7fbfc5bbde5da225c876ab67293f37bda3afb8b35d9a55dfad6f65d9060200160405180910390a45b8054600160d01b900465ffffffffffff164211156133e25760405163e3fd7ac360e01b815260040160405180910390fd5b5f6116e28383614c6d565b5f6116e28383614d0e565b5f807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0831115614b5f5750600190505f614bca565b604080515f81526020810180835288905260ff871691810191909152606081018590526080810184905260019060a0016020604051602081039080840390855afa158015614baf573d5f803e3d5ffd5b5050604051601f1901516001600160a01b0381161593509150505b94509492505050565b836001600160a01b03163b5f03614bfd576040516373c919b560e01b815260040160405180910390fd5b614c0984848484614e1d565b611f8f576040516373c919b560e01b815260040160405180910390fd5b5f807f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8316601b60ff85901c01614c5f87828885614b2a565b909890975095505050505050565b5f818152600183016020526040812054614cb257508154600181810184555f84815260208082209093018490558454848252828601909352604090209190915561149c565b505f61149c565b5f80614cc483614e99565b9050806001600160a01b03811660011480614cf05750846001600160a01b0316816001600160a01b0316145b9250509250929050565b5f61149c825490565b5f6116e28383614edd565b5f8181526001830160205260408120548015614de8575f614d30600183615c85565b85549091505f90614d4390600190615c85565b9050818114614da2575f865f018281548110614d6157614d61615ba6565b905f5260205f200154905080875f018481548110614d8157614d81615ba6565b5f918252602080832090910192909255918252600188019052604090208390555b8554869080614db357614db3615ca4565b600190038181905f5260205f20015f90559055856001015f8681526020019081526020015f205f90556001935050505061149c565b5f91505061149c565b6001600160a01b0385165f908152600760205260408120546133e2611bb88c8c8c8c8b8b8b8b8a614f03565b5f614e8d565b5f604051630b135d3f60e11b81528360048201526040602482015285604482015285856064830137601f19601f87011660640180820160405260205f8284875afa60203d10151615614e8357630b135d3f60e11b5f511492505050613b85565b5050949350505050565b612f6282848688614e23565b5f7f000000000000000000000000000000000000000000000000000000000000000015614ec557505c90565b5f5460ff1615614ed457505c90565b5080545b919050565b5f825f018281548110614ef257614ef2615ba6565b905f5260205f200154905092915050565b60408051602081018490529081018a90526001600160a01b03891660608201526080810188905260a0810187905260c081018690523360e08201526101008101859052610120810182905261014081018490525f906101600161391c565b80356001600160781b0381168114614ed8575f80fd5b5f8083601f840112614f87575f80fd5b50813567ffffffffffffffff811115614f9e575f80fd5b6020830191508360208260051b8501011115614fb8575f80fd5b9250929050565b5f805f60408486031215614fd1575f80fd5b614fda84614f61565b9250602084013567ffffffffffffffff811115614ff5575f80fd5b61500186828701614f77565b9497909650939450505050565b5f6020828403121561501e575f80fd5b81356001600160e01b0319811681146116e2575f80fd5b80356001600160a01b0381168114614ed8575f80fd5b5f806040838503121561505c575f80fd5b61506583614f61565b915061507360208401615035565b90509250929050565b5f6020828403121561508c575f80fd5b6116e282615035565b5f8083601f8401126150a5575f80fd5b50813567ffffffffffffffff8111156150bc575f80fd5b602083019150836020828501011115614fb8575f80fd5b5f805f805f805f805f806101208b8d0312156150ed575f80fd5b6150f68b615035565b995060208b0135985060408b0135975060608b0135965060808b0135955061512060a08c01615035565b945061512e60c08c01615035565b935060e08b013592506101008b013567ffffffffffffffff811115615151575f80fd5b61515d8d828e01615095565b915080935050809150509295989b9194979a5092959850565b5f805f805f805f805f805f806101608d8f031215615192575f80fd5b61519b8d615035565b9b5060208d01359a5060408d0135995060608d0135985060808d013597506151c560a08e01615035565b96506151d360c08e01615035565b955060e08d013594506101008d013593506101208d0135925067ffffffffffffffff6101408e01351115615205575f80fd5b6152168e6101408f01358f01615095565b81935080925050509295989b509295989b509295989b565b5f6020828403121561523e575f80fd5b6116e282614f61565b602080825282518282018190525f9190848201906040850190845b818110156152875783516001600160a01b031683529284019291840191600101615262565b50909695505050505050565b5f805f805f60a086880312156152a7575f80fd5b6152b086615035565b94506152be60208701615035565b93506152cc60408701615035565b94979396509394606081013594506080013592915050565b5f602082840312156152f4575f80fd5b5035919050565b602080825282518282018190525f9190848201906040850190845b8181101561528757835183529284019291840191600101615316565b5f8060408385031215615343575f80fd5b61534c83615035565b946020939093013593505050565b5f805f6060848603121561536c575f80fd5b61537584615035565b925061538360208501615035565b915061539160408501615035565b90509250925092565b5f805f606084860312156153ac575f80fd5b6153b584615035565b95602085013595506040909401359392505050565b80151581146114ac575f80fd5b5f805f805f60a086880312156153eb575f80fd5b6153f486615035565b9450602086013560ff81168114615409575f80fd5b93506040860135615419816153ca565b92506060860135615429816153ca565b91506080860135615439816153ca565b809150509295509295909350565b5f60608284031215615457575f80fd5b50919050565b803565ffffffffffff81168114614ed8575f80fd5b5f805f805f805f805f805f6101808c8e03121561548d575f80fd5b8b3567ffffffffffffffff8111156154a3575f80fd5b6154af8e828f01615095565b909c509a506154c390508d60208e01615447565b98506154d160808d01615035565b975060a08c013596506154e660c08d01615035565b95506154f460e08d01615035565b94506101008c0135935061550b6101208d0161545d565b92506101408c013591506101608c013590509295989b509295989b9093969950565b5f806040838503121561553e575f80fd5b61506583615035565b5f805f805f60a0868803121561555b575f80fd5b61556486615035565b945061557260208701615035565b93506040860135925061558760608701615035565b949793965091946080013592915050565b5f805f805f805f805f805f6101408c8e0312156155b3575f80fd5b6155bc8c615035565b9a5060208c0135995060408c0135985060608c013597506155df60808d01615035565b96506155ed60a08d01615035565b955060c08c0135945060e08c013593506101008c013592506101208c013567ffffffffffffffff81111561561f575f80fd5b61562b8e828f01615095565b915080935050809150509295989b509295989b9093969950565b80356001600160c81b0381168114614ed8575f80fd5b5f805f805f805f805f805f6101408c8e031215615676575f80fd5b8b359a5061568660208d01615035565b995060408c0135985060608c013597506156a260808d01615645565b96506156b060a08d01615035565b95506156be60c08d0161545d565b94506156cc60e08d0161545d565b93506156db6101008d01615035565b92506101208c013567ffffffffffffffff81111561561f575f80fd5b5f8060408385031215615708575f80fd5b61534c83614f61565b5f805f805f805f8060e0898b031215615728575f80fd5b61573189615035565b975060208901359650604089013595506060890135945061575460808a01615035565b935061576260a08a01615035565b925060c089013567ffffffffffffffff81111561577d575f80fd5b6157898b828c01615095565b999c989b5096995094979396929594505050565b5f805f805f8060c087890312156157b2575f80fd5b6157bb87615035565b95506157c960208801615035565b9450604087013593506157de60608801615035565b92506080870135915060a087013590509295509295509295565b5f805f6040848603121561580a575f80fd5b614fda84615035565b5f805f8060808587031215615826575f80fd5b61582f85615035565b935061583d60208601615035565b925061584b60408601615035565b9396929550929360600135925050565b5f805f805f805f805f806101208b8d031215615875575f80fd5b61587e8b615035565b995060208b0135985060408b0135975060608b013596506158a160808c01615035565b95506158af60a08c01615035565b945060c08b0135935060e08b013592506101008b013567ffffffffffffffff811115615151575f80fd5b5f80602083850312156158ea575f80fd5b823567ffffffffffffffff811115615900575f80fd5b61590c85828601615095565b90969095509350505050565b5f805f805f8060c0878903121561592d575f80fd5b8635955061593d60208801615035565b94506040870135935061595260608801615035565b925061596060808801615645565b915061596e60a0880161545d565b90509295509295509295565b5f806040838503121561598b575f80fd5b61599483615035565b915061507360208401614f61565b5f805f805f805f805f806101608b8d0312156159bc575f80fd5b8a3567ffffffffffffffff8111156159d2575f80fd5b6159de8d828e01615095565b909b5099506159f290508c60208d01615447565b9750615a0060808c01615035565b9650615a0e60a08c01615035565b9550615a1c60c08c01615035565b945060e08b01359350615a326101008c0161545d565b92506101208b013591506101408b013590509295989b9194979a5092959850565b5f805f805f805f805f6101008a8c031215615a6c575f80fd5b615a758a615035565b985060208a0135975060408a0135965060608a01359550615a9860808b01615035565b9450615aa660a08b01615035565b935060c08a0135925060e08a013567ffffffffffffffff811115615ac8575f80fd5b615ad48c828d01615095565b915080935050809150509295985092959850929598565b5f805f60608486031215615afd575f80fd5b615b0684615035565b9250615b1460208501615035565b9150604084013590509250925092565b5f805f60408486031215615b36575f80fd5b833567ffffffffffffffff811115615b4c575f80fd5b615b5886828701615095565b9094509250615391905060208501614f61565b5f8060408385031215615b7c575f80fd5b615b8583615035565b9150602083013561ffff81168114615b9b575f80fd5b809150509250929050565b634e487b7160e01b5f52603260045260245ffd5b5f60208284031215615bca575f80fd5b81516116e2816153ca565b60208152816020820152818360408301375f818301604090810191909152601f909201601f19160101919050565b5f5b83811015615c1d578181015183820152602001615c05565b50505f910152565b5f8451615c36818460208901615c03565b8201838582375f930192835250909392505050565b5f8251615c5c818460208701615c03565b9190910192915050565b5f82615c8057634e487b7160e01b5f52601260045260245ffd5b500490565b8181038181111561149c57634e487b7160e01b5f52601160045260245ffd5b634e487b7160e01b5f52603160045260245ffdfe5065726d69744f72646572576974684164646974696f6e616c446174612875696e7432353620746f6b656e547970652c6164647265737320746f6b656e2c75696e743235362069642c75696e7432353620616d6f756e742c75696e743235362073616c742c61646472657373206f70657261746f722c75696e743235362065787069726174696f6e2c75696e74323536206d61737465724e6f6e63652c5065726d69745472616e7366657246726f6d576974684164646974696f6e616c446174612875696e7432353620746f6b656e547970652c6164647265737320746f6b656e2c75696e743235362069642c75696e7432353620616d6f756e742c75696e74323536206e6f6e63652c61646472657373206f70657261746f722c75696e743235362065787069726174696f6e2c75696e74323536206d61737465724e6f6e63652ca26469706673582212204aed5a9438fa1f93a19402978e99446b15ffab9a7272f92ed814de426e3dc30964736f6c63430008180033000000000000000000000000c327b16c33fdd558c1ad7daaff1a4081e91049f3000000000000000000000000e0a0004cb92d89cd9b056927df0fe008c16cf86f00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000721c006d06dc5c03b6106fa9fe32c10efb1ddb62000000000000000000000000000000000000000000000000000000000000001d43726561746f72546f6b656e5472616e7366657256616c696461746f7200000000000000000000000000000000000000000000000000000000000000000000013300000000000000000000000000000000000000000000000000000000000000

Deployed Bytecode

0x6080604052600436106105c1575f3560e01c80638254fcb7116102f0578063b97f6f8b11610191578063ddae38f2116100e7578063e34fda8511610092578063f7a2000a1161006d578063f7a2000a14611374578063fb2de5d7146113ae578063ffbcb4aa146113cd575f80fd5b8063e34fda85146105e4578063e991dc3014611336578063f2fde38b14611355575f80fd5b8063df21dc1d116100c2578063df21dc1d146112c4578063df5fd29a146112f8578063e07cb24014611317575f80fd5b8063ddae38f214611267578063de02cbb114611286578063def0125b146112a5575f80fd5b8063d415f62f11610147578063db5f30a811610122578063db5f30a81461120a578063dd71105d14611229578063dda964e314611248575f80fd5b8063d415f62f14611184578063d5dc239a146111cc578063db188e63146111eb575f80fd5b8063c435f43511610177578063c435f43514611127578063caee23ea14611146578063cfea7ecf14611165575f80fd5b8063b97f6f8b146110e9578063bf7bfd7e14611108575f80fd5b8063a5d56b4611610246578063b67d8f99116101fc578063b89c4b0d116101d7578063b89c4b0d14610f78578063b8dcc68f14610f97578063b955455214610fb6575f80fd5b8063b67d8f9914610ee8578063b6e39ba114610f07578063b70510f514610f26575f80fd5b8063ad1ff6851161022c578063ad1ff68514610e76578063ae602f4414610e95578063b3992ab114610ec9575f80fd5b8063a5d56b4614610e0c578063a87b03b614610e2b575f80fd5b80638da5cb5b116102a6578063982d03c011610281578063982d03c014610d5d5780639c2a9c6f14610d7c578063a1cc5cc114610ded575f80fd5b80638da5cb5b14610cea5780638e28800f14610d1f5780639340a7cc14610d3e575f80fd5b806389631626116102d65780638963162614610c8d57806389a9c85514610cac5780638b6ee86514610ccb575f80fd5b80638254fcb714610c4057806386e1177414610c6e575f80fd5b80633e5c139d116104655780636498c045116103bb578063725d07c5116103665780637bac97de116103415780637bac97de14610be35780637c1e14b414610c025780637df81b9014610c21575f80fd5b8063725d07c514610b8e5780637423eb3c14610bad57806378e890ba14610bc1575f80fd5b8063715018a611610396578063715018a614610b3c5780637161ac8d14610b5057806371be859d14610b6f575f80fd5b80636498c04514610adf5780636971082814610afe5780636bfab91d14610b1d575f80fd5b80634c9d0b451161041b578063539d2602116103f6578063539d260214610a595780635e17263d14610a785780635ed5917f14610ac0575f80fd5b80634c9d0b45146109fc5780635079331514610a1b578063515f7b2814610a3a575f80fd5b80633f6560ee1161044b5780633f6560ee1461098a578063409dc573146109be5780634be52a89146109dd575f80fd5b80633e5c139d146109575780633e8a0bc914610976575f80fd5b80631854b2411161051a5780632c7fe70a116104d05780633779e6fd116104ab5780633779e6fd146108e55780633a0e3160146109195780633cda743a14610938575f80fd5b80632c7fe70a146108885780632eb0b98a146108a7578063317e3e8d146108c6575f80fd5b806323c992621161050057806323c9926214610813578063285fb8c81461084a57806328cc113114610869575f80fd5b80631854b241146107a55780631f2fdc79146107c4575f80fd5b80630f59197d1161057a578063136439dd11610555578063136439dd1461071457806316a17ce01461072757806316f18d7414610779575f80fd5b80630f59197d146106aa57806310b5c6a0146106c957806312d3848a146106f5575f80fd5b8063057497cb116105aa578063057497cb1461061a5780630ad388991461066c5780630e14021a1461068b575f80fd5b806301549930146105c557806301ffc9a7146105e6575b5f80fd5b3480156105d0575f80fd5b506105e46105df366004614fbf565b6113fb565b005b3480156105f1575f80fd5b5061060561060036600461500e565b611423565b60405190151581526020015b60405180910390f35b348015610625575f80fd5b5061060561063436600461504b565b6001600160781b0382165f908152600c602090815260408083206001600160a01b038516845260040190915290205460ff1692915050565b348015610677575f80fd5b506105e461068636600461507c565b6114a2565b348015610696575f80fd5b506106056106a53660046150d3565b6114af565b3480156106b5575f80fd5b506106056106c4366004615176565b61151f565b3480156106d4575f80fd5b506106e86106e336600461522e565b61159d565b6040516106119190615247565b348015610700575f80fd5b5061060561070f366004615293565b6115c0565b6105e46107223660046152e4565b61162e565b348015610732575f80fd5b5061060561074136600461504b565b6001600160781b0382165f908152600b602090815260408083206001600160a01b038516845260040190915290205460ff1692915050565b348015610784575f80fd5b5061079861079336600461507c565b61163f565b60405161061191906152fb565b3480156107b0575f80fd5b506105e46107bf366004615293565b611673565b3480156107cf575f80fd5b506106056107de366004615332565b6001600160a01b039091165f908152600660209081526040808320600885901c845290915290205460ff9091161c6001161590565b34801561081e575f80fd5b50600854610832906001600160781b031681565b6040516001600160781b039091168152602001610611565b348015610855575f80fd5b506105e461086436600461535a565b611686565b348015610874575f80fd5b506105e461088336600461539a565b611691565b348015610893575f80fd5b506106056108a2366004615332565b61169e565b3480156108b2575f80fd5b506105e46108c1366004614fbf565b6116e9565b3480156108d1575f80fd5b506105e46108e03660046153d7565b61174a565b3480156108f0575f80fd5b506109046108ff366004615472565b6118b6565b60408051928352901515602083015201610611565b348015610924575f80fd5b506105e4610933366004615332565b611942565b348015610943575f80fd5b5061060561095236600461552d565b611953565b348015610962575f80fd5b506106e861097136600461507c565b6119a0565b348015610981575f80fd5b506105e46119d4565b348015610995575f80fd5b506109a96109a4366004615547565b611a11565b60408051928352602083019190915201610611565b3480156109c9575f80fd5b506105e46109d8366004614fbf565b611a31565b3480156109e8575f80fd5b506106056109f7366004615598565b611a56565b348015610a07575f80fd5b50610798610a1636600461522e565b611ad1565b348015610a26575f80fd5b506105e4610a3536600461552d565b611af7565b348015610a45575f80fd5b506105e4610a54366004614fbf565b611b04565b348015610a64575f80fd5b506105e4610a7336600461565b565b611b29565b348015610a83575f80fd5b50610605610a923660046156f7565b6001600160781b03919091165f908152600c6020908152604080832093835260059093019052205460ff1690565b348015610acb575f80fd5b50610605610ada366004615711565b611bf9565b348015610aea575f80fd5b506109a9610af936600461579d565b611c68565b348015610b09575f80fd5b506105e4610b18366004614fbf565b611c8a565b348015610b28575f80fd5b506105e4610b3736600461504b565b611cea565b348015610b47575f80fd5b506105e4611d1b565b348015610b5b575f80fd5b50610798610b6a36600461522e565b611d2e565b348015610b7a575f80fd5b506105e4610b893660046157f8565b611d54565b348015610b99575f80fd5b506106e8610ba836600461507c565b611e25565b348015610bb8575f80fd5b506105e4611e48565b348015610bcc575f80fd5b50610bd5611f07565b604051908152602001610611565b348015610bee575f80fd5b50610605610bfd36600461552d565b611f15565b348015610c0d575f80fd5b506105e4610c1c36600461535a565b611f62565b348015610c2c575f80fd5b50610605610c3b366004615813565b611f95565b348015610c4b575f80fd5b50610605610c5a3660046152e4565b5f9081526004602052604090205460ff1690565b348015610c79575f80fd5b506105e4610c88366004615332565b612003565b348015610c98575f80fd5b506106e8610ca736600461522e565b61208a565b348015610cb7575f80fd5b50610605610cc636600461507c565b6120ad565b348015610cd6575f80fd5b506105e4610ce536600461522e565b612139565b348015610cf5575f80fd5b505f5461010090046001600160a01b03165b6040516001600160a01b039091168152602001610611565b348015610d2a575f80fd5b50610605610d3936600461552d565b612143565b348015610d49575f80fd5b50610798610d5836600461507c565b612190565b348015610d68575f80fd5b506106e8610d7736600461507c565b6121c4565b348015610d87575f80fd5b506109a9610d963660046152e4565b60ff7f000000000000000000000000000000000000000000000403030202030201000260039290921b91821c8116927f000000000000000000000000000000000000000000000302010201000000000090921c1690565b348015610df8575f80fd5b506105e4610e07366004614fbf565b6121f8565b348015610e17575f80fd5b506105e4610e26366004614fbf565b61221d565b348015610e36575f80fd5b50610605610e4536600461552d565b6001600160a01b039182165f908152600e602090815260408083209390941682526002909201909152205460ff1690565b348015610e81575f80fd5b506105e4610e9036600461579d565b612240565b348015610ea0575f80fd5b50610d07610eaf36600461522e565b60096020525f90815260409020546001600160a01b031681565b348015610ed4575f80fd5b50610605610ee336600461585b565b612313565b348015610ef3575f80fd5b50610605610f02366004615332565b61238e565b348015610f12575f80fd5b506105e4610f21366004615332565b6123d6565b348015610f31575f80fd5b50610605610f4036600461504b565b6001600160781b0382165f908152600d602090815260408083206001600160a01b038516845260040190915290205460ff1692915050565b348015610f83575f80fd5b506105e4610f92366004615332565b6123dc565b348015610fa2575f80fd5b50610832610fb13660046158d9565b6123e8565b348015610fc1575f80fd5b50611088610fd036600461507c565b6040805160c0810182525f80825260208201819052918101829052606081018290526080810182905260a0810191909152506001600160a01b03165f908152600a6020908152604091829020825160c081018452905460ff8082161515835261010082048116151593830193909352620100008104831693820193909352630100000083046001600160781b03166060820152600160901b830490911615156080820152600160981b90910461ffff1660a082015290565b60405161061191905f60c08201905082511515825260208301511515602083015260ff60408401511660408301526001600160781b03606084015116606083015260808301511515608083015261ffff60a08401511660a083015292915050565b3480156110f4575f80fd5b506105e4611103366004615918565b6124b5565b348015611113575f80fd5b506105e461112236600461597a565b6124d5565b348015611132575f80fd5b506105e46111413660046152e4565b612582565b348015611151575f80fd5b506105e4611160366004615813565b61258c565b348015611170575f80fd5b5061090461117f3660046159a2565b6125b9565b34801561118f575f80fd5b5061060561119e3660046156f7565b6001600160781b03919091165f908152600b6020908152604080832093835260059093019052205460ff1690565b3480156111d7575f80fd5b506106056111e6366004615a53565b612644565b3480156111f6575f80fd5b506105e46112053660046158d9565b6126b2565b348015611215575f80fd5b506105e4611224366004615aeb565b612786565b348015611234575f80fd5b50610605611243366004615813565b612792565b348015611253575f80fd5b506106e861126236600461522e565b6127f5565b348015611272575f80fd5b506105e4611281366004614fbf565b612818565b348015611291575f80fd5b506108326112a0366004615b24565b61283d565b3480156112b0575f80fd5b506105e46112bf366004614fbf565b6129d8565b3480156112cf575f80fd5b50610bd56112de36600461507c565b6001600160a01b03165f9081526007602052604090205490565b348015611303575f80fd5b506105e4611312366004614fbf565b6129fb565b348015611322575f80fd5b506105e46113313660046157f8565b612a5c565b348015611341575f80fd5b506106e861135036600461507c565b612b2a565b348015611360575f80fd5b506105e461136f36600461507c565b612b5e565b34801561137f575f80fd5b50600180547f00000000000000000000000000000000000000000000000000753d533d967fff909101906109a9565b3480156113b9575f80fd5b506105e46113c8366004615b6b565b612b96565b3480156113d8575f80fd5b506106056113e73660046152e4565b5f9081526005602052604090205460ff1690565b6001600160781b0383165f908152600b6020526040812061141e91858585612d35565b505050565b5f6001600160e01b03198216158061144b57506001600160e01b03198216630dd9a30760e11b145b8061146657506001600160e01b03198216633016020d60e21b145b8061148157506001600160e01b031982166389a9c85560e01b145b8061149c57506301ffc9a760e01b6001600160e01b03198316145b92915050565b6114ac815f6123dc565b50565b5f6114de60107f0000000000000000000000000000000000000000000000000000037d00002c2663ffffffff16565b6114f26104838c8c8b8d8c8c8b8b8b612dfd565b6114ff8b87878d88612ece565b9050801561151157611511868a612f6b565b9a9950505050505050505050565b5f8361152a81612fc0565b61155860107f0000000000000000000000000000000000000000000000000000037d00002c2663ffffffff16565b61156b8e8e8d8f8e8e8d8b8b8f8f612fee565b8c6115798f8b8b848c612ece565b9250821561158b5761158b8a8e612f6b565b50509c9b505050505050505050505050565b6001600160781b0381165f908152600b6020526040902060609061149c90613004565b5f6115ef60027f0000000000000000000000000000000000000000000000000000037d00002c2663ffffffff16565b5f611600876104838787875f613010565b905061160f8588888787612ece565b915081156116245761162481885f86816130ec565b5095945050505050565b6116366131c7565b6114ac816131cf565b6001600160a01b0381165f908152600a602052604090205460609061149c90630100000090046001600160781b0316611d2e565b61167f8585858561258c565b5050505050565b61141e838383611f62565b61141e600184845f613214565b6001600160a01b0382165f908152600a6020908152604080832054630100000090046001600160781b03168352600b825280832084845260050190915281205460ff165b9392505050565b61141e600c5f856001600160781b03166001600160781b031681526020019081526020015f206001858585808060200260200160405190810160405280939291908181526020018383602002808284375f9201919091525061327892505050565b600960ff8516111561176f576040516317c5702360e01b815260040160405180910390fd5b6117788561332a565b6001600160a01b0385165f818152600a6020908152604091829020805462ff00ff19166201000060ff8a1690810260ff1916919091178815151772ff00000000000000000000000000000000ff0019166101008815150272ff000000000000000000000000000000000000191617600160901b871515021790915591519182527fb39d8f1e6f05413a407e46fc950eb92e9f5b3d65a47c3f0bdc7a2741a6ec0f7d910160405180910390a260408051841515815283151560208201526001600160a01b038716917f9c615afab54584e53810beb24cced6ca36919dfc62bff2d4a0d244906c41c2ac910160405180910390a2846001600160a01b03167f9c6e8620d0004b9dd8b49560e2de8ced6f409f529cc23d0bb9e5a106d5c43242826040516118a7911515815260200190565b60405180910390a25050505050565b5f80826118c28161339e565b6118f060407f0000000000000000000000000000000000000000000000000000037d00002c2663ffffffff16565b5f6119038f8f8f8f8f8f8e8e8e8e6133cc565b9050611917818e8e8e8e8e8c612ece6133f1565b9094509250821561193057611930818b888760016130ec565b50509b509b9950505050505050505050565b61194f600183835f613214565b5050565b6001600160a01b038281165f908152600a6020908152604080832054630100000090046001600160781b03168352600d82528083209385168352600490930190529081205460ff166116e2565b6001600160a01b0381165f908152600a602052604090205460609061149c90630100000090046001600160781b03166127f5565b335f8181526007602052604080822080546001019055517f8e8cebe67607ce50a14a2e3261437f641a7b33ecc053e3d9c90b25ae5e66c6569190a2565b5f80611a236002888888888887613537565b915091509550959350505050565b6001600160781b0383165f908152600c6020526040902061141e9060018585856135a6565b5f83611a6181612fc0565b611a8f60207f0000000000000000000000000000000000000000000000000000037d00002c2663ffffffff16565b611aa28d5f8d8f8e8e8d8b8b8f8f61364f565b611aaf8d8a8a5f8b613664565b91508115611ac157611ac1898d612f6b565b509b9a5050505050505050505050565b6001600160781b0381165f908152600c6020526040902060609061149c90600201613004565b61194f82825f6001613214565b6001600160781b0383165f908152600c6020526040902061141e90600185858561376e565b8365ffffffffffff16421115611b5257604051630104f5db60e71b815260040160405180910390fd5b611b5b8b613814565b611b65838961384d565b611bc5611bbd611bb88d8d8d8c6001600160c81b03168e8d8d65ffffffffffff168d65ffffffffffff1660075f8f6001600160a01b03166001600160a01b031681526020019081526020015f20546138a1565b613941565b83838661398d565b65ffffffffffff851615611bd95784611bdb565b425b9450611bec8b8b8b8a89888c613a56565b5050505050505050505050565b5f611c2860087f0000000000000000000000000000000000000000000000000000037d00002c2663ffffffff16565b611c3e6102d18a8a60018b8b8b60018b8b612dfd565b611c4a85858b8b613b04565b90508015611c5c57611c5c8588612f6b565b98975050505050505050565b5f80611c7a6003898989898989613537565b915091505b965096945050505050565b61141e600b5f856001600160781b03166001600160781b031681526020019081526020015f205f858585808060200260200160405190810160405280939291908181526020018383602002808284375f9201919091525061327892505050565b6001600160a01b038116611d11576040516303e9dcb360e31b815260040160405180910390fd5b61194f8282613b8d565b611d23613c03565b611d2c5f613c32565b565b6001600160781b0381165f908152600b6020526040902060609061149c90600201613004565b611d5d8361332a565b6001600160a01b0383165f908152600e60205260408120905b8281101561167f575f848483818110611d9157611d91615ba6565b9050602002016020810190611da6919061507c565b9050611db28382613ca1565b15611e1c57806001600160a01b0316866001600160a01b03167f1138edbff75f319641eb242dbcacee294ebc2a473eb6aa1454a7706da5bf96b060405160405180910390a36001600160a01b0381165f9081526002840160205260409020805460ff191660011790555b50600101611d76565b6001600160a01b0381165f908152600e6020526040902060609061149c90613004565b333214611e685760405163096650c560e21b815260040160405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000180611e9557505f5460ff165b15611eb357604051630f45b98b60e41b815260040160405180910390fd5b611edc7f000000000000000000000000b924c643f478fbfe3423c9188bdf4f095b9f3828613cb5565b611ef9576040516370a4078f60e01b815260040160405180910390fd5b5f805460ff19166001179055565b5f611f10613d13565b905090565b6001600160a01b038281165f908152600a6020908152604080832054630100000090046001600160781b03168352600b82528083209385168352600490930190529081205460ff166116e2565b5f611f73613e07338686865f613e20565b5090506001600160e01b0319811615611f8f57611f8f816143e9565b50505050565b5f611fc460017f0000000000000000000000000000000000000000000000000000037d00002c2663ffffffff16565b5f611fd6866102d18686600180613010565b9050611fe486868686613b04565b91508115611ffa57611ffa81875f6001816130ec565b50949350505050565b61200b6131c7565b6120145f6131cf565b801561194f575f826001600160a01b0316826040515f6040518083038185875af1925050503d805f8114612063576040519150601f19603f3d011682016040523d82523d5f602084013e612068565b606091505b505090508061141e57604051630fc77c5160e21b815260040160405180910390fd5b6001600160781b0381165f908152600c6020526040902060609061149c90613004565b6040516389a9c85560e01b81526001600160a01b0382811660048301525f917f000000000000000000000000e0a0004cb92d89cd9b056927df0fe008c16cf86f909116906389a9c85590602401602060405180830381865afa158015612115573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061149c9190615bba565b6114ac815f613b8d565b6001600160a01b038281165f908152600a6020908152604080832054630100000090046001600160781b03168352600c82528083209385168352600490930190529081205460ff166116e2565b6001600160a01b0381165f908152600a602052604090205460609061149c90630100000090046001600160781b0316611ad1565b6001600160a01b0381165f908152600a602052604090205460609061149c90630100000090046001600160781b031661159d565b6001600160781b0383165f908152600c6020526040902061141e906001858585612d35565b6001600160781b0383165f908152600b6020526040812061141e918585856135a6565b336001600160a01b038716148061225f5750336001600160a01b038616145b61227c5760405163eda7110360e01b815260040160405180910390fd5b61228584613814565b5f612296600388878787878c6143f1565b805490915060ff166122f15760028155604051600181526001600160a01b03878116919089169084907f705db7ac401a8091bb37a7838ad73d1fa8e1c663cb345f347fefe71280e3f03b9060200160405180910390a461230a565b60405163465fc3a960e11b815260040160405180910390fd5b50505050505050565b5f8361231e81612fc0565b61234c60087f0000000000000000000000000000000000000000000000000000037d00002c2663ffffffff16565b6123618c8c60018d8d8d60018b8b8f8f61449b565b61236d88888e8e613b04565b9150811561237f5761237f888b612f6b565b509a9950505050505050505050565b6001600160a01b0382165f908152600a6020908152604080832054630100000090046001600160781b03168352600c825280832084845260050190915281205460ff166116e2565b61194f82825b61194f5f838382613214565b600880546001600160781b038082166001019081166effffffffffffffffffffffffffffff1990921682179092555f8181526009602052604090819020805473ffffffffffffffffffffffffffffffffffffffff191633179055517f5cc365f89543268cb9f25c255f7f610e9147e733c589bc2732279575f125be14906124729086908690615bd5565b60405180910390a260405133906001600160781b038316907f9b0894203394c3cbb23140db7a23b224d3e18e0366e9f65bd9c8402650e27367905f90a392915050565b6124be86613814565b6124cd86868685853389613a56565b505050505050565b6124de8261332a565b6008546001600160781b03908116908216111561250e57604051637b8b439360e11b815260040160405180910390fd5b6001600160a01b0382165f818152600a6020526040808220805471ffffffffffffffffffffffffffffff000000191663010000006001600160781b0387169081029190911790915590519092917fa66ff5557b7dc1562bb5e83306e15b513a25aa7537369bce38fc29c20847a79191a35050565b6114ac338261384d565b5f61259d6144b13387878787613e20565b5090506001600160e01b031981161561167f5761167f816143e9565b5f80826125c58161339e565b6125f360807f0000000000000000000000000000000000000000000000000000037d00002c2663ffffffff16565b5f6126068e8e8e8e5f8f8e8e8e8e614511565b905061261a818d8d5f8e8e8c6136646133f1565b9094509250821561263357612633818b888760016130ec565b50509a509a98505050505050505050565b5f61267360207f0000000000000000000000000000000000000000000000000000037d00002c2663ffffffff16565b61268660148b5f8b8d8c8c8b8b8b612dfd565b6126938a87875f88613664565b905080156126a5576126a5868a612f6b565b9998505050505050505050565b600160045f6040518060e0016040528060a58152602001615d5660a5913985856040516020016126e493929190615c25565b6040516020818303038152906040528051906020012081526020019081526020015f205f6101000a81548160ff021916908315150217905550600160055f6040518060c00160405280609d8152602001615cb9609d9139858560405160200161274f93929190615c25565b60408051808303601f190181529181528151602092830120835290820192909252015f20805460ff19169115159190911790555050565b61141e8383835f613214565b5f6127c160047f0000000000000000000000000000000000000000000000000000037d00002c2663ffffffff16565b5f6127d1866014865f875f613010565b90506127e08487875f87613664565b91508115611ffa57611ffa81875f86816130ec565b6001600160781b0381165f908152600d6020526040902060609061149c90613004565b6001600160781b0383165f908152600d6020526040902061141e906002858585612d35565b600880546effffffffffffffffffffffffffffff19811660016001600160781b039283169081018084169290921790935591908316111561289157604051637b8b439360e11b815260040160405180910390fd5b6001600160781b0381165f8181526009602052604090819020805473ffffffffffffffffffffffffffffffffffffffff191633179055517f5cc365f89543268cb9f25c255f7f610e9147e733c589bc2732279575f125be14906128f79087908790615bd5565b60405180910390a260405133906001600160781b038316907f9b0894203394c3cbb23140db7a23b224d3e18e0366e9f65bd9c8402650e27367905f90a36001600160781b038083165f908152600b60208181526040808420600c808452828620600d808652848820988a16885295855283872091855283872095909452918520909592949293919261298b90888886614526565b6129975f8888866145d9565b6129a46001888785614526565b6129b160018887856145d9565b6129be6002888684614526565b6129cb60028886846145d9565b5050505050509392505050565b6001600160781b0383165f908152600b6020526040812061141e9185858561376e565b61141e600d5f856001600160781b03166001600160781b031681526020019081526020015f206002858585808060200260200160405190810160405280939291908181526020018383602002808284375f9201919091525061327892505050565b612a658361332a565b6001600160a01b0383165f908152600e60205260408120905b8281101561167f575f848483818110612a9957612a99615ba6565b9050602002016020810190612aae919061507c565b9050612aba8382614673565b15612b2157806001600160a01b0316866001600160a01b03167f208795fcac393398e42038456348398d8cac9067232f671ab240444cb51b1d2060405160405180910390a36001600160a01b0381165f9081526002840160205260409020805460ff191690555b50600101612a7e565b6001600160a01b0381165f908152600a602052604090205460609061149c90630100000090046001600160781b031661208a565b612b66613c03565b6001600160a01b038116612b8d57604051633e58254b60e01b815260040160405180910390fd5b6114ac81613c32565b612b9f8261332a565b6001600160a01b0382165f818152600a602090815260409182902080547fffffffffffffffffffffff0000ffffffffffffffffffffffffffffffffffffff16600160981b61ffff87169081029190911790915591519182527fa5c37ab91519073edd58e608f19f7ce383fd171f4f22c3612a1d0a7c1047794a910160405180910390a25050565b7f00000000000000000000000000000000000000000000000000753d533d967fff34470311156114ac576001548116156114ac57604051636623b92d60e11b815260040160405180910390fd5b7f00000000000000000000000000000000000000000000000000753d533d967fff34470311612cb55760405163fd2c901360e01b815260040160405180910390fd5b80600154165f036114ac5760405163fd2c901360e01b815260040160405180910390fd5b5f7f00000000000000000000000000000000000000000000000000753d533d967fff3447031115612d0957506001545b90565b6001548116156114ac57604051636623b92d60e11b815260040160405180910390fd5b60015490565b82612d3f81614687565b5f805b83811015612df357848482818110612d5c57612d5c615ba6565b9050602002016020810190612d71919061507c565b9150612d7d8883613ca1565b15612deb57816001600160a01b0316866001600160781b03168860ff167fda8f3bd170446760f0f965a9b52bf271cb9679b5e0a70059eff2d49425229d1760405160405180910390a46001600160a01b0382165f9081526004890160205260409020805460ff191660011790555b600101612d42565b5050505050505050565b5f612ebc611bb88c8c8c8c8c8c60075f8e6001600160a01b03166001600160a01b031681526020019081526020015f2054604080517f932b8553b8e35bbee682d275cbe1cf115e14a777e2ca3266b4797369fb6317d3602080830191909152818301999099526001600160a01b03979097166060880152608087019590955260a086019390935260c08501919091523360e085015261010084015261012080840191909152815180840390910181526101409092019052805191012090565b9050611bec8787868b898689896146c7565b5f612edf610483878787878761471f565b905080612f6257604051637921219560e11b81526001600160a01b0386811660048301528581166024830152604482018590526064820184905260a060848301525f60a483015287169063f242432a9060c4015f604051808303815f87803b158015612f49575f80fd5b505af1925050508015612f5a575060015b612f62575060015b95945050505050565b6001600160a01b0382165f908152600660209081526040808320600885901c845290915290208054600160ff84161b90811891829055161561194f5760405163e4adc0bf60e01b815260040160405180910390fd5b5f8181526004602052604090205460ff166114ac57604051632391283760e11b815260040160405180910390fd5b611bec6104838c8c8c8c8c8c8c8c8c8c8c614789565b60605f6116e2836147bc565b5f61302160028888888886336143f1565b805490915042600160d01b90910465ffffffffffff16101561305657604051630104f5db60e71b815260040160405180910390fd5b805461010090046001600160c81b03168311156130865760405163fee142c560e01b815260040160405180910390fd5b81156130a0578054610100600160d01b03191681556130e2565b80546001600160c81b03610100909104811610156130e25780546001600160c81b03610100808304821686900390911602610100600160d01b03199091161781555b9695505050505050565b811561167f57801561318057845460ff196001600160c81b036101008084048216860190911602167fffffffffffff00000000000000000000000000000000000000000000000000009091161785556040518281526001600160a01b0385169084907f83e0ca2c1392f14286fa1e41c797789d48c5827572e8bcc352d8943c1961eaf09060200160405180910390a361167f565b84546001600160c81b036101009091048116101561167f5784546001600160c81b036101008083048216850190911602610100600160d01b03199091161785555050505050565b611d2c613c03565b600180549082905560408051828152602081018490527fe0d8d9ad73c586e8cf60ffd390b6f3654200a2d8857eb6abba4f6842a1210aae910160405180910390a15050565b828433613222838383614815565b6132506001600160a01b038716886001600160a01b031686613244575f61324a565b600160ff1b5b176148df565b61230a61326987875f9182526020526040902060041c90565b886001600160a01b03166148df565b8161328281614687565b5f805b835181101561230a578381815181106132a0576132a0615ba6565b602090810291909101015191506132b78783614673565b1561332257816001600160a01b0316856001600160781b03168760ff167f503012490a650739416858609e898957b874d17415a062945179c5735797884060405160405180910390a46001600160a01b0382165f9081526004880160205260409020805460ff191690555b600101613285565b336001600160a01b038216810361333f575050565b5f61334983614921565b509050806001600160a01b0316826001600160a01b03160361336a57505050565b5f613376848285614969565b50905080156133855750505050565b604051637f954ba160e01b815260040160405180910390fd5b5f8181526005602052604090205460ff166114ac57604051632391283760e11b815260040160405180910390fd5b5f6133e28b8b8b6104838c8c8c8c8c8c8c6149d5565b9b9a5050505050505050505050565b87546020880135905f9061010090046001600160c81b031682111561342357895461010090046001600160c81b031691505b88604001358210156134485760405163b9ff981560e01b815260040160405180910390fd5b89546001600160c81b03610100808304821685900390911602610100600160d01b0319909116178a5560405182815233906001600160a01b0388169086907f2203cb053e6b01ec07e87d67d288d360ae164171185684936663b7d8fa9c534c9060200160405180910390a4895461010090046001600160c81b03165f0361351757895460ff19166001178a556040515f815233906001600160a01b0388169086907f705db7ac401a8091bb37a7838ad73d1fa8e1c663cb345f347fefe71280e3f03b9060200160405180910390a45b6135288887878a868863ffffffff16565b90509850989650505050505050565b5f805f6135498a8a898989898e6143f1565b805490915042600160d01b90910465ffffffffffff161061357957805461010090046001600160c81b031661357b565b5f5b90546001600160c81b039091169a600160d01b90910465ffffffffffff169950975050505050505050565b826135b081614687565b5f805b83811015612df3578484828181106135cd576135cd615ba6565b9050602002013591506135ec8289600201614b1490919063ffffffff16565b156136475781866001600160781b03168860ff167fc8615322788d404dfe307db9eef031bc148d1cec5e270a1fd6528a02b445d44560405160405180910390a45f8281526005890160205260409020805460ff191660011790555b6001016135b3565b611bec60148c8c8c8c8c8c8c8c8c8c8c614789565b5f61367460148787875f8761471f565b905080612f6257604080516001600160a01b0387811660248301528681166044830152606480830186905283518084039091018152608490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166323b872dd60e01b17905291515f928392908a16916136f39190615c4b565b5f604051808303815f865af19150503d805f811461372c576040519150601f19603f3d011682016040523d82523d5f602084013e613731565b606091505b5091509150816137445760019250613763565b805115613763578080602001905181019061375f9190615bba565b1592505b505095945050505050565b8261377881614687565b5f805b83811015612df35784848281811061379557613795615ba6565b9050602002013591506137b48289600201614b1f90919063ffffffff16565b1561380c5781866001600160781b03168860ff167f061d78094976b1d9ae7bb858f141c915b46152756409caadb07482983c2ca30160405160405180910390a45f8281526005890160205260409020805460ff191690555b60010161377b565b6102d1811480613825575061048381145b806138305750601481145b6114ac57604051639d36a97960e01b815260040160405180910390fd5b6001600160a01b0382165f908152600660209081526040808320600885901c845290915290208054600160ff84161b908118918290551661194f5760405163d979627360e01b815260040160405180910390fd5b604080517ff9c04f8b028fcfa3315ea5accaee4589194a685f07cda0392e6ba9550706111960208201529081018a90526001600160a01b03808a1660608301526080820189905260a0820188905260c08201879052851660e08201526101008101849052610120810183905261014081018290525f90610160015b6040516020818303038152906040528051906020012090509998505050505050505050565b5f61149c61394d613d13565b8360405161190160f01b602082015260228101839052604281018290525f9060620160405160208183030381529060405280519060200120905092915050565b60418290036139f1578235602084013560408501355f90811a90806139b489848787614b2a565b91509150806001600160a01b0316866001600160a01b03161415806139d65750815b156139e7576139e7868a8a8a614bd3565b5050505050611f8f565b6040829003613a4a57823560208401355f80613a0e888585614c26565b91509150806001600160a01b0316856001600160a01b0316141580613a305750815b15613a4157613a4185898989614bd3565b50505050611f8f565b611f8f81858585614bd3565b5f613a676002848a8a8a86886143f1565b805460ff16600160d01b65ffffffffffff8716908102610100600160d01b031916919091176101006001600160c81b038916908102919091178355604080518a815260208101929092528101919091529091506001600160a01b0383811691898216918616907f0ec867d4f1b037422566cd0248bae620e6c142dcf5631948271916e8ca8dd2639060600160405180910390a45050505050505050565b5f613b166102d184878786600161471f565b905080613b85576040516323b872dd60e01b81526001600160a01b0386811660048301528581166024830152604482018490528416906323b872dd906064015f604051808303815f87803b158015613b6c575f80fd5b505af1925050508015613b7d575060015b613b85575060015b949350505050565b613b9682614687565b6001600160781b0382165f81815260096020526040808220805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b03861690811790915590519092917f9b0894203394c3cbb23140db7a23b224d3e18e0366e9f65bd9c8402650e2736791a35050565b5f546001600160a01b03610100909104163314611d2c576040516208650f60e61b815260040160405180910390fd5b5f80546001600160a01b038381166101008181027fffffffffffffffffffffff0000000000000000000000000000000000000000ff851617855560405193049190911692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a35050565b5f6116e2836001600160a01b038416614c6d565b5f816001600160a01b0316600a5a613ccd9190615c66565b6040515f8181818686fa925050503d805f8114613d05576040519150601f19603f3d011682016040523d82523d5f602084013e613d0a565b606091505b50909392505050565b5f7f00000000000000000000000000000000000000000000000000000000000081734603613d6057507f4d8c2ba97605cda93024fa917c2f5b060f10c2949ad80d49e0ff4a348bd1eb3990565b611f10604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201527f12900c1b40e87470816fa5cb16069bf5b3df9cf052332bf21440c4318ccddcfe918101919091527f2a80e1ef1d7842f27f2e6be0972bb708b9a135c38860dbe73c27c3486c34f4de60608201524660808201523060a08201525f9060c00160405160208183030381529060405280519060200120905090565b5f611ffa836001600160a01b038616614cb9565b614cb9565b5f80306001600160a01b03871603613e3c57505f905080611c7f565b6001600160a01b0387165f908152600a602052604090208054630100000081046001600160781b0316907f00000000000000000000000000000000000000000000040303020203020100026107f862010000830460031b1690811c60ff908116927f000000000000000000000000000000000000000000000302010201000000000090921c811691600160901b90041615613f57576001600160a01b03808c165f908152600e60209081526040808320938d1683526002840190915290205460ff1615613f1b575063056c6b9160e31b95505f9450611c7f9350505050565b6001600160a01b0389165f90815260028201602052604090205460ff1615613f555750632a5cb1c360e01b95505f9450611c7f9350505050565b505b60048203613f76575063d73e63af60e01b94505f9350611c7f92505050565b6001600160781b0383165f908152600c602052604090205f19820161400a57883b15614005576001600160a01b0389165f90815260048201602052604090205460ff16614005578c613fcd8d8d8b63ffffffff8516565b61400357893f5f90815260058301602052604090205460ff1661400357506305652c5560e51b96505f9550611c7f945050505050565b505b614088565b600282036140885761401b896120ad565b614088576001600160a01b0389165f90815260048201602052604090205460ff16614088578c6140508d8d8b63ffffffff8516565b61408657893f5f90815260058301602052604090205460ff166140865750636719795560e11b96505f9550611c7f945050505050565b505b896001600160a01b03168b6001600160a01b0316036140c557600383146140c557505091545f9450600160981b900461ffff169250611c7f915050565b600183036141b1578c6140dd8d8d8b63ffffffff8516565b156140ff57505092545f9550600160981b900461ffff169350611c7f92505050565b5f600b5f876001600160781b03166001600160781b031681526020019081526020015f209050806004015f8e6001600160a01b03166001600160a01b031681526020019081526020015f205f9054906101000a900460ff1615614176575063204f370f60e11b97505f9650611c7f95505050505050565b8c3f5f90815260058201602052604090205460ff16156141aa575063204f370f60e11b97505f9650611c7f95505050505050565b50506143c9565b60028303614279576001600160a01b038b165f90815260048201602052604090205460ff16156141f757505091545f9450600160981b900461ffff169250611c7f915050565b8c6142078d8d8b63ffffffff8516565b1561422957505092545f9550600160981b900461ffff169350611c7f92505050565b8b3f5f90815260058301602052604090205460ff161561426057505092545f9550600160981b900461ffff169350611c7f92505050565b5063ef28f90160e01b96505f9550611c7f945050505050565b600383036143c9576001600160a01b038b165f90815260048201602081905260409091205460ff16156142c357505092545f9550600160981b900461ffff169350611c7f92505050565b6001600160a01b038b165f9081526020829052604090205460ff161561430057505092545f9550600160981b900461ffff169350611c7f92505050565b8d6143108e8e8c63ffffffff8516565b1561433357505093545f9650600160981b900461ffff169450611c7f9350505050565b8c3f5f9081526005840160208190526040909120548e9060ff161561437257505095545f9850600160981b900461ffff169650611c7f95505050505050565b508c3f5f908152602082905260409020548d9060ff16156143ad57505095545f9850600160981b900461ffff169650611c7f95505050505050565b5063ef28f90160e01b99505f9850611c7f975050505050505050565b505091545f9b600160981b90910461ffff169a5098505050505050505050565b805f5260045ffd5b5f875f61446089898989896001600160a01b039485165f818152600760209081526040918290205482518083019490945283830197909752949096166060820152608081019290925260a082015260c0808201939093528351808203909301835260e001909252805191012090565b81526020019081526020015f205f836001600160a01b03166001600160a01b031681526020019081526020015f209050979650505050505050565b611bec6102d18c8c8c8c8c8c8c8c8c8c8c614789565b5f806144cd84613e1b87865f9182526020526040902060041c90565b50915081156144e05760019150506116e2565b6144f3846001600160a01b038716614cb9565b9092509050818015612f62575060ff81901c60011495945050505050565b5f6133e28b8b8b60148c8c8c8c8c8c8c6149d5565b8181600481015f61453684614cfa565b90505f805b828110156145cd5761454d8682614d03565b91506145598583613ca1565b156145c557816001600160a01b0316896001600160781b03168b60ff167fda8f3bd170446760f0f965a9b52bf271cb9679b5e0a70059eff2d49425229d1760405160405180910390a46001600160a01b0382165f908152602085905260409020805460ff191660011790555b60010161453b565b50505050505050505050565b6002808301908201600583015f6145ef84614cfa565b90505f805b828110156145cd576146068682614d03565b91506146128583614b14565b1561466b5781896001600160781b03168b60ff167fc8615322788d404dfe307db9eef031bc148d1cec5e270a1fd6528a02b445d44560405160405180910390a45f828152602085905260409020805460ff191660011790555b6001016145f4565b5f6116e2836001600160a01b038416614d0e565b6001600160781b0381165f908152600960205260409020546001600160a01b031633146114ac576040516304e680cb60e41b815260040160405180910390fd5b864211156146e85760405163e3fd7ac360e01b815260040160405180910390fd5b848611156147095760405163de7fafeb60e01b815260040160405180910390fd5b614713848961384d565b612df38383838761398d565b5f805f6147326144b189338a8a8a613e20565b909250905061ffff8116158061474b5750888161ffff16145b15614764576001600160e01b031982161515925061477d565b604051633d29aed360e11b815260040160405180910390fd5b50509695505050505050565b5f61479b8d8d8d8d8b8e8e8a8a614df1565b90506147ad8989888d8b868b8b6146c7565b50505050505050505050505050565b6060815f0180548060200260200160405190810160405280929190818152602001828054801561480957602002820191905f5260205f20905b8154815260200190600101908083116147f5575b50505050509050919050565b6001600160a01b0383165f908152600a60205260409020805460ff161561484f5760405163b1ae736760e01b815260040160405180910390fd5b8054610100900460ff1615614888575f196001600160a01b038416016148885760405163bf729bb160e01b815260040160405180910390fd5b8054630100000090046001600160781b03165f908152600d602090815260408083206001600160a01b038616845260040190915290205460ff16611f8f57604051635e47503160e11b815260040160405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000011561490c5780825d5050565b5f5460ff161561491d5780825d5050565b9055565b5f80614957565b638da5cb5b5f525f8060205f6004601c865afa60203d1015161561494f575f519150915091565b509160019150565b61496083614928565b91509150915091565b5f806149be565b5f80604051606081016040526391d14854815284816020015285816040015260205f6044601c8401875afa60203d101516156149b0575f519250506149b6565b50600190505b935093915050565b6149c9838587614970565b91509150935093915050565b5f6001600160c81b038a3511156149ff57604051633b0a334360e01b815260040160405180910390fd5b614a0f6003878b8b8b88336143f1565b805490915060ff166122f157805461010090046001600160c81b03165f03614ae357614a56614a4e8a8a8a8e358b8b65ffffffffffff8c168b8b614df1565b8d8d8961398d565b805460ff166101008b356001600160c81b0381169190910279ffffffffffffffffffffffffffffffffffffffffffffffffffff1691909117600160d01b65ffffffffffff87160217825560405190815233906001600160a01b0388169085907f257001e1f7fbfc5bbde5da225c876ab67293f37bda3afb8b35d9a55dfad6f65d9060200160405180910390a45b8054600160d01b900465ffffffffffff164211156133e25760405163e3fd7ac360e01b815260040160405180910390fd5b5f6116e28383614c6d565b5f6116e28383614d0e565b5f807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0831115614b5f5750600190505f614bca565b604080515f81526020810180835288905260ff871691810191909152606081018590526080810184905260019060a0016020604051602081039080840390855afa158015614baf573d5f803e3d5ffd5b5050604051601f1901516001600160a01b0381161593509150505b94509492505050565b836001600160a01b03163b5f03614bfd576040516373c919b560e01b815260040160405180910390fd5b614c0984848484614e1d565b611f8f576040516373c919b560e01b815260040160405180910390fd5b5f807f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8316601b60ff85901c01614c5f87828885614b2a565b909890975095505050505050565b5f818152600183016020526040812054614cb257508154600181810184555f84815260208082209093018490558454848252828601909352604090209190915561149c565b505f61149c565b5f80614cc483614e99565b9050806001600160a01b03811660011480614cf05750846001600160a01b0316816001600160a01b0316145b9250509250929050565b5f61149c825490565b5f6116e28383614edd565b5f8181526001830160205260408120548015614de8575f614d30600183615c85565b85549091505f90614d4390600190615c85565b9050818114614da2575f865f018281548110614d6157614d61615ba6565b905f5260205f200154905080875f018481548110614d8157614d81615ba6565b5f918252602080832090910192909255918252600188019052604090208390555b8554869080614db357614db3615ca4565b600190038181905f5260205f20015f90559055856001015f8681526020019081526020015f205f90556001935050505061149c565b5f91505061149c565b6001600160a01b0385165f908152600760205260408120546133e2611bb88c8c8c8c8b8b8b8b8a614f03565b5f614e8d565b5f604051630b135d3f60e11b81528360048201526040602482015285604482015285856064830137601f19601f87011660640180820160405260205f8284875afa60203d10151615614e8357630b135d3f60e11b5f511492505050613b85565b5050949350505050565b612f6282848688614e23565b5f7f000000000000000000000000000000000000000000000000000000000000000115614ec557505c90565b5f5460ff1615614ed457505c90565b5080545b919050565b5f825f018281548110614ef257614ef2615ba6565b905f5260205f200154905092915050565b60408051602081018490529081018a90526001600160a01b03891660608201526080810188905260a0810187905260c081018690523360e08201526101008101859052610120810182905261014081018490525f906101600161391c565b80356001600160781b0381168114614ed8575f80fd5b5f8083601f840112614f87575f80fd5b50813567ffffffffffffffff811115614f9e575f80fd5b6020830191508360208260051b8501011115614fb8575f80fd5b9250929050565b5f805f60408486031215614fd1575f80fd5b614fda84614f61565b9250602084013567ffffffffffffffff811115614ff5575f80fd5b61500186828701614f77565b9497909650939450505050565b5f6020828403121561501e575f80fd5b81356001600160e01b0319811681146116e2575f80fd5b80356001600160a01b0381168114614ed8575f80fd5b5f806040838503121561505c575f80fd5b61506583614f61565b915061507360208401615035565b90509250929050565b5f6020828403121561508c575f80fd5b6116e282615035565b5f8083601f8401126150a5575f80fd5b50813567ffffffffffffffff8111156150bc575f80fd5b602083019150836020828501011115614fb8575f80fd5b5f805f805f805f805f806101208b8d0312156150ed575f80fd5b6150f68b615035565b995060208b0135985060408b0135975060608b0135965060808b0135955061512060a08c01615035565b945061512e60c08c01615035565b935060e08b013592506101008b013567ffffffffffffffff811115615151575f80fd5b61515d8d828e01615095565b915080935050809150509295989b9194979a5092959850565b5f805f805f805f805f805f806101608d8f031215615192575f80fd5b61519b8d615035565b9b5060208d01359a5060408d0135995060608d0135985060808d013597506151c560a08e01615035565b96506151d360c08e01615035565b955060e08d013594506101008d013593506101208d0135925067ffffffffffffffff6101408e01351115615205575f80fd5b6152168e6101408f01358f01615095565b81935080925050509295989b509295989b509295989b565b5f6020828403121561523e575f80fd5b6116e282614f61565b602080825282518282018190525f9190848201906040850190845b818110156152875783516001600160a01b031683529284019291840191600101615262565b50909695505050505050565b5f805f805f60a086880312156152a7575f80fd5b6152b086615035565b94506152be60208701615035565b93506152cc60408701615035565b94979396509394606081013594506080013592915050565b5f602082840312156152f4575f80fd5b5035919050565b602080825282518282018190525f9190848201906040850190845b8181101561528757835183529284019291840191600101615316565b5f8060408385031215615343575f80fd5b61534c83615035565b946020939093013593505050565b5f805f6060848603121561536c575f80fd5b61537584615035565b925061538360208501615035565b915061539160408501615035565b90509250925092565b5f805f606084860312156153ac575f80fd5b6153b584615035565b95602085013595506040909401359392505050565b80151581146114ac575f80fd5b5f805f805f60a086880312156153eb575f80fd5b6153f486615035565b9450602086013560ff81168114615409575f80fd5b93506040860135615419816153ca565b92506060860135615429816153ca565b91506080860135615439816153ca565b809150509295509295909350565b5f60608284031215615457575f80fd5b50919050565b803565ffffffffffff81168114614ed8575f80fd5b5f805f805f805f805f805f6101808c8e03121561548d575f80fd5b8b3567ffffffffffffffff8111156154a3575f80fd5b6154af8e828f01615095565b909c509a506154c390508d60208e01615447565b98506154d160808d01615035565b975060a08c013596506154e660c08d01615035565b95506154f460e08d01615035565b94506101008c0135935061550b6101208d0161545d565b92506101408c013591506101608c013590509295989b509295989b9093969950565b5f806040838503121561553e575f80fd5b61506583615035565b5f805f805f60a0868803121561555b575f80fd5b61556486615035565b945061557260208701615035565b93506040860135925061558760608701615035565b949793965091946080013592915050565b5f805f805f805f805f805f6101408c8e0312156155b3575f80fd5b6155bc8c615035565b9a5060208c0135995060408c0135985060608c013597506155df60808d01615035565b96506155ed60a08d01615035565b955060c08c0135945060e08c013593506101008c013592506101208c013567ffffffffffffffff81111561561f575f80fd5b61562b8e828f01615095565b915080935050809150509295989b509295989b9093969950565b80356001600160c81b0381168114614ed8575f80fd5b5f805f805f805f805f805f6101408c8e031215615676575f80fd5b8b359a5061568660208d01615035565b995060408c0135985060608c013597506156a260808d01615645565b96506156b060a08d01615035565b95506156be60c08d0161545d565b94506156cc60e08d0161545d565b93506156db6101008d01615035565b92506101208c013567ffffffffffffffff81111561561f575f80fd5b5f8060408385031215615708575f80fd5b61534c83614f61565b5f805f805f805f8060e0898b031215615728575f80fd5b61573189615035565b975060208901359650604089013595506060890135945061575460808a01615035565b935061576260a08a01615035565b925060c089013567ffffffffffffffff81111561577d575f80fd5b6157898b828c01615095565b999c989b5096995094979396929594505050565b5f805f805f8060c087890312156157b2575f80fd5b6157bb87615035565b95506157c960208801615035565b9450604087013593506157de60608801615035565b92506080870135915060a087013590509295509295509295565b5f805f6040848603121561580a575f80fd5b614fda84615035565b5f805f8060808587031215615826575f80fd5b61582f85615035565b935061583d60208601615035565b925061584b60408601615035565b9396929550929360600135925050565b5f805f805f805f805f806101208b8d031215615875575f80fd5b61587e8b615035565b995060208b0135985060408b0135975060608b013596506158a160808c01615035565b95506158af60a08c01615035565b945060c08b0135935060e08b013592506101008b013567ffffffffffffffff811115615151575f80fd5b5f80602083850312156158ea575f80fd5b823567ffffffffffffffff811115615900575f80fd5b61590c85828601615095565b90969095509350505050565b5f805f805f8060c0878903121561592d575f80fd5b8635955061593d60208801615035565b94506040870135935061595260608801615035565b925061596060808801615645565b915061596e60a0880161545d565b90509295509295509295565b5f806040838503121561598b575f80fd5b61599483615035565b915061507360208401614f61565b5f805f805f805f805f806101608b8d0312156159bc575f80fd5b8a3567ffffffffffffffff8111156159d2575f80fd5b6159de8d828e01615095565b909b5099506159f290508c60208d01615447565b9750615a0060808c01615035565b9650615a0e60a08c01615035565b9550615a1c60c08c01615035565b945060e08b01359350615a326101008c0161545d565b92506101208b013591506101408b013590509295989b9194979a5092959850565b5f805f805f805f805f6101008a8c031215615a6c575f80fd5b615a758a615035565b985060208a0135975060408a0135965060608a01359550615a9860808b01615035565b9450615aa660a08b01615035565b935060c08a0135925060e08a013567ffffffffffffffff811115615ac8575f80fd5b615ad48c828d01615095565b915080935050809150509295985092959850929598565b5f805f60608486031215615afd575f80fd5b615b0684615035565b9250615b1460208501615035565b9150604084013590509250925092565b5f805f60408486031215615b36575f80fd5b833567ffffffffffffffff811115615b4c575f80fd5b615b5886828701615095565b9094509250615391905060208501614f61565b5f8060408385031215615b7c575f80fd5b615b8583615035565b9150602083013561ffff81168114615b9b575f80fd5b809150509250929050565b634e487b7160e01b5f52603260045260245ffd5b5f60208284031215615bca575f80fd5b81516116e2816153ca565b60208152816020820152818360408301375f818301604090810191909152601f909201601f19160101919050565b5f5b83811015615c1d578181015183820152602001615c05565b50505f910152565b5f8451615c36818460208901615c03565b8201838582375f930192835250909392505050565b5f8251615c5c818460208701615c03565b9190910192915050565b5f82615c8057634e487b7160e01b5f52601260045260245ffd5b500490565b8181038181111561149c57634e487b7160e01b5f52601160045260245ffd5b634e487b7160e01b5f52603160045260245ffdfe5065726d69744f72646572576974684164646974696f6e616c446174612875696e7432353620746f6b656e547970652c6164647265737320746f6b656e2c75696e743235362069642c75696e7432353620616d6f756e742c75696e743235362073616c742c61646472657373206f70657261746f722c75696e743235362065787069726174696f6e2c75696e74323536206d61737465724e6f6e63652c5065726d69745472616e7366657246726f6d576974684164646974696f6e616c446174612875696e7432353620746f6b656e547970652c6164647265737320746f6b656e2c75696e743235362069642c75696e7432353620616d6f756e742c75696e74323536206e6f6e63652c61646472657373206f70657261746f722c75696e743235362065787069726174696f6e2c75696e74323536206d61737465724e6f6e63652ca26469706673582212204aed5a9438fa1f93a19402978e99446b15ffab9a7272f92ed814de426e3dc30964736f6c63430008180033

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

000000000000000000000000c327b16c33fdd558c1ad7daaff1a4081e91049f3000000000000000000000000e0a0004cb92d89cd9b056927df0fe008c16cf86f00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000721c006d06dc5c03b6106fa9fe32c10efb1ddb62000000000000000000000000000000000000000000000000000000000000001d43726561746f72546f6b656e5472616e7366657256616c696461746f7200000000000000000000000000000000000000000000000000000000000000000000013300000000000000000000000000000000000000000000000000000000000000

-----Decoded View---------------
Arg [0] : defaultOwner (address): 0xc327B16C33fdd558C1ad7DaaFf1a4081E91049f3
Arg [1] : eoaRegistry_ (address): 0xE0A0004CB92d89CD9b056927DF0FE008c16CF86F
Arg [2] : name (string): CreatorTokenTransferValidator
Arg [3] : version (string): 3
Arg [4] : validatorConfiguration (address): 0x721C006D06DC5c03B6106fa9fe32C10EFB1ddB62

-----Encoded View---------------
9 Constructor Arguments found :
Arg [0] : 000000000000000000000000c327b16c33fdd558c1ad7daaff1a4081e91049f3
Arg [1] : 000000000000000000000000e0a0004cb92d89cd9b056927df0fe008c16cf86f
Arg [2] : 00000000000000000000000000000000000000000000000000000000000000a0
Arg [3] : 00000000000000000000000000000000000000000000000000000000000000e0
Arg [4] : 000000000000000000000000721c006d06dc5c03b6106fa9fe32c10efb1ddb62
Arg [5] : 000000000000000000000000000000000000000000000000000000000000001d
Arg [6] : 43726561746f72546f6b656e5472616e7366657256616c696461746f72000000
Arg [7] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [8] : 3300000000000000000000000000000000000000000000000000000000000000


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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