MON Price: $0.018862 (+2.96%)

Contract

0x721C008fdff27BF06E7E123956E2Fe03B63342e3

Overview

MON Balance

Monad Chain LogoMonad Chain LogoMonad Chain Logo0 MON

MON Value

$0.00

Token Holdings

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Set Ruleset Of C...481957572026-01-12 9:20:0512 days ago1768209605IN
0x721C008f...3B63342e3
0 MON0.00780238101.94
Set Ruleset Of C...481952632026-01-12 9:16:4712 days ago1768209407IN
0x721C008f...3B63342e3
0 MON0.0078036101.94
Apply List To Co...440592842025-12-24 4:07:3831 days ago1766549258IN
0x721C008f...3B63342e3
0 MON0.01041246102
Apply List To Co...440590522025-12-24 4:06:0531 days ago1766549165IN
0x721C008f...3B63342e3
0 MON0.01041246102
Apply List To Co...423643752025-12-16 7:19:0439 days ago1765869544IN
0x721C008f...3B63342e3
0 MON0.01469645102.5
Set Ruleset Of C...418314952025-12-13 19:48:5741 days ago1765655337IN
0x721C008f...3B63342e3
0 MON0.00859743102.10004247
Set Ruleset Of C...415472902025-12-12 12:05:5443 days ago1765541154IN
0x721C008f...3B63342e3
0 MON0.00859743102.10004247
Apply List To Co...415184942025-12-12 8:53:0743 days ago1765529587IN
0x721C008f...3B63342e3
0 MON0.0073122100
Add Accounts To ...415175622025-12-12 8:46:5343 days ago1765529213IN
0x721C008f...3B63342e3
0 MON0.02383372102
Create List Copy415171972025-12-12 8:44:2643 days ago1765529066IN
0x721C008f...3B63342e3
0 MON0.10809674102
Apply List To Co...414099722025-12-11 20:46:1043 days ago1765485970IN
0x721C008f...3B63342e3
0 MON0.01119439102
Apply List To Co...413602372025-12-11 15:12:4043 days ago1765465960IN
0x721C008f...3B63342e3
0 MON0.01048164102.51
Apply List To Co...407474222025-12-08 18:44:3146 days ago1765219471IN
0x721C008f...3B63342e3
0 MON0.01564731153.03
Apply List To Co...406978592025-12-08 13:12:1847 days ago1765199538IN
0x721C008f...3B63342e3
0 MON0.01141389104
Apply List To Co...406969332025-12-08 13:06:0647 days ago1765199166IN
0x721C008f...3B63342e3
0 MON0.01130634103.02
Set Ruleset Of C...405796092025-12-07 23:58:0447 days ago1765151884IN
0x721C008f...3B63342e3
0 MON0.01177315102.5
Apply List To Co...405757382025-12-07 23:31:5447 days ago1765150314IN
0x721C008f...3B63342e3
0 MON0.01124927102.5
Apply List To Co...405756452025-12-07 23:31:1647 days ago1765150276IN
0x721C008f...3B63342e3
0 MON0.01119256102
Apply List To Co...405729772025-12-07 23:13:1847 days ago1765149198IN
0x721C008f...3B63342e3
0 MON0.01119439102
Add Accounts To ...405684502025-12-07 22:42:5047 days ago1765147370IN
0x721C008f...3B63342e3
0 MON0.0328597102.6
Add Accounts To ...405675542025-12-07 22:36:5047 days ago1765147010IN
0x721C008f...3B63342e3
0 MON0.03268675102.06
Add Accounts To ...405674282025-12-07 22:35:5947 days ago1765146959IN
0x721C008f...3B63342e3
0 MON0.03330808104
Add Accounts To ...405671132025-12-07 22:33:5347 days ago1765146833IN
0x721C008f...3B63342e3
0 MON0.03299421103.02
Set Ruleset Of C...405027692025-12-07 15:23:1147 days ago1765120991IN
0x721C008f...3B63342e3
0 MON0.01182947103.02
Apply List To Co...403042572025-12-06 17:10:4448 days ago1765041044IN
0x721C008f...3B63342e3
0 MON0.01173961107
View all transactions

Latest 2 internal transactions

Advanced mode:
Parent Transaction Hash Block From To
336035642025-11-05 8:07:0080 days ago1762330020
0x721C008f...3B63342e3
 Contract Creation0 MON
336035642025-11-05 8:07:0080 days ago1762330020  Contract Creation0 MON
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
CreatorTokenTransferValidator

Compiler Version
v0.8.24+commit.e11b9ed9

Optimization Enabled:
Yes with 1000000 runs

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

import "./Constants.sol";
import "./DataTypes.sol";
import "./Errors.sol";
import "./ValidatorBase.sol";
import "./interfaces/IRuleset.sol";
import "./interfaces/IRulesetDelegateCall.sol";
import "@limitbreak/permit-c/PermitC.sol";
import "@limitbreak/tm-core-lib/src/utils/introspection/ERC165.sol";
import "@limitbreak/tm-core-lib/src/utils/structs/EnumerableSet.sol";
import "@limitbreak/tm-core-lib/src/utils/token/IEOARegistry.sol";
import "@limitbreak/tm-core-lib/src/utils/token/ITransferValidator.sol";
import "@limitbreak/tm-core-lib/src/utils/misc/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 choose a 
 *         validation ruleset to apply to their collection, global and ruleset-specific options to fine-tune validation
 *         behavior, and manage blacklists/whitelists/authorizer/expansion lists and expansion settings for rulesets.
 */
contract CreatorTokenTransferValidator is 
IEOARegistry, 
ITransferValidator, 
ValidatorBase, 
ERC165, 
Tstorish, 
PermitC {
    using EnumerableSet for EnumerableSet.AddressSet;
    using EnumerableSet for EnumerableSet.Bytes32Set;

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

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

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

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

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

    /// @dev The address of the management module that implements list and collection settings management.
    address private immutable _managementModule;

    /// @dev The address of the safe delegate module that implements the safe delegate code checker.
    address private immutable _safeDelegateModule;

    constructor(
        address defaultOwner,
        address eoaRegistry_,
        address managementModule_,
        address safeDelegateModule_,
        string memory name,
        string memory version
    ) 
    Tstorish()
    PermitC(
        name,
        version,
        defaultOwner,
        block.chainid == 1 ? 0.33 ether : 0.01 ether
    ) {
        if (defaultOwner == address(0) || 
            eoaRegistry_ == address(0) || eoaRegistry_.code.length == 0 ||
            managementModule_ == address(0) || managementModule_.code.length == 0 ||
            safeDelegateModule_ == address(0) || safeDelegateModule_.code.length == 0) {
            revert CreatorTokenTransferValidator__InvalidConstructorArgs();
        }

        _createDefaultList(defaultOwner);

        _eoaRegistry = eoaRegistry_;
        _managementModule = managementModule_;
        _safeDelegateModule = safeDelegateModule_;
    }

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

        validatorStorage().listOwners[id] = defaultOwner;

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

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

    /**
     * @dev This modifier delegatecalls the specified module with the exact same calldata
     *      that came into the function, properly handling returndata when necessary.
     *      Assumes that the complete implementation of a function call is done in an external
     *      module.
     */
    modifier delegateCall(address module) {
        assembly {
            calldatacopy(0, 0, calldatasize())
            let result := delegatecall(gas(), module, 0, calldatasize(), 0, 0)
            let size := returndatasize()
            returndatacopy(0, 0, size)
            if iszero(result) {
                revert(0, size)
            }
            return(0, size)
        }
        _;
    }

    /*************************************************************************/
    /*                               RULESET ADMIN                           */
    /*************************************************************************/

    /**
     * @notice Registers a ruleset to be used for transfer validation.
     * 
     * @notice The ruleset must be a contract and goes through a sanitization / purity check process.
     *         The ruleset may not contain any banned opcodes (either undefined as of Prague or any known opcode that
     *         carries any risk of making state changes on the EVM). 
     *
     * @dev    Throws if the `msg.sender` does not own the default list (validator admin).
     * @dev    Throws if codehash of the ruleset denotes that it is either an uninitialized account or is an EOA.
     * @dev    Throws if the ruleset is not pure (meaning that it contains an invalid or banned opcode).
     *
     * @dev <h4>Postconditions:</h4>
     *      1. The ruleset is marked as registered in the validator storage.
     *      2. A `RulesetRegistered` event is emitted, the first time the ruleset is registered.
     *         (Subsequent registrations of the same ruleset do not emit this event).
     *
     * @param ruleset The address of the ruleset to register.
     */
    function registerRuleset(address ruleset) external delegateCall(_safeDelegateModule) {}

    /**
     * @notice Binds a ruleset to a ruleset ID.
     * 
     * @dev    Throws if the `msg.sender` does not own the default list (validator admin).
     * @dev    Throws if the ruleset is not registered.
     * @dev    Throws if the ruleset ID is RULESET_ID_FIXED_OR_CUSTOM, as this is reserved for fixed rulesets that do not follow the
     *         automatic update process.
     *
     * @dev <h4>Postconditions:</h4>
     *      1. The ruleset is bound to the ruleset ID.
     *      2. A `RulesetBindingUpdated` event is emitted to indicate the ruleset binding has been updated.
     *         (If the ruleset is already bound to the ruleset ID, no event is emitted).
     *
     * @param rulesetId The ID of the ruleset to bind.
     * @param ruleset The address of the ruleset to bind.
     */
    function bindRuleset(uint8 rulesetId, address ruleset) external delegateCall(_safeDelegateModule) {}

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

    /**
     * @notice Apply the collection ruleset and options 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 Modular rulesets with options determine the logic governing transfers.
     *
     * @dev Throws when the ruleset returns a non-zero error selector.
     *
     * @dev <h4>Postconditions:</h4>
     *      1. Transfer is allowed or denied based on the ruleset.
     *
     * @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(AUTHORIZER_CHECK_TYPE_COLLECTION, msg.sender, caller, from, to, 0, 0);
        if (errorSelector != SELECTOR_NO_ERROR) {
            _revertCustomErrorSelectorAsm(errorSelector);
        }
    }

    /**
     * @notice Apply the collection ruleset and options 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 Modular rulesets with options determine the logic governing transfers.
     *
     * @dev Throws when the ruleset returns a non-zero error selector.
     *
     * @dev <h4>Postconditions:</h4>
     *      1. Transfer is allowed or denied based on the ruleset.
     *
     * @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(AUTHORIZER_CHECK_TYPE_TOKEN, msg.sender, caller, from, to, tokenId, 0);
        if (errorSelector != SELECTOR_NO_ERROR) {
            _revertCustomErrorSelectorAsm(errorSelector);
        }
    }

    /**
     * @notice Apply the collection ruleset and options 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 Modular rulesets with options determine the logic governing transfers.
     *
     * @dev Throws when the ruleset returns a non-zero error selector.
     * @dev Throws when authorization mode with amounts are active and the amount exceeds the authorization amount.
     *
     * @dev <h4>Postconditions:</h4>
     *      1. Transfer is allowed or denied based on the ruleset.
     *
     * @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.
     * @param amount      The amount of the token being transferred.
     */
    function validateTransfer(address caller, address from, address to, uint256 tokenId, uint256 amount) external {
        (bytes4 errorSelector,) = _validateTransfer(AUTHORIZER_CHECK_TYPE_TOKEN_AND_AMOUNT, msg.sender, caller, from, to, tokenId, amount);
        if (errorSelector != SELECTOR_NO_ERROR) {
            _revertCustomErrorSelectorAsm(errorSelector);
        }

        uint256 collectionAndTokenIdSlot = _getTransientOperatorSlot(msg.sender, tokenId);
        if (_getTstorish(collectionAndTokenIdSlot) != 0) {
            uint256 amountSlot = collectionAndTokenIdSlot + 1;
            uint256 authorizedAmount = _getTstorish(amountSlot);

            if (amount > authorizedAmount) {
                revert CreatorTokenTransferValidator__AmountExceedsAuthorization();
            }

            unchecked {
                _setTstorish(amountSlot, authorizedAmount - amount);
            }
        }
    }

    /**
     * @notice Apply the collection ruleset and options 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 Modular rulesets with options determine the logic governing transfers.
     *
     * @dev Throws when the ruleset returns a non-zero error selector.
     *
     * @dev <h4>Postconditions:</h4>
     *      1. Transfer is allowed or denied based on the ruleset.
     *
     * @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 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.
     *      2. The specified amount is stored as the authorized amount.
     * 
     * @param token     The address of the token to authorize.
     * @param tokenId   The token id to set the authorized operator for.
     * @param amount    The amount of the token to authorize.
     */
    function beforeAuthorizedTransferWithAmount(address token, uint256 tokenId, uint256 amount) external {
        _setOperatorInTransientStorage(WILDCARD_OPERATOR_ADDRESS, token, tokenId, amount);
    }

    /**
     * @notice Sets the specified 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 specified operator is stored as an authorized operator for transfers.
     *      2. The specified amount is stored as the authorized amount for the operator.
     * 
     * @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.
     * @param amount    The amount of the token to authorize.
     */
    function beforeAuthorizedTransferWithAmount(address operator, address token, uint256 tokenId, uint256 amount) external {
        _setOperatorInTransientStorage(operator, token, tokenId, amount);
    }

    /**
     * @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 {
        _setOperatorInTransientStorage(address(uint160(uint256(BYTES32_ZERO))), token, tokenId, 0);
    }

    /*************************************************************************/
    /*                               SIMULATION                              */
    /*************************************************************************/

    /**
     * @notice Simulates transfer validation of a collection with its current ruleset and options.
     * 
     * @dev    Authorization mode overrides are not taken into account.
     *
     * @param collection The address of the collection.
     * @param caller     The address initiating the transfer.
     * @param from       The address of the token owner.
     * @param to         The address of the token receiver.
     * @return isTransferAllowed True if the transfer is allowed, false otherwise.
     * @return errorCode         The error code if the transfer is not allowed.
     */
    function validateTransferSim(
        address collection, 
        address caller, 
        address from, 
        address to
    ) external view returns (bool isTransferAllowed, bytes4 errorCode) {
        (errorCode,) = _validateTransfer(AUTHORIZER_CHECK_TYPE_COLLECTION, collection, caller, from, to, 0, 0);
        isTransferAllowed = errorCode == SELECTOR_NO_ERROR;
    }

    /**
     * @notice Simulates transfer validation of a collection with its current ruleset and options.
     *
     * @dev    Authorization mode overrides are not taken into account.
     *
     * @param collection The address of the collection.
     * @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 isTransferAllowed True if the transfer is allowed, false otherwise.
     * @return errorCode         The error code if the transfer is not allowed.
     */
    function validateTransferSim(
        address collection,
        address caller, 
        address from, 
        address to, 
        uint256 tokenId
    ) external view returns (bool isTransferAllowed, bytes4 errorCode) {
        (errorCode,) = _validateTransfer(AUTHORIZER_CHECK_TYPE_TOKEN, collection, caller, from, to, tokenId, 0);
        isTransferAllowed = errorCode == SELECTOR_NO_ERROR;
    }

    /**
     * @notice Simulates transfer validation of a collection with its current ruleset and options.
     *
     * @dev    Authorization mode overrides are not taken into account.
     *
     * @param collection The address of the collection.
     * @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.
     * @param amount     The amount of the token being transferred.
     * @return isTransferAllowed True if the transfer is allowed, false otherwise.
     * @return errorCode         The error code if the transfer is not allowed.     
     */
    function validateTransferSim(
        address collection,
        address caller, 
        address from, 
        address to, 
        uint256 tokenId, 
        uint256 amount
    ) external view returns (bool isTransferAllowed, bytes4 errorCode) {
        (errorCode,) = _validateTransfer(AUTHORIZER_CHECK_TYPE_TOKEN_AND_AMOUNT, collection, caller, from, to, tokenId, amount);
        isTransferAllowed = errorCode == SELECTOR_NO_ERROR;
    }

    /*************************************************************************/
    /*                    COLLECTION AND 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 delegateCall(_managementModule) returns (uint48 id) {}

    /**
     * @notice Creates a new list id, and copies all blacklisted, whitelisted, and authorizer 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, uint48 sourceListId) external delegateCall(_managementModule) returns (uint48 id) {}

    /**
     * @notice Creates a new list id, and copies all accounts and codehashes from the
     *         specified source list for each specified list type.
     *
     * @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 accounts and codehashes from the specified source list / list types are copied
     *            to the new list.
     *         6. An `AddedAccountToList` event is emitted for each account copied.
     *         7. An `AddedCodeHashToList` event is emitted for each codehash copied.
     *
     * @param  name         The name of the new list.
     * @param  sourceListId The id of the source list to copy from.
     * @param  listTypes    The list types to copy from the source list.
     * @return id           The id of the new list.
     */
    function createListCopy(string calldata name, uint48 sourceListId, uint8[] calldata listTypes) external delegateCall(_managementModule) returns (uint48 id) {}

    /**
     * @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(uint48 id, address newOwner) public delegateCall(_managementModule) {}

    /**
     * @notice Renounce the ownership of a list, rendering the list immutable.
     *
     * @dev Throws when the caller does not own the specified list.
     * @dev Throws when list id is zero (default 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(uint48 id) public delegateCall(_managementModule) {}

    /**
     * @notice Set the ruleset id, global / ruleset options, fixed / custom ruleset for a collection.
     *
     * @dev Throws when the caller is neither collection contract, nor the owner or admin of the specified collection.
     * @dev Throws when setting a custom ruleset to an unregistered ruleset address.
     * @dev Throws when setting a ruleset id that is not bound to a ruleset address.
     * @dev Throws when setting a custom ruleset with a managed ruleset id.
     *
     * @dev <h4>Postconditions:</h4>
     *      1. The ruleset of the specified collection is set to the new value.
     *      2. Global options and ruleset-specific options of the specified collection are set to the new value.
     *      3. A `SetCollectionRuleset` event is emitted.
     *      4. A `SetCollectionSecurityPolicyOptions` event is emitted.
     *
     * @param collection                 The address of the collection.
     * @param rulesetId                  The new ruleset id to apply.
     * @param customRuleset              The address of the custom ruleset to apply. Must be address(0) unless ruleset
     *                                   id is RULESET_ID_FIXED_OR_CUSTOM (255).
     * @param globalOptions              The global options to apply.
     * @param rulesetOptions             The ruleset-specific options to apply.
     */
    function setRulesetOfCollection(
        address collection, 
        uint8 rulesetId,
        address customRuleset,
        uint8 globalOptions,
        uint16 rulesetOptions
    ) external delegateCall(_managementModule) {}

    /**
     * @notice Set expansion words and datums for 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. Included expansion words and datums are set for the specified collection.
     *      2. A `SetCollectionExpansionWords` event is emitted for each expansion word included in the request.
     *      3. A `SetCollectionExpansionDatums` event is emitted for each expansion datum included in the request.
     *
     * @param collection                 The address of the collection.
     * @param expansionWords             The expansion words to set.
     * @param expansionDatums            The expansion datums to set.
     */
    function setExpansionSettingsOfCollection(
        address collection, 
        ExpansionWord[] calldata expansionWords,
        ExpansionDatum[] calldata expansionDatums
    ) external delegateCall(_managementModule) {}

    /**
     * @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 delegateCall(_managementModule) {}

    /**
     * @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, uint48 id) public delegateCall(_managementModule) {}

    /**
     * @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 delegateCall(_managementModule) {}

    /**
     * @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 delegateCall(_managementModule) {}

    /**
     * @notice Adds one or more accounts to a list of specified list type.
     *
     * @dev Throws when the caller does not own the specified list.
     *
     * @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 listType The type of the list.
     * @param accounts The addresses of the accounts to add.
     */
    function addAccountsToList(
        uint48 id,
        uint8 listType,
        address[] calldata accounts
    ) external delegateCall(_managementModule) {}

    /**
     * @notice Removes one or more accounts from a list of the specified list type.
     *
     * @dev Throws when the caller does not own the specified list.
     *
     * @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 listType The type of the list.
     * @param accounts The addresses of the accounts to remove.
     */
    function removeAccountsFromList(
        uint48 id,
        uint8 listType,
        address[] calldata accounts
    ) external delegateCall(_managementModule) {}

    /**
     * @notice Adds one or more codehashes to a list of specified list type.
     *
     * @dev Throws when the caller does not own the specified list.
     *
     * @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 listType   The type of the list.
     * @param codehashes The codehashes to add.
     */
    function addCodeHashesToList(
        uint48 id,
        uint8 listType,
        bytes32[] calldata codehashes
    ) external delegateCall(_managementModule) {}

    /**
     * @notice Removes one or more codehashes from a list of the specified list type.
     *
     * @dev Throws when the caller does not own the specified list.
     *
     * @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 listType   The type of the list.
     * @param codehashes The codehashes to remove.
     */
    function removeCodeHashesFromList(
        uint48 id,
        uint8 listType,
        bytes32[] calldata codehashes
    ) external delegateCall(_managementModule) {}

    /*************************************************************************/
    /*                      RULESET VIEW ACCESSORS                           */
    /*************************************************************************/

    /**
     * @notice Returns true if the specified ruleset is registered, false otherwise.
     */
    function isRulesetRegistered(address ruleset) external view returns (bool) {
        return validatorStorage().registeredRulesets[ruleset];
    }

    /**
     * @notice Returns the address of the ruleset currently bound to the specified ruleset id.
     */
    function boundRuleset(uint8 rulesetId) external view returns (address) {
        if (rulesetId == RULESET_ID_FIXED_OR_CUSTOM) {
            return address(0);
        }

        return validatorStorage().rulesetBindings[rulesetId];
    }

    /*************************************************************************/
    /*           COLLECTION AND LIST SETTINGS VIEW ACCESSORS                 */
    /*************************************************************************/

    /**
     * @notice Returns the id of the list that was created.
     */
    function lastListId() external view returns (uint48) {
        return validatorStorage().lastListId;
    }

    /**
     * @notice Returns the owner of the specified list id.
     */
    function listOwners(uint48 id) external view returns (address) {
        return validatorStorage().listOwners[id];
    }

    /**
     * @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:
     *                   Ruleset id, list id, global options, ruleset-specific options, optional custom ruleset address,
     *                   and token type (if registered).
     */
    function getCollectionSecurityPolicy(
        address collection
    ) external view returns (CollectionSecurityPolicy memory) {
        return validatorStorage().collectionSecurityPolicies[collection];
    }

    /**
     * @notice Returns expansion word settings for a collection.
     *
     * @param  tokenAddress     The smart contract address of the token.
     * @param  keys             The expansion word keys to retrieve.
     */
    function getCollectionExpansionWords(
        address tokenAddress, 
        bytes32[] calldata keys
    ) public view returns (bytes32[] memory values) {
        if(keys.length > 0) {
            mapping (bytes32 => bytes32) storage ptrExpansionWordsForCollection = 
                validatorStorage().collectionExpansionWords[tokenAddress];

            values = new bytes32[](keys.length);
            for (uint256 i = 0; i < keys.length; ++i) {
                values[i] = ptrExpansionWordsForCollection[keys[i]];
            }
        }
    }

    /**
     * @notice Returns expansion datum settings for a collection.
     *
     * @param  tokenAddress     The smart contract address of the token.
     * @param  keys             The expansion datum keys to retrieve.
     */
    function getCollectionExpansionDatums(
        address tokenAddress, 
        bytes32[] calldata keys
    ) public view returns (bytes[] memory values) {
        if(keys.length > 0) {
            mapping (bytes32 => bytes) storage ptrExpansionDatumsForCollection = 
                validatorStorage().collectionExpansionDatums[tokenAddress];

            values = new bytes[](keys.length);
            for (uint256 i = 0; i < keys.length; ++i) {
                values[i] = ptrExpansionDatumsForCollection[keys[i]];
            }
        }
    }

    /**
     * @notice Get accounts by list id and list type.
     * @param  id The id of the list.
     * @param  listType The type of the list.
     * @return An array of accounts in the list of the specified type.
     */
    function getListAccounts(uint48 id, uint8 listType) public view returns (address[] memory) {
        return validatorStorage().lists[listType][id].enumerableAccounts.values();
    }

    /**
     * @notice Get codehashes by list id and list type.
     * @param  id The id of the list.
     * @param  listType The type of the list.
     * @return An array of codehashes in the list of the specified type.
     */
    function getListCodeHashes(uint48 id, uint8 listType) public view returns (bytes32[] memory) {
        return validatorStorage().lists[listType][id].enumerableCodehashes.values();
    }

    /**
     * @notice Check if an account is found in a specified list id / list type.
     * @param id       The id of the list.
     * @param listType The type of the list.
     * @param account  The address of the account to check.
     * @return         True if the account is in the specified list / type, false otherwise.
     */
    function isAccountInList(uint48 id, uint8 listType, address account) public view returns (bool) {
        return validatorStorage().lists[listType][id].nonEnumerableAccounts[account];
    }

    /**
     * @notice Check if a codehash is in a specified list / type.
     * @param id       The id of the list.
     * @param listType The type of the list.
     * @param codehash  The codehash to check.
     * @return         True if the codehash is in the specified list / type, false otherwise.
     */
    function isCodeHashInList(uint48 id, uint8 listType, bytes32 codehash) public view returns (bool) {
        return validatorStorage().lists[listType][id].nonEnumerableCodehashes[codehash];
    }

    /**
     * @notice Get accounts in list by collection and list type.
     * @param collection The address of the collection.
     * @param listType   The type of the list.
     * @return           An array of accounts.
     */
    function getListAccountsByCollection(address collection, uint8 listType) external view returns (address[] memory) {
        return getListAccounts(validatorStorage().collectionSecurityPolicies[collection].listId, listType);
    }

    /**
     * @notice Get codehashes in list by collection and list type.
     * @param collection The address of the collection.
     * @param listType   The type of the list.
     * @return           An array of codehashes.
     */
    function getListCodeHashesByCollection(address collection, uint8 listType) external view returns (bytes32[] memory) {
        return getListCodeHashes(validatorStorage().collectionSecurityPolicies[collection].listId, listType);
    }

    /**
     * @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 validatorStorage().frozenAccounts[collection].enumerableAccounts.values();
    }

    /**
     * @notice Check if an account is in the list by a specified collection and list type.
     * @param collection The address of the collection.
     * @param listType   The type of the list.
     * @param account    The address of the account to check.
     * @return           True if the account is in the list / list type of the specified collection, false otherwise.
     */
    function isAccountInListByCollection(address collection, uint8 listType, address account) external view returns (bool) {
        return isAccountInList(validatorStorage().collectionSecurityPolicies[collection].listId, listType, account);
    }

    /**
     * @notice Check if a codehash is in the list by a specified collection / list type.
     * @param collection The address of the collection.
     * @param listType   The type of the list.
     * @param codehash   The codehash to check.
     * @return           True if the codehash is in the list / list type of the specified collection, false otherwise.
     */
    function isCodeHashInListByCollection(address collection, uint8 listType, bytes32 codehash) external view returns (bool) {
        return isCodeHashInList(validatorStorage().collectionSecurityPolicies[collection].listId, listType, codehash);
    }

    /**
     * @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 validatorStorage().frozenAccounts[collection].nonEnumerableAccounts[account];
    }

    /**
     * @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 Makes a delegatecall to the validator ruleset module that perform the transfer validation logic.
     *
     * @dev Throws when the `msg.sender` is not equal to address(this).  This guarantees that staticcall context
     *      is properly preserved.
     */
    function validateTransferDelegateCall(
        uint256 authorizerCheckType,
        address collection,
        address caller, 
        address from, 
        address to,
        uint256 tokenId,
        uint256 amount) external /*view*/ returns (bytes4 errorSelector, uint16 tokenType) { 
        if (msg.sender != address(this)) {
            revert CreatorTokenTransferValidator__OnlyValidatorCanAccessThisFunction();
        }

        address validatorModuleAddress;
        (
            validatorModuleAddress,
            tokenType
        ) = _getCollectionTokenTypeAndValidatorModule(collection);

        errorSelector = _delegateCallRuleset(
            validatorModuleAddress,
            authorizerCheckType,
            collection,
            caller,
            from,
            to,
            tokenId,
            amount
        );
    }

    /**
     * @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                                */
    /*************************************************************************/

    /**
     * @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(
                tokenType == TOKEN_TYPE_ERC721 ? AUTHORIZER_CHECK_TYPE_TOKEN : AUTHORIZER_CHECK_TYPE_TOKEN_AND_AMOUNT, 
                token, 
                msg.sender, 
                from, 
                to, 
                id, 
                amount
            );

        if (collectionTokenType == DEFAULT_TOKEN_TYPE || collectionTokenType == tokenType) {
            isError = SELECTOR_NO_ERROR != selector;
        } else {
            revert CreatorTokenTransferValidator__TokenTypesDoNotMatch();
        }
    }

    /**
     * @notice Apply the collection ruleset and options 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 Modular rulesets with options determine the logic governing transfers.
     *
     * @dev Throws when the ruleset returns a non-zero error selector.
     *
     * @dev <h4>Postconditions:</h4>
     *      1. Transfer is allowed or denied based on the ruleset.
     *
     * @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(
        uint256 authorizerCheckType,
        address collection, 
        address caller, 
        address from, 
        address to,
        uint256 tokenId,
        uint256 amount
    ) 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);
        }

        return IRulesetDelegateCall(address(this)).validateTransferDelegateCall(
            authorizerCheckType,
            collection,
            caller,
            from,
            to,
            tokenId,
            amount
        );
    }

    /**
     * @dev Internal function used to get the collection token type and validator module address.
     *
     * @param collection The collection address to get the token type and validator module address for.
     * @return validatorModuleAddress  The validator module address afor the collection.
     * @return tokenType               The token type for the collection.
     */
    function _getCollectionTokenTypeAndValidatorModule(
        address collection
    ) internal view returns (address validatorModuleAddress, uint16 tokenType) {
        CollectionSecurityPolicy storage ptrSecurityPolicy = validatorStorage().collectionSecurityPolicies[collection];

        validatorModuleAddress = 
            ptrSecurityPolicy.rulesetId < RULESET_ID_FIXED_OR_CUSTOM ? 
            validatorStorage().rulesetBindings[ptrSecurityPolicy.rulesetId] : 
            ptrSecurityPolicy.customRuleset;

        tokenType = ptrSecurityPolicy.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 {
        CollectionSecurityPolicy storage collectionSecurityPolicy = validatorStorage().collectionSecurityPolicies[collection];

        if (_isFlagSet(collectionSecurityPolicy.globalOptions, FLAG_GLOBAL_DISABLE_AUTHORIZATION_MODE)) {
            revert CreatorTokenTransferValidator__AuthorizationDisabledForCollection();
        }

        if (_isFlagSet(collectionSecurityPolicy.globalOptions, FLAG_GLOBAL_AUTHORIZERS_CANNOT_SET_WILDCARD_OPERATORS)) {
            if (operator == WILDCARD_OPERATOR_ADDRESS) {
                revert CreatorTokenTransferValidator__WildcardOperatorsCannotBeAuthorizedForCollection();
            }
        }

        if (!validatorStorage().lists[LIST_TYPE_AUTHORIZERS][collectionSecurityPolicy.listId].nonEnumerableAccounts[authorizer]) {
            if (!_isFlagSet(collectionSecurityPolicy.globalOptions, FLAG_GLOBAL_CUSTOM_LIST_SUPPLEMENTS_DEFAULT_LIST) || 
                !validatorStorage().lists[LIST_TYPE_AUTHORIZERS][DEFAULT_LIST_ID].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 for setting the authorized operator and amount 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 amount      The amount of the token that is authorized.
     */
    function _setOperatorInTransientStorage(
        address operator,
        address collection, 
        uint256 tokenId,
        uint256 amount
    ) internal whenAuthorizerAndOperatorEnabledForCollection(collection, operator, msg.sender) {
        _setTstorish(_getTransientOperatorSlot(collection), uint256(uint160(operator)));
        uint256 collectionAndTokenIdSlot = _getTransientOperatorSlot(collection, tokenId);
        _setTstorish(collectionAndTokenIdSlot, uint256(uint160(operator)));
        _setTstorish(collectionAndTokenIdSlot + 1, amount);
    }
    
    /**
     * @dev Internal function triggered when the Tstore support is activated.
     */
    function _onTstoreSupportActivated() internal virtual override {
        // Nothing to do here
    }

    /**
     * @dev Internal function used to delegate call the ruleset module for transfer validation and return results.
     */
    function _delegateCallRuleset(
        address _ruleSet,
        uint256 _authorizerCheckType,
        address _collection,
        address _caller, 
        address _from, 
        address _to,
        uint256 _tokenId,
        uint256 _amount
    ) internal returns (bytes4 errorSelector) {
        bytes4 selector = IRuleset.validateTransfer.selector;
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, selector)
            mstore(add(ptr,0x04), _authorizerCheckType)
            mstore(add(ptr,0x24), _collection)
            mstore(add(ptr,0x44), _caller)
            mstore(add(ptr,0x64), _from)
            mstore(add(ptr,0x84), _to)
            mstore(add(ptr,0xA4), _tokenId)
            mstore(add(ptr,0xC4), _amount)
            mstore(0x40, add(ptr,0xE4))

            let result := delegatecall(gas(), _ruleSet, ptr, 0xE4, 0x00, 0x20)
            if iszero(result) {
                // Call has failed, retrieve the error message and revert
                let size := returndatasize()
                returndatacopy(0, 0, size)
                revert(0, size)
            }
            errorSelector := mload(0x00)
        }
    }
}

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

pragma solidity ^0.8.0;

import "../token/ERC1155/IERC1155.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 4 of 36 : 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 5 of 36 : IERC721.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC721.sol)

pragma solidity ^0.8.0;

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.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;
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.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);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.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);
}

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)

pragma solidity ^0.8.0;

import "./math/Math.sol";
import "./math/SignedMath.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 `int256` to its ASCII `string` decimal representation.
     */
    function toString(int256 value) internal pure returns (string memory) {
        return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value))));
    }

    /**
     * @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);
    }

    /**
     * @dev Returns true if the two strings are equal.
     */
    function equal(string memory a, string memory b) internal pure returns (bool) {
        return keccak256(bytes(a)) == keccak256(bytes(b));
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.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 message) {
        // 32 is the length in bytes of hash,
        // enforced by the type signature above
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, "\x19Ethereum Signed Message:\n32")
            mstore(0x1c, hash)
            message := keccak256(0x00, 0x3c)
        }
    }

    /**
     * @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 data) {
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, "\x19\x01")
            mstore(add(ptr, 0x02), domainSeparator)
            mstore(add(ptr, 0x22), structHash)
            data := keccak256(ptr, 0x42)
        }
    }

    /**
     * @dev Returns an Ethereum Signed Data with intended validator, created from a
     * `validator` and `data` according to the version 0 of EIP-191.
     *
     * See {recover}.
     */
    function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19\x00", validator, data));
    }
}

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.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) {
                // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                // The surrounding unchecked block does not change this fact.
                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1, "Math: mulDiv overflow");

            ///////////////////////////////////////////////
            // 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 256, 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 << 3) < value ? 1 : 0);
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard signed math utilities missing in the Solidity language.
 */
library SignedMath {
    /**
     * @dev Returns the largest of two signed numbers.
     */
    function max(int256 a, int256 b) internal pure returns (int256) {
        return a > b ? a : b;
    }

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

    /**
     * @dev Returns the average of two signed numbers without overflow.
     * The result is rounded towards zero.
     */
    function average(int256 a, int256 b) internal pure returns (int256) {
        // Formula from the book "Hacker's Delight"
        int256 x = (a & b) + ((a ^ b) >> 1);
        return x + (int256(uint256(x) >> 255) & (a ^ b));
    }

    /**
     * @dev Returns the absolute unsigned value of a signed value.
     */
    function abs(int256 n) internal pure returns (uint256) {
        unchecked {
            // must be unchecked in order to support `n = type(int256).min`
            return uint256(n >= 0 ? n : -n);
        }
    }
}

//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 16 of 36 : 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 17 of 36 : 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 18 of 36 : 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 19 of 36 : 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();
    }
}

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

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

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

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

pragma solidity ^0.8.4;

import "./IERC165.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC-165 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);
 * }
 * ```
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

pragma solidity ^0.8.4;

/**
 * @dev Interface of the ERC-165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[ERC].
 *
 * 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[ERC 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 26 of 36 : StorageTstorish.sol
pragma solidity ^0.8.24;

library StorageTstorish {   
    // keccak256(abi.encode(uint256(keccak256("storage.Tstorish")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant DATA_STORAGE_SLOT = 
        0xdacd49f6a6c42b45a5c3d195b83b324104542d9147bb8064a39c6a8d23ba9b00;

    struct Data {
        // Indicates if TSTORE support has been activated during or post-deployment.
        bool tstoreSupport;
    }

    function data() internal pure returns (Data storage ptr) {
        bytes32 slot = DATA_STORAGE_SLOT;
        assembly {
            ptr.slot := slot
        }
    }
}

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

import "./StorageTstorish.sol";

/**
 * @title  Tstorish
 * @notice Based on https://github.com/ProjectOpenSea/tstorish/commit/a81ed74453ed7b9fe7e96a9906bc4def19b73e33
 */
abstract contract Tstorish {

    /*
     * ------------------------------------------------------------------------+
     * 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 tstore test contract address.
    address private immutable _tloadTestContract;

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

    // Declare an immutable function type variable for the _setTstorish function
    // based on chain support for tstore at time of deployment.
    function(uint256,uint256) internal immutable _setTstorish;

    // Declare an immutable function type variable for the _getTstorish function
    // based on chain support for tstore at time of deployment.
    function(uint256) view returns (uint256) internal immutable _getTstorish;

    // Declare an immutable function type variable for the _clearTstorish function
    // based on chain support for tstore at time of deployment.
    function(uint256) internal immutable _clearTstorish;

    // 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.
        _tstoreInitialSupport = StorageTstorish.data().tstoreSupport = _testTload(tloadTestContract);

        if (_tstoreInitialSupport) {
            // If TSTORE is supported, set functions to their versions that use
            // tstore/tload directly without support checks.
            _setTstorish = _setTstore;
            _getTstorish = _getTstore;
            _clearTstorish = _clearTstore;
        } else {
            // If TSTORE is not supported, set functions to their versions that 
            // fallback to sstore/sload until tstoreSupport is true.
            _setTstorish = _setTstorishWithSstoreFallback;
            _getTstorish = _getTstorishWithSloadFallback;
            _clearTstorish = _clearTstorishWithSstoreFallback;
        }

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

    /**
     * @dev Called internally when tstore is activated by an external call to 
     *      `__activateTstore`. Developers must override this function and handle
     *      relevant transfers of data from regular storage to transient storage *OR*
     *      revert the transaction if it is in a state that should not support the activation
     *      of tstore.
     */
    function _onTstoreSupportActivated() internal virtual;

    /**
     * @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 {
        // Determine if TSTORE can potentially be activated.
        if (_tstoreInitialSupport || StorageTstorish.data().tstoreSupport) {
            revert TStoreAlreadyActivated();
        }

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

        // Mark TSTORE as activated.
        StorageTstorish.data().tstoreSupport = true;

        _onTstoreSupportActivated();
    }

    /**
     * @dev Private function to set a TSTORISH value. Assigned to _setTstorish 
     *      internal function variable at construction if chain has tstore support.
     *
     * @param storageSlot The slot to write the TSTORISH value to.
     * @param value       The value to write to the given storage slot.
     */
    function _setTstore(uint256 storageSlot, uint256 value) internal {
        assembly {
            tstore(storageSlot, value)
        }
    }

    /**
     * @dev Private function to set a TSTORISH value with sstore fallback. 
     *      Assigned to _setTstorish internal function variable at construction
     *      if chain does not have tstore support.
     *
     * @param storageSlot The slot to write the TSTORISH value to.
     * @param value       The value to write to the given storage slot.
     */
    function _setTstorishWithSstoreFallback(uint256 storageSlot, uint256 value) internal {
        if (StorageTstorish.data().tstoreSupport) {
            assembly {
                tstore(storageSlot, value)
            }
        } else {
            assembly {
                sstore(storageSlot, value)
            }
        }
    }

    /**
     * @dev Private function to read a TSTORISH value. Assigned to _getTstorish
     *      internal function variable at construction if chain has tstore support.
     *
     * @param storageSlot The slot to read the TSTORISH value from.
     *
     * @return value The TSTORISH value at the given storage slot.
     */
    function _getTstore(
        uint256 storageSlot
    ) internal view returns (uint256 value) {
        assembly {
            value := tload(storageSlot)
        }
    }

    /**
     * @dev Private function to read a TSTORISH value with sload fallback. 
     *      Assigned to _getTstorish internal function variable at construction
     *      if chain does not have tstore support.
     *
     * @param storageSlot The slot to read the TSTORISH value from.
     *
     * @return value The TSTORISH value at the given storage slot.
     */
    function _getTstorishWithSloadFallback(
        uint256 storageSlot
    ) internal view returns (uint256 value) {
        if (StorageTstorish.data().tstoreSupport) {
            assembly {
                value := tload(storageSlot)
            }
        } else {
            assembly {
                value := sload(storageSlot)
            }
        }
    }

    /**
     * @dev Private function to clear a TSTORISH value. Assigned to _clearTstorish internal 
     *      function variable at construction if chain has tstore support.
     *
     * @param storageSlot The slot to clear the TSTORISH value for.
     */
    function _clearTstore(uint256 storageSlot) internal {
        assembly {
            tstore(storageSlot, 0)
        }
    }

    /**
     * @dev Private function to clear a TSTORISH value with sstore fallback. 
     *      Assigned to _clearTstorish internal function variable at construction
     *      if chain does not have tstore support.
     *
     * @param storageSlot The slot to clear the TSTORISH value for.
     */
    function _clearTstorishWithSstoreFallback(uint256 storageSlot) internal {
        if (StorageTstorish.data().tstoreSupport) {
            assembly {
                tstore(storageSlot, 0)
            }
        } else {
            assembly {
                sstore(storageSlot, 0)
            }
        }
    }

    /**
     * @dev Private function to copy a value from storage to transient storage at the same slot.
     *      Useful when tstore is activated on a chain that didn't initially support it.
     */
    function _copyFromStorageToTransient(uint256 storageSlot) internal {
        if (StorageTstorish.data().tstoreSupport) {
            assembly {
                tstore(storageSlot, sload(storageSlot))
            }
        } else {
            revert TStoreNotSupported();
        }
    }

    /**
     * @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 }("");
    }
}

pragma solidity ^0.8.4;

/**
 * @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.
 *
 * ```solidity
 * 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 is the index of the value in the `values` array plus 1.
        // Position 0 is used to mean a value is not in the set.
        mapping(bytes32 value => uint256) _positions;
    }

    /**
     * @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._positions[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 cache the value's position to prevent multiple reads from the same storage slot
        uint256 position = set._positions[value];

        if (position != 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 valueIndex = position - 1;
            uint256 lastIndex = set._values.length - 1;

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

                // Move the lastValue to the index where the value to delete is
                set._values[valueIndex] = lastValue;
                // Update the tracked position of the lastValue (that was just moved)
                set._positions[lastValue] = position;
            }

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

            // Delete the tracked position for the deleted slot
            delete set._positions[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._positions[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;
    }
}

pragma solidity ^0.8.4;

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

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

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 31 of 36 : Constants.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

/**************************************************************/
/*                        LIST TYPES                          */
/**************************************************************/

uint8 constant LIST_TYPE_BLACKLIST = 0;
uint8 constant LIST_TYPE_WHITELIST = 1;
uint8 constant LIST_TYPE_AUTHORIZERS = 2;
uint8 constant EXPANSION_LIST_TYPE_WHITELIST_EXTENSION_CONTRACTS = 3;
uint8 constant EXPANSION_LIST_TYPE_7702_DELEGATE_WHITELIST = 4;
uint8 constant EXPANSION_LIST_TYPE_7702_DELEGATE_WHITELIST_EXTENSION_CONTRACTS = 5;

/**************************************************************/
/*                        RULESET IDS                         */
/**************************************************************/

uint8 constant RULESET_ID_DEFAULT = 0;
uint8 constant RULESET_ID_VANILLA = 1;
uint8 constant RULESET_ID_SOULBOUND = 2;
uint8 constant RULESET_ID_BLACKLIST = 3;
uint8 constant RULESET_ID_WHITELIST = 4;
uint8 constant RULESET_ID_FIXED_OR_CUSTOM = 255;

/**************************************************************/
/*                    AUTHORIZER CHECK TYPES                  */
/**************************************************************/

uint256 constant AUTHORIZER_CHECK_TYPE_TOKEN = 1;
uint256 constant AUTHORIZER_CHECK_TYPE_COLLECTION = 2;
uint256 constant AUTHORIZER_CHECK_TYPE_TOKEN_AND_AMOUNT = 3;

/**************************************************************/
/*                       MISCELLANEOUS                        */
/**************************************************************/

bytes4 constant LEGACY_TRANSFER_VALIDATOR_INTERFACE_ID = bytes4(0x00000000);
bytes4 constant SELECTOR_NO_ERROR = bytes4(0x00000000);
bytes32 constant BYTES32_ZERO = 0x0000000000000000000000000000000000000000000000000000000000000000;
bytes32 constant DEFAULT_ACCESS_CONTROL_ADMIN_ROLE = 0x0000000000000000000000000000000000000000000000000000000000000000;
address constant WILDCARD_OPERATOR_ADDRESS = address(0x01);
uint48 constant DEFAULT_LIST_ID = 0;
uint16 constant DEFAULT_TOKEN_TYPE = 0;

/**************************************************************/
/*                       FLAGS - GLOBAL                       */
/**************************************************************/

// Flags are used to efficiently store and retrieve boolean values in a single uint8.
// Each flag is a power of 2, so they can be combined using bitwise OR (|) and checked using bitwise AND (&).
// For example, to set the first and third flags, you would use: flags = FLAG1 | FLAG3;
// To check if the first flag is set, you would use: if (flags & FLAG1 != 0) { ... }
uint8 constant FLAG_GLOBAL_DISABLE_AUTHORIZATION_MODE = 1 << 0;
uint8 constant FLAG_GLOBAL_AUTHORIZERS_CANNOT_SET_WILDCARD_OPERATORS = 1 << 1;
uint8 constant FLAG_GLOBAL_ENABLE_ACCOUNT_FREEZING_MODE = 1 << 2;
uint8 constant FLAG_GLOBAL_CUSTOM_LIST_SUPPLEMENTS_DEFAULT_LIST = 1 << 3;

/**************************************************************/
/*                  FLAGS - RULESET WHITELIST                 */
/**************************************************************/

uint16 constant FLAG_RULESET_WHITELIST_BLOCK_ALL_OTC = 1 << 0;
uint16 constant FLAG_RULESET_WHITELIST_ALLOW_OTC_FOR_7702_DELEGATES = 1 << 1;
uint16 constant FLAG_RULESET_WHITELIST_ALLOW_OTC_FOR_SMART_WALLETS = 1 << 2;
uint16 constant FLAG_RULESET_WHITELIST_BLOCK_SMART_WALLET_RECEIVERS = 1 << 3;
uint16 constant FLAG_RULESET_WHITELIST_BLOCK_UNVERIFIED_EOA_RECEIVERS = 1 << 4;

File 32 of 36 : DataTypes.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import "@limitbreak/tm-core-lib/src/utils/structs/EnumerableSet.sol";

/**
 * @dev This struct contains the security policy settings for a collection.
 */
struct CollectionSecurityPolicy {
    uint8 rulesetId;
    uint48 listId;
    address customRuleset;
    uint8 globalOptions;
    uint16 rulesetOptions;
    uint16 tokenType;
}


/**
 * @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;
}

/**
 * @dev This struct contains a key and value pair for future expansion data words that are 32 bytes long.
 */
struct ExpansionWord {
    bytes32 key;
    bytes32 value;
}

/**
 * @dev This struct contains a key and value pair for future expansion data bytes that are variable length.
 */
struct ExpansionDatum {
    bytes32 key;
    bytes value;
}

/**
 * @dev This struct contains the storage layout for the validator contract, excluding Permit-C and Tstorish data.
 */
struct ValidatorStorage {
    /// @notice Keeps track of the most recently created list id.
    uint48 lastListId;

    /// @dev Used as a collision guard.
    mapping (address => address) transientOperator;

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

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

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

    /// @dev Mapping of list ids to list data
    mapping (uint8 => mapping (uint48 => List)) lists;

    /// @dev Mapping of collection addressses to any future expansion data words settings that may be used in the future
    mapping (address collection => mapping (bytes32 extension => bytes32 word)) collectionExpansionWords;

    /// @dev Mapping of collection addressses to any future expansion data bytes settings that may be used in the future
    mapping (address collection => mapping (bytes32 extension => bytes data)) collectionExpansionDatums;

    /// @dev Mapping of addresses to a boolean indicating if they are registered trusted validator modules
    mapping (address => bool) registeredRulesets;

    /// @dev Mapping of security levels to their current implementation modules
    mapping (uint8 => address) rulesetBindings;
}

File 33 of 36 : Errors.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

/// @dev Thrown when admin attempts to bind a ruleset to the fixed/custom ruleset ID.
error CreatorTokenTransferValidator__AdminCannotAssignRulesetToRulesetIdCustom();

/// @dev Thrown when validating transfers with amount if authorization by amount mode is active and the amount
///      exceeds the pre-authorized amount.
error CreatorTokenTransferValidator__AmountExceedsAuthorization();

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

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

/// @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 validating a transfer for a collection using whitelists and the operator is not on the whitelist.
error CreatorTokenTransferValidator__CallerMustBeWhitelisted();

/// @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 validating a transfer for a collection using whitelists and the operator or from account is not on the whitelist.
error CreatorTokenTransferValidator__CallerOrFromMustBeWhitelisted();

/// @dev Thrown when attempting to renounce ownership of the default list id.
error CreatorTokenTransferValidator__CannotRenounceOwnershipOfDefaultList();

/// @dev Thrown when setting the ruleset for a collection when a reserved ruleset id is used, but a custom ruleset
///      is specified.
error CreatorTokenTransferValidator__CannotSetCustomRulesetOnManagedRulesetId();

/// @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 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 the transfer validation logic externally, as staticcall guarantees are needed.
error CreatorTokenTransferValidator__OnlyValidatorCanAccessThisFunction();

/// @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 an OTC transfer with EIP-7702 Delegation when disabled by security settings.
error CreatorTokenTransferValidator__OTCNotAllowedFor7702Delegates();

/// @dev Thrown when validating an OTC transfer from Smart Wallets when disabled by security settings.
error CreatorTokenTransferValidator__OTCNotAllowedForSmartWallets();

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

/// @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 attempting to register a ruleset that is not a contract.
error CreatorTokenTransferValidator__RulesetIsNotContract();

/// @dev Thrown when attempting to register a ruleset that is not pure.
error CreatorTokenTransferValidator__RulesetIsNotPure();

/// @dev Thrown when admin attempts to bind a ruleset that is not registered, or a collection admin tries to 
///      set their fixed/custom ruleset to an unregistered ruleset.
error CreatorTokenTransferValidator__RulesetIsNotRegistered();

/// @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 attempting to validate a permitted transfer where the permit type does not match the 
///      collection-defined token type.
error CreatorTokenTransferValidator__TokenTypesDoNotMatch();

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

// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import "./DataTypes.sol";
import "./Errors.sol";

/**
 * @title ValidatorBase
 * @author Limit Break, Inc.
 * @notice Base contract for all Creator Token Validator, Module, and Ruleset contracts. 
 *         Includes some helper functions and modifiers and easy access to validator diamond storage
 *         to allow modules and rulesets to access the storage of the validator contract.
 */
contract ValidatorBase {

    /*************************************************************************/
    /*                               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(uint48 id) {
        _requireCallerOwnsList(id);
        _;
    }

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

    /**
     * @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(uint48 id) private view {
        if (msg.sender != validatorStorage().listOwners[id]) {
            revert CreatorTokenTransferValidator__CallerDoesNotOwnList();
        }
    }

    /**
     * @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 view returns (uint256 operatorSlot) {
        mapping (address => address) storage _transientOperator = validatorStorage().transientOperator;
        assembly {
            mstore(0x00, collection)
            mstore(0x20, _transientOperator.slot)
            operatorSlot := keccak256(0x00, 0x40)
        }
    }

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

    /**
     * @notice Returns true if the `flagValue` has the `flag` set, false otherwise.
     *
     * @dev    This function uses the bitwise AND operator to check if the `flag` is set in `flagValue`.
     *
     * @param flagValue  The value to check for the presence of the `flag`.
     * @param flag       The flag to check for in the `flagValue`.
     */
    function _isFlagSet(uint256 flagValue, uint256 flag) internal pure returns (bool flagSet) {
        flagSet = (flagValue & flag) != 0;
    }

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

    /// @dev The base storage slot for Validator V5 contract storage items.
    bytes32 constant DIAMOND_STORAGE_VALIDATOR = 
        0x0000000000000000000000000000000000000000000000000000000000721C05;

    /**
     * @dev Returns a storage object that follows the Diamond standard storage pattern for
     * @dev contract storage across multiple module contracts.
     */
    function validatorStorage() internal pure returns (ValidatorStorage storage diamondStorage) {
        bytes32 slot = DIAMOND_STORAGE_VALIDATOR;
        assembly {
            diamondStorage.slot := slot
        }
    }
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

/** 
 * @title IRuleset
 * @author Limit Break, Inc.
 * @notice Interface for Creator Token Standards ruleset contracts.
 *
 * @dev Ruleset contracts are logic-gate contracts used to validate transfers of Creator Tokens
 *      (ERC20-C / ERC721-C / ERC1155-C). Rulesets are view-only, and may not contain any opcodes
 *      that could modify the state of the blockchain.
 */
interface IRuleset {

    /**
     * @notice Validates a transfer of a Creator Token.
     *
     * @param authorizerCheckType The type of authorizer check to perform.
     * @param collection          The address of the Creator Token contract.
     * @param caller              The address of the caller (msg.sender) of the transfer function.
     * @param from                The address of the sender of the token.
     * @param to                  The address of the recipient of the token.
     * @param tokenId             The ID of the token.
     * @param amount              The amount of the token to transfer.
     * @return 0x00000000 when the transfer is allowed, or a custom error selector to block a transfer.
     */
    function validateTransfer(
        uint256 authorizerCheckType,
        address collection,
        address caller, 
        address from, 
        address to,
        uint256 tokenId,
        uint256 amount) external view returns (bytes4);
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

/** 
 * @title IRulesetDelegateCall
 * @author Limit Break, Inc.
 * @notice Interface for delegate calling Creator Token Standards ruleset contracts.
 *
 * @dev Transfer validator implements this interface. It will STATICCALL itself to put itself into a 
 *      state where a ruleset check cannot possibly make any stateful changes, ensuring that they are always read-only.
 */
interface IRulesetDelegateCall {

    /**
     * @notice Validates a transfer of a Creator Token in the context of a STATICCALL.
     *
     * @param authorizerCheckType The type of authorizer check to perform.
     * @param collection          The address of the Creator Token contract.
     * @param caller              The address of the caller (msg.sender) of the transfer function.
     * @param from                The address of the sender of the token.
     * @param to                  The address of the recipient of the token.
     * @param tokenId             The ID of the token.
     * @param amount              The amount of the token to transfer.
     * @return 0x00000000 when the transfer is allowed, or a custom error selector to block a transfer.
     * @return The token type (20, 721, 1155) for use in permit transfer checks when applicable.
     */
    function validateTransferDelegateCall(
        uint256 authorizerCheckType,
        address collection,
        address caller, 
        address from, 
        address to,
        uint256 tokenId,
        uint256 amount) external view returns (bytes4,uint16);
}

Settings
{
  "remappings": [
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "forge-std/=lib/forge-std/src/",
    "@limitbreak/tm-core-lib/=lib/tm-core-lib/",
    "@limitbreak/permit-c/=lib/PermitC/src/",
    "@openzeppelin/=lib/PermitC/lib/openzeppelin-contracts/",
    "@rari-capital/solmate/=lib/PermitC/lib/solmate/",
    "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/PermitC/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/",
    "tm-core-lib/=lib/tm-core-lib/src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 1000000
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "abi"
      ]
    }
  },
  "evmVersion": "cancun",
  "viaIR": false
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"defaultOwner","type":"address"},{"internalType":"address","name":"eoaRegistry_","type":"address"},{"internalType":"address","name":"managementModule_","type":"address"},{"internalType":"address","name":"safeDelegateModule_","type":"address"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"version","type":"string"}],"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__AmountExceedsAuthorization","type":"error"},{"inputs":[],"name":"CreatorTokenTransferValidator__AuthorizationDisabledForCollection","type":"error"},{"inputs":[],"name":"CreatorTokenTransferValidator__CallerMustBeAnAuthorizer","type":"error"},{"inputs":[],"name":"CreatorTokenTransferValidator__InvalidConstructorArgs","type":"error"},{"inputs":[],"name":"CreatorTokenTransferValidator__OnlyValidatorCanAccessThisFunction","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":"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"},{"inputs":[],"name":"__activateTstore","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint48","name":"id","type":"uint48"},{"internalType":"uint8","name":"listType","type":"uint8"},{"internalType":"address[]","name":"accounts","type":"address[]"}],"name":"addAccountsToList","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint48","name":"id","type":"uint48"},{"internalType":"uint8","name":"listType","type":"uint8"},{"internalType":"bytes32[]","name":"codehashes","type":"bytes32[]"}],"name":"addCodeHashesToList","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":"uint48","name":"id","type":"uint48"}],"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":"amount","type":"uint256"}],"name":"beforeAuthorizedTransferWithAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"beforeAuthorizedTransferWithAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"rulesetId","type":"uint8"},{"internalType":"address","name":"ruleset","type":"address"}],"name":"bindRuleset","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"rulesetId","type":"uint8"}],"name":"boundRuleset","outputs":[{"internalType":"address","name":"","type":"address"}],"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":"closePermittedOrder","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"name","type":"string"}],"name":"createList","outputs":[{"internalType":"uint48","name":"id","type":"uint48"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"uint48","name":"sourceListId","type":"uint48"},{"internalType":"uint8[]","name":"listTypes","type":"uint8[]"}],"name":"createListCopy","outputs":[{"internalType":"uint48","name":"id","type":"uint48"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"uint48","name":"sourceListId","type":"uint48"}],"name":"createListCopy","outputs":[{"internalType":"uint48","name":"id","type":"uint48"}],"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":"address","name":"tokenAddress","type":"address"},{"internalType":"bytes32[]","name":"keys","type":"bytes32[]"}],"name":"getCollectionExpansionDatums","outputs":[{"internalType":"bytes[]","name":"values","type":"bytes[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"bytes32[]","name":"keys","type":"bytes32[]"}],"name":"getCollectionExpansionWords","outputs":[{"internalType":"bytes32[]","name":"values","type":"bytes32[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"collection","type":"address"}],"name":"getCollectionSecurityPolicy","outputs":[{"components":[{"internalType":"uint8","name":"rulesetId","type":"uint8"},{"internalType":"uint48","name":"listId","type":"uint48"},{"internalType":"address","name":"customRuleset","type":"address"},{"internalType":"uint8","name":"globalOptions","type":"uint8"},{"internalType":"uint16","name":"rulesetOptions","type":"uint16"},{"internalType":"uint16","name":"tokenType","type":"uint16"}],"internalType":"struct CollectionSecurityPolicy","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":"uint48","name":"id","type":"uint48"},{"internalType":"uint8","name":"listType","type":"uint8"}],"name":"getListAccounts","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"collection","type":"address"},{"internalType":"uint8","name":"listType","type":"uint8"}],"name":"getListAccountsByCollection","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint48","name":"id","type":"uint48"},{"internalType":"uint8","name":"listType","type":"uint8"}],"name":"getListCodeHashes","outputs":[{"internalType":"bytes32[]","name":"","type":"bytes32[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"collection","type":"address"},{"internalType":"uint8","name":"listType","type":"uint8"}],"name":"getListCodeHashesByCollection","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":"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":"uint48","name":"id","type":"uint48"},{"internalType":"uint8","name":"listType","type":"uint8"},{"internalType":"address","name":"account","type":"address"}],"name":"isAccountInList","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"collection","type":"address"},{"internalType":"uint8","name":"listType","type":"uint8"},{"internalType":"address","name":"account","type":"address"}],"name":"isAccountInListByCollection","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint48","name":"id","type":"uint48"},{"internalType":"uint8","name":"listType","type":"uint8"},{"internalType":"bytes32","name":"codehash","type":"bytes32"}],"name":"isCodeHashInList","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"collection","type":"address"},{"internalType":"uint8","name":"listType","type":"uint8"},{"internalType":"bytes32","name":"codehash","type":"bytes32"}],"name":"isCodeHashInListByCollection","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":"ruleset","type":"address"}],"name":"isRulesetRegistered","outputs":[{"internalType":"bool","name":"","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":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint48","name":"id","type":"uint48"}],"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":"uint48","name":"id","type":"uint48"},{"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":"address","name":"ruleset","type":"address"}],"name":"registerRuleset","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint48","name":"id","type":"uint48"},{"internalType":"uint8","name":"listType","type":"uint8"},{"internalType":"address[]","name":"accounts","type":"address[]"}],"name":"removeAccountsFromList","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint48","name":"id","type":"uint48"},{"internalType":"uint8","name":"listType","type":"uint8"},{"internalType":"bytes32[]","name":"codehashes","type":"bytes32[]"}],"name":"removeCodeHashesFromList","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint48","name":"id","type":"uint48"}],"name":"renounceOwnershipOfList","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"collection","type":"address"},{"components":[{"internalType":"bytes32","name":"key","type":"bytes32"},{"internalType":"bytes32","name":"value","type":"bytes32"}],"internalType":"struct ExpansionWord[]","name":"expansionWords","type":"tuple[]"},{"components":[{"internalType":"bytes32","name":"key","type":"bytes32"},{"internalType":"bytes","name":"value","type":"bytes"}],"internalType":"struct ExpansionDatum[]","name":"expansionDatums","type":"tuple[]"}],"name":"setExpansionSettingsOfCollection","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"collection","type":"address"},{"internalType":"uint8","name":"rulesetId","type":"uint8"},{"internalType":"address","name":"customRuleset","type":"address"},{"internalType":"uint8","name":"globalOptions","type":"uint8"},{"internalType":"uint16","name":"rulesetOptions","type":"uint16"}],"name":"setRulesetOfCollection","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":"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":"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":"amount","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"},{"inputs":[{"internalType":"uint256","name":"authorizerCheckType","type":"uint256"},{"internalType":"address","name":"collection","type":"address"},{"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":"amount","type":"uint256"}],"name":"validateTransferDelegateCall","outputs":[{"internalType":"bytes4","name":"errorSelector","type":"bytes4"},{"internalType":"uint16","name":"tokenType","type":"uint16"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"collection","type":"address"},{"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":"amount","type":"uint256"}],"name":"validateTransferSim","outputs":[{"internalType":"bool","name":"isTransferAllowed","type":"bool"},{"internalType":"bytes4","name":"errorCode","type":"bytes4"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"collection","type":"address"},{"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":"validateTransferSim","outputs":[{"internalType":"bool","name":"isTransferAllowed","type":"bool"},{"internalType":"bytes4","name":"errorCode","type":"bytes4"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"collection","type":"address"},{"internalType":"address","name":"caller","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"}],"name":"validateTransferSim","outputs":[{"internalType":"bool","name":"isTransferAllowed","type":"bool"},{"internalType":"bytes4","name":"errorCode","type":"bytes4"}],"stateMutability":"view","type":"function"}]

61028060405234801562000011575f80fd5b506040516200670a3803806200670a833981016040819052620000349162000728565b818187466001146200004e57662386f26fc1000062000058565b670494654067e100005b6001600160401b03168383825f6200006f62000369565b90506001600160a01b0381166200009957604051632aea588760e01b815260040160405180910390fd5b620000a48162000382565b5f80516020620066ea833981519152805460ff19169115801592831790915560a0919091526200011157620003e4602090811b6200293a176001600160401b0390811660c052620003eb821b6200294117811660e052620003ef90911b620029451716610100526200014f565b620003f5602090811b6200294b176001600160401b0390811660c05262000419821b6200298017811660e0526200044190911b620029b91716610100525b6001600160a01b0316608052620001663362000467565b8015620001b557620004b6602090811b620029ee176001600160401b0390811661014052620004e8821b62002a54178116610160526200053390911b62002aec1716610180525f1901620001f5565b6200054b602090811b62002b1f176001600160401b03908116610140526200050e821b62002aaf178116610160526200056f90911b62002b5b1716610180525b6101205281516020808401919091206101e05281519082012061020052466101c052620002856101e05161020051604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201529081019290925260608201524660808201523060a08201525f9060c00160405160208183030381529060405280519060200120905090565b6101a052506200029790508262000467565b505050506001600160a01b0386161580620002b957506001600160a01b038516155b80620002cd57506001600160a01b0385163b155b80620002e057506001600160a01b038416155b80620002f457506001600160a01b0384163b155b806200030757506001600160a01b038316155b806200031b57506001600160a01b0383163b155b156200033a5760405163c56e5d7360e01b815260040160405180910390fd5b620003458662000575565b50506001600160a01b039283166102205290821661024052166102605250620007f6565b5f696002601e613d5c3d52f35f52600a60165ff0905090565b5f816001600160a01b0316600a5a6200039c9190620007d6565b6040515f8181818686fa925050503d805f8114620003d6576040519150601f19603f3d011682016040523d82523d5f602084013e620003db565b606091505b50909392505050565b80825d5050565b5c90565b5f815d50565b5f80516020620066ea8339815191525460ff1615620004155780825d5050565b9055565b5f5f80516020620066ea8339815191525460ff16156200043857505c90565b5080545b919050565b5f80516020620066ea8339815191525460ff161562000460575f815d50565b5f81555b50565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b61012051344703111562000464576001548116156200046457604051636623b92d60e11b815260040160405180910390fd5b61012051344703116200050e5760405163fd2c901360e01b815260040160405180910390fd5b80600154165f03620004645760405163fd2c901360e01b815260040160405180910390fd5b610120515f9034470311156200054857506001545b90565b6001548116156200046457604051636623b92d60e11b815260040160405180910390fd5b60015490565b5f80805262721c0760209081527f3afc1a9185ee9493e6c7b84067661c3f990c1c459276628dc19d8569e33dfa3080546001600160a01b0319166001600160a01b03851617905560408051828152600c928101929092526b1111519055531508131254d560a21b9082015281907f5cc365f89543268cb9f25c255f7f610e9147e733c589bc2732279575f125be149060600160405180910390a26040516001600160a01b0383169065ffffffffffff8316907f9b0894203394c3cbb23140db7a23b224d3e18e0366e9f65bd9c8402650e27367905f90a35050565b80516001600160a01b03811681146200043c575f80fd5b634e487b7160e01b5f52604160045260245ffd5b5f82601f8301126200068b575f80fd5b81516001600160401b0380821115620006a857620006a862000667565b604051601f8301601f19908116603f01168101908282118183101715620006d357620006d362000667565b8160405283815260209250866020858801011115620006f0575f80fd5b5f91505b83821015620007135785820183015181830184015290820190620006f4565b5f602085830101528094505050505092915050565b5f805f805f8060c087890312156200073e575f80fd5b620007498762000650565b9550620007596020880162000650565b9450620007696040880162000650565b9350620007796060880162000650565b60808801519093506001600160401b038082111562000796575f80fd5b620007a48a838b016200067b565b935060a0890151915080821115620007ba575f80fd5b50620007c989828a016200067b565b9150509295509295509295565b5f82620007f157634e487b7160e01b5f52601260045260245ffd5b500490565b60805160a05160c05160e05161010051610120516101405161016051610180516101a0516101c0516101e05161020051610220516102405161026051615dcc6200091e5f395f6115dc01525f8181611b860152611d2501525f61219601525f613f8c01525f613f6401525f613eeb01525f613f1301525f50505f50505f8181611475015281816114ef0152818161156301528181611a2101528181611b1a01528181611d6501528181611fc6015281816123af015281816125b60152818161263601526127c001525f818161124a015281816129f001528181612a560152612aef01525f50505f81816116c101526116fd01525f81816117690152818161337a015281816133d301528181613406015261370101525f611e3401525f611ebb0152615dcc5ff3fe60806040526004361061057f575f3560e01c806386e11774116102cf578063c5ba65091161017b578063e07cb240116100dc578063f6b8561a11610092578063fb2de5d71161006d578063fb2de5d714611272578063ff924f531461128c578063ffbcb4aa146112d7575f80fd5b8063f6b8561a1461121e578063f6e1ff8914610a5d578063f7a2000a14611238575f80fd5b8063e9a7be29116100c2578063e9a7be29146111c1578063eeca41a6146111e0578063f2fde38b146111ff575f80fd5b8063e07cb24014610aaa578063e34fda85146105d6575f80fd5b8063d5dc239a11610131578063db5f30a811610117578063db5f30a814611142578063dd71105d14611161578063df21dc1d14611180575f80fd5b8063d5dc239a14611104578063db188e6314611123575f80fd5b8063caee23ea11610161578063caee23ea14611080578063ccee9c511461109f578063cfea7ecf146110e5575f80fd5b8063c5ba65091461104c578063c815270e14611066575f80fd5b8063b3992ab111610230578063b97f6f8b116101e6578063c43f946b116101c1578063c43f946b14610fb6578063c44a076a1461100e578063c482160f1461102d575f80fd5b8063b97f6f8b14610f5e578063bc8aa28414610f7d578063c435f43514610f97575f80fd5b8063b89c4b0d11610216578063b89c4b0d14610d81578063b8dcc68f14610da0578063b955455214610dba575f80fd5b8063b3992ab114610d43578063b6e39ba114610d62575f80fd5b8063a029957911610285578063a4e28f7d1161026b578063a4e28f7d14610cab578063a87b03b614610cca578063ad1ff68514610d24575f80fd5b8063a029957914610a5d578063a20df79f14610c91575f80fd5b80638cdfc296116102b55780638cdfc29614610c0e5780638da5cb5b14610c2d5780639c84033414610c77575f80fd5b806386e1177414610bd057806389a9c85514610bef575f80fd5b80634be52a891161042e57806366e8304a1161038f57806378e890ba116103455780638254fcb7116103205780638254fcb714610b645780638567413514610b92578063859840a514610bb1575f80fd5b806378e890ba14610b045780637c1e14b414610b265780637df81b9014610b45575f80fd5b806371be859d1161037557806371be859d14610aaa578063725d07c514610ac45780637423eb3c14610af0575f80fd5b806366e8304a14610a5d578063715018a614610a96575f80fd5b8063539d2602116103e45780635ed5917f116103ca5780635ed5917f14610a3e57806363fc56bc14610a5d5780636498c04514610a77575f80fd5b8063539d260214610a00578063599d2c6214610a1f575f80fd5b80634f3c18fe116104145780634f3c18fe146109a3578063505efb65146109c257806350793315146109e1575f80fd5b80634be52a89146109145780634ca104b814610933575f80fd5b80631bb610e7116104e357806328cc1131116104995780633a0e3160116104745780633a0e3160146108ad5780633e8a0bc9146108cc5780633f6560ee146108e0575f80fd5b806328cc11311461083b5780632d975d141461085a5780633779e6fd14610879575f80fd5b806323c99262116104c957806323c992621461079157806327d21f50146107c2578063285fb8c81461081c575f80fd5b80631bb610e7146106de5780631f2fdc7914610735575f80fd5b8063136439dd1161053857806315ec12791161051e57806315ec1279146106675780631854b2411461069357806319007fc9146106b2575f80fd5b8063136439dd1461063557806314e4f88814610648575f80fd5b80630e14021a116105685780630e14021a146105d85780630f59197d146105f757806312d3848a14610616575f80fd5b806301ffc9a7146105835780630ad38899146105b7575b5f80fd5b34801561058e575f80fd5b506105a261059d3660046149d3565b611305565b60405190151581526020015b60405180910390f35b3480156105c2575f80fd5b506105d66105d1366004614a11565b611460565b005b3480156105e3575f80fd5b506105a26105f2366004614a6f565b61146d565b348015610602575f80fd5b506105a2610611366004614b12565b6114dd565b348015610621575f80fd5b506105a2610630366004614bca565b61155b565b6105d6610643366004614c1b565b6115c9565b348015610653575f80fd5b506105d6610662366004614c42565b6115da565b348015610672575f80fd5b50610686610681366004614c73565b611618565b6040516105ae9190614c9b565b34801561069e575f80fd5b506105d66106ad366004614bca565b611660565b3480156106bd575f80fd5b506106d16106cc366004614d1f565b61179c565b6040516105ae9190614d90565b3480156106e9575f80fd5b506106fd6106f8366004614e44565b611900565b6040805192151583527fffffffff000000000000000000000000000000000000000000000000000000009091166020830152016105ae565b348015610740575f80fd5b506105a261074f366004614ea6565b73ffffffffffffffffffffffffffffffffffffffff9091165f908152600660209081526040808320600885901c845290915290205460ff9091161c6001161590565b34801561079c575f80fd5b5062721c055465ffffffffffff165b60405165ffffffffffff90911681526020016105ae565b3480156107cd575f80fd5b506107e16107dc366004614ece565b611945565b604080517fffffffff00000000000000000000000000000000000000000000000000000000909316835261ffff9091166020830152016105ae565b348015610827575f80fd5b506105d6610836366004614f3a565b6119ad565b348015610846575f80fd5b506105d6610855366004614f7a565b6119bd565b348015610865575f80fd5b506106fd610874366004614faa565b6119ca565b348015610884575f80fd5b5061089861089336600461502d565b611a0e565b604080519283529015156020830152016105ae565b3480156108b8575f80fd5b506105d66108c7366004614ea6565b611a9a565b3480156108d7575f80fd5b506105d6611aab565b3480156108eb575f80fd5b506108ff6108fa3660046150e8565b611ae8565b604080519283526020830191909152016105ae565b34801561091f575f80fd5b506105a261092e366004615128565b611b08565b34801561093e575f80fd5b506105a261094d3660046151d5565b60ff9182165f90815262721c0a6020908152604080832065ffffffffffff96909616835294815284822073ffffffffffffffffffffffffffffffffffffffff939093168252600490920190915291909120541690565b3480156109ae575f80fd5b506107ab6109bd3660046151fe565b611b83565b3480156109cd575f80fd5b506106fd6109dc36600461527c565b611bbe565b3480156109ec575f80fd5b506105d66109fb3660046152cd565b611c01565b348015610a0b575f80fd5b506105d6610a1a36600461530f565b611c0e565b348015610a2a575f80fd5b506105d6610a393660046153ab565b611d23565b348015610a49575f80fd5b506105a2610a58366004615436565b611d5d565b348015610a68575f80fd5b506105d6610a393660046154c2565b348015610a82575f80fd5b506108ff610a9136600461551f565b611dcc565b348015610aa1575f80fd5b506105d6611ded565b348015610ab5575f80fd5b506105d6610a39366004614d1f565b348015610acf575f80fd5b50610ae3610ade366004614a11565b611e00565b6040516105ae9190615560565b348015610afb575f80fd5b506105d6611e32565b348015610b0f575f80fd5b50610b18611f65565b6040519081526020016105ae565b348015610b31575f80fd5b506105d6610b40366004614f3a565b611f73565b348015610b50575f80fd5b506105a2610b5f3660046155ad565b611fbe565b348015610b6f575f80fd5b506105a2610b7e366004614c1b565b5f9081526004602052604090205460ff1690565b348015610b9d575f80fd5b50610686610bac3660046155f5565b61202c565b348015610bbc575f80fd5b50610ae3610bcb366004614c73565b612061565b348015610bdb575f80fd5b506105d6610bea366004614ea6565b6120a2565b348015610bfa575f80fd5b506105a2610c09366004614a11565b61214f565b348015610c19575f80fd5b506105d6610c2836600461560f565b612201565b348015610c38575f80fd5b505f5473ffffffffffffffffffffffffffffffffffffffff165b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016105ae565b348015610c82575f80fd5b506105d6610a3936600461564e565b348015610c9c575f80fd5b506107ab6109bd366004615676565b348015610cb6575f80fd5b506105a2610cc53660046156bd565b61220d565b348015610cd5575f80fd5b506105a2610ce43660046152cd565b73ffffffffffffffffffffffffffffffffffffffff9182165f90815262721c09602090815260408083209390941682526002909201909152205460ff1690565b348015610d2f575f80fd5b506105d6610d3e36600461551f565b61227a565b348015610d4e575f80fd5b506105a2610d5d3660046156d8565b61239d565b348015610d6d575f80fd5b506105d6610d7c366004614ea6565b612418565b348015610d8c575f80fd5b506105d6610d9b366004614ea6565b612424565b348015610dab575f80fd5b506107ab6109bd366004615756565b348015610dc5575f80fd5b50610ee5610dd4366004614a11565b6040805160c080820183525f80835260208084018290528385018290526060808501839052608080860184905260a095860184905273ffffffffffffffffffffffffffffffffffffffff978816845262721c08835292869020865194850187525460ff808216865265ffffffffffff610100830416938601939093526701000000000000008104909716958401959095527b010000000000000000000000000000000000000000000000000000008604169382019390935261ffff7c010000000000000000000000000000000000000000000000000000000085048116938201939093527e010000000000000000000000000000000000000000000000000000000000009093049091169082015290565b6040516105ae91905f60c08201905060ff835116825265ffffffffffff602084015116602083015273ffffffffffffffffffffffffffffffffffffffff604084015116604083015260ff6060840151166060830152608083015161ffff80821660808501528060a08601511660a0850152505092915050565b348015610f69575f80fd5b506105d6610f78366004615795565b612430565b348015610f88575f80fd5b506105d6610a39366004615806565b348015610fa2575f80fd5b506105d6610fb1366004614c1b565b612450565b348015610fc1575f80fd5b506105a2610fd036600461586b565b60ff9182165f90815262721c0a6020908152604080832065ffffffffffff969096168352948152848220928252600590920190915291909120541690565b348015611019575f80fd5b50610686611028366004614d1f565b61245a565b348015611038575f80fd5b50610ae36110473660046155f5565b612525565b348015611057575f80fd5b506105d6610662366004614a11565b348015611071575f80fd5b506105d6610a393660046158a4565b34801561108b575f80fd5b506105d661109a3660046155ad565b612557565b3480156110aa575f80fd5b506105a26110b9366004614a11565b73ffffffffffffffffffffffffffffffffffffffff165f90815262721c0d602052604090205460ff1690565b3480156110f0575f80fd5b506108986110ff3660046158be565b6125a3565b34801561110f575f80fd5b506105a261111e36600461596f565b61262e565b34801561112e575f80fd5b506105d661113d366004615756565b61269c565b34801561114d575f80fd5b506105d661115c366004615a07565b6127ac565b34801561116c575f80fd5b506105a261117b3660046155ad565b6127b8565b34801561118b575f80fd5b50610b1861119a366004614a11565b73ffffffffffffffffffffffffffffffffffffffff165f9081526007602052604090205490565b3480156111cc575f80fd5b50610c526111db366004615a30565b61281b565b3480156111eb575f80fd5b506105a26111fa366004615a49565b61287a565b34801561120a575f80fd5b506105d6611219366004614a11565b6128dc565b348015611229575f80fd5b506105d6610a39366004615a64565b348015611243575f80fd5b50600180547f0000000000000000000000000000000000000000000000000000000000000000909101906108ff565b34801561127d575f80fd5b506105d6610a39366004615a7d565b348015611297575f80fd5b50610c526112a6366004615a64565b65ffffffffffff165f90815262721c07602052604090205473ffffffffffffffffffffffffffffffffffffffff1690565b3480156112e2575f80fd5b506105a26112f1366004614c1b565b5f9081526005602052604090205460ff1690565b5f7fffffffff000000000000000000000000000000000000000000000000000000008216158061137657507fffffffff0000000000000000000000000000000000000000000000000000000082167f1bb3460e00000000000000000000000000000000000000000000000000000000145b806113c257507fffffffff0000000000000000000000000000000000000000000000000000000082167fc058083400000000000000000000000000000000000000000000000000000000145b8061140e57507fffffffff0000000000000000000000000000000000000000000000000000000082167f89a9c85500000000000000000000000000000000000000000000000000000000145b8061145a57507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b92915050565b61146a815f612424565b50565b5f61149c60107f000000000000000000000000000000000000000000000000000000000000000063ffffffff16565b6114b06104838c8c8b8d8c8c8b8b8b612b61565b6114bd8b87878d88612c59565b905080156114cf576114cf868a612d1c565b9a9950505050505050505050565b5f836114e881612d97565b61151660107f000000000000000000000000000000000000000000000000000000000000000063ffffffff16565b6115298e8e8d8f8e8e8d8b8b8f8f612dde565b8c6115378f8b8b848c612c59565b92508215611549576115498a8e612d1c565b50509c9b505050505050505050505050565b5f61158a60027f000000000000000000000000000000000000000000000000000000000000000063ffffffff16565b5f61159b876104838787875f612df4565b90506115aa8588888787612c59565b915081156115bf576115bf81885f8681612f7d565b5095945050505050565b6115d16130d0565b61146a816130d8565b7f0000000000000000000000000000000000000000000000000000000000000000365f80375f80365f845af43d805f803e8161161457805ffd5b805ff35b73ffffffffffffffffffffffffffffffffffffffff82165f90815262721c08602052604090205460609061165990610100900465ffffffffffff168361202c565b9392505050565b5f611671600333888888888861311d565b5090507fffffffff000000000000000000000000000000000000000000000000000000008116156116a5576116a58161320f565b335f90815260208490526040902060041c6116e38163ffffffff7f000000000000000000000000000000000000000000000000000000000000000016565b15611793575f6116f4826001615ab2565b90505f611724827f000000000000000000000000000000000000000000000000000000000000000063ffffffff16565b905080851115611760576040517ff9e7abe700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611790828683037f000000000000000000000000000000000000000000000000000000000000000063ffffffff16565b50505b50505050505050565b606081156116595773ffffffffffffffffffffffffffffffffffffffff84165f90815262721c0c602052604090208267ffffffffffffffff8111156117e3576117e3615aea565b60405190808252806020026020018201604052801561181657816020015b60608152602001906001900390816118015790505b5091505f5b838110156118f757815f86868481811061183757611837615b17565b9050602002013581526020019081526020015f20805461185690615b44565b80601f016020809104026020016040519081016040528092919081815260200182805461188290615b44565b80156118cd5780601f106118a4576101008083540402835291602001916118cd565b820191905f5260205f20905b8154815290600101906020018083116118b057829003601f168201915b50505050508382815181106118e4576118e4615b17565b602090810291909101015260010161181b565b50509392505050565b5f80611912600389898989898961311d565b507fffffffff00000000000000000000000000000000000000000000000000000000811615999098509650505050505050565b5f80333014611980576040517ffa103f6700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f61198a89613217565b9250905061199e818b8b8b8b8b8b8b6132d2565b92505097509795505050505050565b6119b8838383611f73565b505050565b6119b8600184848461333f565b5f806119dc600188888888885f61311d565b507fffffffff000000000000000000000000000000000000000000000000000000008116159890975095505050505050565b5f8082611a1a81613437565b611a4860407f000000000000000000000000000000000000000000000000000000000000000063ffffffff16565b5f611a5b8f8f8f8f8f8f8e8e8e8e61347e565b9050611a6f818e8e8e8e8e8c612c596134a3565b90945092508215611a8857611a88818b88876001612f7d565b50509b509b9950505050505050505050565b611aa7600183835f613699565b5050565b335f8181526007602052604080822080546001019055517f8e8cebe67607ce50a14a2e3261437f641a7b33ecc053e3d9c90b25ae5e66c6569190a2565b5f80611afa6002888888888887613741565b915091509550959350505050565b5f83611b1381612d97565b611b4160207f000000000000000000000000000000000000000000000000000000000000000063ffffffff16565b611b548d5f8d8f8e8e8d8b8b8f8f613802565b611b618d8a8a5f8b613817565b91508115611b7357611b73898d612d1c565b509b9a5050505050505050505050565b5f7f0000000000000000000000000000000000000000000000000000000000000000365f80375f80365f845af43d805f803e8161161457805ffd5b5f80611bd06002878787875f8061311d565b507fffffffff0000000000000000000000000000000000000000000000000000000081161597909650945050505050565b611aa782825f6001613699565b8365ffffffffffff16421115611c50576040517f827aed8000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611c598b613947565b611c638389613999565b611cef611ce7611ce28d8d8d8c78ffffffffffffffffffffffffffffffffffffffffffffffffff168e8d8d65ffffffffffff168d65ffffffffffff1660075f8f73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f2054613a13565b613ac0565b838386613b07565b65ffffffffffff851615611d035784611d05565b425b9450611d168b8b8b8a89888c613c04565b5050505050505050505050565b7f0000000000000000000000000000000000000000000000000000000000000000365f80375f80365f845af43d805f803e8161161457805ffd5b5f611d8c60087f000000000000000000000000000000000000000000000000000000000000000063ffffffff16565b611da26102d18a8a60018b8b8b60018b8b612b61565b611dae85858b8b613cff565b90508015611dc057611dc08588612d1c565b98975050505050505050565b5f80611dde6003898989898989613741565b91509150965096945050505050565b611df5613dad565b611dfe5f613dfd565b565b73ffffffffffffffffffffffffffffffffffffffff81165f90815262721c096020526040902060609061145a90613e71565b7f000000000000000000000000000000000000000000000000000000000000000080611e7f57507fdacd49f6a6c42b45a5c3d195b83b324104542d9147bb8064a39c6a8d23ba9b005460ff165b15611eb6576040517ff45b98b000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611edf7f0000000000000000000000000000000000000000000000000000000000000000613e7d565b611f15576040517f70a4078f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fdacd49f6a6c42b45a5c3d195b83b324104542d9147bb8064a39c6a8d23ba9b0080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055611dfe80565b5f611f6e613ee8565b905090565b5f611f846002338686865f8061311d565b5090507fffffffff00000000000000000000000000000000000000000000000000000000811615611fb857611fb88161320f565b50505050565b5f611fed60017f000000000000000000000000000000000000000000000000000000000000000063ffffffff16565b5f611fff866102d18686600180612df4565b905061200d86868686613cff565b915081156120235761202381875f600181612f7d565b50949350505050565b60ff81165f90815262721c0a6020908152604080832065ffffffffffff86168452909152902060609061165990600201613e71565b73ffffffffffffffffffffffffffffffffffffffff82165f90815262721c08602052604090205460609061165990610100900465ffffffffffff1683612525565b6120aa6130d0565b6120b35f6130d8565b8015611aa7575f8273ffffffffffffffffffffffffffffffffffffffff16826040515f6040518083038185875af1925050503d805f811461210f576040519150601f19603f3d011682016040523d82523d5f602084013e612114565b606091505b50509050806119b8576040517f3f1df14400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f89a9c85500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82811660048301525f917f0000000000000000000000000000000000000000000000000000000000000000909116906389a9c85590602401602060405180830381865afa1580156121dd573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061145a9190615b8f565b611fb88484848461333f565b73ffffffffffffffffffffffffffffffffffffffff8381165f90815262721c08602090815260408083205460ff808816855262721c0a845282852061010090920465ffffffffffff168552908352818420948616845260049094019091528120549091165b949350505050565b3373ffffffffffffffffffffffffffffffffffffffff871614806122b357503373ffffffffffffffffffffffffffffffffffffffff8616145b6122e9576040517feda7110300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6122f284613947565b5f612303600388878787878c613fdc565b805490915060ff1661236b57600281556040516001815273ffffffffffffffffffffffffffffffffffffffff878116919089169084907f705db7ac401a8091bb37a7838ad73d1fa8e1c663cb345f347fefe71280e3f03b9060200160405180910390a4611793565b6040517f8cbf875200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f836123a881612d97565b6123d660087f000000000000000000000000000000000000000000000000000000000000000063ffffffff16565b6123eb8c8c60018d8d8d60018b8b8f8f6140ad565b6123f788888e8e613cff565b9150811561240957612409888b612d1c565b509a9950505050505050505050565b611aa75f83838261333f565b611aa75f838382613699565b61243986613947565b61244886868685853389613c04565b505050505050565b61146a3382613999565b606081156116595773ffffffffffffffffffffffffffffffffffffffff84165f90815262721c0b602052604090208267ffffffffffffffff8111156124a1576124a1615aea565b6040519080825280602002602001820160405280156124ca578160200160208202803683370190505b5091505f5b838110156118f757815f8686848181106124eb576124eb615b17565b9050602002013581526020019081526020015f205483828151811061251257612512615b17565b60209081029190910101526001016124cf565b60ff81165f90815262721c0a6020908152604080832065ffffffffffff86168452909152902060609061165990613e71565b5f612568600133878787875f61311d565b5090507fffffffff0000000000000000000000000000000000000000000000000000000081161561259c5761259c8161320f565b5050505050565b5f80826125af81613437565b6125dd60807f000000000000000000000000000000000000000000000000000000000000000063ffffffff16565b5f6125f08e8e8e8e5f8f8e8e8e8e6140c3565b9050612604818d8d5f8e8e8c6138176134a3565b9094509250821561261d5761261d818b88876001612f7d565b50509a509a98505050505050505050565b5f61265d60207f000000000000000000000000000000000000000000000000000000000000000063ffffffff16565b61267060148b5f8b8d8c8c8b8b8b612b61565b61267d8a87875f88613817565b9050801561268f5761268f868a612d1c565b9998505050505050505050565b600160045f6040518060e0016040528060a58152602001615cf260a5913985856040516020016126ce93929190615bae565b6040516020818303038152906040528051906020012081526020019081526020015f205f6101000a81548160ff021916908315150217905550600160055f6040518060c00160405280609d8152602001615c55609d9139858560405160200161273993929190615bae565b604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe00181529181528151602092830120835290820192909252015f2080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169115159190911790555050565b6119b88383835f613699565b5f6127e760047f000000000000000000000000000000000000000000000000000000000000000063ffffffff16565b5f6127f7866014865f875f612df4565b90506128068487875f87613817565b915081156120235761202381875f8681612f7d565b5f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0160ff83160161284d57505f919050565b5060ff165f90815262721c0e602052604090205473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff83165f90815262721c08602090815260408083205460ff808716855262721c0a845282852061010090920465ffffffffffff16855290835281842085855260050190925282205416612272565b6128e4613dad565b73ffffffffffffffffffffffffffffffffffffffff8116612931576040517f3e58254b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61146a81613dfd565b80825d5050565b5c90565b5f815d50565b7fdacd49f6a6c42b45a5c3d195b83b324104542d9147bb8064a39c6a8d23ba9b005460ff161561297c5780825d5050565b9055565b5f7fdacd49f6a6c42b45a5c3d195b83b324104542d9147bb8064a39c6a8d23ba9b005460ff16156129b057505c90565b5080545b919050565b7fdacd49f6a6c42b45a5c3d195b83b324104542d9147bb8064a39c6a8d23ba9b005460ff16156129e9575f815d50565b5f9055565b7f0000000000000000000000000000000000000000000000000000000000000000344703111561146a5760015481161561146a576040517fcc47725a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000034470311612aaf576040517ffd2c901300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80600154165f0361146a576040517ffd2c901300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f7f00000000000000000000000000000000000000000000000000000000000000003447031115612b1c57506001545b90565b60015481161561146a576040517fcc47725a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60015490565b5f612c47611ce28c8c8c8c8c8c60075f8e73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f2054604080517f932b8553b8e35bbee682d275cbe1cf115e14a777e2ca3266b4797369fb6317d36020808301919091528183019990995273ffffffffffffffffffffffffffffffffffffffff979097166060880152608087019590955260a086019390935260c08501919091523360e085015261010084015261012080840191909152815180840390910181526101409092019052805191012090565b9050611d168787868b898689896140d8565b5f612c6a6104838787878787614162565b905080612d13576040517ff242432a00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff86811660048301528581166024830152604482018590526064820184905260a060848301525f60a483015287169063f242432a9060c4015f604051808303815f87803b158015612cfa575f80fd5b505af1925050508015612d0b575060015b612d13575060015b95945050505050565b73ffffffffffffffffffffffffffffffffffffffff82165f908152600660209081526040808320600885901c845290915290208054600160ff84161b908118918290551615611aa7576040517fe4adc0bf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f8181526004602052604090205460ff1661146a576040517f4722506e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611d166104838c8c8c8c8c8c8c8c8c8c8c61420e565b5f612e056002888888888633613fdc565b8054909150427a01000000000000000000000000000000000000000000000000000090910465ffffffffffff161015612e6a576040517f827aed8000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8054610100900478ffffffffffffffffffffffffffffffffffffffffffffffffff16831115612ec5576040517ffee142c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8115612ef65780547fffffffffffff00000000000000000000000000000000000000000000000000ff168155612f73565b805478ffffffffffffffffffffffffffffffffffffffffffffffffff61010090910481161015612f7357805478ffffffffffffffffffffffffffffffffffffffffffffffffff6101008083048216869003909116027fffffffffffff00000000000000000000000000000000000000000000000000ff9091161781555b9695505050505050565b811561259c57801561304e5784547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0078ffffffffffffffffffffffffffffffffffffffffffffffffff6101008084048216860190911602167fffffffffffff000000000000000000000000000000000000000000000000000090911617855560405182815273ffffffffffffffffffffffffffffffffffffffff85169084907f83e0ca2c1392f14286fa1e41c797789d48c5827572e8bcc352d8943c1961eaf09060200160405180910390a361259c565b845478ffffffffffffffffffffffffffffffffffffffffffffffffff6101009091048116101561259c57845478ffffffffffffffffffffffffffffffffffffffffffffffffff61010080830482168501909116027fffffffffffff00000000000000000000000000000000000000000000000000ff9091161785555050505050565b611dfe613dad565b600180549082905560408051828152602081018490527fe0d8d9ad73c586e8cf60ffd390b6f3654200a2d8857eb6abba4f6842a1210aae910160405180910390a15050565b5f803073ffffffffffffffffffffffffffffffffffffffff88160361314657505f905080613203565b6040517f27d21f50000000000000000000000000000000000000000000000000000000008152600481018a905273ffffffffffffffffffffffffffffffffffffffff808a166024830152808916604483015280881660648301528616608482015260a4810185905260c4810184905230906327d21f509060e4016040805180830381865afa1580156131da573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906131fe9190615bd4565b915091505b97509795505050505050565b805f5260045ffd5b73ffffffffffffffffffffffffffffffffffffffff81165f90815262721c0860205260408120805482919060ff90811610613274578054670100000000000000900473ffffffffffffffffffffffffffffffffffffffff166132a0565b805460ff165f90815262721c0e602052604090205473ffffffffffffffffffffffffffffffffffffffff165b905490947e0100000000000000000000000000000000000000000000000000000000000090910461ffff169350915050565b5f80630be200ea60e01b90506040518181528960048201528860248201528760448201528660648201528560848201528460a48201528360c482015260e4810160405260205f60e4838e5af490508061332e573d805f803e805ffd5b50505f519998505050505050505050565b82843361334d838383614241565b5f86815262721c06602052604090206133a1905b8873ffffffffffffffffffffffffffffffffffffffff167f000000000000000000000000000000000000000000000000000000000000000063ffffffff16565b5f86815260208690526040902060041c6133f58173ffffffffffffffffffffffffffffffffffffffff8a1663ffffffff7f000000000000000000000000000000000000000000000000000000000000000016565b61342d613403826001615ab2565b867f000000000000000000000000000000000000000000000000000000000000000063ffffffff16565b5050505050505050565b5f8181526005602052604090205460ff1661146a576040517f4722506e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6134948b8b8b6104838c8c8c8c8c8c8c61446c565b9b9a5050505050505050505050565b87546020880135905f90610100900478ffffffffffffffffffffffffffffffffffffffffffffffffff168211156134f9578954610100900478ffffffffffffffffffffffffffffffffffffffffffffffffff1691505b8860400135821015613537576040517fb9ff981500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b895478ffffffffffffffffffffffffffffffffffffffffffffffffff6101008083048216859003909116027fffffffffffff00000000000000000000000000000000000000000000000000ff909116178a55604051828152339073ffffffffffffffffffffffffffffffffffffffff88169086907f2203cb053e6b01ec07e87d67d288d360ae164171185684936663b7d8fa9c534c9060200160405180910390a48954610100900478ffffffffffffffffffffffffffffffffffffffffffffffffff165f036136795789547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001178a556040515f8152339073ffffffffffffffffffffffffffffffffffffffff88169086907f705db7ac401a8091bb37a7838ad73d1fa8e1c663cb345f347fefe71280e3f03b9060200160405180910390a45b61368a8887878a868863ffffffff16565b90509850989650505050505050565b8284336136a7838383614241565b5f86815262721c0660205260409020613728908873ffffffffffffffffffffffffffffffffffffffff16866136dc575f6136fe565b7f80000000000000000000000000000000000000000000000000000000000000005b177f000000000000000000000000000000000000000000000000000000000000000063ffffffff16565b61179361336187875f9182526020526040902060041c90565b5f805f6137538a8a898989898e613fdc565b8054909150427a01000000000000000000000000000000000000000000000000000090910465ffffffffffff16106137ac578054610100900478ffffffffffffffffffffffffffffffffffffffffffffffffff166137ae565b5f5b905478ffffffffffffffffffffffffffffffffffffffffffffffffff9091169a7a01000000000000000000000000000000000000000000000000000090910465ffffffffffff169950975050505050505050565b611d1660148c8c8c8c8c8c8c8c8c8c8c61420e565b5f61382760148787875f87614162565b905080612d13576040805173ffffffffffffffffffffffffffffffffffffffff87811660248301528681166044830152606480830186905283518084039091018152608490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f23b872dd0000000000000000000000000000000000000000000000000000000017905291515f928392908a16916138cc9190615c01565b5f604051808303815f865af19150503d805f8114613905576040519150601f19603f3d011682016040523d82523d5f602084013e61390a565b606091505b50915091508161391d576001925061393c565b80511561393c57808060200190518101906139389190615b8f565b1592505b505095945050505050565b6102d1811480613958575061048381145b806139635750601481145b61146a576040517f9d36a97900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff82165f908152600660209081526040808320600885901c845290915290208054600160ff84161b9081189182905516611aa7576040517fd979627300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080517ff9c04f8b028fcfa3315ea5accaee4589194a685f07cda0392e6ba9550706111960208201529081018a905273ffffffffffffffffffffffffffffffffffffffff808a1660608301526080820189905260a0820188905260c08201879052851660e08201526101008101849052610120810183905261014081018290525f90610160015b6040516020818303038152906040528051906020012090509998505050505050505050565b5f61145a613acc613ee8565b836040517f19010000000000000000000000000000000000000000000000000000000000008152600281019290925260228201526042902090565b6041829003613b85578235602084013560408501355f90811a9080613b2e8984878761464e565b915091508073ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff16141580613b6a5750815b15613b7b57613b7b868a8a8a614722565b5050505050611fb8565b6040829003613bf857823560208401355f80613ba28885856147b4565b915091508073ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff16141580613bde5750815b15613bef57613bef85898989614722565b50505050611fb8565b611fb881858585614722565b5f613c156002848a8a8a8688613fdc565b805460ff167a01000000000000000000000000000000000000000000000000000065ffffffffffff87169081027fffffffffffff00000000000000000000000000000000000000000000000000ff169190911761010078ffffffffffffffffffffffffffffffffffffffffffffffffff8916908102919091178355604080518a8152602081019290925281019190915290915073ffffffffffffffffffffffffffffffffffffffff83811691898216918616907f0ec867d4f1b037422566cd0248bae620e6c142dcf5631948271916e8ca8dd2639060600160405180910390a45050505050505050565b5f613d116102d1848787866001614162565b905080612272576040517f23b872dd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff86811660048301528581166024830152604482018490528416906323b872dd906064015f604051808303815f87803b158015613d8d575f80fd5b505af1925050508015613d9e575060015b61227257506001949350505050565b5f5473ffffffffffffffffffffffffffffffffffffffff163314611dfe576040517f021943c000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60605f611659836147fb565b5f8173ffffffffffffffffffffffffffffffffffffffff16600a5a613ea29190615c1c565b6040515f8181818686fa925050503d805f8114613eda576040519150601f19603f3d011682016040523d82523d5f602084013e613edf565b606091505b50909392505050565b5f7f00000000000000000000000000000000000000000000000000000000000000004603613f3557507f000000000000000000000000000000000000000000000000000000000000000090565b611f6e604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201527f0000000000000000000000000000000000000000000000000000000000000000918101919091527f000000000000000000000000000000000000000000000000000000000000000060608201524660808201523060a08201525f9060c00160405160208183030381529060405280519060200120905090565b5f875f614058898989898973ffffffffffffffffffffffffffffffffffffffff9485165f818152600760209081526040918290205482518083019490945283830197909752949096166060820152608081019290925260a082015260c0808201939093528351808203909301835260e001909252805191012090565b81526020019081526020015f205f8373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f209050979650505050505050565b611d166102d18c8c8c8c8c8c8c8c8c8c8c61420e565b5f6134948b8b8b60148c8c8c8c8c8c8c61446c565b86421115614112576040517fe3fd7ac300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8486111561414c576040517fde7fafeb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6141568489613999565b61342d83838387613b07565b5f805f6141866102d18a1461417857600361417b565b60015b89338a8a8a8a61311d565b909250905061ffff8116158061419f5750888161ffff16145b156141d0577fffffffff00000000000000000000000000000000000000000000000000000000821615159250614202565b6040517f7a535da600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50509695505050505050565b5f6142208d8d8d8d8b8e8e8a8a614854565b90506142328989888d8b868b8b6140d8565b50505050505050505050505050565b73ffffffffffffffffffffffffffffffffffffffff83165f90815262721c086020526040902080547b010000000000000000000000000000000000000000000000000000009004600116156142c2576040517fb1ae736700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80547b0100000000000000000000000000000000000000000000000000000090046002161561435a577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff73ffffffffffffffffffffffffffffffffffffffff84160161435a576040517fbf729bb100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8054610100900465ffffffffffff165f9081527f7e074e02264140693c3fbf3ca5a7e5aa542554b2d564345f76c58d7f98bdbbff6020908152604080832073ffffffffffffffffffffffffffffffffffffffff8616845260040190915290205460ff16611fb85780547b0100000000000000000000000000000000000000000000000000000090046008161580614435575073ffffffffffffffffffffffffffffffffffffffff82165f9081527f442599b958bf0d8fbe0ddbf2d50f16197374228cad346cd58d2da01fd8e057f8602052604090205460ff16155b15611fb8576040517fbc8ea06200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f78ffffffffffffffffffffffffffffffffffffffffffffffffff8a3511156144c1576040517f3b0a334300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6144d16003878b8b8b8833613fdc565b805490915060ff1661236b578054610100900478ffffffffffffffffffffffffffffffffffffffffffffffffff165f036145ed5761452a6145228a8a8a8e358b8b65ffffffffffff8c168b8b614854565b8d8d89613b07565b805460ff166101008b3578ffffffffffffffffffffffffffffffffffffffffffffffffff81169190910279ffffffffffffffffffffffffffffffffffffffffffffffffffff16919091177a01000000000000000000000000000000000000000000000000000065ffffffffffff871602178255604051908152339073ffffffffffffffffffffffffffffffffffffffff88169085907f257001e1f7fbfc5bbde5da225c876ab67293f37bda3afb8b35d9a55dfad6f65d9060200160405180910390a45b80547a010000000000000000000000000000000000000000000000000000900465ffffffffffff16421115613494576040517fe3fd7ac300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08311156146835750600190505f614719565b604080515f81526020810180835288905260ff871691810191909152606081018590526080810184905260019060a0016020604051602081039080840390855afa1580156146d3573d5f803e3d5ffd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015173ffffffffffffffffffffffffffffffffffffffff81161593509150505b94509492505050565b8373ffffffffffffffffffffffffffffffffffffffff163b5f03614772576040517f73c919b500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61477e8484848461488d565b611fb8576040517f73c919b500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f807f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8316601b60ff85901c016147ed8782888561464e565b909890975095505050505050565b6060815f0180548060200260200160405190810160405280929190818152602001828054801561484857602002820191905f5260205f20905b815481526020019060010190808311614834575b50505050509050919050565b73ffffffffffffffffffffffffffffffffffffffff85165f90815260076020526040812054613494611ce28c8c8c8c8b8b8b8b8a61493b565b5f61492f565b5f6040517f1626ba7e0000000000000000000000000000000000000000000000000000000081528360048201526040602482015285604482015285856064830137601f19601f87011660640180820160405260205f8284875afa60203d10151615614925577f1626ba7e000000000000000000000000000000000000000000000000000000005f511492505050612272565b5050949350505050565b612d1382848688614893565b60408051602081018490529081018a905273ffffffffffffffffffffffffffffffffffffffff891660608201526080810188905260a0810187905260c081018690523360e08201526101008101859052610120810182905261014081018490525f9061016001613a9b565b7fffffffff000000000000000000000000000000000000000000000000000000008116811461146a575f80fd5b5f602082840312156149e3575f80fd5b8135611659816149a6565b803573ffffffffffffffffffffffffffffffffffffffff811681146129b4575f80fd5b5f60208284031215614a21575f80fd5b611659826149ee565b5f8083601f840112614a3a575f80fd5b50813567ffffffffffffffff811115614a51575f80fd5b602083019150836020828501011115614a68575f80fd5b9250929050565b5f805f805f805f805f806101208b8d031215614a89575f80fd5b614a928b6149ee565b995060208b0135985060408b0135975060608b0135965060808b01359550614abc60a08c016149ee565b9450614aca60c08c016149ee565b935060e08b013592506101008b013567ffffffffffffffff811115614aed575f80fd5b614af98d828e01614a2a565b915080935050809150509295989b9194979a5092959850565b5f805f805f805f805f805f806101608d8f031215614b2e575f80fd5b614b378d6149ee565b9b5060208d01359a5060408d0135995060608d0135985060808d01359750614b6160a08e016149ee565b9650614b6f60c08e016149ee565b955060e08d013594506101008d013593506101208d0135925067ffffffffffffffff6101408e01351115614ba1575f80fd5b614bb28e6101408f01358f01614a2a565b81935080925050509295989b509295989b509295989b565b5f805f805f60a08688031215614bde575f80fd5b614be7866149ee565b9450614bf5602087016149ee565b9350614c03604087016149ee565b94979396509394606081013594506080013592915050565b5f60208284031215614c2b575f80fd5b5035919050565b803560ff811681146129b4575f80fd5b5f8060408385031215614c53575f80fd5b614c5c83614c32565b9150614c6a602084016149ee565b90509250929050565b5f8060408385031215614c84575f80fd5b614c8d836149ee565b9150614c6a60208401614c32565b602080825282518282018190525f9190848201906040850190845b81811015614cd257835183529284019291840191600101614cb6565b50909695505050505050565b5f8083601f840112614cee575f80fd5b50813567ffffffffffffffff811115614d05575f80fd5b6020830191508360208260051b8501011115614a68575f80fd5b5f805f60408486031215614d31575f80fd5b614d3a846149ee565b9250602084013567ffffffffffffffff811115614d55575f80fd5b614d6186828701614cde565b9497909650939450505050565b5f5b83811015614d88578181015183820152602001614d70565b50505f910152565b5f602080830181845280855180835260408601915060408160051b87010192508387015f5b82811015614e37577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc088860301845281518051808752614dfa818989018a8501614d6e565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01695909501860194509285019290850190600101614db5565b5092979650505050505050565b5f805f805f8060c08789031215614e59575f80fd5b614e62876149ee565b9550614e70602088016149ee565b9450614e7e604088016149ee565b9350614e8c606088016149ee565b92506080870135915060a087013590509295509295509295565b5f8060408385031215614eb7575f80fd5b614ec0836149ee565b946020939093013593505050565b5f805f805f805f60e0888a031215614ee4575f80fd5b87359650614ef4602089016149ee565b9550614f02604089016149ee565b9450614f10606089016149ee565b9350614f1e608089016149ee565b925060a0880135915060c0880135905092959891949750929550565b5f805f60608486031215614f4c575f80fd5b614f55846149ee565b9250614f63602085016149ee565b9150614f71604085016149ee565b90509250925092565b5f805f60608486031215614f8c575f80fd5b614f95846149ee565b95602085013595506040909401359392505050565b5f805f805f60a08688031215614fbe575f80fd5b614fc7866149ee565b9450614fd5602087016149ee565b9350614fe3604087016149ee565b9250614ff1606087016149ee565b949793965091946080013592915050565b5f60608284031215615012575f80fd5b50919050565b803565ffffffffffff811681146129b4575f80fd5b5f805f805f805f805f805f6101808c8e031215615048575f80fd5b8b3567ffffffffffffffff81111561505e575f80fd5b61506a8e828f01614a2a565b909c509a5061507e90508d60208e01615002565b985061508c60808d016149ee565b975060a08c013596506150a160c08d016149ee565b95506150af60e08d016149ee565b94506101008c013593506150c66101208d01615018565b92506101408c013591506101608c013590509295989b509295989b9093969950565b5f805f805f60a086880312156150fc575f80fd5b615105866149ee565b9450615113602087016149ee565b935060408601359250614ff1606087016149ee565b5f805f805f805f805f805f6101408c8e031215615143575f80fd5b61514c8c6149ee565b9a5060208c0135995060408c0135985060608c0135975061516f60808d016149ee565b965061517d60a08d016149ee565b955060c08c0135945060e08c013593506101008c013592506101208c013567ffffffffffffffff8111156151af575f80fd5b6151bb8e828f01614a2a565b915080935050809150509295989b509295989b9093969950565b5f805f606084860312156151e7575f80fd5b6151f084615018565b9250614f6360208501614c32565b5f805f805f60608688031215615212575f80fd5b853567ffffffffffffffff80821115615229575f80fd5b61523589838a01614a2a565b909750955085915061524960208901615018565b9450604088013591508082111561525e575f80fd5b5061526b88828901614cde565b969995985093965092949392505050565b5f805f806080858703121561528f575f80fd5b615298856149ee565b93506152a6602086016149ee565b92506152b4604086016149ee565b91506152c2606086016149ee565b905092959194509250565b5f80604083850312156152de575f80fd5b614c5c836149ee565b803578ffffffffffffffffffffffffffffffffffffffffffffffffff811681146129b4575f80fd5b5f805f805f805f805f805f6101408c8e03121561532a575f80fd5b8b359a5061533a60208d016149ee565b995060408c0135985060608c0135975061535660808d016152e7565b965061536460a08d016149ee565b955061537260c08d01615018565b945061538060e08d01615018565b935061538f6101008d016149ee565b92506101208c013567ffffffffffffffff8111156151af575f80fd5b5f805f805f606086880312156153bf575f80fd5b6153c8866149ee565b9450602086013567ffffffffffffffff808211156153e4575f80fd5b818801915088601f8301126153f7575f80fd5b813581811115615405575f80fd5b8960208260061b8501011115615419575f80fd5b60208301965080955050604088013591508082111561525e575f80fd5b5f805f805f805f8060e0898b03121561544d575f80fd5b615456896149ee565b975060208901359650604089013595506060890135945061547960808a016149ee565b935061548760a08a016149ee565b925060c089013567ffffffffffffffff8111156154a2575f80fd5b6154ae8b828c01614a2a565b999c989b5096995094979396929594505050565b5f805f80606085870312156154d5575f80fd5b6154de85615018565b93506154ec60208601614c32565b9250604085013567ffffffffffffffff811115615507575f80fd5b61551387828801614cde565b95989497509550505050565b5f805f805f8060c08789031215615534575f80fd5b61553d876149ee565b955061554b602088016149ee565b945060408701359350614e8c606088016149ee565b602080825282518282018190525f9190848201906040850190845b81811015614cd257835173ffffffffffffffffffffffffffffffffffffffff168352928401929184019160010161557b565b5f805f80608085870312156155c0575f80fd5b6155c9856149ee565b93506155d7602086016149ee565b92506155e5604086016149ee565b9396929550929360600135925050565b5f8060408385031215615606575f80fd5b614c8d83615018565b5f805f8060808587031215615622575f80fd5b61562b856149ee565b9350615639602086016149ee565b93969395505050506040820135916060013590565b5f806040838503121561565f575f80fd5b615668836149ee565b9150614c6a60208401615018565b5f805f60408486031215615688575f80fd5b833567ffffffffffffffff81111561569e575f80fd5b6156aa86828701614a2a565b9094509250614f71905060208501615018565b5f805f606084860312156156cf575f80fd5b6151f0846149ee565b5f805f805f805f805f806101208b8d0312156156f2575f80fd5b6156fb8b6149ee565b995060208b0135985060408b0135975060608b0135965061571e60808c016149ee565b955061572c60a08c016149ee565b945060c08b0135935060e08b013592506101008b013567ffffffffffffffff811115614aed575f80fd5b5f8060208385031215615767575f80fd5b823567ffffffffffffffff81111561577d575f80fd5b61578985828601614a2a565b90969095509350505050565b5f805f805f8060c087890312156157aa575f80fd5b863595506157ba602088016149ee565b9450604087013593506157cf606088016149ee565b92506157dd608088016152e7565b91506157eb60a08801615018565b90509295509295509295565b61ffff8116811461146a575f80fd5b5f805f805f60a0868803121561581a575f80fd5b615823866149ee565b945061583160208701614c32565b935061583f604087016149ee565b925061584d60608701614c32565b9150608086013561585d816157f7565b809150509295509295909350565b5f805f6060848603121561587d575f80fd5b61588684615018565b925061589460208501614c32565b9150604084013590509250925092565b5f80604083850312156158b5575f80fd5b614c5c83615018565b5f805f805f805f805f806101608b8d0312156158d8575f80fd5b8a3567ffffffffffffffff8111156158ee575f80fd5b6158fa8d828e01614a2a565b909b50995061590e90508c60208d01615002565b975061591c60808c016149ee565b965061592a60a08c016149ee565b955061593860c08c016149ee565b945060e08b0135935061594e6101008c01615018565b92506101208b013591506101408b013590509295989b9194979a5092959850565b5f805f805f805f805f6101008a8c031215615988575f80fd5b6159918a6149ee565b985060208a0135975060408a0135965060608a013595506159b460808b016149ee565b94506159c260a08b016149ee565b935060c08a0135925060e08a013567ffffffffffffffff8111156159e4575f80fd5b6159f08c828d01614a2a565b915080935050809150509295985092959850929598565b5f805f60608486031215615a19575f80fd5b615a22846149ee565b9250615894602085016149ee565b5f60208284031215615a40575f80fd5b61165982614c32565b5f805f60608486031215615a5b575f80fd5b615886846149ee565b5f60208284031215615a74575f80fd5b61165982615018565b5f8060408385031215615a8e575f80fd5b615a97836149ee565b91506020830135615aa7816157f7565b809150509250929050565b8082018082111561145a577f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b600181811c90821680615b5857607f821691505b602082108103615012577f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b5f60208284031215615b9f575f80fd5b81518015158114611659575f80fd5b5f8451615bbf818460208901614d6e565b8201838582375f930192835250909392505050565b5f8060408385031215615be5575f80fd5b8251615bf0816149a6565b6020840151909250615aa7816157f7565b5f8251615c12818460208701614d6e565b9190910192915050565b5f82615c4f577f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b50049056fe5065726d69744f72646572576974684164646974696f6e616c446174612875696e7432353620746f6b656e547970652c6164647265737320746f6b656e2c75696e743235362069642c75696e7432353620616d6f756e742c75696e743235362073616c742c61646472657373206f70657261746f722c75696e743235362065787069726174696f6e2c75696e74323536206d61737465724e6f6e63652c5065726d69745472616e7366657246726f6d576974684164646974696f6e616c446174612875696e7432353620746f6b656e547970652c6164647265737320746f6b656e2c75696e743235362069642c75696e7432353620616d6f756e742c75696e74323536206e6f6e63652c61646472657373206f70657261746f722c75696e743235362065787069726174696f6e2c75696e74323536206d61737465724e6f6e63652ca26469706673582212203c722515cb1b94dd8d752f9b83ae90dcbec2eb559da6ba5732cbef23b095379164736f6c63430008180033dacd49f6a6c42b45a5c3d195b83b324104542d9147bb8064a39c6a8d23ba9b00000000000000000000000000591aa9dff01b8144dc17cb416001d9ac84b951cd000000000000000000000000e0a0004dfa318fc38298ae81a666710eadceba5c000000000000000000000000721c002d2cae3522602b93a0c48e11dc573a15e3000000000000000000000000721c0086cc4f335407cc84a38ce7dcb1560476b000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000001d43726561746f72546f6b656e5472616e7366657256616c696461746f7200000000000000000000000000000000000000000000000000000000000000000000013500000000000000000000000000000000000000000000000000000000000000

Deployed Bytecode

0x60806040526004361061057f575f3560e01c806386e11774116102cf578063c5ba65091161017b578063e07cb240116100dc578063f6b8561a11610092578063fb2de5d71161006d578063fb2de5d714611272578063ff924f531461128c578063ffbcb4aa146112d7575f80fd5b8063f6b8561a1461121e578063f6e1ff8914610a5d578063f7a2000a14611238575f80fd5b8063e9a7be29116100c2578063e9a7be29146111c1578063eeca41a6146111e0578063f2fde38b146111ff575f80fd5b8063e07cb24014610aaa578063e34fda85146105d6575f80fd5b8063d5dc239a11610131578063db5f30a811610117578063db5f30a814611142578063dd71105d14611161578063df21dc1d14611180575f80fd5b8063d5dc239a14611104578063db188e6314611123575f80fd5b8063caee23ea11610161578063caee23ea14611080578063ccee9c511461109f578063cfea7ecf146110e5575f80fd5b8063c5ba65091461104c578063c815270e14611066575f80fd5b8063b3992ab111610230578063b97f6f8b116101e6578063c43f946b116101c1578063c43f946b14610fb6578063c44a076a1461100e578063c482160f1461102d575f80fd5b8063b97f6f8b14610f5e578063bc8aa28414610f7d578063c435f43514610f97575f80fd5b8063b89c4b0d11610216578063b89c4b0d14610d81578063b8dcc68f14610da0578063b955455214610dba575f80fd5b8063b3992ab114610d43578063b6e39ba114610d62575f80fd5b8063a029957911610285578063a4e28f7d1161026b578063a4e28f7d14610cab578063a87b03b614610cca578063ad1ff68514610d24575f80fd5b8063a029957914610a5d578063a20df79f14610c91575f80fd5b80638cdfc296116102b55780638cdfc29614610c0e5780638da5cb5b14610c2d5780639c84033414610c77575f80fd5b806386e1177414610bd057806389a9c85514610bef575f80fd5b80634be52a891161042e57806366e8304a1161038f57806378e890ba116103455780638254fcb7116103205780638254fcb714610b645780638567413514610b92578063859840a514610bb1575f80fd5b806378e890ba14610b045780637c1e14b414610b265780637df81b9014610b45575f80fd5b806371be859d1161037557806371be859d14610aaa578063725d07c514610ac45780637423eb3c14610af0575f80fd5b806366e8304a14610a5d578063715018a614610a96575f80fd5b8063539d2602116103e45780635ed5917f116103ca5780635ed5917f14610a3e57806363fc56bc14610a5d5780636498c04514610a77575f80fd5b8063539d260214610a00578063599d2c6214610a1f575f80fd5b80634f3c18fe116104145780634f3c18fe146109a3578063505efb65146109c257806350793315146109e1575f80fd5b80634be52a89146109145780634ca104b814610933575f80fd5b80631bb610e7116104e357806328cc1131116104995780633a0e3160116104745780633a0e3160146108ad5780633e8a0bc9146108cc5780633f6560ee146108e0575f80fd5b806328cc11311461083b5780632d975d141461085a5780633779e6fd14610879575f80fd5b806323c99262116104c957806323c992621461079157806327d21f50146107c2578063285fb8c81461081c575f80fd5b80631bb610e7146106de5780631f2fdc7914610735575f80fd5b8063136439dd1161053857806315ec12791161051e57806315ec1279146106675780631854b2411461069357806319007fc9146106b2575f80fd5b8063136439dd1461063557806314e4f88814610648575f80fd5b80630e14021a116105685780630e14021a146105d85780630f59197d146105f757806312d3848a14610616575f80fd5b806301ffc9a7146105835780630ad38899146105b7575b5f80fd5b34801561058e575f80fd5b506105a261059d3660046149d3565b611305565b60405190151581526020015b60405180910390f35b3480156105c2575f80fd5b506105d66105d1366004614a11565b611460565b005b3480156105e3575f80fd5b506105a26105f2366004614a6f565b61146d565b348015610602575f80fd5b506105a2610611366004614b12565b6114dd565b348015610621575f80fd5b506105a2610630366004614bca565b61155b565b6105d6610643366004614c1b565b6115c9565b348015610653575f80fd5b506105d6610662366004614c42565b6115da565b348015610672575f80fd5b50610686610681366004614c73565b611618565b6040516105ae9190614c9b565b34801561069e575f80fd5b506105d66106ad366004614bca565b611660565b3480156106bd575f80fd5b506106d16106cc366004614d1f565b61179c565b6040516105ae9190614d90565b3480156106e9575f80fd5b506106fd6106f8366004614e44565b611900565b6040805192151583527fffffffff000000000000000000000000000000000000000000000000000000009091166020830152016105ae565b348015610740575f80fd5b506105a261074f366004614ea6565b73ffffffffffffffffffffffffffffffffffffffff9091165f908152600660209081526040808320600885901c845290915290205460ff9091161c6001161590565b34801561079c575f80fd5b5062721c055465ffffffffffff165b60405165ffffffffffff90911681526020016105ae565b3480156107cd575f80fd5b506107e16107dc366004614ece565b611945565b604080517fffffffff00000000000000000000000000000000000000000000000000000000909316835261ffff9091166020830152016105ae565b348015610827575f80fd5b506105d6610836366004614f3a565b6119ad565b348015610846575f80fd5b506105d6610855366004614f7a565b6119bd565b348015610865575f80fd5b506106fd610874366004614faa565b6119ca565b348015610884575f80fd5b5061089861089336600461502d565b611a0e565b604080519283529015156020830152016105ae565b3480156108b8575f80fd5b506105d66108c7366004614ea6565b611a9a565b3480156108d7575f80fd5b506105d6611aab565b3480156108eb575f80fd5b506108ff6108fa3660046150e8565b611ae8565b604080519283526020830191909152016105ae565b34801561091f575f80fd5b506105a261092e366004615128565b611b08565b34801561093e575f80fd5b506105a261094d3660046151d5565b60ff9182165f90815262721c0a6020908152604080832065ffffffffffff96909616835294815284822073ffffffffffffffffffffffffffffffffffffffff939093168252600490920190915291909120541690565b3480156109ae575f80fd5b506107ab6109bd3660046151fe565b611b83565b3480156109cd575f80fd5b506106fd6109dc36600461527c565b611bbe565b3480156109ec575f80fd5b506105d66109fb3660046152cd565b611c01565b348015610a0b575f80fd5b506105d6610a1a36600461530f565b611c0e565b348015610a2a575f80fd5b506105d6610a393660046153ab565b611d23565b348015610a49575f80fd5b506105a2610a58366004615436565b611d5d565b348015610a68575f80fd5b506105d6610a393660046154c2565b348015610a82575f80fd5b506108ff610a9136600461551f565b611dcc565b348015610aa1575f80fd5b506105d6611ded565b348015610ab5575f80fd5b506105d6610a39366004614d1f565b348015610acf575f80fd5b50610ae3610ade366004614a11565b611e00565b6040516105ae9190615560565b348015610afb575f80fd5b506105d6611e32565b348015610b0f575f80fd5b50610b18611f65565b6040519081526020016105ae565b348015610b31575f80fd5b506105d6610b40366004614f3a565b611f73565b348015610b50575f80fd5b506105a2610b5f3660046155ad565b611fbe565b348015610b6f575f80fd5b506105a2610b7e366004614c1b565b5f9081526004602052604090205460ff1690565b348015610b9d575f80fd5b50610686610bac3660046155f5565b61202c565b348015610bbc575f80fd5b50610ae3610bcb366004614c73565b612061565b348015610bdb575f80fd5b506105d6610bea366004614ea6565b6120a2565b348015610bfa575f80fd5b506105a2610c09366004614a11565b61214f565b348015610c19575f80fd5b506105d6610c2836600461560f565b612201565b348015610c38575f80fd5b505f5473ffffffffffffffffffffffffffffffffffffffff165b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016105ae565b348015610c82575f80fd5b506105d6610a3936600461564e565b348015610c9c575f80fd5b506107ab6109bd366004615676565b348015610cb6575f80fd5b506105a2610cc53660046156bd565b61220d565b348015610cd5575f80fd5b506105a2610ce43660046152cd565b73ffffffffffffffffffffffffffffffffffffffff9182165f90815262721c09602090815260408083209390941682526002909201909152205460ff1690565b348015610d2f575f80fd5b506105d6610d3e36600461551f565b61227a565b348015610d4e575f80fd5b506105a2610d5d3660046156d8565b61239d565b348015610d6d575f80fd5b506105d6610d7c366004614ea6565b612418565b348015610d8c575f80fd5b506105d6610d9b366004614ea6565b612424565b348015610dab575f80fd5b506107ab6109bd366004615756565b348015610dc5575f80fd5b50610ee5610dd4366004614a11565b6040805160c080820183525f80835260208084018290528385018290526060808501839052608080860184905260a095860184905273ffffffffffffffffffffffffffffffffffffffff978816845262721c08835292869020865194850187525460ff808216865265ffffffffffff610100830416938601939093526701000000000000008104909716958401959095527b010000000000000000000000000000000000000000000000000000008604169382019390935261ffff7c010000000000000000000000000000000000000000000000000000000085048116938201939093527e010000000000000000000000000000000000000000000000000000000000009093049091169082015290565b6040516105ae91905f60c08201905060ff835116825265ffffffffffff602084015116602083015273ffffffffffffffffffffffffffffffffffffffff604084015116604083015260ff6060840151166060830152608083015161ffff80821660808501528060a08601511660a0850152505092915050565b348015610f69575f80fd5b506105d6610f78366004615795565b612430565b348015610f88575f80fd5b506105d6610a39366004615806565b348015610fa2575f80fd5b506105d6610fb1366004614c1b565b612450565b348015610fc1575f80fd5b506105a2610fd036600461586b565b60ff9182165f90815262721c0a6020908152604080832065ffffffffffff969096168352948152848220928252600590920190915291909120541690565b348015611019575f80fd5b50610686611028366004614d1f565b61245a565b348015611038575f80fd5b50610ae36110473660046155f5565b612525565b348015611057575f80fd5b506105d6610662366004614a11565b348015611071575f80fd5b506105d6610a393660046158a4565b34801561108b575f80fd5b506105d661109a3660046155ad565b612557565b3480156110aa575f80fd5b506105a26110b9366004614a11565b73ffffffffffffffffffffffffffffffffffffffff165f90815262721c0d602052604090205460ff1690565b3480156110f0575f80fd5b506108986110ff3660046158be565b6125a3565b34801561110f575f80fd5b506105a261111e36600461596f565b61262e565b34801561112e575f80fd5b506105d661113d366004615756565b61269c565b34801561114d575f80fd5b506105d661115c366004615a07565b6127ac565b34801561116c575f80fd5b506105a261117b3660046155ad565b6127b8565b34801561118b575f80fd5b50610b1861119a366004614a11565b73ffffffffffffffffffffffffffffffffffffffff165f9081526007602052604090205490565b3480156111cc575f80fd5b50610c526111db366004615a30565b61281b565b3480156111eb575f80fd5b506105a26111fa366004615a49565b61287a565b34801561120a575f80fd5b506105d6611219366004614a11565b6128dc565b348015611229575f80fd5b506105d6610a39366004615a64565b348015611243575f80fd5b50600180547f000000000000000000000000000000000000000000000000002386f26fc0ffff909101906108ff565b34801561127d575f80fd5b506105d6610a39366004615a7d565b348015611297575f80fd5b50610c526112a6366004615a64565b65ffffffffffff165f90815262721c07602052604090205473ffffffffffffffffffffffffffffffffffffffff1690565b3480156112e2575f80fd5b506105a26112f1366004614c1b565b5f9081526005602052604090205460ff1690565b5f7fffffffff000000000000000000000000000000000000000000000000000000008216158061137657507fffffffff0000000000000000000000000000000000000000000000000000000082167f1bb3460e00000000000000000000000000000000000000000000000000000000145b806113c257507fffffffff0000000000000000000000000000000000000000000000000000000082167fc058083400000000000000000000000000000000000000000000000000000000145b8061140e57507fffffffff0000000000000000000000000000000000000000000000000000000082167f89a9c85500000000000000000000000000000000000000000000000000000000145b8061145a57507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b92915050565b61146a815f612424565b50565b5f61149c60107f000000000000000000000000000000000000000000000000000004b6000029ee63ffffffff16565b6114b06104838c8c8b8d8c8c8b8b8b612b61565b6114bd8b87878d88612c59565b905080156114cf576114cf868a612d1c565b9a9950505050505050505050565b5f836114e881612d97565b61151660107f000000000000000000000000000000000000000000000000000004b6000029ee63ffffffff16565b6115298e8e8d8f8e8e8d8b8b8f8f612dde565b8c6115378f8b8b848c612c59565b92508215611549576115498a8e612d1c565b50509c9b505050505050505050505050565b5f61158a60027f000000000000000000000000000000000000000000000000000004b6000029ee63ffffffff16565b5f61159b876104838787875f612df4565b90506115aa8588888787612c59565b915081156115bf576115bf81885f8681612f7d565b5095945050505050565b6115d16130d0565b61146a816130d8565b7f000000000000000000000000721c0086cc4f335407cc84a38ce7dcb1560476b0365f80375f80365f845af43d805f803e8161161457805ffd5b805ff35b73ffffffffffffffffffffffffffffffffffffffff82165f90815262721c08602052604090205460609061165990610100900465ffffffffffff168361202c565b9392505050565b5f611671600333888888888861311d565b5090507fffffffff000000000000000000000000000000000000000000000000000000008116156116a5576116a58161320f565b335f90815260208490526040902060041c6116e38163ffffffff7f000000000000000000000000000000000000000000000000000003eb0000294116565b15611793575f6116f4826001615ab2565b90505f611724827f000000000000000000000000000000000000000000000000000003eb0000294163ffffffff16565b905080851115611760576040517ff9e7abe700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611790828683037f000000000000000000000000000000000000000000000000000003e40000293a63ffffffff16565b50505b50505050505050565b606081156116595773ffffffffffffffffffffffffffffffffffffffff84165f90815262721c0c602052604090208267ffffffffffffffff8111156117e3576117e3615aea565b60405190808252806020026020018201604052801561181657816020015b60608152602001906001900390816118015790505b5091505f5b838110156118f757815f86868481811061183757611837615b17565b9050602002013581526020019081526020015f20805461185690615b44565b80601f016020809104026020016040519081016040528092919081815260200182805461188290615b44565b80156118cd5780601f106118a4576101008083540402835291602001916118cd565b820191905f5260205f20905b8154815290600101906020018083116118b057829003601f168201915b50505050508382815181106118e4576118e4615b17565b602090810291909101015260010161181b565b50509392505050565b5f80611912600389898989898961311d565b507fffffffff00000000000000000000000000000000000000000000000000000000811615999098509650505050505050565b5f80333014611980576040517ffa103f6700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f61198a89613217565b9250905061199e818b8b8b8b8b8b8b6132d2565b92505097509795505050505050565b6119b8838383611f73565b505050565b6119b8600184848461333f565b5f806119dc600188888888885f61311d565b507fffffffff000000000000000000000000000000000000000000000000000000008116159890975095505050505050565b5f8082611a1a81613437565b611a4860407f000000000000000000000000000000000000000000000000000004b6000029ee63ffffffff16565b5f611a5b8f8f8f8f8f8f8e8e8e8e61347e565b9050611a6f818e8e8e8e8e8c612c596134a3565b90945092508215611a8857611a88818b88876001612f7d565b50509b509b9950505050505050505050565b611aa7600183835f613699565b5050565b335f8181526007602052604080822080546001019055517f8e8cebe67607ce50a14a2e3261437f641a7b33ecc053e3d9c90b25ae5e66c6569190a2565b5f80611afa6002888888888887613741565b915091509550959350505050565b5f83611b1381612d97565b611b4160207f000000000000000000000000000000000000000000000000000004b6000029ee63ffffffff16565b611b548d5f8d8f8e8e8d8b8b8f8f613802565b611b618d8a8a5f8b613817565b91508115611b7357611b73898d612d1c565b509b9a5050505050505050505050565b5f7f000000000000000000000000721c002d2cae3522602b93a0c48e11dc573a15e3365f80375f80365f845af43d805f803e8161161457805ffd5b5f80611bd06002878787875f8061311d565b507fffffffff0000000000000000000000000000000000000000000000000000000081161597909650945050505050565b611aa782825f6001613699565b8365ffffffffffff16421115611c50576040517f827aed8000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611c598b613947565b611c638389613999565b611cef611ce7611ce28d8d8d8c78ffffffffffffffffffffffffffffffffffffffffffffffffff168e8d8d65ffffffffffff168d65ffffffffffff1660075f8f73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f2054613a13565b613ac0565b838386613b07565b65ffffffffffff851615611d035784611d05565b425b9450611d168b8b8b8a89888c613c04565b5050505050505050505050565b7f000000000000000000000000721c002d2cae3522602b93a0c48e11dc573a15e3365f80375f80365f845af43d805f803e8161161457805ffd5b5f611d8c60087f000000000000000000000000000000000000000000000000000004b6000029ee63ffffffff16565b611da26102d18a8a60018b8b8b60018b8b612b61565b611dae85858b8b613cff565b90508015611dc057611dc08588612d1c565b98975050505050505050565b5f80611dde6003898989898989613741565b91509150965096945050505050565b611df5613dad565b611dfe5f613dfd565b565b73ffffffffffffffffffffffffffffffffffffffff81165f90815262721c096020526040902060609061145a90613e71565b7f000000000000000000000000000000000000000000000000000000000000000180611e7f57507fdacd49f6a6c42b45a5c3d195b83b324104542d9147bb8064a39c6a8d23ba9b005460ff165b15611eb6576040517ff45b98b000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611edf7f00000000000000000000000079a95940c17a9a8148633a11ff9d0ddcffd9c00d613e7d565b611f15576040517f70a4078f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fdacd49f6a6c42b45a5c3d195b83b324104542d9147bb8064a39c6a8d23ba9b0080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055611dfe80565b5f611f6e613ee8565b905090565b5f611f846002338686865f8061311d565b5090507fffffffff00000000000000000000000000000000000000000000000000000000811615611fb857611fb88161320f565b50505050565b5f611fed60017f000000000000000000000000000000000000000000000000000004b6000029ee63ffffffff16565b5f611fff866102d18686600180612df4565b905061200d86868686613cff565b915081156120235761202381875f600181612f7d565b50949350505050565b60ff81165f90815262721c0a6020908152604080832065ffffffffffff86168452909152902060609061165990600201613e71565b73ffffffffffffffffffffffffffffffffffffffff82165f90815262721c08602052604090205460609061165990610100900465ffffffffffff1683612525565b6120aa6130d0565b6120b35f6130d8565b8015611aa7575f8273ffffffffffffffffffffffffffffffffffffffff16826040515f6040518083038185875af1925050503d805f811461210f576040519150601f19603f3d011682016040523d82523d5f602084013e612114565b606091505b50509050806119b8576040517f3f1df14400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f89a9c85500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82811660048301525f917f000000000000000000000000e0a0004dfa318fc38298ae81a666710eadceba5c909116906389a9c85590602401602060405180830381865afa1580156121dd573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061145a9190615b8f565b611fb88484848461333f565b73ffffffffffffffffffffffffffffffffffffffff8381165f90815262721c08602090815260408083205460ff808816855262721c0a845282852061010090920465ffffffffffff168552908352818420948616845260049094019091528120549091165b949350505050565b3373ffffffffffffffffffffffffffffffffffffffff871614806122b357503373ffffffffffffffffffffffffffffffffffffffff8616145b6122e9576040517feda7110300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6122f284613947565b5f612303600388878787878c613fdc565b805490915060ff1661236b57600281556040516001815273ffffffffffffffffffffffffffffffffffffffff878116919089169084907f705db7ac401a8091bb37a7838ad73d1fa8e1c663cb345f347fefe71280e3f03b9060200160405180910390a4611793565b6040517f8cbf875200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f836123a881612d97565b6123d660087f000000000000000000000000000000000000000000000000000004b6000029ee63ffffffff16565b6123eb8c8c60018d8d8d60018b8b8f8f6140ad565b6123f788888e8e613cff565b9150811561240957612409888b612d1c565b509a9950505050505050505050565b611aa75f83838261333f565b611aa75f838382613699565b61243986613947565b61244886868685853389613c04565b505050505050565b61146a3382613999565b606081156116595773ffffffffffffffffffffffffffffffffffffffff84165f90815262721c0b602052604090208267ffffffffffffffff8111156124a1576124a1615aea565b6040519080825280602002602001820160405280156124ca578160200160208202803683370190505b5091505f5b838110156118f757815f8686848181106124eb576124eb615b17565b9050602002013581526020019081526020015f205483828151811061251257612512615b17565b60209081029190910101526001016124cf565b60ff81165f90815262721c0a6020908152604080832065ffffffffffff86168452909152902060609061165990613e71565b5f612568600133878787875f61311d565b5090507fffffffff0000000000000000000000000000000000000000000000000000000081161561259c5761259c8161320f565b5050505050565b5f80826125af81613437565b6125dd60807f000000000000000000000000000000000000000000000000000004b6000029ee63ffffffff16565b5f6125f08e8e8e8e5f8f8e8e8e8e6140c3565b9050612604818d8d5f8e8e8c6138176134a3565b9094509250821561261d5761261d818b88876001612f7d565b50509a509a98505050505050505050565b5f61265d60207f000000000000000000000000000000000000000000000000000004b6000029ee63ffffffff16565b61267060148b5f8b8d8c8c8b8b8b612b61565b61267d8a87875f88613817565b9050801561268f5761268f868a612d1c565b9998505050505050505050565b600160045f6040518060e0016040528060a58152602001615cf260a5913985856040516020016126ce93929190615bae565b6040516020818303038152906040528051906020012081526020019081526020015f205f6101000a81548160ff021916908315150217905550600160055f6040518060c00160405280609d8152602001615c55609d9139858560405160200161273993929190615bae565b604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe00181529181528151602092830120835290820192909252015f2080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169115159190911790555050565b6119b88383835f613699565b5f6127e760047f000000000000000000000000000000000000000000000000000004b6000029ee63ffffffff16565b5f6127f7866014865f875f612df4565b90506128068487875f87613817565b915081156120235761202381875f8681612f7d565b5f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0160ff83160161284d57505f919050565b5060ff165f90815262721c0e602052604090205473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff83165f90815262721c08602090815260408083205460ff808716855262721c0a845282852061010090920465ffffffffffff16855290835281842085855260050190925282205416612272565b6128e4613dad565b73ffffffffffffffffffffffffffffffffffffffff8116612931576040517f3e58254b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61146a81613dfd565b80825d5050565b5c90565b5f815d50565b7fdacd49f6a6c42b45a5c3d195b83b324104542d9147bb8064a39c6a8d23ba9b005460ff161561297c5780825d5050565b9055565b5f7fdacd49f6a6c42b45a5c3d195b83b324104542d9147bb8064a39c6a8d23ba9b005460ff16156129b057505c90565b5080545b919050565b7fdacd49f6a6c42b45a5c3d195b83b324104542d9147bb8064a39c6a8d23ba9b005460ff16156129e9575f815d50565b5f9055565b7f000000000000000000000000000000000000000000000000002386f26fc0ffff344703111561146a5760015481161561146a576040517fcc47725a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000000000000000000000000000002386f26fc0ffff34470311612aaf576040517ffd2c901300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80600154165f0361146a576040517ffd2c901300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f7f000000000000000000000000000000000000000000000000002386f26fc0ffff3447031115612b1c57506001545b90565b60015481161561146a576040517fcc47725a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60015490565b5f612c47611ce28c8c8c8c8c8c60075f8e73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f2054604080517f932b8553b8e35bbee682d275cbe1cf115e14a777e2ca3266b4797369fb6317d36020808301919091528183019990995273ffffffffffffffffffffffffffffffffffffffff979097166060880152608087019590955260a086019390935260c08501919091523360e085015261010084015261012080840191909152815180840390910181526101409092019052805191012090565b9050611d168787868b898689896140d8565b5f612c6a6104838787878787614162565b905080612d13576040517ff242432a00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff86811660048301528581166024830152604482018590526064820184905260a060848301525f60a483015287169063f242432a9060c4015f604051808303815f87803b158015612cfa575f80fd5b505af1925050508015612d0b575060015b612d13575060015b95945050505050565b73ffffffffffffffffffffffffffffffffffffffff82165f908152600660209081526040808320600885901c845290915290208054600160ff84161b908118918290551615611aa7576040517fe4adc0bf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f8181526004602052604090205460ff1661146a576040517f4722506e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611d166104838c8c8c8c8c8c8c8c8c8c8c61420e565b5f612e056002888888888633613fdc565b8054909150427a01000000000000000000000000000000000000000000000000000090910465ffffffffffff161015612e6a576040517f827aed8000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8054610100900478ffffffffffffffffffffffffffffffffffffffffffffffffff16831115612ec5576040517ffee142c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8115612ef65780547fffffffffffff00000000000000000000000000000000000000000000000000ff168155612f73565b805478ffffffffffffffffffffffffffffffffffffffffffffffffff61010090910481161015612f7357805478ffffffffffffffffffffffffffffffffffffffffffffffffff6101008083048216869003909116027fffffffffffff00000000000000000000000000000000000000000000000000ff9091161781555b9695505050505050565b811561259c57801561304e5784547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0078ffffffffffffffffffffffffffffffffffffffffffffffffff6101008084048216860190911602167fffffffffffff000000000000000000000000000000000000000000000000000090911617855560405182815273ffffffffffffffffffffffffffffffffffffffff85169084907f83e0ca2c1392f14286fa1e41c797789d48c5827572e8bcc352d8943c1961eaf09060200160405180910390a361259c565b845478ffffffffffffffffffffffffffffffffffffffffffffffffff6101009091048116101561259c57845478ffffffffffffffffffffffffffffffffffffffffffffffffff61010080830482168501909116027fffffffffffff00000000000000000000000000000000000000000000000000ff9091161785555050505050565b611dfe613dad565b600180549082905560408051828152602081018490527fe0d8d9ad73c586e8cf60ffd390b6f3654200a2d8857eb6abba4f6842a1210aae910160405180910390a15050565b5f803073ffffffffffffffffffffffffffffffffffffffff88160361314657505f905080613203565b6040517f27d21f50000000000000000000000000000000000000000000000000000000008152600481018a905273ffffffffffffffffffffffffffffffffffffffff808a166024830152808916604483015280881660648301528616608482015260a4810185905260c4810184905230906327d21f509060e4016040805180830381865afa1580156131da573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906131fe9190615bd4565b915091505b97509795505050505050565b805f5260045ffd5b73ffffffffffffffffffffffffffffffffffffffff81165f90815262721c0860205260408120805482919060ff90811610613274578054670100000000000000900473ffffffffffffffffffffffffffffffffffffffff166132a0565b805460ff165f90815262721c0e602052604090205473ffffffffffffffffffffffffffffffffffffffff165b905490947e0100000000000000000000000000000000000000000000000000000000000090910461ffff169350915050565b5f80630be200ea60e01b90506040518181528960048201528860248201528760448201528660648201528560848201528460a48201528360c482015260e4810160405260205f60e4838e5af490508061332e573d805f803e805ffd5b50505f519998505050505050505050565b82843361334d838383614241565b5f86815262721c06602052604090206133a1905b8873ffffffffffffffffffffffffffffffffffffffff167f000000000000000000000000000000000000000000000000000003e40000293a63ffffffff16565b5f86815260208690526040902060041c6133f58173ffffffffffffffffffffffffffffffffffffffff8a1663ffffffff7f000000000000000000000000000000000000000000000000000003e40000293a16565b61342d613403826001615ab2565b867f000000000000000000000000000000000000000000000000000003e40000293a63ffffffff16565b5050505050505050565b5f8181526005602052604090205460ff1661146a576040517f4722506e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6134948b8b8b6104838c8c8c8c8c8c8c61446c565b9b9a5050505050505050505050565b87546020880135905f90610100900478ffffffffffffffffffffffffffffffffffffffffffffffffff168211156134f9578954610100900478ffffffffffffffffffffffffffffffffffffffffffffffffff1691505b8860400135821015613537576040517fb9ff981500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b895478ffffffffffffffffffffffffffffffffffffffffffffffffff6101008083048216859003909116027fffffffffffff00000000000000000000000000000000000000000000000000ff909116178a55604051828152339073ffffffffffffffffffffffffffffffffffffffff88169086907f2203cb053e6b01ec07e87d67d288d360ae164171185684936663b7d8fa9c534c9060200160405180910390a48954610100900478ffffffffffffffffffffffffffffffffffffffffffffffffff165f036136795789547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001178a556040515f8152339073ffffffffffffffffffffffffffffffffffffffff88169086907f705db7ac401a8091bb37a7838ad73d1fa8e1c663cb345f347fefe71280e3f03b9060200160405180910390a45b61368a8887878a868863ffffffff16565b90509850989650505050505050565b8284336136a7838383614241565b5f86815262721c0660205260409020613728908873ffffffffffffffffffffffffffffffffffffffff16866136dc575f6136fe565b7f80000000000000000000000000000000000000000000000000000000000000005b177f000000000000000000000000000000000000000000000000000003e40000293a63ffffffff16565b61179361336187875f9182526020526040902060041c90565b5f805f6137538a8a898989898e613fdc565b8054909150427a01000000000000000000000000000000000000000000000000000090910465ffffffffffff16106137ac578054610100900478ffffffffffffffffffffffffffffffffffffffffffffffffff166137ae565b5f5b905478ffffffffffffffffffffffffffffffffffffffffffffffffff9091169a7a01000000000000000000000000000000000000000000000000000090910465ffffffffffff169950975050505050505050565b611d1660148c8c8c8c8c8c8c8c8c8c8c61420e565b5f61382760148787875f87614162565b905080612d13576040805173ffffffffffffffffffffffffffffffffffffffff87811660248301528681166044830152606480830186905283518084039091018152608490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f23b872dd0000000000000000000000000000000000000000000000000000000017905291515f928392908a16916138cc9190615c01565b5f604051808303815f865af19150503d805f8114613905576040519150601f19603f3d011682016040523d82523d5f602084013e61390a565b606091505b50915091508161391d576001925061393c565b80511561393c57808060200190518101906139389190615b8f565b1592505b505095945050505050565b6102d1811480613958575061048381145b806139635750601481145b61146a576040517f9d36a97900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff82165f908152600660209081526040808320600885901c845290915290208054600160ff84161b9081189182905516611aa7576040517fd979627300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080517ff9c04f8b028fcfa3315ea5accaee4589194a685f07cda0392e6ba9550706111960208201529081018a905273ffffffffffffffffffffffffffffffffffffffff808a1660608301526080820189905260a0820188905260c08201879052851660e08201526101008101849052610120810183905261014081018290525f90610160015b6040516020818303038152906040528051906020012090509998505050505050505050565b5f61145a613acc613ee8565b836040517f19010000000000000000000000000000000000000000000000000000000000008152600281019290925260228201526042902090565b6041829003613b85578235602084013560408501355f90811a9080613b2e8984878761464e565b915091508073ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff16141580613b6a5750815b15613b7b57613b7b868a8a8a614722565b5050505050611fb8565b6040829003613bf857823560208401355f80613ba28885856147b4565b915091508073ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff16141580613bde5750815b15613bef57613bef85898989614722565b50505050611fb8565b611fb881858585614722565b5f613c156002848a8a8a8688613fdc565b805460ff167a01000000000000000000000000000000000000000000000000000065ffffffffffff87169081027fffffffffffff00000000000000000000000000000000000000000000000000ff169190911761010078ffffffffffffffffffffffffffffffffffffffffffffffffff8916908102919091178355604080518a8152602081019290925281019190915290915073ffffffffffffffffffffffffffffffffffffffff83811691898216918616907f0ec867d4f1b037422566cd0248bae620e6c142dcf5631948271916e8ca8dd2639060600160405180910390a45050505050505050565b5f613d116102d1848787866001614162565b905080612272576040517f23b872dd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff86811660048301528581166024830152604482018490528416906323b872dd906064015f604051808303815f87803b158015613d8d575f80fd5b505af1925050508015613d9e575060015b61227257506001949350505050565b5f5473ffffffffffffffffffffffffffffffffffffffff163314611dfe576040517f021943c000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60605f611659836147fb565b5f8173ffffffffffffffffffffffffffffffffffffffff16600a5a613ea29190615c1c565b6040515f8181818686fa925050503d805f8114613eda576040519150601f19603f3d011682016040523d82523d5f602084013e613edf565b606091505b50909392505050565b5f7f000000000000000000000000000000000000000000000000000000000000008f4603613f3557507f388e51789c13d2bbb342d4135594d5cdcada31d3c67ba2f1c4c9d256705dca5490565b611f6e604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201527f12900c1b40e87470816fa5cb16069bf5b3df9cf052332bf21440c4318ccddcfe918101919091527fceebf77a833b30520287ddd9478ff51abbdffa30aa90a8d655dba0e8a79ce0c160608201524660808201523060a08201525f9060c00160405160208183030381529060405280519060200120905090565b5f875f614058898989898973ffffffffffffffffffffffffffffffffffffffff9485165f818152600760209081526040918290205482518083019490945283830197909752949096166060820152608081019290925260a082015260c0808201939093528351808203909301835260e001909252805191012090565b81526020019081526020015f205f8373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f209050979650505050505050565b611d166102d18c8c8c8c8c8c8c8c8c8c8c61420e565b5f6134948b8b8b60148c8c8c8c8c8c8c61446c565b86421115614112576040517fe3fd7ac300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8486111561414c576040517fde7fafeb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6141568489613999565b61342d83838387613b07565b5f805f6141866102d18a1461417857600361417b565b60015b89338a8a8a8a61311d565b909250905061ffff8116158061419f5750888161ffff16145b156141d0577fffffffff00000000000000000000000000000000000000000000000000000000821615159250614202565b6040517f7a535da600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50509695505050505050565b5f6142208d8d8d8d8b8e8e8a8a614854565b90506142328989888d8b868b8b6140d8565b50505050505050505050505050565b73ffffffffffffffffffffffffffffffffffffffff83165f90815262721c086020526040902080547b010000000000000000000000000000000000000000000000000000009004600116156142c2576040517fb1ae736700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80547b0100000000000000000000000000000000000000000000000000000090046002161561435a577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff73ffffffffffffffffffffffffffffffffffffffff84160161435a576040517fbf729bb100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8054610100900465ffffffffffff165f9081527f7e074e02264140693c3fbf3ca5a7e5aa542554b2d564345f76c58d7f98bdbbff6020908152604080832073ffffffffffffffffffffffffffffffffffffffff8616845260040190915290205460ff16611fb85780547b0100000000000000000000000000000000000000000000000000000090046008161580614435575073ffffffffffffffffffffffffffffffffffffffff82165f9081527f442599b958bf0d8fbe0ddbf2d50f16197374228cad346cd58d2da01fd8e057f8602052604090205460ff16155b15611fb8576040517fbc8ea06200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f78ffffffffffffffffffffffffffffffffffffffffffffffffff8a3511156144c1576040517f3b0a334300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6144d16003878b8b8b8833613fdc565b805490915060ff1661236b578054610100900478ffffffffffffffffffffffffffffffffffffffffffffffffff165f036145ed5761452a6145228a8a8a8e358b8b65ffffffffffff8c168b8b614854565b8d8d89613b07565b805460ff166101008b3578ffffffffffffffffffffffffffffffffffffffffffffffffff81169190910279ffffffffffffffffffffffffffffffffffffffffffffffffffff16919091177a01000000000000000000000000000000000000000000000000000065ffffffffffff871602178255604051908152339073ffffffffffffffffffffffffffffffffffffffff88169085907f257001e1f7fbfc5bbde5da225c876ab67293f37bda3afb8b35d9a55dfad6f65d9060200160405180910390a45b80547a010000000000000000000000000000000000000000000000000000900465ffffffffffff16421115613494576040517fe3fd7ac300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08311156146835750600190505f614719565b604080515f81526020810180835288905260ff871691810191909152606081018590526080810184905260019060a0016020604051602081039080840390855afa1580156146d3573d5f803e3d5ffd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015173ffffffffffffffffffffffffffffffffffffffff81161593509150505b94509492505050565b8373ffffffffffffffffffffffffffffffffffffffff163b5f03614772576040517f73c919b500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61477e8484848461488d565b611fb8576040517f73c919b500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f807f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8316601b60ff85901c016147ed8782888561464e565b909890975095505050505050565b6060815f0180548060200260200160405190810160405280929190818152602001828054801561484857602002820191905f5260205f20905b815481526020019060010190808311614834575b50505050509050919050565b73ffffffffffffffffffffffffffffffffffffffff85165f90815260076020526040812054613494611ce28c8c8c8c8b8b8b8b8a61493b565b5f61492f565b5f6040517f1626ba7e0000000000000000000000000000000000000000000000000000000081528360048201526040602482015285604482015285856064830137601f19601f87011660640180820160405260205f8284875afa60203d10151615614925577f1626ba7e000000000000000000000000000000000000000000000000000000005f511492505050612272565b5050949350505050565b612d1382848688614893565b60408051602081018490529081018a905273ffffffffffffffffffffffffffffffffffffffff891660608201526080810188905260a0810187905260c081018690523360e08201526101008101859052610120810182905261014081018490525f9061016001613a9b565b7fffffffff000000000000000000000000000000000000000000000000000000008116811461146a575f80fd5b5f602082840312156149e3575f80fd5b8135611659816149a6565b803573ffffffffffffffffffffffffffffffffffffffff811681146129b4575f80fd5b5f60208284031215614a21575f80fd5b611659826149ee565b5f8083601f840112614a3a575f80fd5b50813567ffffffffffffffff811115614a51575f80fd5b602083019150836020828501011115614a68575f80fd5b9250929050565b5f805f805f805f805f806101208b8d031215614a89575f80fd5b614a928b6149ee565b995060208b0135985060408b0135975060608b0135965060808b01359550614abc60a08c016149ee565b9450614aca60c08c016149ee565b935060e08b013592506101008b013567ffffffffffffffff811115614aed575f80fd5b614af98d828e01614a2a565b915080935050809150509295989b9194979a5092959850565b5f805f805f805f805f805f806101608d8f031215614b2e575f80fd5b614b378d6149ee565b9b5060208d01359a5060408d0135995060608d0135985060808d01359750614b6160a08e016149ee565b9650614b6f60c08e016149ee565b955060e08d013594506101008d013593506101208d0135925067ffffffffffffffff6101408e01351115614ba1575f80fd5b614bb28e6101408f01358f01614a2a565b81935080925050509295989b509295989b509295989b565b5f805f805f60a08688031215614bde575f80fd5b614be7866149ee565b9450614bf5602087016149ee565b9350614c03604087016149ee565b94979396509394606081013594506080013592915050565b5f60208284031215614c2b575f80fd5b5035919050565b803560ff811681146129b4575f80fd5b5f8060408385031215614c53575f80fd5b614c5c83614c32565b9150614c6a602084016149ee565b90509250929050565b5f8060408385031215614c84575f80fd5b614c8d836149ee565b9150614c6a60208401614c32565b602080825282518282018190525f9190848201906040850190845b81811015614cd257835183529284019291840191600101614cb6565b50909695505050505050565b5f8083601f840112614cee575f80fd5b50813567ffffffffffffffff811115614d05575f80fd5b6020830191508360208260051b8501011115614a68575f80fd5b5f805f60408486031215614d31575f80fd5b614d3a846149ee565b9250602084013567ffffffffffffffff811115614d55575f80fd5b614d6186828701614cde565b9497909650939450505050565b5f5b83811015614d88578181015183820152602001614d70565b50505f910152565b5f602080830181845280855180835260408601915060408160051b87010192508387015f5b82811015614e37577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc088860301845281518051808752614dfa818989018a8501614d6e565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01695909501860194509285019290850190600101614db5565b5092979650505050505050565b5f805f805f8060c08789031215614e59575f80fd5b614e62876149ee565b9550614e70602088016149ee565b9450614e7e604088016149ee565b9350614e8c606088016149ee565b92506080870135915060a087013590509295509295509295565b5f8060408385031215614eb7575f80fd5b614ec0836149ee565b946020939093013593505050565b5f805f805f805f60e0888a031215614ee4575f80fd5b87359650614ef4602089016149ee565b9550614f02604089016149ee565b9450614f10606089016149ee565b9350614f1e608089016149ee565b925060a0880135915060c0880135905092959891949750929550565b5f805f60608486031215614f4c575f80fd5b614f55846149ee565b9250614f63602085016149ee565b9150614f71604085016149ee565b90509250925092565b5f805f60608486031215614f8c575f80fd5b614f95846149ee565b95602085013595506040909401359392505050565b5f805f805f60a08688031215614fbe575f80fd5b614fc7866149ee565b9450614fd5602087016149ee565b9350614fe3604087016149ee565b9250614ff1606087016149ee565b949793965091946080013592915050565b5f60608284031215615012575f80fd5b50919050565b803565ffffffffffff811681146129b4575f80fd5b5f805f805f805f805f805f6101808c8e031215615048575f80fd5b8b3567ffffffffffffffff81111561505e575f80fd5b61506a8e828f01614a2a565b909c509a5061507e90508d60208e01615002565b985061508c60808d016149ee565b975060a08c013596506150a160c08d016149ee565b95506150af60e08d016149ee565b94506101008c013593506150c66101208d01615018565b92506101408c013591506101608c013590509295989b509295989b9093969950565b5f805f805f60a086880312156150fc575f80fd5b615105866149ee565b9450615113602087016149ee565b935060408601359250614ff1606087016149ee565b5f805f805f805f805f805f6101408c8e031215615143575f80fd5b61514c8c6149ee565b9a5060208c0135995060408c0135985060608c0135975061516f60808d016149ee565b965061517d60a08d016149ee565b955060c08c0135945060e08c013593506101008c013592506101208c013567ffffffffffffffff8111156151af575f80fd5b6151bb8e828f01614a2a565b915080935050809150509295989b509295989b9093969950565b5f805f606084860312156151e7575f80fd5b6151f084615018565b9250614f6360208501614c32565b5f805f805f60608688031215615212575f80fd5b853567ffffffffffffffff80821115615229575f80fd5b61523589838a01614a2a565b909750955085915061524960208901615018565b9450604088013591508082111561525e575f80fd5b5061526b88828901614cde565b969995985093965092949392505050565b5f805f806080858703121561528f575f80fd5b615298856149ee565b93506152a6602086016149ee565b92506152b4604086016149ee565b91506152c2606086016149ee565b905092959194509250565b5f80604083850312156152de575f80fd5b614c5c836149ee565b803578ffffffffffffffffffffffffffffffffffffffffffffffffff811681146129b4575f80fd5b5f805f805f805f805f805f6101408c8e03121561532a575f80fd5b8b359a5061533a60208d016149ee565b995060408c0135985060608c0135975061535660808d016152e7565b965061536460a08d016149ee565b955061537260c08d01615018565b945061538060e08d01615018565b935061538f6101008d016149ee565b92506101208c013567ffffffffffffffff8111156151af575f80fd5b5f805f805f606086880312156153bf575f80fd5b6153c8866149ee565b9450602086013567ffffffffffffffff808211156153e4575f80fd5b818801915088601f8301126153f7575f80fd5b813581811115615405575f80fd5b8960208260061b8501011115615419575f80fd5b60208301965080955050604088013591508082111561525e575f80fd5b5f805f805f805f8060e0898b03121561544d575f80fd5b615456896149ee565b975060208901359650604089013595506060890135945061547960808a016149ee565b935061548760a08a016149ee565b925060c089013567ffffffffffffffff8111156154a2575f80fd5b6154ae8b828c01614a2a565b999c989b5096995094979396929594505050565b5f805f80606085870312156154d5575f80fd5b6154de85615018565b93506154ec60208601614c32565b9250604085013567ffffffffffffffff811115615507575f80fd5b61551387828801614cde565b95989497509550505050565b5f805f805f8060c08789031215615534575f80fd5b61553d876149ee565b955061554b602088016149ee565b945060408701359350614e8c606088016149ee565b602080825282518282018190525f9190848201906040850190845b81811015614cd257835173ffffffffffffffffffffffffffffffffffffffff168352928401929184019160010161557b565b5f805f80608085870312156155c0575f80fd5b6155c9856149ee565b93506155d7602086016149ee565b92506155e5604086016149ee565b9396929550929360600135925050565b5f8060408385031215615606575f80fd5b614c8d83615018565b5f805f8060808587031215615622575f80fd5b61562b856149ee565b9350615639602086016149ee565b93969395505050506040820135916060013590565b5f806040838503121561565f575f80fd5b615668836149ee565b9150614c6a60208401615018565b5f805f60408486031215615688575f80fd5b833567ffffffffffffffff81111561569e575f80fd5b6156aa86828701614a2a565b9094509250614f71905060208501615018565b5f805f606084860312156156cf575f80fd5b6151f0846149ee565b5f805f805f805f805f806101208b8d0312156156f2575f80fd5b6156fb8b6149ee565b995060208b0135985060408b0135975060608b0135965061571e60808c016149ee565b955061572c60a08c016149ee565b945060c08b0135935060e08b013592506101008b013567ffffffffffffffff811115614aed575f80fd5b5f8060208385031215615767575f80fd5b823567ffffffffffffffff81111561577d575f80fd5b61578985828601614a2a565b90969095509350505050565b5f805f805f8060c087890312156157aa575f80fd5b863595506157ba602088016149ee565b9450604087013593506157cf606088016149ee565b92506157dd608088016152e7565b91506157eb60a08801615018565b90509295509295509295565b61ffff8116811461146a575f80fd5b5f805f805f60a0868803121561581a575f80fd5b615823866149ee565b945061583160208701614c32565b935061583f604087016149ee565b925061584d60608701614c32565b9150608086013561585d816157f7565b809150509295509295909350565b5f805f6060848603121561587d575f80fd5b61588684615018565b925061589460208501614c32565b9150604084013590509250925092565b5f80604083850312156158b5575f80fd5b614c5c83615018565b5f805f805f805f805f806101608b8d0312156158d8575f80fd5b8a3567ffffffffffffffff8111156158ee575f80fd5b6158fa8d828e01614a2a565b909b50995061590e90508c60208d01615002565b975061591c60808c016149ee565b965061592a60a08c016149ee565b955061593860c08c016149ee565b945060e08b0135935061594e6101008c01615018565b92506101208b013591506101408b013590509295989b9194979a5092959850565b5f805f805f805f805f6101008a8c031215615988575f80fd5b6159918a6149ee565b985060208a0135975060408a0135965060608a013595506159b460808b016149ee565b94506159c260a08b016149ee565b935060c08a0135925060e08a013567ffffffffffffffff8111156159e4575f80fd5b6159f08c828d01614a2a565b915080935050809150509295985092959850929598565b5f805f60608486031215615a19575f80fd5b615a22846149ee565b9250615894602085016149ee565b5f60208284031215615a40575f80fd5b61165982614c32565b5f805f60608486031215615a5b575f80fd5b615886846149ee565b5f60208284031215615a74575f80fd5b61165982615018565b5f8060408385031215615a8e575f80fd5b615a97836149ee565b91506020830135615aa7816157f7565b809150509250929050565b8082018082111561145a577f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b600181811c90821680615b5857607f821691505b602082108103615012577f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b5f60208284031215615b9f575f80fd5b81518015158114611659575f80fd5b5f8451615bbf818460208901614d6e565b8201838582375f930192835250909392505050565b5f8060408385031215615be5575f80fd5b8251615bf0816149a6565b6020840151909250615aa7816157f7565b5f8251615c12818460208701614d6e565b9190910192915050565b5f82615c4f577f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b50049056fe5065726d69744f72646572576974684164646974696f6e616c446174612875696e7432353620746f6b656e547970652c6164647265737320746f6b656e2c75696e743235362069642c75696e7432353620616d6f756e742c75696e743235362073616c742c61646472657373206f70657261746f722c75696e743235362065787069726174696f6e2c75696e74323536206d61737465724e6f6e63652c5065726d69745472616e7366657246726f6d576974684164646974696f6e616c446174612875696e7432353620746f6b656e547970652c6164647265737320746f6b656e2c75696e743235362069642c75696e7432353620616d6f756e742c75696e74323536206e6f6e63652c61646472657373206f70657261746f722c75696e743235362065787069726174696f6e2c75696e74323536206d61737465724e6f6e63652ca26469706673582212203c722515cb1b94dd8d752f9b83ae90dcbec2eb559da6ba5732cbef23b095379164736f6c63430008180033

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

000000000000000000000000591aa9dff01b8144dc17cb416001d9ac84b951cd000000000000000000000000e0a0004dfa318fc38298ae81a666710eadceba5c000000000000000000000000721c002d2cae3522602b93a0c48e11dc573a15e3000000000000000000000000721c0086cc4f335407cc84a38ce7dcb1560476b000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000001d43726561746f72546f6b656e5472616e7366657256616c696461746f7200000000000000000000000000000000000000000000000000000000000000000000013500000000000000000000000000000000000000000000000000000000000000

-----Decoded View---------------
Arg [0] : defaultOwner (address): 0x591Aa9dfF01B8144DC17Cb416001D9aC84b951cd
Arg [1] : eoaRegistry_ (address): 0xE0A0004Dfa318fc38298aE81a666710eaDCEba5C
Arg [2] : managementModule_ (address): 0x721C002d2CAe3522602b93a0c48e11dC573A15E3
Arg [3] : safeDelegateModule_ (address): 0x721C0086CC4f335407cC84a38cE7dcb1560476B0
Arg [4] : name (string): CreatorTokenTransferValidator
Arg [5] : version (string): 5

-----Encoded View---------------
10 Constructor Arguments found :
Arg [0] : 000000000000000000000000591aa9dff01b8144dc17cb416001d9ac84b951cd
Arg [1] : 000000000000000000000000e0a0004dfa318fc38298ae81a666710eadceba5c
Arg [2] : 000000000000000000000000721c002d2cae3522602b93a0c48e11dc573a15e3
Arg [3] : 000000000000000000000000721c0086cc4f335407cc84a38ce7dcb1560476b0
Arg [4] : 00000000000000000000000000000000000000000000000000000000000000c0
Arg [5] : 0000000000000000000000000000000000000000000000000000000000000100
Arg [6] : 000000000000000000000000000000000000000000000000000000000000001d
Arg [7] : 43726561746f72546f6b656e5472616e7366657256616c696461746f72000000
Arg [8] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [9] : 3500000000000000000000000000000000000000000000000000000000000000


Deployed Bytecode Sourcemap

1178:59377:30:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;48555:434;;;;;;;;;;-1:-1:-1;48555:434:30;;;;;:::i;:::-;;:::i;:::-;;;611:14:36;;604:22;586:41;;574:2;559:18;48555:434:30;;;;;;;;17364:107;;;;;;;;;;-1:-1:-1;17364:107:30;;;;;:::i;:::-;;:::i;:::-;;21865:670:17;;;;;;;;;;-1:-1:-1;21865:670:17;;;;;:::i;:::-;;:::i;24672:1085::-;;;;;;;;;;-1:-1:-1;24672:1085:17;;;;;:::i;:::-;;:::i;45782:558::-;;;;;;;;;;-1:-1:-1;45782:558:17;;;;;:::i;:::-;;:::i;8427:134:13:-;;;;;;:::i;:::-;;:::i;7076:100:30:-;;;;;;;;;;-1:-1:-1;7076:100:30;;;;;:::i;:::-;;:::i;44506:233::-;;;;;;;;;;-1:-1:-1;44506:233:30;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;11215:916::-;;;;;;;;;;-1:-1:-1;11215:916:30;;;;;:::i;:::-;;:::i;41379:545::-;;;;;;;;;;-1:-1:-1;41379:545:30;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;25184:443::-;;;;;;;;;;-1:-1:-1;25184:443:30;;;;;:::i;:::-;;:::i;:::-;;;;8493:14:36;;8486:22;8468:41;;8557:66;8545:79;;;8540:2;8525:18;;8518:107;8441:18;25184:443:30;8302:329:36;42005:203:17;;;;;;;;;;-1:-1:-1;42005:203:17;;;;;:::i;:::-;42125:23;;;;42089:12;42125:23;;;:16;:23;;;;;;;;42166:1;42157:10;;;42125:44;;;;;;;;:60;;;;;257:1:14;42124:68:17;42123:78;;42005:203;39429:106:30;;;;;;;;;;-1:-1:-1;4767:66:33;39499:29:30;;;39429:106;;;9069:14:36;9057:27;;;9039:46;;9027:2;9012:18;39429:106:30;8895:196:36;47466:858:30;;;;;;;;;;-1:-1:-1;47466:858:30;;;;;:::i;:::-;;:::i;:::-;;;;9917:66:36;9905:79;;;9887:98;;10033:6;10021:19;;;10016:2;10001:18;;9994:47;9860:18;47466:858:30;9717:330:36;13072:146:30;;;;;;;;;;-1:-1:-1;13072:146:30;;;;;:::i;:::-;;:::i;19627:199::-;;;;;;;;;;-1:-1:-1;19627:199:30;;;;;:::i;:::-;;:::i;24047:402::-;;;;;;;;;;-1:-1:-1;24047:402:30;;;;;:::i;:::-;;:::i;34140:1232:17:-;;;;;;;;;;-1:-1:-1;34140:1232:17;;;;;:::i;:::-;;:::i;:::-;;;;12845:25:36;;;12913:14;;12906:22;12901:2;12886:18;;12879:50;12818:18;34140:1232:17;12677:258:36;18396:172:30;;;;;;;;;;-1:-1:-1;18396:172:30;;;;;:::i;:::-;;:::i;42477:143:17:-;;;;;;;;;;;;;:::i;13192:320::-;;;;;;;;;;-1:-1:-1;13192:320:17;;;;;:::i;:::-;;:::i;:::-;;;;13591:25:36;;;13647:2;13632:18;;13625:34;;;;13564:18;13192:320:17;13417:248:36;30017:962:17;;;;;;;;;;-1:-1:-1;30017:962:17;;;;;:::i;:::-;;:::i;43099:189:30:-;;;;;;;;;;-1:-1:-1;43099:189:30;;;;;:::i;:::-;43212:34;;;;43189:4;43212:34;;;:24;:34;;;;;;;;:38;;;;;;;;;;;;;:69;;;;;;;:60;;;;:69;;;;;;;;;;43099:189;28719:158;;;;;;;;;;-1:-1:-1;28719:158:30;;;;;:::i;:::-;;:::i;23009:376::-;;;;;;;;;;-1:-1:-1;23009:376:30;;;;;:::i;:::-;;:::i;16423:149::-;;;;;;;;;;-1:-1:-1;16423:149:30;;;;;:::i;:::-;;:::i;11182:1326:17:-;;;;;;;;;;-1:-1:-1;11182:1326:17;;;;;:::i;:::-;;:::i;32381:220:30:-;;;;;;;;;;-1:-1:-1;32381:220:30;;;;;:::i;:::-;;:::i;16967:568:17:-;;;;;;;;;;-1:-1:-1;16967:568:17;;;;;:::i;:::-;;:::i;38094:165:30:-;;;;;;;;;;-1:-1:-1;38094:165:30;;;;;:::i;40812:338:17:-;;;;;;;;;;-1:-1:-1;40812:338:17;;;;;:::i;:::-;;:::i;1938:101:21:-;;;;;;;;;;;;;:::i;34538:137:30:-;;;;;;;;;;-1:-1:-1;34538:137:30;;;;;:::i;44921:197::-;;;;;;;;;;-1:-1:-1;44921:197:30;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;4954:532:25:-;;;;;;;;;;;;;:::i;51854:132:17:-;;;;;;;;;;;;;:::i;:::-;;;22820:25:36;;;22808:2;22793:18;51854:132:17;22674:177:36;8358:328:30;;;;;;;;;;-1:-1:-1;8358:328:30;;;;;:::i;:::-;;:::i;44149:515:17:-;;;;;;;;;;-1:-1:-1;44149:515:17;;;;;:::i;:::-;;:::i;31240:167::-;;;;;;;;;;-1:-1:-1;31240:167:17;;;;;:::i;:::-;31325:17;31369:31;;;:25;:31;;;;;;;;;31240:167;42571:185:30;;;;;;;;;;-1:-1:-1;42571:185:30;;;;;:::i;:::-;;:::i;44036:229::-;;;;;;;;;;-1:-1:-1;44036:229:30;;;;;:::i;:::-;;:::i;9506:334:13:-;;;;;;;;;;-1:-1:-1;9506:334:13;;;;;:::i;:::-;;:::i;47037:140:30:-;;;;;;;;;;-1:-1:-1;47037:140:30;;;;;:::i;:::-;;:::i;20992:200::-;;;;;;;;;;-1:-1:-1;20992:200:30;;;;;:::i;:::-;;:::i;1321:85:21:-;;;;;;;;;;-1:-1:-1;1367:7:21;1393:6;;;1321:85;;;24286:42:36;24274:55;;;24256:74;;24244:2;24229:18;1321:85:21;24110:226:36;33830:103:30;;;;;;;;;;-1:-1:-1;33830:103:30;;;;;:::i;27560:130::-;;;;;;;;;;-1:-1:-1;27560:130:30;;;;;:::i;45514:243::-;;;;;;;;;;-1:-1:-1;45514:243:30;;;;;:::i;:::-;;:::i;46699:204::-;;;;;;;;;;-1:-1:-1;46699:204:30;;;;;:::i;:::-;46820:45;;;;46797:4;46820:45;;;:33;:45;;;;;;;;:76;;;;;;:67;;;;:76;;;;;;;;46699:204;39400:843:17;;;;;;;;;;-1:-1:-1;39400:843:17;;;;;:::i;:::-;;:::i;19514:883::-;;;;;;;;;;-1:-1:-1;19514:883:17;;;;;:::i;:::-;;:::i;21971:191:30:-;;;;;;;;;;-1:-1:-1;21971:191:30;;;;;:::i;:::-;;:::i;15198:183::-;;;;;;;;;;-1:-1:-1;15198:183:30;;;;;:::i;:::-;;:::i;26417:103::-;;;;;;;;;;-1:-1:-1;26417:103:30;;;;;:::i;40150:208::-;;;;;;;;;;-1:-1:-1;40150:208:30;;;;;:::i;:::-;-1:-1:-1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;40294:57:30;;;;;;:45;:57;;;;;;40287:64;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;40150:208;;;;;;;26999:4:36;27041:3;27030:9;27026:19;27018:27;;27091:4;27082:6;27076:13;27072:24;27061:9;27054:43;27165:14;27157:4;27149:6;27145:17;27139:24;27135:45;27128:4;27117:9;27113:20;27106:75;27249:42;27241:4;27233:6;27229:17;27223:24;27219:73;27212:4;27201:9;27197:20;27190:103;27361:4;27353;27345:6;27341:17;27335:24;27331:35;27324:4;27313:9;27309:20;27302:65;27414:4;27406:6;27402:17;27396:24;27439:6;27501:2;27487:12;27483:21;27476:4;27465:9;27461:20;27454:51;27573:2;27565:4;27557:6;27553:17;27547:24;27543:33;27536:4;27525:9;27521:20;27514:63;;;26823:760;;;;;9000:321:17;;;;;;;;;;-1:-1:-1;9000:321:17;;;;;:::i;:::-;;:::i;31364:226:30:-;;;;;;;;;;-1:-1:-1;31364:226:30;;;;;:::i;41641:118:17:-;;;;;;;;;;-1:-1:-1;41641:118:17;;;;;:::i;:::-;;:::i;43605:194:30:-;;;;;;;;;;-1:-1:-1;43605:194:30;;;;;:::i;:::-;43720:34;;;;43697:4;43720:34;;;:24;:34;;;;;;;;:38;;;;;;;;;;;;;:72;;;:24;:62;;;:72;;;;;;;;;;43605:194;40594:547;;;;;;;;;;-1:-1:-1;40594:547:30;;;;;:::i;:::-;;:::i;42155:181::-;;;;;;;;;;-1:-1:-1;42155:181:30;;;;;:::i;:::-;;:::i;6174:87::-;;;;;;;;;;-1:-1:-1;6174:87:30;;;;;:::i;29368:103::-;;;;;;;;;;-1:-1:-1;29368:103:30;;;;;:::i;9685:346::-;;;;;;;;;;-1:-1:-1;9685:346:30;;;;;:::i;:::-;;:::i;38607:145::-;;;;;;;;;;-1:-1:-1;38607:145:30;;;;;:::i;:::-;38699:46;;38676:4;38699:46;;;:37;:46;;;;;;;;;38607:145;37432:1208:17;;;;;;;;;;-1:-1:-1;37432:1208:17;;;;;:::i;:::-;;:::i;27235:646::-;;;;;;;;;;-1:-1:-1;27235:646:17;;;;;:::i;:::-;;:::i;14814:704::-;;;;;;;;;;-1:-1:-1;14814:704:17;;;;;:::i;:::-;;:::i;14246:173:30:-;;;;;;;;;;-1:-1:-1;14246:173:30;;;;;:::i;:::-;;:::i;47406:534:17:-;;;;;;;;;;-1:-1:-1;47406:534:17;;;;;:::i;:::-;;:::i;42798:112::-;;;;;;;;;;-1:-1:-1;42798:112:17;;;;;:::i;:::-;42883:20;;42857:7;42883:20;;;:13;:20;;;;;;;42798:112;38869:237:30;;;;;;;;;;-1:-1:-1;38869:237:30;;;;;:::i;:::-;;:::i;46138:247::-;;;;;;;;;;-1:-1:-1;46138:247:30;;;;;:::i;:::-;;:::i;2188:191:21:-;;;;;;;;;;-1:-1:-1;2188:191:21;;;;;:::i;:::-;;:::i;29917:85:30:-;;;;;;;;;;-1:-1:-1;29917:85:30;;;;;:::i;10156:308:13:-;;;;;;;;;;-1:-1:-1;10402:1:13;10434:13;;10371:28;:32;;;;10156:308;;33117:138:30;;;;;;;;;;-1:-1:-1;33117:138:30;;;;;:::i;39616:120::-;;;;;;;;;;-1:-1:-1;39616:120:30;;;;;:::i;:::-;39696:33;;39670:7;39696:33;;;:29;:33;;;;;;;;;39616:120;31665:161:17;;;;;;;;;;-1:-1:-1;31665:161:17;;;;;:::i;:::-;31747:17;31791:28;;;:22;:28;;;;;;;;;31665:161;48555:434:30;48657:4;48692:53;;;;;:120;;-1:-1:-1;48761:51:30;;;48776:36;48761:51;48692:120;:177;;;-1:-1:-1;48828:41:30;;;48843:26;48828:41;48692:177;:238;;;-1:-1:-1;48885:45:30;;;48900:30;48885:45;48692:238;:290;;;-1:-1:-1;747:25:22;732:40;;;;48946:36:30;48673:309;48555:434;-1:-1:-1;;48555:434:30:o;17364:107::-;17431:33;17455:5;17462:1;17431:23;:33::i;:::-;17364:107;:::o;21865:670:17:-;22160:12;22184:59;3140:6:14;22184:17:17;:59;;:::i;:::-;22254:121;838:4:14;22295:5:17;22302:2;22306:12;22320:5;22327:10;22339:5;22346:14;22362:12;;22254:20;:121::i;:::-;22395:58;22416:5;22423;22430:2;22434;22438:14;22395:20;:58::i;:::-;22385:68;;22468:7;22464:65;;;22491:27;22505:5;22512;22491:13;:27::i;:::-;21865:670;;;;;;;;;;;;:::o;24672:1085::-;25112:12;25083:18;7336:66;7383:18;7336:46;:66::i;:::-;25136:59:::1;3140:6:14;25136:17:17;:59;;:::i;:::-;25206:292;25265:5;25284:2;25300:12;25326:5;25345:10;25369:5;25388:14;25416:12;;25442:14;25470:18;25206:45;:292::i;:::-;25592:2:::0;25614:61:::1;25635:5:::0;25642;25649:2;25592;25660:14;25614:20:::1;:61::i;:::-;25604:71;;25690:7;25686:65;;;25713:27;25727:5;25734;25713:13;:27::i;:::-;25126:631;24672:1085:::0;;;;;;;;;;;;;;;:::o;45782:558::-;45945:12;45969:58;2725:6:14;45969:17:17;:58;;:::i;:::-;46038:31;46072:76;46096:5;838:4:14;46123:5:17;46130:2;46134:6;46142:5;46072:23;:76::i;:::-;46038:110;;46168:50;46189:5;46196;46203:2;46207;46211:6;46168:20;:50::i;:::-;46158:60;;46233:7;46229:105;;;46256:67;46278:8;46288:5;144:1:14;46309:6:17;144:1:14;46256:21:17;:67::i;:::-;45959:381;45782:558;;;;;;;:::o;8427:134:13:-;7797:35;:33;:35::i;:::-;8521:33:::1;8539:14;8521:17;:33::i;7076:100:30:-:0;7153:19;4545:14;4542:1;4539;4526:34;4637:1;4634;4618:14;4615:1;4607:6;4600:5;4587:52;4664:16;4714:4;4711:1;4708;4693:26;4742:6;4732:65;;4778:4;4775:1;4768:15;4732:65;4820:4;4817:1;4810:15;44506:233;44657:57;;;;;;;:45;:57;;;;;:64;44604:16;;44639:93;;44657:64;;;;;44723:8;44639:17;:93::i;:::-;44632:100;44506:233;-1:-1:-1;;;44506:233:30:o;11215:916::-;11336:20;11361:104;1400:1:29;11419:10:30;11431:6;11439:4;11445:2;11449:7;11458:6;11361:17;:104::i;:::-;-1:-1:-1;11335:130:30;-1:-1:-1;11479:34:30;;;;11475:109;;11529:44;11559:13;11529:29;:44::i;:::-;11655:10;11594:32;2233:24:33;;;2277:4;2270:21;;;2343:4;2327:21;;2324:1;2320:29;11689:38:30;2320:29:33;11689:38:30;:12;:38;:::i;:::-;:43;11685:440;;11748:18;11769:28;:24;11796:1;11769:28;:::i;:::-;11748:49;;11811:24;11838;11851:10;11838:12;:24;;:::i;:::-;11811:51;;11890:16;11881:6;:25;11877:130;;;11933:59;;;;;;;;;;;;;;11877:130;12049:51;12062:10;12093:6;12074:16;:25;12049:12;:51;;:::i;:::-;11734:391;;11685:440;11325:806;;11215:916;;;;;:::o;41379:545::-;41508:21;41544:15;;41541:377;;41661:58;;;41575:66;41661:58;;;:44;:58;;;;;41755:4;41743:24;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;41734:33;;41786:9;41781:127;41801:15;;;41781:127;;;41853:31;:40;41885:4;;41890:1;41885:7;;;;;;;:::i;:::-;;;;;;;41853:40;;;;;;;;;;;41841:52;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:6;41848:1;41841:9;;;;;;;;:::i;:::-;;;;;;;;;;:52;41818:3;;41781:127;;;;41561:357;41379:545;;;;;:::o;25184:443::-;25389:22;25413:16;25456:104;1400:1:29;25514:10:30;25526:6;25534:4;25540:2;25544:7;25553:6;25456:17;:104::i;:::-;-1:-1:-1;25590:30:30;;;;;25441:119;;-1:-1:-1;25184:443:30;-1:-1:-1;;;;;;;25184:443:30:o;47466:858::-;47714:20;;47769:10;47791:4;47769:27;47765:132;;47819:67;;;;;;;;;;;;;;47765:132;47907:30;48019:53;48061:10;48019:41;:53::i;:::-;47947:125;-1:-1:-1;47947:125:30;-1:-1:-1;48099:218:30;47947:125;48169:19;48202:10;48226:6;48246:4;48264:2;48280:7;48301:6;48099:20;:218::i;:::-;48083:234;;47754:570;47466:858;;;;;;;;;;:::o;13072:146::-;13177:34;13194:6;13202:4;13208:2;13177:16;:34::i;:::-;13072:146;;;:::o;19627:199::-;19738:81;2007:4:29;19796:5:30;19803:7;19812:6;19738:30;:81::i;24047:402::-;24227:22;24251:16;24294:88;1284:1:29;24341:10:30;24353:6;24361:4;24367:2;24371:7;24380:1;24294:17;:88::i;:::-;-1:-1:-1;24412:30:30;;;;;24279:103;;-1:-1:-1;24047:402:30;-1:-1:-1;;;;;;24047:402:30:o;34140:1232:17:-;34540:22;34564:12;34511:18;7509:63;7553:18;7509:43;:63::i;:::-;34588:55:::1;3408:6:14;34588:17:17;:55;;:::i;:::-;34654:34;34691:241;34731:12;;34757:16;34787:5;34806:2;34822:5;34841:4;34859:10;34883:7;34904:18;34691:26;:241::i;:::-;34654:278;;35005:240;35037:11;35066:16;35100:5;35124:2;35145:5;35169:2;35190:7;35215:20;35005:14;:240::i;:::-;34943:302:::0;;-1:-1:-1;34943:302:17;-1:-1:-1;35256:110:17;::::1;;;35283:72;35305:11;35318:5;35325:7;35334:14;35350:4;35283:21;:72::i;:::-;34578:794;34140:1232:::0;;;;;;;;;;;;;;;:::o;18396:172:30:-;18481:80;2007:4:29;18539:5:30;18546:7;18555:5;18481:30;:80::i;:::-;18396:172;;:::o;42477:143:17:-;42554:10;42540:25;;;;:13;:25;;;;;;:27;;;;;;42593:20;;;42540:25;42593:20;42477:143::o;13192:320::-;13362:21;;13422:83;13433:18;13453:5;13460:8;13470:9;13481:5;13488:2;13362:21;13422:10;:83::i;:::-;13415:90;;;;13192:320;;;;;;;;:::o;30017:962::-;30435:12;30406:18;7336:66;7383:18;7336:46;:66::i;:::-;30459:57:::1;3277:6:14;30459:17:17;:57;;:::i;:::-;30527:292;30584:5;202:1:14;30621:12:17;30647:5;30666:10;30690:5;30709:14;30737:12;;30763:14;30791:18;30527:43;:292::i;:::-;30839:58;30858:5;30865;30872:2;202:1:14;30882:14:17;30839:18;:58::i;:::-;30829:68;;30912:7;30908:65;;;30935:27;30949:5;30956;30935:13;:27::i;:::-;30017:962:::0;;;;;;;;;;;;;;:::o;28719:158:30:-;28864:9;28836:17;4545:14;4542:1;4539;4526:34;4637:1;4634;4618:14;4615:1;4607:6;4600:5;4587:52;4664:16;4714:4;4711:1;4708;4693:26;4742:6;4732:65;;4778:4;4775:1;4768:15;23009:376;23164:22;23188:16;23231:87;1339:1:29;23283:10:30;23295:6;23303:4;23309:2;23313:1;23316;23231:17;:87::i;:::-;-1:-1:-1;23348:30:30;;;;;23216:102;;-1:-1:-1;23009:376:30;-1:-1:-1;;;;;23009:376:30:o;16423:149::-;16509:56;16540:8;16550:5;16557:1;16560:4;16509:30;:56::i;11182:1326:17:-;11530:11;11512:29;;:15;:29;11508:114;;;11564:47;;;;;;;;;;;;;;11508:114;11631:33;11654:9;11631:22;:33::i;:::-;11674:38;11699:5;11706;11674:24;:38::i;:::-;11722:470;11758:378;11792:330;11844:9;11875:5;11902:2;11926:6;11792:330;;11954:5;11981:8;12011:18;11792:330;;12051:11;11792:330;;12084:13;:20;12098:5;12084:20;;;;;;;;;;;;;;;;11792:30;:330::i;:::-;11758:16;:378::i;:::-;12150:12;;12177:5;11722:22;:470::i;:::-;12339:23;;;;:70;;12391:18;12339:70;;;12372:15;12339:70;12318:91;;12420:81;12435:9;12446:5;12453:2;12457:6;12465:18;12485:5;12492:8;12420:14;:81::i;:::-;11182:1326;;;;;;;;;;;:::o;32381:220:30:-;32580:17;4545:14;4542:1;4539;4526:34;4637:1;4634;4618:14;4615:1;4607:6;4600:5;4587:52;4664:16;4714:4;4711:1;4708;4693:26;4742:6;4732:65;;4778:4;4775:1;4768:15;16967:568:17;17199:12;17223:58;2999:6:14;17223:17:17;:58;;:::i;:::-;17292:100;701:3:14;17332:5:17;17339:2;257:1:14;17348:5:17;17355:10;17367:5;257:1:14;17379:12:17;;17292:20;:100::i;:::-;17412:41;17432:5;17439:2;17443:5;17450:2;17412:19;:41::i;:::-;17402:51;;17468:7;17464:65;;;17491:27;17505:5;17512;17491:13;:27::i;:::-;16967:568;;;;;;;;;;:::o;40812:338::-;41008:21;41031:18;41068:75;41079:15;41096:5;41103:8;41113:9;41124:5;41131:2;41135:7;41068:10;:75::i;:::-;41061:82;;;;40812:338;;;;;;;;;:::o;1938:101:21:-;1214:13;:11;:13::i;:::-;2002:30:::1;2029:1;2002:18;:30::i;:::-;1938:101::o:0;44921:197:30:-;45038:45;;;;;;;:33;:45;;;;;45003:16;;45038:73;;:71;:73::i;4954:532:25:-;5066:21;:61;;;-1:-1:-1;212:66:24;5091:36:25;;;5066:61;5062:123;;;5150:24;;;;;;;;;;;;;;5062:123;5267:30;5278:18;5267:10;:30::i;:::-;5262:89;;5320:20;;;;;;;;;;;;;;5262:89;212:66:24;5398:43:25;;;;5437:4;5398:43;;;5452:27;;:::i;51854:132:17:-;51906:23;51959:20;:18;:20::i;:::-;51941:38;;51854:132;:::o;8358:328:30:-;8449:20;8474:87;1339:1:29;8526:10:30;8538:6;8546:4;8552:2;8556:1;8559;8474:17;:87::i;:::-;-1:-1:-1;8448:113:30;-1:-1:-1;8575:34:30;;;;8571:109;;8625:44;8655:13;8625:29;:44::i;:::-;8438:248;8358:328;;;:::o;44149:515:17:-;44287:12;44311:57;2587:6:14;44311:17:17;:57;;:::i;:::-;44379:31;44413:71;44437:5;701:3:14;44463:5:17;44470:2;257:1:14;44479:4:17;44413:23;:71::i;:::-;44379:105;;44504:41;44524:5;44531:2;44535:5;44542:2;44504:19;:41::i;:::-;44494:51;;44560:7;44556:102;;;44583:64;44605:8;44615:5;144:1:14;257;144;44583:21:17;:64::i;:::-;44301:363;44149:515;;;;;;:::o;42571:185:30:-;42681:34;;;;;;;:24;:34;;;;;;;;:38;;;;;;;;;;42646:16;;42681:68;;:59;;:66;:68::i;44036:229::-;44183:57;;;;;;;:45;:57;;;;;:64;44132:16;;44167:91;;44183:64;;;;;44249:8;44167:15;:91::i;9506:334:13:-;7797:35;:33;:35::i;:::-;9614:20:::1;9632:1;9614:17;:20::i;:::-;9649:18:::0;;9645:189:::1;;9684:12;9702:10;:15;;9725:14;9702:42;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;9683:61;;;9762:7;9758:65;;9778:45;;;;;;;;;;;;;;47037:140:30::0;47121:49;;;;;:40;24274:55:36;;;47121:49:30;;;24256:74:36;47098:4:30;;47134:12;47121:40;;;;;;24229:18:36;;47121:49:30;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;20992:200::-;21121:64;21152:8;21162:5;21169:7;21178:6;21121:30;:64::i;45514:243::-;45666:57;;;;45627:4;45666:57;;;:45;:57;;;;;;;;:64;43212:34;;;;;;:24;:34;;;;;45666:64;;;;;;43212:38;;;;;;;;:69;;;;;:60;;;;:69;;;;;;45627:4;;43212:69;45650:100;45643:107;45514:243;-1:-1:-1;;;;45514:243:30:o;39400:843:17:-;39603:10;:19;;;;;:45;;-1:-1:-1;39626:10:17;:22;;;;39603:45;39598:123;;39672:38;;;;;;;;;;;;;;39598:123;39730:33;39753:9;39730:22;:33::i;:::-;39773:34;39810:86;39832:15;39849:5;39856:9;39867:5;39874:2;39878:7;39887:8;39810:21;:86::i;:::-;39915:17;;39773:123;;-1:-1:-1;39915:37:17;:17;39911:326;;567:1:14;40059:26:17;;40104:43;;39968:41;586::36;;40104:43:17;;;;;;;;;40116:7;;40104:43;;574:2:36;559:18;40104:43:17;;;;;;;39911:326;;;40185:41;;;;;;;;;;;;;;19514:883;19890:12;19861:18;7336:66;7383:18;7336:46;:66::i;:::-;19914:58:::1;2999:6:14;19914:17:17;:58;;:::i;:::-;19983:271;20041:5;20060:2;257:1:14;20093:5:17;20112:10;20136:5;257:1:14;20172:12:17;;20198:14;20226:18;19983:44;:271::i;:::-;20274:41;20294:5;20301:2;20305:5;20312:2;20274:19;:41::i;:::-;20264:51;;20330:7;20326:65;;;20353:27;20367:5;20374;20353:13;:27::i;:::-;19514:883:::0;;;;;;;;;;;;;:::o;21971:191:30:-;22065:90;1765:66:29;22137:5:30;22144:7;1765:66:29;22065:30:30;:90::i;15198:183::-;15280:94;1765:66:29;15352:5:30;15359:7;1765:66:29;15280:30:30;:94::i;9000:321:17:-;9193:33;9216:9;9193:22;:33::i;:::-;9236:78;9251:9;9262:5;9269:2;9273:6;9281:10;9293;9305:8;9236:14;:78::i;:::-;9000:321;;;;;;:::o;41641:118::-;41709:43;41734:10;41746:5;41709:24;:43::i;40594:547:30:-;40722:23;40760:15;;40757:378;;40878:57;;;40791:67;40878:57;;;:43;:57;;;;;40973:4;40959:26;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;40959:26:30;;40950:35;;41004:9;40999:126;41019:15;;;40999:126;;;41071:30;:39;41102:4;;41107:1;41102:7;;;;;;;:::i;:::-;;;;;;;41071:39;;;;;;;;;;;;41059:6;41066:1;41059:9;;;;;;;;:::i;:::-;;;;;;;;;;:51;41036:3;;40999:126;;42155:181;42263:34;;;;;;;:24;:34;;;;;;;;:38;;;;;;;;;;42228:16;;42263:66;;:64;:66::i;9685:346::-;9793:20;9818:88;1284:1:29;9865:10:30;9877:6;9885:4;9891:2;9895:7;9904:1;9818:17;:88::i;:::-;-1:-1:-1;9792:114:30;-1:-1:-1;9920:34:30;;;;9916:109;;9970:44;10000:13;9970:29;:44::i;:::-;9782:249;9685:346;;;;:::o;37432:1208:17:-;37810:22;37834:12;37781:18;7509:63;7553:18;7509:43;:63::i;:::-;37858:53:::1;3534:6:14;37858:17:17;:53;;:::i;:::-;37922:34;37959:241;37997:12;;38023:16;38053:5;202:1:14;38090:5:17;38109:4;38127:10;38151:7;38172:18;37959:24;:241::i;:::-;37922:278;;38273:240;38305:11;38334:16;38368:5;202:1:14;38415:5:17;38439:2;38460:7;38485:18;38273:14;:240::i;:::-;38211:302:::0;;-1:-1:-1;38211:302:17;-1:-1:-1;38524:110:17;::::1;;;38551:72;38573:11;38586:5;38593:7;38602:14;38618:4;38551:21;:72::i;:::-;37848:792;37432:1208:::0;;;;;;;;;;;;;;:::o;27235:646::-;27508:12;27532:57;3277:6:14;27532:17:17;:57;;:::i;:::-;27600:121;972:2:14;27639:5:17;202:1:14;27652:12:17;27666:5;27673:10;27685:5;27692:14;27708:12;;27600:20;:121::i;:::-;27741:58;27760:5;27767;27774:2;202:1:14;27784:14:17;27741:18;:58::i;:::-;27731:68;;27814:7;27810:65;;;27837:27;27851:5;27858;27837:13;:27::i;:::-;27235:646;;;;;;;;;;;:::o;14814:704::-;15210:4;14911:25;:296;15043:49;;;;;;;;;;;;;;;;;15119:24;;15004:161;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;14950:247;;;;;;14911:296;;;;;;;;;;;;:303;;;;;;;;;;;;;;;;;;15507:4;15225:22;:279;15354:35;;;;;;;;;;;;;;;;;15416:24;;15315:147;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;15261:233;;15315:147;15261:233;;;;15225:279;;;;;;;;;;-1:-1:-1;15225:279:17;:286;;;;;;;;;;;;;-1:-1:-1;;14814:704:17:o;14246:173:30:-;14349:63;14380:8;14390:5;14397:7;14406:5;14349:30;:63::i;47406:534:17:-;47547:12;47571:56;2859:6:14;47571:17:17;:56;;:::i;:::-;47638:31;47672:76;47696:5;972:2:14;47721:5:17;202:1:14;47734:6:17;47742:5;47672:23;:76::i;:::-;47638:110;;47768:50;47787:5;47794;47801:2;202:1:14;47811:6:17;47768:18;:50::i;:::-;47758:60;;47833:7;47829:105;;;47856:67;47878:8;47888:5;144:1:14;47909:6:17;144:1:14;47856:21:17;:67::i;38869:237:30:-;38931:7;38954:39;1035:3:29;38954:39:30;;;38950:87;;-1:-1:-1;39024:1:30;;38869:237;-1:-1:-1;38869:237:30:o;38950:87::-;-1:-1:-1;39054:45:30;;;;;;:34;:45;;;;;;;;;38869:237::o;46138:247::-;46293:57;;;46253:4;46293:57;;;:45;:57;;;;;;;;:64;43720:34;;;;;;:24;:34;;;;;46293:64;;;;;;43720:38;;;;;;;;:72;;;:24;:62;:72;;;;;;;46276:102;43605:194;2188:191:21;1214:13;:11;:13::i;:::-;2271:22:::1;::::0;::::1;2268:66;;2302:32;;;;;;;;;;;;;;2268:66;2344:28;2363:8;2344:18;:28::i;5818:140:25:-:0;5936:5;5923:11;5916:26;5818:140;;:::o;6999:169::-;7134:18;;6999:169::o;8171:123::-;8276:1;8263:11;8256:22;8171:123;:::o;6333:331::-;212:66:24;6432:36:25;;;6428:230;;;6531:5;6518:11;6511:26;18396:172:30;;:::o;6428:230:25:-;6608:26;;6333:331::o;7546:360::-;7643:13;212:66:24;7672:36:25;;;7668:232;;;-1:-1:-1;7760:18:25;;7546:360::o;7668:232::-;-1:-1:-1;7858:18:25;;7668:232;7546:360;;;:::o;8602:310::-;212:66:24;8688:36:25;;;8684:222;;;8787:1;8774:11;8767:22;17364:107:30;:::o;8684:222:25:-;8880:1;8860:22;;8602:310::o;11336:288:13:-;11455:28;14901:9;14877:21;:33;11425:58;11421:197;;;11503:13;;:22;;:26;11499:109;;11556:37;;;;;;;;;;;;;;12555:336;12672:28;14901:9;14877:21;:33;12641:59;12637:248;;12723:40;;;;;;;;;;;;;;12637:248;12800:6;12784:13;;:22;12810:1;12784:27;12780:105;;12834:40;;;;;;;;;;;;;;13634:228;13703:22;13771:28;14901:9;14877:21;:33;13741:58;13737:119;;;-1:-1:-1;13832:13:13;;13737:119;13634:228;:::o;11894:195::-;11986:13;;:22;;:26;11982:101;;12035:37;;;;;;;;;;;;;;14027:143;14150:13;;;14027:143::o;65416:793:17:-;65713:14;65730:273;65760:233;65808:9;65835:5;65858:2;65878:12;65908:5;65931:10;65959:13;:20;65973:5;65959:20;;;;;;;;;;;;;;;;2503:270:19;;;1652:160:14;2503:270:19;;;;39390:25:36;;;;39431:18;;;39424:34;;;;39477:42;39555:15;;;;39535:18;;;39528:43;39587:18;;;39580:34;;;;39630:19;;;39623:35;;;;39674:19;;;39667:35;;;;2692:10:19;39718:19:36;;;39711:44;39771:19;;;39764:35;39815:19;;;;39808:35;;;;2503:270:19;;;;;;;;;;39362:19:36;;;;2503:270:19;;2480:303;;;;;;2217:573;65730:273:17;65713:290;;66014:188;66044:5;66063:10;66087:14;66115:12;66141:5;66160:6;66180:12;;66014:16;:188::i;49770:442::-;49933:12;49967:69;838:4:14;50007:5:17;50014;50021:2;50025;50029:6;49967:19;:69::i;:::-;49957:79;;50052:7;50047:159;;50079:59;;;;;:32;35360:15:36;;;50079:59:17;;;35342:34:36;35412:15;;;35392:18;;;35385:43;35444:18;;;35437:34;;;35487:18;;;35480:34;;;35551:3;35530:19;;;35523:32;-1:-1:-1;35571:19:36;;;35564:30;50079:32:17;;;;;35611:19:36;;50079:59:17;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;50075:121;;-1:-1:-1;50177:4:17;50075:121;49770:442;;;;;;;:::o;85323:320::-;85432:25;;;202:1:14;85432:25:17;;;:16;:25;;;;;;;;85475:1;85466:10;;;85432:46;;;;;;;:71;;257:1:14;85525:19:17;;;;85432:71;;;;;;;85424:121;:129;85420:207;;85580:32;;;;;;;;;;;;;;59978:248;60090:45;;;;:25;:45;;;;;;;;60085:135;;60158:51;;;;;;;;;;;;;;67380:687;67743:317;838:4:14;67827:5:17;67846:2;67862:12;67888:5;67907:10;67931:5;67950:14;67978:12;;68004:14;68032:18;67743:38;:317::i;62002:843::-;62205:31;62259:96;62281:18;62301:5;62308:9;62319:5;62326:2;62205:31;62344:10;62259:21;:96::i;:::-;62378:19;;;;-1:-1:-1;62400:15:17;62378:19;;;;;;:37;62374:122;;;62438:47;;;;;;;;;;;;;;62374:122;62509:15;;;;;;;-1:-1:-1;;62505:112:17;;;62556:50;;;;;;;;;;;;;;62505:112;62630:15;62627:212;;;62661:19;;;;;;62627:212;;;62701:15;;62719:17;62701:15;;;;;;:35;62697:142;;;62780:34;;;;;;;;;;;;;;;;;;;;;;;62697:142;62002:843;;;;;;;;:::o;86103:825::-;86311:18;;86307:615;;86349:13;86345:567;;;86489:42;;86568:33;86489:42;;;;;;;;;;;;;86568:33;;;;;;;;86624:45;;22820:25:36;;;86624:45:17;;;;86638:7;;86624:45;;22808:2:36;22793:18;86624:45:17;;;;;;;86345:567;;;86694:15;;86712:17;86694:15;;;;;;:35;86690:222;;;86837:42;;;;;;;;;;;;;;;;;;;;;;86103:825;;;;;:::o;90443:106::-;90529:13;:11;:13::i;10646:222:13:-;10740:13;;;10764:30;;;;10810:51;;;13591:25:36;;;13647:2;13632:18;;13625:34;;;10810:51:13;;13564:18:36;10810:51:13;;;;;;;10706:162;10646:222;:::o;51844:968:30:-;52083:6;;52130:4;52112:23;;;;52108:454;;-1:-1:-1;1720:10:29;;-1:-1:-1;1720:10:29;52505:46:30;;52108:454;52579:226;;;;;;;;35956:25:36;;;36000:42;36078:15;;;36058:18;;;36051:43;36130:15;;;36110:18;;;36103:43;36182:15;;;36162:18;;;36155:43;36235:15;;36214:19;;;36207:44;36267:19;;;36260:35;;;36311:19;;;36304:35;;;52608:4:30;;52579:64;;35928:19:36;;52579:226:30;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;52572:233;;;;51844:968;;;;;;;;;;;:::o;53951:182::-;54072:13;54066:4;54059:27;54112:4;54106;54099:18;53206:566;53427:57;;;53314:30;53427:57;;;:45;:57;;;;;53533:27;;53314:30;;53427:57;1035:3:29;53533:27:30;;;:56;:182;;53684:31;;;;;;;53533:182;;;53640:27;;;;53605:63;;;;:34;:63;;;;;;;;53533:182;53738:27;;;;;;;;;;;-1:-1:-1;53495:220:30;-1:-1:-1;;53206:566:30:o;59381:1172::-;59650:20;59682:15;59700:34;;;59682:52;;59784:4;59778:11;59814:8;59809:3;59802:21;59858:20;59851:4;59847:3;59843:13;59836:43;59914:11;59907:4;59903:3;59899:13;59892:34;59961:7;59954:4;59950:3;59946:13;59939:30;60004:5;59997:4;59993:3;59989:13;59982:28;60045:3;60038:4;60034:3;60030:13;60023:26;60084:8;60077:4;60073:3;60069:13;60062:31;60128:7;60121:4;60117:3;60113:13;60106:30;60170:4;60166:3;60162:13;60156:4;60149:27;60251:4;60245;60239;60234:3;60224:8;60217:5;60204:52;60190:66;;60279:6;60269:227;;60391:16;60445:4;60442:1;60439;60424:26;60477:4;60474:1;60467:15;60269:227;-1:-1:-1;;60532:4:30;60526:11;;59381:1172;-1:-1:-1;;;;;;;;;59381:1172:30:o;58478:562::-;58683:10;58695:8;58705:10;56982:77;57026:10;57038:8;57048:10;56982:43;:77::i;:::-;2767:20:33;2926:24;;;2857:36;2970:4;2963:37;3045:4;3029:21;;58727:79:30::1;::::0;58740:37:::1;58795:8;58779:26;;58727:12;:79;;:::i;:::-;58816:32;2233:24:33::0;;;2277:4;2270:21;;;2343:4;2327:21;;2324:1;2320:29;58907:66:30::1;2320:29:33::0;58946:26:30::1;::::0;::::1;58907:66;:12;:66;:::i;:::-;58983:50;58996:28;:24:::0;59023:1:::1;58996:28;:::i;:::-;59026:6;58983:12;:50;;:::i;:::-;58717:323;58478:562:::0;;;;;;;:::o;60477:242:17:-;60586:42;;;;:22;:42;;;;;;;;60581:132;;60651:51;;;;;;;;;;;;;;77475:657;77799:34;77859:266;77892:12;;77918:16;838:4:14;77980:5:17;77999:2;78015:5;78034:4;78052:10;78076:7;78097:18;77859:19;:266::i;:::-;77845:280;77475:657;-1:-1:-1;;;;;;;;;;;77475:657:17:o;83908:1132::-;84405:18;;84329:36;;;;;84264:22;;84405:18;;;;;84388:35;;84384:101;;;84456:18;;;;;;;;-1:-1:-1;84384:101:17;84516:16;:34;;;84499:14;:51;84495:136;;;84573:47;;;;;;;;;;;;;;84495:136;84665:45;;;;;;;;;;;;;;;;;;;;;;;84729:55;;22820:25:36;;;84757:10:17;;84729:55;;;;84741:7;;84729:55;;22808:2:36;22793:18;84729:55:17;;;;;;;84809:18;;;;;;;;:23;84805:157;;84848:38;;;;459:1:14;84848:38:17;;;84905:46;;-1:-1:-1;586:41:36;;84933:10:17;;84905:46;;;;84917:7;;84905:46;;574:2:36;559:18;84905:46:17;;;;;;;84805:157;84982:51;84996:5;85003;85010:2;85014;85018:14;84982:13;:51;;:::i;:::-;84972:61;;83908:1132;;;;;;;;;;;:::o;57554:474:30:-;57765:10;57777:8;57787:10;56982:77;57026:10;57038:8;57048:10;56982:43;:77::i;:::-;2767:20:33;2926:24;;;2857:36;2970:4;2963:37;3045:4;3029:21;;57809:114:30::1;::::0;57912:8:::1;57896:26;;57862:15;:30;;57891:1;57862:30;;;57880:8;57862:30;57861:61;57809:12;:114;;:::i;:::-;57933:88;57946:46;57972:10;57984:7;2178:20:33::0;2233:24;;;2277:4;2270:21;2343:4;2327:21;;2324:1;2320:29;;2060:304;89640:578:17;89920:21;89943:18;89973:30;90006:81;90028:10;90040:5;90047:9;90058:5;90065:2;90069:7;90078:8;90006:21;:81::i;:::-;90113:18;;;;-1:-1:-1;90134:15:17;90113:18;;;;;;:36;:57;;90156:14;;;;;;;90113:57;;;90152:1;90113:57;90193:18;;90097:73;;;;;90193:18;;;;;;;-1:-1:-1;90097:73:17;-1:-1:-1;;;;;;;;89640:578:17:o;69235:683::-;69596:315;972:2:14;69678:5:17;69697:2;69713:12;69739:5;69758:10;69782:5;69801:14;69829:12;;69855:14;69883:18;69596:38;:315::i;50898:614::-;51065:12;51099:69;972:2:14;51137:5:17;51144;51151:2;202:1:14;51161:6:17;51099:19;:69::i;:::-;51089:79;;51184:7;51179:327;;51254:71;;;51243:10;37017:15:36;;;51254:71:17;;;36999:34:36;37069:15;;;37049:18;;;37042:43;37101:18;;;;37094:34;;;51254:71:17;;;;;;;;;;36911:18:36;;;;51254:71:17;;;;;;;;;51277:28;51254:71;;;51243:83;;-1:-1:-1;;;;51243:10:17;;;;:83;;51254:71;51243:83;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;51207:119;;;;51345:7;51340:156;;51382:4;51372:14;;51340:156;;;51411:11;;:15;51407:89;;51468:4;51457:24;;;;;;;;;;;;:::i;:::-;51456:25;51446:35;;51407:89;51193:313;;50898:614;;;;;;;:::o;86934:306::-;701:3:14;87026:9:17;:30;:78;;;;838:4:14;87073:9:17;:31;87026:78;:124;;;;972:2:14;87121:9:17;:29;87026:124;87008:226;;87196:27;;;;;;;;;;;;;;61004:335;61124:25;;;202:1:14;61124:25:17;;;:16;:25;;;;;;;;61167:1;61158:10;;;61124:46;;;;;;;:71;;257:1:14;61217:19:17;;;;61124:71;;;;;;;61116:121;61112:211;;61272:36;;;;;;;;;;;;;;908:670:19;1258:303;;;1314:195:14;1258:303:19;;;37830:25:36;37871:18;;;37864:34;;;37917:42;37995:15;;;37975:18;;;37968:43;38027:18;;;38020:34;;;38070:19;;;38063:35;;;38114:19;;;38107:35;;;38179:15;;38158:19;;;38151:44;38211:19;;;38204:35;;;38255:19;;;38248:35;;;38299:19;;;38292:35;;;1204:12:19;;37802:19:36;;1258:303:19;;;;;;;;;;;;;1235:336;;;;;;1228:343;;908:670;;;;;;;;;;;:::o;4292:165:20:-;4369:7;4395:55;4417:20;:18;:20::i;:::-;4439:10;8536:4:9;8530:11;8566:10;8554:23;;8606:4;8597:14;;8590:39;;;;8658:4;8649:14;;8642:34;8712:4;8697:20;;;8336:397;52615:1385:17;52751:2;52731:22;;;52727:1267;;52975:30;;53062:2;53040:25;;53027:39;53131:2;53109:25;;53096:39;52769:9;53088:48;;;;52769:9;53196:30;53210:6;53088:48;52975:30;53027:39;53196:13;:30::i;:::-;53163:63;;;;53253:6;53244:15;;:5;:15;;;;:26;;;;53263:7;53244:26;53240:114;;;53290:49;53314:5;53321:6;53329:9;;53290:23;:49::i;:::-;52755:609;;;;;52727:1267;;;53394:2;53374:22;;;53370:624;;53596:30;;53684:2;53662:25;;53649:39;53412:9;;53748:28;53762:6;53596:30;53649:39;53748:13;:28::i;:::-;53715:61;;;;53803:6;53794:15;;:5;:15;;;;:26;;;;53813:7;53794:26;53790:114;;;53840:49;53864:5;53871:6;53879:9;;53840:23;:49::i;:::-;53398:516;;;;53370:624;;;53934:49;53958:5;53965:6;53973:9;;53934:23;:49::i;75829:510::-;76047:31;76081:94;76103:18;76123:5;76130:9;76141:5;76148:2;76047:31;76166:8;76081:21;:94::i;:::-;76194:32;;76236:24;;76194:32;;;;;;;76236:24;;;;;;76194:32;76236:24;;;;;;;;;;;;76276:56;;;38538:25:36;;;38594:2;38579:18;;38572:93;;;;38681:18;;38674:55;;;;76194:32:17;;-1:-1:-1;76276:56:17;;;;;;;;;;;;;;38526:2:36;38511:18;76276:56:17;;;;;;;76037:302;75829:510;;;;;;;:::o;48621:409::-;48759:12;48793:65;701:3:14;48832:5:17;48839;48846:2;48850;257:1:14;48793:19:17;:65::i;:::-;48783:75;;48874:7;48869:155;;48901:42;;;;;:27;37017:15:36;;;48901:42:17;;;36999:34:36;37069:15;;;37049:18;;;37042:43;37101:18;;;37094:34;;;48901:27:17;;;;;36911:18:36;;48901:42:17;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;48897:117;;-1:-1:-1;48995:4:17;48621:409;;;;;;:::o;1479:124:21:-;1367:7;1393:6;1537:23;1393:6;719:10:7;1537:23:21;1534:62;;1569:27;;;;;;;;;;;;;;2533:187;2606:16;2625:6;;;2641:17;;;;;;;;;;2673:40;;2625:6;;;;;;;2673:40;;2606:16;2673:40;2596:124;2533:187;:::o;10064:300:26:-;10127:16;10155:22;10180:19;10188:3;10180:7;:19::i;10291:407:25:-;10374:7;10636:17;:28;;10683:2;10671:9;:14;;;;:::i;:::-;10636:55;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;10627:64:25;;10291:407;-1:-1:-1;;;10291:407:25:o;3251:230:20:-;3304:7;3344:14;3327:13;:31;3323:152;;-1:-1:-1;3381:22:20;;3251:230::o;3323:152::-;3441:23;3578:81;;;1857:95;3578:81;;;40516:25:36;3601:11:20;40557:18:36;;;40550:34;;;;3614:14:20;40600:18:36;;;40593:34;3630:13:20;40643:18:36;;;40636:34;3653:4:20;40686:19:36;;;40679:84;3542:7:20;;40488:19:36;;3578:81:20;;;;;;;;;;;;3568:92;;;;;;3561:99;;3487:180;;63403:434:17;63693:31;63747:10;:73;63758:61;63780:7;63789:9;63800:5;63807:2;63811:7;64490:20;;;;64402:11;64490:20;;;:13;:20;;;;;;;;;;64441:70;;;;;41122:34:36;;;;41172:18;;;41165:34;;;;41235:15;;;;41215:18;;;41208:43;41267:18;;;41260:34;;;;41310:19;;;41303:35;41354:19;;;;41347:35;;;;64441:70:17;;;;;;;;;;41033:19:36;;64441:70:17;;;64431:81;;;;;;64271:248;63758:61;63747:73;;;;;;;;;;;:83;63821:8;63747:83;;;;;;;;;;;;;;;63736:94;;63403:434;;;;;;;;;:::o;71087:685::-;71449:316;701:3:14;71532:5:17;71551:2;71567:12;71593:5;71612:10;71636:5;71655:14;71683:12;;71709:14;71737:18;71449:38;:316::i;79264:653::-;79586:34;79646:264;79679:12;;79705:16;972:2:14;79765:5:17;79784:2;79800:5;79819:4;79837:10;79861:7;79882:18;79646:19;:264::i;74659:610::-;74932:10;74914:15;:28;74910:115;;;74965:49;;;;;;;;;;;;;;74910:115;75056:12;75039:14;:29;75035:118;;;75091:51;;;;;;;;;;;;;;75035:118;75163:38;75188:5;75195;75163:24;:38::i;:::-;75211:51;75234:6;75242:12;;75256:5;75211:22;:51::i;49794:835:30:-;49996:12;50021:15;50038:26;50081:293;701:3:14;50116:9:30;:30;:101;;1400:1:29;50116:101:30;;;1284:1:29;50116:101:30;50236:5;50260:10;50289:4;50312:2;50333;50354:6;50081:17;:293::i;:::-;50020:354;;-1:-1:-1;50020:354:30;-1:-1:-1;50389:41:30;;;;;:77;;;50457:9;50434:19;:32;;;50389:77;50385:238;;;50492:29;;;;;;-1:-1:-1;50385:238:30;;;50559:53;;;;;;;;;;;;;;50385:238;50010:619;;49794:835;;;;;;;;:::o;72891:869:17:-;73274:14;73291:255;73338:9;73361:5;73381:2;73398:12;73425:5;73444;73464:10;73489:14;73518:18;73291:33;:255::i;:::-;73274:272;;73565:188;73595:5;73614:10;73638:14;73666:12;73692:5;73711:6;73731:12;;73565:16;:188::i;:::-;73264:496;72891:869;;;;;;;;;;;;:::o;54843:1284:30:-;55070:57;;;55010;55070;;;:45;:57;;;;;55153:38;;;;;2713:6:29;4361:16:33;4360:23;55138:195:30;;55255:67;;;;;;;;;;;;;;55138:195;55358:38;;;;;2792:6:29;4361:16:33;4360:23;55343:299:30;;55472:37;;;;;55468:164;;55536:81;;;;;;;;;;;;;;55468:164;55705:31;;;;;;;55657:47;:80;;;:47;;:80;;;:47;:80;;;:114;;;;;:102;;:114;;;;;;:47;:114;55652:469;;55803:38;;;;;2932:6:29;4361:16:33;4360:23;;55791:221:30;;-1:-1:-1;55914:98:30;;;:47;:98;;;:86;:47;:98;:47;:98;;;:47;:98;55913:99;55791:221;55787:324;;;56039:57;;;;;;;;;;;;;;81104:1752:17;81448:34;81534:17;81498:33;;:53;81494:129;;;81574:38;;;;;;;;;;;;;;81494:129;81647:88;81669:15;81686:5;81693:9;81704:5;81711:2;81715:7;81724:10;81647:21;:88::i;:::-;81750:17;;81633:102;;-1:-1:-1;81750:37:17;:17;81746:1104;;81807:18;;;;;;;;:23;81803:809;;81850:512;81894:387;81953:9;81988:5;82020:2;82049:33;;82108:5;82139:4;81894:387;;;82207:7;82241:18;81894:33;:387::i;:::-;82304:12;;82339:5;81850:22;:512::i;:::-;82381:63;;82462:35;;82381:63;82410:33;;82381:63;;;;;;;82462:35;;;;;;;;;;;;;;82523:74;;22820:25:36;;;82551:10:17;;82523:74;;;;82535:7;;82523:74;;22808:2:36;22793:18;82523:74:17;;;;;;;81803:809;82648:22;;;;;;;82630:15;:40;82626:135;;;82697:49;;;;;;;;;;;;;;56184:470;56276:12;;56333:66;56320:79;;56316:244;;;-1:-1:-1;56532:4:17;;-1:-1:-1;56546:1:17;56524:25;;56316:244;56579:26;;;;;;;;;;;;40081:25:36;;;40154:4;40142:17;;40122:18;;;40115:45;;;;40176:18;;;40169:34;;;40219:18;;;40212:34;;;56579:26:17;;40053:19:36;;56579:26:17;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;56579:26:17;;;;;56626:20;;;;;-1:-1:-1;56579:26:17;-1:-1:-1;;56184:470:17;;;;;;;;:::o;54405:361::-;54520:6;:18;;;54542:1;54520:23;54517:104;;54566:44;;;;;;;;;;;;;;54517:104;54636:46;54658:6;54666:4;54672:9;;54636:21;:46::i;:::-;54631:129;;54705:44;;;;;;;;;;;;;;55211:315;55295:12;;1118:66:14;55371:19:17;;55442:2;55434:3;55428:9;;;55414:30;55479;55493:6;55414:30;55504:1;55371:19;55479:13;:30::i;:::-;55459:50;;;;-1:-1:-1;55211:315:17;-1:-1:-1;;;;;;55211:315:17:o;5375:109:26:-;5431:16;5466:3;:11;;5459:18;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5375:109;;;:::o;88007:877:17:-;88425:20;;;88314:14;88425:20;;;:13;:20;;;;;;88477:400;88511:352;88581:9;88612:5;88640:2;88665:6;88694:5;88722:10;88755:14;88792:18;88425:20;88511:48;:352::i;57105:1792::-;57243:12;57290:1496;;;57376:8;57420:4;57414:11;57520:13;57515:3;57508:26;57642:5;57635:4;57630:3;57626:14;57619:29;57762:4;57755;57750:3;57746:14;57739:28;57855:16;57848:4;57843:3;57839:14;57832:40;57995:16;57977;57970:4;57965:3;57961:14;57948:64;58194:4;58190:9;58183:4;58165:16;58161:27;58157:43;58151:4;58147:54;58286:10;58281:3;58277:20;58271:4;58264:34;58572:4;58566;58554:10;58549:3;58540:7;58533:5;58522:55;58514:4;58496:16;58493:26;58486:34;58482:96;58479:293;;;58714:13;58707:4;58701:11;58698:30;58686:42;;58749:5;;;;58479:293;;;57290:1496;;;;;;:::o;:::-;58810:71;58864:16;58846;58840:4;58832:6;58810:71;:::i;3619:691:19:-;3995:298;;;;;;37830:25:36;;;37871:18;;;37864:34;;;37917:42;37995:15;;37975:18;;;37968:43;38027:18;;;38020:34;;;38070:19;;;38063:35;;;38114:19;;;38107:35;;;4180:10:19;38158:19:36;;;38151:44;38211:19;;;38204:35;;;38255:19;;;38248:35;;;38299:19;;;38292:35;;;3941:12:19;;37802:19:36;;3995:298:19;37431:902:36;14:177;99:66;92:5;88:78;81:5;78:89;68:117;;181:1;178;171:12;196:245;254:6;307:2;295:9;286:7;282:23;278:32;275:52;;;323:1;320;313:12;275:52;362:9;349:23;381:30;405:5;381:30;:::i;638:196::-;706:20;;766:42;755:54;;745:65;;735:93;;824:1;821;814:12;839:186;898:6;951:2;939:9;930:7;926:23;922:32;919:52;;;967:1;964;957:12;919:52;990:29;1009:9;990:29;:::i;1030:347::-;1081:8;1091:6;1145:3;1138:4;1130:6;1126:17;1122:27;1112:55;;1163:1;1160;1153:12;1112:55;-1:-1:-1;1186:20:36;;1229:18;1218:30;;1215:50;;;1261:1;1258;1251:12;1215:50;1298:4;1290:6;1286:17;1274:29;;1350:3;1343:4;1334:6;1326;1322:19;1318:30;1315:39;1312:59;;;1367:1;1364;1357:12;1312:59;1030:347;;;;;:::o;1382:977::-;1524:6;1532;1540;1548;1556;1564;1572;1580;1588;1596;1649:3;1637:9;1628:7;1624:23;1620:33;1617:53;;;1666:1;1663;1656:12;1617:53;1689:29;1708:9;1689:29;:::i;:::-;1679:39;;1765:2;1754:9;1750:18;1737:32;1727:42;;1816:2;1805:9;1801:18;1788:32;1778:42;;1867:2;1856:9;1852:18;1839:32;1829:42;;1918:3;1907:9;1903:19;1890:33;1880:43;;1942:39;1976:3;1965:9;1961:19;1942:39;:::i;:::-;1932:49;;2000:39;2034:3;2023:9;2019:19;2000:39;:::i;:::-;1990:49;;2086:3;2075:9;2071:19;2058:33;2048:43;;2142:3;2131:9;2127:19;2114:33;2170:18;2162:6;2159:30;2156:50;;;2202:1;2199;2192:12;2156:50;2241:58;2291:7;2282:6;2271:9;2267:22;2241:58;:::i;:::-;2215:84;;2318:8;2308:18;;;2345:8;2335:18;;;1382:977;;;;;;;;;;;;;:::o;2364:1121::-;2524:6;2532;2540;2548;2556;2564;2572;2580;2588;2596;2604:7;2613;2667:3;2655:9;2646:7;2642:23;2638:33;2635:53;;;2684:1;2681;2674:12;2635:53;2707:29;2726:9;2707:29;:::i;:::-;2697:39;;2783:2;2772:9;2768:18;2755:32;2745:42;;2834:2;2823:9;2819:18;2806:32;2796:42;;2885:2;2874:9;2870:18;2857:32;2847:42;;2936:3;2925:9;2921:19;2908:33;2898:43;;2960:39;2994:3;2983:9;2979:19;2960:39;:::i;:::-;2950:49;;3018:39;3052:3;3041:9;3037:19;3018:39;:::i;:::-;3008:49;;3104:3;3093:9;3089:19;3076:33;3066:43;;3156:3;3145:9;3141:19;3128:33;3118:43;;3208:3;3197:9;3193:19;3180:33;3170:43;;3263:18;3256:3;3245:9;3241:19;3228:33;3225:57;3222:77;;;3295:1;3292;3285:12;3222:77;3336:85;3413:7;3405:3;3394:9;3390:19;3377:33;3366:9;3362:49;3336:85;:::i;:::-;3441:9;3430:20;;3470:9;3459:20;;;;2364:1121;;;;;;;;;;;;;;:::o;3490:472::-;3585:6;3593;3601;3609;3617;3670:3;3658:9;3649:7;3645:23;3641:33;3638:53;;;3687:1;3684;3677:12;3638:53;3710:29;3729:9;3710:29;:::i;:::-;3700:39;;3758:38;3792:2;3781:9;3777:18;3758:38;:::i;:::-;3748:48;;3815:38;3849:2;3838:9;3834:18;3815:38;:::i;:::-;3490:472;;;;-1:-1:-1;3805:48:36;;3900:2;3885:18;;3872:32;;-1:-1:-1;3951:3:36;3936:19;3923:33;;3490:472;-1:-1:-1;;3490:472:36:o;3967:180::-;4026:6;4079:2;4067:9;4058:7;4054:23;4050:32;4047:52;;;4095:1;4092;4085:12;4047:52;-1:-1:-1;4118:23:36;;3967:180;-1:-1:-1;3967:180:36:o;4152:156::-;4218:20;;4278:4;4267:16;;4257:27;;4247:55;;4298:1;4295;4288:12;4313:256;4379:6;4387;4440:2;4428:9;4419:7;4415:23;4411:32;4408:52;;;4456:1;4453;4446:12;4408:52;4479:27;4496:9;4479:27;:::i;:::-;4469:37;;4525:38;4559:2;4548:9;4544:18;4525:38;:::i;:::-;4515:48;;4313:256;;;;;:::o;4574:::-;4640:6;4648;4701:2;4689:9;4680:7;4676:23;4672:32;4669:52;;;4717:1;4714;4707:12;4669:52;4740:29;4759:9;4740:29;:::i;:::-;4730:39;;4788:36;4820:2;4809:9;4805:18;4788:36;:::i;4835:632::-;5006:2;5058:21;;;5128:13;;5031:18;;;5150:22;;;4977:4;;5006:2;5229:15;;;;5203:2;5188:18;;;4977:4;5272:169;5286:6;5283:1;5280:13;5272:169;;;5347:13;;5335:26;;5416:15;;;;5381:12;;;;5308:1;5301:9;5272:169;;;-1:-1:-1;5458:3:36;;4835:632;-1:-1:-1;;;;;;4835:632:36:o;5472:367::-;5535:8;5545:6;5599:3;5592:4;5584:6;5580:17;5576:27;5566:55;;5617:1;5614;5607:12;5566:55;-1:-1:-1;5640:20:36;;5683:18;5672:30;;5669:50;;;5715:1;5712;5705:12;5669:50;5752:4;5744:6;5740:17;5728:29;;5812:3;5805:4;5795:6;5792:1;5788:14;5780:6;5776:27;5772:38;5769:47;5766:67;;;5829:1;5826;5819:12;5844:511;5939:6;5947;5955;6008:2;5996:9;5987:7;5983:23;5979:32;5976:52;;;6024:1;6021;6014:12;5976:52;6047:29;6066:9;6047:29;:::i;:::-;6037:39;;6127:2;6116:9;6112:18;6099:32;6154:18;6146:6;6143:30;6140:50;;;6186:1;6183;6176:12;6140:50;6225:70;6287:7;6278:6;6267:9;6263:22;6225:70;:::i;:::-;5844:511;;6314:8;;-1:-1:-1;6199:96:36;;-1:-1:-1;;;;5844:511:36:o;6360:250::-;6445:1;6455:113;6469:6;6466:1;6463:13;6455:113;;;6545:11;;;6539:18;6526:11;;;6519:39;6491:2;6484:10;6455:113;;;-1:-1:-1;;6602:1:36;6584:16;;6577:27;6360:250::o;6615:1130::-;6775:4;6804:2;6844;6833:9;6829:18;6874:2;6863:9;6856:21;6897:6;6932;6926:13;6963:6;6955;6948:22;7001:2;6990:9;6986:18;6979:25;;7063:2;7053:6;7050:1;7046:14;7035:9;7031:30;7027:39;7013:53;;7101:2;7093:6;7089:15;7122:1;7132:584;7146:6;7143:1;7140:13;7132:584;;;7235:66;7223:9;7215:6;7211:22;7207:95;7202:3;7195:108;7332:6;7326:13;7374:2;7368:9;7405:8;7397:6;7390:24;7427:74;7492:8;7487:2;7479:6;7475:15;7470:2;7466;7462:11;7427:74;:::i;:::-;7558:2;7544:17;7563:66;7540:90;7528:103;;;;7524:112;;;-1:-1:-1;7694:12:36;;;;7659:15;;;;7168:1;7161:9;7132:584;;;-1:-1:-1;7733:6:36;;6615:1130;-1:-1:-1;;;;;;;6615:1130:36:o;7750:547::-;7854:6;7862;7870;7878;7886;7894;7947:3;7935:9;7926:7;7922:23;7918:33;7915:53;;;7964:1;7961;7954:12;7915:53;7987:29;8006:9;7987:29;:::i;:::-;7977:39;;8035:38;8069:2;8058:9;8054:18;8035:38;:::i;:::-;8025:48;;8092:38;8126:2;8115:9;8111:18;8092:38;:::i;:::-;8082:48;;8149:38;8183:2;8172:9;8168:18;8149:38;:::i;:::-;8139:48;;8234:3;8223:9;8219:19;8206:33;8196:43;;8286:3;8275:9;8271:19;8258:33;8248:43;;7750:547;;;;;;;;:::o;8636:254::-;8704:6;8712;8765:2;8753:9;8744:7;8740:23;8736:32;8733:52;;;8781:1;8778;8771:12;8733:52;8804:29;8823:9;8804:29;:::i;:::-;8794:39;8880:2;8865:18;;;;8852:32;;-1:-1:-1;;;8636:254:36:o;9096:616::-;9209:6;9217;9225;9233;9241;9249;9257;9310:3;9298:9;9289:7;9285:23;9281:33;9278:53;;;9327:1;9324;9317:12;9278:53;9363:9;9350:23;9340:33;;9392:38;9426:2;9415:9;9411:18;9392:38;:::i;:::-;9382:48;;9449:38;9483:2;9472:9;9468:18;9449:38;:::i;:::-;9439:48;;9506:38;9540:2;9529:9;9525:18;9506:38;:::i;:::-;9496:48;;9563:39;9597:3;9586:9;9582:19;9563:39;:::i;:::-;9553:49;;9649:3;9638:9;9634:19;9621:33;9611:43;;9701:3;9690:9;9686:19;9673:33;9663:43;;9096:616;;;;;;;;;;:::o;10052:334::-;10129:6;10137;10145;10198:2;10186:9;10177:7;10173:23;10169:32;10166:52;;;10214:1;10211;10204:12;10166:52;10237:29;10256:9;10237:29;:::i;:::-;10227:39;;10285:38;10319:2;10308:9;10304:18;10285:38;:::i;:::-;10275:48;;10342:38;10376:2;10365:9;10361:18;10342:38;:::i;:::-;10332:48;;10052:334;;;;;:::o;10391:322::-;10468:6;10476;10484;10537:2;10525:9;10516:7;10512:23;10508:32;10505:52;;;10553:1;10550;10543:12;10505:52;10576:29;10595:9;10576:29;:::i;:::-;10566:39;10652:2;10637:18;;10624:32;;-1:-1:-1;10703:2:36;10688:18;;;10675:32;;10391:322;-1:-1:-1;;;10391:322:36:o;10718:478::-;10813:6;10821;10829;10837;10845;10898:3;10886:9;10877:7;10873:23;10869:33;10866:53;;;10915:1;10912;10905:12;10866:53;10938:29;10957:9;10938:29;:::i;:::-;10928:39;;10986:38;11020:2;11009:9;11005:18;10986:38;:::i;:::-;10976:48;;11043:38;11077:2;11066:9;11062:18;11043:38;:::i;:::-;11033:48;;11100:38;11134:2;11123:9;11119:18;11100:38;:::i;:::-;10718:478;;;;-1:-1:-1;10718:478:36;;11185:3;11170:19;11157:33;;10718:478;-1:-1:-1;;10718:478:36:o;11201:164::-;11270:5;11315:2;11306:6;11301:3;11297:16;11293:25;11290:45;;;11331:1;11328;11321:12;11290:45;-1:-1:-1;11353:6:36;11201:164;-1:-1:-1;11201:164:36:o;11370:167::-;11437:20;;11497:14;11486:26;;11476:37;;11466:65;;11527:1;11524;11517:12;11542:1130;11728:6;11736;11744;11752;11760;11768;11776;11784;11792;11800;11808:7;11862:3;11850:9;11841:7;11837:23;11833:33;11830:53;;;11879:1;11876;11869:12;11830:53;11919:9;11906:23;11952:18;11944:6;11941:30;11938:50;;;11984:1;11981;11974:12;11938:50;12023:58;12073:7;12064:6;12053:9;12049:22;12023:58;:::i;:::-;12100:8;;-1:-1:-1;11997:84:36;-1:-1:-1;12154:72:36;;-1:-1:-1;12218:7:36;12213:2;12198:18;;12154:72;:::i;:::-;12144:82;;12245:39;12279:3;12268:9;12264:19;12245:39;:::i;:::-;12235:49;;12331:3;12320:9;12316:19;12303:33;12293:43;;12355:39;12389:3;12378:9;12374:19;12355:39;:::i;:::-;12345:49;;12413:39;12447:3;12436:9;12432:19;12413:39;:::i;:::-;12403:49;;12499:3;12488:9;12484:19;12471:33;12461:43;;12523:38;12556:3;12545:9;12541:19;12523:38;:::i;:::-;12513:48;;12608:3;12597:9;12593:19;12580:33;12570:43;;12661:3;12650:9;12646:19;12633:33;12622:44;;11542:1130;;;;;;;;;;;;;;:::o;12940:472::-;13035:6;13043;13051;13059;13067;13120:3;13108:9;13099:7;13095:23;13091:33;13088:53;;;13137:1;13134;13127:12;13088:53;13160:29;13179:9;13160:29;:::i;:::-;13150:39;;13208:38;13242:2;13231:9;13227:18;13208:38;:::i;:::-;13198:48;;13293:2;13282:9;13278:18;13265:32;13255:42;;13316:38;13350:2;13339:9;13335:18;13316:38;:::i;13670:1050::-;13821:6;13829;13837;13845;13853;13861;13869;13877;13885;13893;13901:7;13955:3;13943:9;13934:7;13930:23;13926:33;13923:53;;;13972:1;13969;13962:12;13923:53;13995:29;14014:9;13995:29;:::i;:::-;13985:39;;14071:2;14060:9;14056:18;14043:32;14033:42;;14122:2;14111:9;14107:18;14094:32;14084:42;;14173:2;14162:9;14158:18;14145:32;14135:42;;14196:39;14230:3;14219:9;14215:19;14196:39;:::i;:::-;14186:49;;14254:39;14288:3;14277:9;14273:19;14254:39;:::i;:::-;14244:49;;14340:3;14329:9;14325:19;14312:33;14302:43;;14392:3;14381:9;14377:19;14364:33;14354:43;;14444:3;14433:9;14429:19;14416:33;14406:43;;14500:3;14489:9;14485:19;14472:33;14528:18;14520:6;14517:30;14514:50;;;14560:1;14557;14550:12;14514:50;14600:58;14650:7;14641:6;14630:9;14626:22;14600:58;:::i;:::-;14573:85;;14677:8;14667:18;;;14705:9;14694:20;;;13670:1050;;;;;;;;;;;;;;:::o;14725:328::-;14799:6;14807;14815;14868:2;14856:9;14847:7;14843:23;14839:32;14836:52;;;14884:1;14881;14874:12;14836:52;14907:28;14925:9;14907:28;:::i;:::-;14897:38;;14954:36;14986:2;14975:9;14971:18;14954:36;:::i;15058:816::-;15171:6;15179;15187;15195;15203;15256:2;15244:9;15235:7;15231:23;15227:32;15224:52;;;15272:1;15269;15262:12;15224:52;15312:9;15299:23;15341:18;15382:2;15374:6;15371:14;15368:34;;;15398:1;15395;15388:12;15368:34;15437:58;15487:7;15478:6;15467:9;15463:22;15437:58;:::i;:::-;15514:8;;-1:-1:-1;15411:84:36;-1:-1:-1;15411:84:36;;-1:-1:-1;15568:37:36;15601:2;15586:18;;15568:37;:::i;:::-;15558:47;;15658:2;15647:9;15643:18;15630:32;15614:48;;15687:2;15677:8;15674:16;15671:36;;;15703:1;15700;15693:12;15671:36;;15742:72;15806:7;15795:8;15784:9;15780:24;15742:72;:::i;:::-;15058:816;;;;-1:-1:-1;15058:816:36;;-1:-1:-1;15833:8:36;;15716:98;15058:816;-1:-1:-1;;;15058:816:36:o;15879:409::-;15965:6;15973;15981;15989;16042:3;16030:9;16021:7;16017:23;16013:33;16010:53;;;16059:1;16056;16049:12;16010:53;16082:29;16101:9;16082:29;:::i;:::-;16072:39;;16130:38;16164:2;16153:9;16149:18;16130:38;:::i;:::-;16120:48;;16187:38;16221:2;16210:9;16206:18;16187:38;:::i;:::-;16177:48;;16244:38;16278:2;16267:9;16263:18;16244:38;:::i;:::-;16234:48;;15879:409;;;;;;;:::o;16293:260::-;16361:6;16369;16422:2;16410:9;16401:7;16397:23;16393:32;16390:52;;;16438:1;16435;16428:12;16390:52;16461:29;16480:9;16461:29;:::i;16558:206::-;16626:20;;16686:52;16675:64;;16665:75;;16655:103;;16754:1;16751;16744:12;16769:1064;16918:6;16926;16934;16942;16950;16958;16966;16974;16982;16990;16998:7;17052:3;17040:9;17031:7;17027:23;17023:33;17020:53;;;17069:1;17066;17059:12;17020:53;17105:9;17092:23;17082:33;;17134:38;17168:2;17157:9;17153:18;17134:38;:::i;:::-;17124:48;;17219:2;17208:9;17204:18;17191:32;17181:42;;17270:2;17259:9;17255:18;17242:32;17232:42;;17293:39;17327:3;17316:9;17312:19;17293:39;:::i;:::-;17283:49;;17351:39;17385:3;17374:9;17370:19;17351:39;:::i;:::-;17341:49;;17409:38;17442:3;17431:9;17427:19;17409:38;:::i;:::-;17399:48;;17466:38;17499:3;17488:9;17484:19;17466:38;:::i;:::-;17456:48;;17523:39;17557:3;17546:9;17542:19;17523:39;:::i;:::-;17513:49;;17613:3;17602:9;17598:19;17585:33;17641:18;17633:6;17630:30;17627:50;;;17673:1;17670;17663:12;17838:1071;18036:6;18044;18052;18060;18068;18121:2;18109:9;18100:7;18096:23;18092:32;18089:52;;;18137:1;18134;18127:12;18089:52;18160:29;18179:9;18160:29;:::i;:::-;18150:39;;18240:2;18229:9;18225:18;18212:32;18263:18;18304:2;18296:6;18293:14;18290:34;;;18320:1;18317;18310:12;18290:34;18358:6;18347:9;18343:22;18333:32;;18403:7;18396:4;18392:2;18388:13;18384:27;18374:55;;18425:1;18422;18415:12;18374:55;18465:2;18452:16;18491:2;18483:6;18480:14;18477:34;;;18507:1;18504;18497:12;18477:34;18560:7;18555:2;18545:6;18542:1;18538:14;18534:2;18530:23;18526:32;18523:45;18520:65;;;18581:1;18578;18571:12;18520:65;18612:2;18608;18604:11;18594:21;;18634:6;18624:16;;;18693:2;18682:9;18678:18;18665:32;18649:48;;18722:2;18712:8;18709:16;18706:36;;;18738:1;18735;18728:12;18914:839;19038:6;19046;19054;19062;19070;19078;19086;19094;19147:3;19135:9;19126:7;19122:23;19118:33;19115:53;;;19164:1;19161;19154:12;19115:53;19187:29;19206:9;19187:29;:::i;:::-;19177:39;;19263:2;19252:9;19248:18;19235:32;19225:42;;19314:2;19303:9;19299:18;19286:32;19276:42;;19365:2;19354:9;19350:18;19337:32;19327:42;;19388:39;19422:3;19411:9;19407:19;19388:39;:::i;:::-;19378:49;;19446:39;19480:3;19469:9;19465:19;19446:39;:::i;:::-;19436:49;;19536:3;19525:9;19521:19;19508:33;19564:18;19556:6;19553:30;19550:50;;;19596:1;19593;19586:12;19550:50;19635:58;19685:7;19676:6;19665:9;19661:22;19635:58;:::i;:::-;18914:839;;;;-1:-1:-1;18914:839:36;;-1:-1:-1;18914:839:36;;;;;;19712:8;-1:-1:-1;;;18914:839:36:o;19758:579::-;19859:6;19867;19875;19883;19936:2;19924:9;19915:7;19911:23;19907:32;19904:52;;;19952:1;19949;19942:12;19904:52;19975:28;19993:9;19975:28;:::i;:::-;19965:38;;20022:36;20054:2;20043:9;20039:18;20022:36;:::i;:::-;20012:46;;20109:2;20098:9;20094:18;20081:32;20136:18;20128:6;20125:30;20122:50;;;20168:1;20165;20158:12;20122:50;20207:70;20269:7;20260:6;20249:9;20245:22;20207:70;:::i;:::-;19758:579;;;;-1:-1:-1;20296:8:36;-1:-1:-1;;;;19758:579:36:o;20342:541::-;20446:6;20454;20462;20470;20478;20486;20539:3;20527:9;20518:7;20514:23;20510:33;20507:53;;;20556:1;20553;20546:12;20507:53;20579:29;20598:9;20579:29;:::i;:::-;20569:39;;20627:38;20661:2;20650:9;20646:18;20627:38;:::i;:::-;20617:48;;20712:2;20701:9;20697:18;20684:32;20674:42;;20735:38;20769:2;20758:9;20754:18;20735:38;:::i;21988:681::-;22159:2;22211:21;;;22281:13;;22184:18;;;22303:22;;;22130:4;;22159:2;22382:15;;;;22356:2;22341:18;;;22130:4;22425:218;22439:6;22436:1;22433:13;22425:218;;;22504:13;;22519:42;22500:62;22488:75;;22618:15;;;;22583:12;;;;22461:1;22454:9;22425:218;;22856:403;22942:6;22950;22958;22966;23019:3;23007:9;22998:7;22994:23;22990:33;22987:53;;;23036:1;23033;23026:12;22987:53;23059:29;23078:9;23059:29;:::i;:::-;23049:39;;23107:38;23141:2;23130:9;23126:18;23107:38;:::i;:::-;23097:48;;23164:38;23198:2;23187:9;23183:18;23164:38;:::i;:::-;22856:403;;;;-1:-1:-1;23154:48:36;;23249:2;23234:18;23221:32;;-1:-1:-1;;22856:403:36:o;23449:254::-;23514:6;23522;23575:2;23563:9;23554:7;23550:23;23546:32;23543:52;;;23591:1;23588;23581:12;23543:52;23614:28;23632:9;23614:28;:::i;23708:397::-;23794:6;23802;23810;23818;23871:3;23859:9;23850:7;23846:23;23842:33;23839:53;;;23888:1;23885;23878:12;23839:53;23911:29;23930:9;23911:29;:::i;:::-;23901:39;;23959:38;23993:2;23982:9;23978:18;23959:38;:::i;:::-;23708:397;;23949:48;;-1:-1:-1;;;;24044:2:36;24029:18;;24016:32;;24095:2;24080:18;24067:32;;23708:397::o;24341:258::-;24408:6;24416;24469:2;24457:9;24448:7;24444:23;24440:32;24437:52;;;24485:1;24482;24475:12;24437:52;24508:29;24527:9;24508:29;:::i;:::-;24498:39;;24556:37;24589:2;24578:9;24574:18;24556:37;:::i;24604:482::-;24683:6;24691;24699;24752:2;24740:9;24731:7;24727:23;24723:32;24720:52;;;24768:1;24765;24758:12;24720:52;24808:9;24795:23;24841:18;24833:6;24830:30;24827:50;;;24873:1;24870;24863:12;24827:50;24912:58;24962:7;24953:6;24942:9;24938:22;24912:58;:::i;:::-;24989:8;;-1:-1:-1;24886:84:36;-1:-1:-1;25043:37:36;;-1:-1:-1;25076:2:36;25061:18;;25043:37;:::i;25091:330::-;25166:6;25174;25182;25235:2;25223:9;25214:7;25210:23;25206:32;25203:52;;;25251:1;25248;25241:12;25203:52;25274:29;25293:9;25274:29;:::i;25426:977::-;25568:6;25576;25584;25592;25600;25608;25616;25624;25632;25640;25693:3;25681:9;25672:7;25668:23;25664:33;25661:53;;;25710:1;25707;25700:12;25661:53;25733:29;25752:9;25733:29;:::i;:::-;25723:39;;25809:2;25798:9;25794:18;25781:32;25771:42;;25860:2;25849:9;25845:18;25832:32;25822:42;;25911:2;25900:9;25896:18;25883:32;25873:42;;25934:39;25968:3;25957:9;25953:19;25934:39;:::i;:::-;25924:49;;25992:39;26026:3;26015:9;26011:19;25992:39;:::i;:::-;25982:49;;26078:3;26067:9;26063:19;26050:33;26040:43;;26130:3;26119:9;26115:19;26102:33;26092:43;;26186:3;26175:9;26171:19;26158:33;26214:18;26206:6;26203:30;26200:50;;;26246:1;26243;26236:12;26408:410;26479:6;26487;26540:2;26528:9;26519:7;26515:23;26511:32;26508:52;;;26556:1;26553;26546:12;26508:52;26596:9;26583:23;26629:18;26621:6;26618:30;26615:50;;;26661:1;26658;26651:12;26615:50;26700:58;26750:7;26741:6;26730:9;26726:22;26700:58;:::i;:::-;26777:8;;26674:84;;-1:-1:-1;26408:410:36;-1:-1:-1;;;;26408:410:36:o;27588:545::-;27691:6;27699;27707;27715;27723;27731;27784:3;27772:9;27763:7;27759:23;27755:33;27752:53;;;27801:1;27798;27791:12;27752:53;27837:9;27824:23;27814:33;;27866:38;27900:2;27889:9;27885:18;27866:38;:::i;:::-;27856:48;;27951:2;27940:9;27936:18;27923:32;27913:42;;27974:38;28008:2;27997:9;27993:18;27974:38;:::i;:::-;27964:48;;28031:39;28065:3;28054:9;28050:19;28031:39;:::i;:::-;28021:49;;28089:38;28122:3;28111:9;28107:19;28089:38;:::i;:::-;28079:48;;27588:545;;;;;;;;:::o;28138:117::-;28223:6;28216:5;28212:18;28205:5;28202:29;28192:57;;28245:1;28242;28235:12;28260:535;28350:6;28358;28366;28374;28382;28435:3;28423:9;28414:7;28410:23;28406:33;28403:53;;;28452:1;28449;28442:12;28403:53;28475:29;28494:9;28475:29;:::i;:::-;28465:39;;28523:36;28555:2;28544:9;28540:18;28523:36;:::i;:::-;28513:46;;28578:38;28612:2;28601:9;28597:18;28578:38;:::i;:::-;28568:48;;28635:36;28667:2;28656:9;28652:18;28635:36;:::i;:::-;28625:46;;28721:3;28710:9;28706:19;28693:33;28735:30;28759:5;28735:30;:::i;:::-;28784:5;28774:15;;;28260:535;;;;;;;;:::o;28800:322::-;28874:6;28882;28890;28943:2;28931:9;28922:7;28918:23;28914:32;28911:52;;;28959:1;28956;28949:12;28911:52;28982:28;29000:9;28982:28;:::i;:::-;28972:38;;29029:36;29061:2;29050:9;29046:18;29029:36;:::i;:::-;29019:46;;29112:2;29101:9;29097:18;29084:32;29074:42;;28800:322;;;;;:::o;29127:258::-;29194:6;29202;29255:2;29243:9;29234:7;29230:23;29226:32;29223:52;;;29271:1;29268;29261:12;29223:52;29294:28;29312:9;29294:28;:::i;29390:1059::-;29567:6;29575;29583;29591;29599;29607;29615;29623;29631;29639;29692:3;29680:9;29671:7;29667:23;29663:33;29660:53;;;29709:1;29706;29699:12;29660:53;29749:9;29736:23;29782:18;29774:6;29771:30;29768:50;;;29814:1;29811;29804:12;29768:50;29853:58;29903:7;29894:6;29883:9;29879:22;29853:58;:::i;:::-;29930:8;;-1:-1:-1;29827:84:36;-1:-1:-1;29984:72:36;;-1:-1:-1;30048:7:36;30043:2;30028:18;;29984:72;:::i;:::-;29974:82;;30075:39;30109:3;30098:9;30094:19;30075:39;:::i;:::-;30065:49;;30133:39;30167:3;30156:9;30152:19;30133:39;:::i;:::-;30123:49;;30191:39;30225:3;30214:9;30210:19;30191:39;:::i;:::-;30181:49;;30277:3;30266:9;30262:19;30249:33;30239:43;;30301:38;30334:3;30323:9;30319:19;30301:38;:::i;:::-;30291:48;;30386:3;30375:9;30371:19;30358:33;30348:43;;30438:3;30427:9;30423:19;30410:33;30400:43;;29390:1059;;;;;;;;;;;;;:::o;30454:908::-;30587:6;30595;30603;30611;30619;30627;30635;30643;30651;30704:3;30692:9;30683:7;30679:23;30675:33;30672:53;;;30721:1;30718;30711:12;30672:53;30744:29;30763:9;30744:29;:::i;:::-;30734:39;;30820:2;30809:9;30805:18;30792:32;30782:42;;30871:2;30860:9;30856:18;30843:32;30833:42;;30922:2;30911:9;30907:18;30894:32;30884:42;;30945:39;30979:3;30968:9;30964:19;30945:39;:::i;:::-;30935:49;;31003:39;31037:3;31026:9;31022:19;31003:39;:::i;:::-;30993:49;;31089:3;31078:9;31074:19;31061:33;31051:43;;31145:3;31134:9;31130:19;31117:33;31173:18;31165:6;31162:30;31159:50;;;31205:1;31202;31195:12;31159:50;31244:58;31294:7;31285:6;31274:9;31270:22;31244:58;:::i;:::-;31218:84;;31321:8;31311:18;;;31348:8;31338:18;;;30454:908;;;;;;;;;;;:::o;31367:328::-;31444:6;31452;31460;31513:2;31501:9;31492:7;31488:23;31484:32;31481:52;;;31529:1;31526;31519:12;31481:52;31552:29;31571:9;31552:29;:::i;:::-;31542:39;;31600:38;31634:2;31623:9;31619:18;31600:38;:::i;31882:182::-;31939:6;31992:2;31980:9;31971:7;31967:23;31963:32;31960:52;;;32008:1;32005;31998:12;31960:52;32031:27;32048:9;32031:27;:::i;32069:324::-;32144:6;32152;32160;32213:2;32201:9;32192:7;32188:23;32184:32;32181:52;;;32229:1;32226;32219:12;32181:52;32252:29;32271:9;32252:29;:::i;32398:184::-;32456:6;32509:2;32497:9;32488:7;32484:23;32480:32;32477:52;;;32525:1;32522;32515:12;32477:52;32548:28;32566:9;32548:28;:::i;32587:319::-;32654:6;32662;32715:2;32703:9;32694:7;32690:23;32686:32;32683:52;;;32731:1;32728;32721:12;32683:52;32754:29;32773:9;32754:29;:::i;:::-;32744:39;;32833:2;32822:9;32818:18;32805:32;32846:30;32870:5;32846:30;:::i;:::-;32895:5;32885:15;;;32587:319;;;;;:::o;32911:279::-;32976:9;;;32997:10;;;32994:190;;;33040:77;33037:1;33030:88;33141:4;33138:1;33131:15;33169:4;33166:1;33159:15;33195:184;33247:77;33244:1;33237:88;33344:4;33341:1;33334:15;33368:4;33365:1;33358:15;33384:184;33436:77;33433:1;33426:88;33533:4;33530:1;33523:15;33557:4;33554:1;33547:15;33573:437;33652:1;33648:12;;;;33695;;;33716:61;;33770:4;33762:6;33758:17;33748:27;;33716:61;33823:2;33815:6;33812:14;33792:18;33789:38;33786:218;;33860:77;33857:1;33850:88;33961:4;33958:1;33951:15;33989:4;33986:1;33979:15;34225:277;34292:6;34345:2;34333:9;34324:7;34320:23;34316:32;34313:52;;;34361:1;34358;34351:12;34313:52;34393:9;34387:16;34446:5;34439:13;34432:21;34425:5;34422:32;34412:60;;34468:1;34465;34458:12;34507:474;34696:3;34734:6;34728:13;34750:66;34809:6;34804:3;34797:4;34789:6;34785:17;34750:66;:::i;:::-;34838:16;;34891:6;34883;34838:16;34863:35;34955:1;34917:18;;34944:13;;;-1:-1:-1;34917:18:36;;34507:474;-1:-1:-1;;;34507:474:36:o;36350:381::-;36427:6;36435;36488:2;36476:9;36467:7;36463:23;36459:32;36456:52;;;36504:1;36501;36494:12;36456:52;36536:9;36530:16;36555:30;36579:5;36555:30;:::i;:::-;36654:2;36639:18;;36633:25;36604:5;;-1:-1:-1;36667:32:36;36633:25;36667:32;:::i;37139:287::-;37268:3;37306:6;37300:13;37322:66;37381:6;37376:3;37369:4;37361:6;37357:17;37322:66;:::i;:::-;37404:16;;;;;37139:287;-1:-1:-1;;37139:287:36:o;38740:274::-;38780:1;38806;38796:189;;38841:77;38838:1;38831:88;38942:4;38939:1;38932:15;38970:4;38967:1;38960:15;38796:189;-1:-1:-1;38999:9:36;;38740:274::o

Swarm Source

ipfs://3c722515cb1b94dd8d752f9b83ae90dcbec2eb559da6ba5732cbef23b0953791

Block Transaction Gas Used Reward
view all blocks produced

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

Validator Index Block Amount
View All Withdrawals

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