ETH Price: $2,508.84 (-0.82%)

Transaction Decoder

Block:
20243102 at Jul-05-2024 09:50:11 PM +UTC
Transaction Fee:
0.000125345622816347 ETH $0.31
Gas Used:
60,239 Gas / 2.080805173 Gwei

Account State Difference:

  Address   Before After State Difference Code
(Titan Builder)
8.525522798721354451 Eth8.525522858960354451 Eth0.000000060239
0xbAbaFdd8...A32F88aC1
0xc0e6bcF3...2830f8983
0.002725626242097855 Eth
Nonce: 979
0.002600280619281508 Eth
Nonce: 980
0.000125345622816347

Execution Trace

BasicERC721CWithBasicRoyalties.safeTransferFrom( from=0xc0e6bcF34cd7FBdb39B7d99D280F8592830f8983, to=0x0f236B71cca4F25a01268979a32EE4Fa7736c083, tokenId=6280 )
  • CreatorTokenTransferValidator.applyCollectionTransferPolicy( caller=0xc0e6bcF34cd7FBdb39B7d99D280F8592830f8983, from=0xc0e6bcF34cd7FBdb39B7d99D280F8592830f8983, to=0x0f236B71cca4F25a01268979a32EE4Fa7736c083 )
    safeTransferFrom[ERC721 (ln:987)]
    File 1 of 2: BasicERC721CWithBasicRoyalties
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.4;
    import "./OwnablePermissions.sol";
    import "@openzeppelin/contracts/access/Ownable.sol";
    abstract contract OwnableBasic is OwnablePermissions, Ownable {
        function _requireCallerIsContractOwner() internal view virtual override {
            _checkOwner();
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.4;
    import "@openzeppelin/contracts/utils/Context.sol";
    abstract contract OwnablePermissions is Context {
        function _requireCallerIsContractOwner() internal view virtual;
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.4;
    import "../utils/CreatorTokenBase.sol";
    import "../token/erc721/ERC721OpenZeppelin.sol";
    /**
     * @title ERC721C
     * @author Limit Break, Inc.
     * @notice Extends OpenZeppelin's ERC721 implementation with Creator Token functionality, which
     *         allows the contract owner to update the transfer validation logic by managing a security policy in
     *         an external transfer validation security policy registry.  See {CreatorTokenTransferValidator}.
     */
    abstract contract ERC721C is ERC721OpenZeppelin, CreatorTokenBase {
        function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
            return interfaceId == type(ICreatorToken).interfaceId || super.supportsInterface(interfaceId);
        }
        /// @dev Ties the open-zeppelin _beforeTokenTransfer hook to more granular transfer validation logic
        function _beforeTokenTransfer(
            address from,
            address to,
            uint256 firstTokenId,
            uint256 batchSize) internal virtual override {
            for (uint256 i = 0; i < batchSize;) {
                _validateBeforeTransfer(from, to, firstTokenId + i);
                unchecked {
                    ++i;
                }
            }
        }
        /// @dev Ties the open-zeppelin _afterTokenTransfer hook to more granular transfer validation logic
        function _afterTokenTransfer(
            address from,
            address to,
            uint256 firstTokenId,
            uint256 batchSize) internal virtual override {
            for (uint256 i = 0; i < batchSize;) {
                _validateAfterTransfer(from, to, firstTokenId + i);
                unchecked {
                    ++i;
                }
            }
        }
    }
    /**
     * @title ERC721CInitializable
     * @author Limit Break, Inc.
     * @notice Initializable implementation of ERC721C to allow for EIP-1167 proxy clones.
     */
    abstract contract ERC721CInitializable is ERC721OpenZeppelinInitializable, CreatorTokenBase {
        function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
            return interfaceId == type(ICreatorToken).interfaceId || super.supportsInterface(interfaceId);
        }
        /// @dev Ties the open-zeppelin _beforeTokenTransfer hook to more granular transfer validation logic
        function _beforeTokenTransfer(
            address from,
            address to,
            uint256 firstTokenId,
            uint256 batchSize) internal virtual override {
            for (uint256 i = 0; i < batchSize;) {
                _validateBeforeTransfer(from, to, firstTokenId + i);
                unchecked {
                    ++i;
                }
            }
        }
        /// @dev Ties the open-zeppelin _afterTokenTransfer hook to more granular transfer validation logic
        function _afterTokenTransfer(
            address from,
            address to,
            uint256 firstTokenId,
            uint256 batchSize) internal virtual override {
            for (uint256 i = 0; i < batchSize;) {
                _validateAfterTransfer(from, to, firstTokenId + i);
                unchecked {
                    ++i;
                }
            }
        }
    }// SPDX-License-Identifier: MIT
    pragma solidity ^0.8.4;
    import "../interfaces/ICreatorTokenTransferValidator.sol";
    interface ICreatorToken {
        event TransferValidatorUpdated(address oldValidator, address newValidator);
        function getTransferValidator() external view returns (ICreatorTokenTransferValidator);
        function getSecurityPolicy() external view returns (CollectionSecurityPolicy memory);
        function getWhitelistedOperators() external view returns (address[] memory);
        function getPermittedContractReceivers() external view returns (address[] memory);
        function isOperatorWhitelisted(address operator) external view returns (bool);
        function isContractReceiverPermitted(address receiver) external view returns (bool);
        function isTransferAllowed(address caller, address from, address to) external view returns (bool);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.4;
    import "./IEOARegistry.sol";
    import "./ITransferSecurityRegistry.sol";
    import "./ITransferValidator.sol";
    interface ICreatorTokenTransferValidator is ITransferSecurityRegistry, ITransferValidator, IEOARegistry {}// SPDX-License-Identifier: MIT
    pragma solidity ^0.8.4;
    import "@openzeppelin/contracts/utils/introspection/IERC165.sol";
    interface IEOARegistry is IERC165 {
        function isVerifiedEOA(address account) external view returns (bool);
    }// SPDX-License-Identifier: MIT
    pragma solidity ^0.8.4;
    import "../utils/TransferPolicy.sol";
    interface ITransferSecurityRegistry {
        event AddedToAllowlist(AllowlistTypes indexed kind, uint256 indexed id, address indexed account);
        event CreatedAllowlist(AllowlistTypes indexed kind, uint256 indexed id, string indexed name);
        event ReassignedAllowlistOwnership(AllowlistTypes indexed kind, uint256 indexed id, address indexed newOwner);
        event RemovedFromAllowlist(AllowlistTypes indexed kind, uint256 indexed id, address indexed account);
        event SetAllowlist(AllowlistTypes indexed kind, address indexed collection, uint120 indexed id);
        event SetTransferSecurityLevel(address indexed collection, TransferSecurityLevels level);
        function createOperatorWhitelist(string calldata name) external returns (uint120);
        function createPermittedContractReceiverAllowlist(string calldata name) external returns (uint120);
        function reassignOwnershipOfOperatorWhitelist(uint120 id, address newOwner) external;
        function reassignOwnershipOfPermittedContractReceiverAllowlist(uint120 id, address newOwner) external;
        function renounceOwnershipOfOperatorWhitelist(uint120 id) external;
        function renounceOwnershipOfPermittedContractReceiverAllowlist(uint120 id) external;
        function setTransferSecurityLevelOfCollection(address collection, TransferSecurityLevels level) external;
        function setOperatorWhitelistOfCollection(address collection, uint120 id) external;
        function setPermittedContractReceiverAllowlistOfCollection(address collection, uint120 id) external;
        function addOperatorToWhitelist(uint120 id, address operator) external;
        function addPermittedContractReceiverToAllowlist(uint120 id, address receiver) external;
        function removeOperatorFromWhitelist(uint120 id, address operator) external;
        function removePermittedContractReceiverFromAllowlist(uint120 id, address receiver) external;
        function getCollectionSecurityPolicy(address collection) external view returns (CollectionSecurityPolicy memory);
        function getWhitelistedOperators(uint120 id) external view returns (address[] memory);
        function getPermittedContractReceivers(uint120 id) external view returns (address[] memory);
        function isOperatorWhitelisted(uint120 id, address operator) external view returns (bool);
        function isContractReceiverPermitted(uint120 id, address receiver) external view returns (bool);
    }// SPDX-License-Identifier: MIT
    pragma solidity ^0.8.4;
    import "../utils/TransferPolicy.sol";
    interface ITransferValidator {
        function applyCollectionTransferPolicy(address caller, address from, address to) external view;
    }// SPDX-License-Identifier: MIT
    pragma solidity ^0.8.4;
    import "@openzeppelin/contracts/token/common/ERC2981.sol";
    /**
     * @title BasicRoyaltiesBase
     * @author Limit Break, Inc.
     * @dev Base functionality of an NFT mix-in contract implementing the most basic form of programmable royalties.
     */
    abstract contract BasicRoyaltiesBase is ERC2981 {
        event DefaultRoyaltySet(address indexed receiver, uint96 feeNumerator);
        event TokenRoyaltySet(uint256 indexed tokenId, address indexed receiver, uint96 feeNumerator);
        function _setDefaultRoyalty(address receiver, uint96 feeNumerator) internal virtual override {
            super._setDefaultRoyalty(receiver, feeNumerator);
            emit DefaultRoyaltySet(receiver, feeNumerator);
        }
        function _setTokenRoyalty(uint256 tokenId, address receiver, uint96 feeNumerator) internal virtual override {
            super._setTokenRoyalty(tokenId, receiver, feeNumerator);
            emit TokenRoyaltySet(tokenId, receiver, feeNumerator);
        }
    }
    /**
     * @title BasicRoyalties
     * @author Limit Break, Inc.
     * @notice Constructable BasicRoyalties Contract implementation.
     */
    abstract contract BasicRoyalties is BasicRoyaltiesBase {
        constructor(address receiver, uint96 feeNumerator) {
            _setDefaultRoyalty(receiver, feeNumerator);
        }
    }
    /**
     * @title BasicRoyaltiesInitializable
     * @author Limit Break, Inc.
     * @notice Initializable BasicRoyalties Contract implementation to allow for EIP-1167 clones. 
     */
    abstract contract BasicRoyaltiesInitializable is BasicRoyaltiesBase {}// SPDX-License-Identifier: MIT
    pragma solidity ^0.8.4;
    import "../../access/OwnablePermissions.sol";
    import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
    abstract contract ERC721OpenZeppelinBase is ERC721 {
        // Token name
        string internal _contractName;
        // Token symbol
        string internal _contractSymbol;
        function name() public view virtual override returns (string memory) {
            return _contractName;
        }
        function symbol() public view virtual override returns (string memory) {
            return _contractSymbol;
        }
        function _setNameAndSymbol(string memory name_, string memory symbol_) internal {
            _contractName = name_;
            _contractSymbol = symbol_;
        }
    }
    abstract contract ERC721OpenZeppelin is ERC721OpenZeppelinBase {
        constructor(string memory name_, string memory symbol_) ERC721("", "") {
            _setNameAndSymbol(name_, symbol_);
        }
    }
    abstract contract ERC721OpenZeppelinInitializable is OwnablePermissions, ERC721OpenZeppelinBase {
        error ERC721OpenZeppelinInitializable__AlreadyInitializedERC721();
        /// @notice Specifies whether or not the contract is initialized
        bool private _erc721Initialized;
        /// @dev Initializes parameters of ERC721 tokens.
        /// These cannot be set in the constructor because this contract is optionally compatible with EIP-1167.
        function initializeERC721(string memory name_, string memory symbol_) public {
            _requireCallerIsContractOwner();
            if(_erc721Initialized) {
                revert ERC721OpenZeppelinInitializable__AlreadyInitializedERC721();
            }
            _erc721Initialized = true;
            _setNameAndSymbol(name_, symbol_);
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.4;
    import "../access/OwnablePermissions.sol";
    import "../interfaces/ICreatorToken.sol";
    import "../interfaces/ICreatorTokenTransferValidator.sol";
    import "../utils/TransferValidation.sol";
    import "@openzeppelin/contracts/interfaces/IERC165.sol";
    /**
     * @title CreatorTokenBase
     * @author Limit Break, Inc.
     * @notice CreatorTokenBase is an abstract contract that provides basic functionality for managing token 
     * transfer policies through an implementation of ICreatorTokenTransferValidator. This contract is intended to be used
     * as a base for creator-specific token contracts, enabling customizable transfer restrictions and security policies.
     *
     * <h4>Features:</h4>
     * <ul>Ownable: This contract can have an owner who can set and update the transfer validator.</ul>
     * <ul>TransferValidation: Implements the basic token transfer validation interface.</ul>
     * <ul>ICreatorToken: Implements the interface for creator tokens, providing view functions for token security policies.</ul>
     *
     * <h4>Benefits:</h4>
     * <ul>Provides a flexible and modular way to implement custom token transfer restrictions and security policies.</ul>
     * <ul>Allows creators to enforce policies such as whitelisted operators and permitted contract receivers.</ul>
     * <ul>Can be easily integrated into other token contracts as a base contract.</ul>
     *
     * <h4>Intended Usage:</h4>
     * <ul>Use as a base contract for creator token implementations that require advanced transfer restrictions and 
     *   security policies.</ul>
     * <ul>Set and update the ICreatorTokenTransferValidator implementation contract to enforce desired policies for the 
     *   creator token.</ul>
     */
    abstract contract CreatorTokenBase is OwnablePermissions, TransferValidation, ICreatorToken {
        
        error CreatorTokenBase__InvalidTransferValidatorContract();
        error CreatorTokenBase__SetTransferValidatorFirst();
        address public constant DEFAULT_TRANSFER_VALIDATOR = address(0x0000721C310194CcfC01E523fc93C9cCcFa2A0Ac);
        TransferSecurityLevels public constant DEFAULT_TRANSFER_SECURITY_LEVEL = TransferSecurityLevels.One;
        uint120 public constant DEFAULT_OPERATOR_WHITELIST_ID = uint120(1);
        ICreatorTokenTransferValidator private transferValidator;
        /**
         * @notice Allows the contract owner to set the transfer validator to the official validator contract
         *         and set the security policy to the recommended default settings.
         * @dev    May be overridden to change the default behavior of an individual collection.
         */
        function setToDefaultSecurityPolicy() public virtual {
            _requireCallerIsContractOwner();
            setTransferValidator(DEFAULT_TRANSFER_VALIDATOR);
            ICreatorTokenTransferValidator(DEFAULT_TRANSFER_VALIDATOR).setTransferSecurityLevelOfCollection(address(this), DEFAULT_TRANSFER_SECURITY_LEVEL);
            ICreatorTokenTransferValidator(DEFAULT_TRANSFER_VALIDATOR).setOperatorWhitelistOfCollection(address(this), DEFAULT_OPERATOR_WHITELIST_ID);
        }
        /**
         * @notice Allows the contract owner to set the transfer validator to a custom validator contract
         *         and set the security policy to their own custom settings.
         */
        function setToCustomValidatorAndSecurityPolicy(
            address validator, 
            TransferSecurityLevels level, 
            uint120 operatorWhitelistId, 
            uint120 permittedContractReceiversAllowlistId) public {
            _requireCallerIsContractOwner();
            setTransferValidator(validator);
            ICreatorTokenTransferValidator(validator).
                setTransferSecurityLevelOfCollection(address(this), level);
            ICreatorTokenTransferValidator(validator).
                setOperatorWhitelistOfCollection(address(this), operatorWhitelistId);
            ICreatorTokenTransferValidator(validator).
                setPermittedContractReceiverAllowlistOfCollection(address(this), permittedContractReceiversAllowlistId);
        }
        /**
         * @notice Allows the contract owner to set the security policy to their own custom settings.
         * @dev    Reverts if the transfer validator has not been set.
         */
        function setToCustomSecurityPolicy(
            TransferSecurityLevels level, 
            uint120 operatorWhitelistId, 
            uint120 permittedContractReceiversAllowlistId) public {
            _requireCallerIsContractOwner();
            ICreatorTokenTransferValidator validator = getTransferValidator();
            if (address(validator) == address(0)) {
                revert CreatorTokenBase__SetTransferValidatorFirst();
            }
            validator.setTransferSecurityLevelOfCollection(address(this), level);
            validator.setOperatorWhitelistOfCollection(address(this), operatorWhitelistId);
            validator.setPermittedContractReceiverAllowlistOfCollection(address(this), permittedContractReceiversAllowlistId);
        }
        /**
         * @notice Sets the transfer validator for the token contract.
         *
         * @dev    Throws when provided validator contract is not the zero address and doesn't support 
         *         the ICreatorTokenTransferValidator interface. 
         * @dev    Throws when the caller is not the contract owner.
         *
         * @dev    <h4>Postconditions:</h4>
         *         1. The transferValidator address is updated.
         *         2. The `TransferValidatorUpdated` event is emitted.
         *
         * @param transferValidator_ The address of the transfer validator contract.
         */
        function setTransferValidator(address transferValidator_) public {
            _requireCallerIsContractOwner();
            bool isValidTransferValidator = false;
            if(transferValidator_.code.length > 0) {
                try IERC165(transferValidator_).supportsInterface(type(ICreatorTokenTransferValidator).interfaceId) 
                    returns (bool supportsInterface) {
                    isValidTransferValidator = supportsInterface;
                } catch {}
            }
            if(transferValidator_ != address(0) && !isValidTransferValidator) {
                revert CreatorTokenBase__InvalidTransferValidatorContract();
            }
            emit TransferValidatorUpdated(address(transferValidator), transferValidator_);
            transferValidator = ICreatorTokenTransferValidator(transferValidator_);
        }
        /**
         * @notice Returns the transfer validator contract address for this token contract.
         */
        function getTransferValidator() public view override returns (ICreatorTokenTransferValidator) {
            return transferValidator;
        }
        /**
         * @notice Returns the security policy for this token contract, which includes:
         *         Transfer security level, operator whitelist id, permitted contract receiver allowlist id.
         */
        function getSecurityPolicy() public view override returns (CollectionSecurityPolicy memory) {
            if (address(transferValidator) != address(0)) {
                return transferValidator.getCollectionSecurityPolicy(address(this));
            }
            return CollectionSecurityPolicy({
                transferSecurityLevel: TransferSecurityLevels.Zero,
                operatorWhitelistId: 0,
                permittedContractReceiversId: 0
            });
        }
        /**
         * @notice Returns the list of all whitelisted operators for this token contract.
         * @dev    This can be an expensive call and should only be used in view-only functions.
         */
        function getWhitelistedOperators() public view override returns (address[] memory) {
            if (address(transferValidator) != address(0)) {
                return transferValidator.getWhitelistedOperators(
                    transferValidator.getCollectionSecurityPolicy(address(this)).operatorWhitelistId);
            }
            return new address[](0);
        }
        /**
         * @notice Returns the list of permitted contract receivers for this token contract.
         * @dev    This can be an expensive call and should only be used in view-only functions.
         */
        function getPermittedContractReceivers() public view override returns (address[] memory) {
            if (address(transferValidator) != address(0)) {
                return transferValidator.getPermittedContractReceivers(
                    transferValidator.getCollectionSecurityPolicy(address(this)).permittedContractReceiversId);
            }
            return new address[](0);
        }
        /**
         * @notice Checks if an operator is whitelisted for this token contract.
         * @param operator The address of the operator to check.
         */
        function isOperatorWhitelisted(address operator) public view override returns (bool) {
            if (address(transferValidator) != address(0)) {
                return transferValidator.isOperatorWhitelisted(
                    transferValidator.getCollectionSecurityPolicy(address(this)).operatorWhitelistId, operator);
            }
            return false;
        }
        /**
         * @notice Checks if a contract receiver is permitted for this token contract.
         * @param receiver The address of the receiver to check.
         */
        function isContractReceiverPermitted(address receiver) public view override returns (bool) {
            if (address(transferValidator) != address(0)) {
                return transferValidator.isContractReceiverPermitted(
                    transferValidator.getCollectionSecurityPolicy(address(this)).permittedContractReceiversId, receiver);
            }
            return false;
        }
        /**
         * @notice Determines if a transfer is allowed based on the token contract's security policy.  Use this function
         *         to simulate whether or not a transfer made by the specified `caller` from the `from` address to the `to`
         *         address would be allowed by this token's security policy.
         *
         * @notice This function only checks the security policy restrictions and does not check whether token ownership
         *         or approvals are in place. 
         *
         * @param caller The address of the simulated caller.
         * @param from   The address of the sender.
         * @param to     The address of the receiver.
         * @return       True if the transfer is allowed, false otherwise.
         */
        function isTransferAllowed(address caller, address from, address to) public view override returns (bool) {
            if (address(transferValidator) != address(0)) {
                try transferValidator.applyCollectionTransferPolicy(caller, from, to) {
                    return true;
                } catch {
                    return false;
                }
            }
            return true;
        }
        /**
         * @dev Pre-validates a token transfer, reverting if the transfer is not allowed by this token's security policy.
         *      Inheriting contracts are responsible for overriding the _beforeTokenTransfer function, or its equivalent
         *      and calling _validateBeforeTransfer so that checks can be properly applied during token transfers.
         *
         * @dev Throws when the transfer doesn't comply with the collection's transfer policy, if the transferValidator is
         *      set to a non-zero address.
         *
         * @param caller  The address of the caller.
         * @param from    The address of the sender.
         * @param to      The address of the receiver.
         */
        function _preValidateTransfer(
            address caller, 
            address from, 
            address to, 
            uint256 /*tokenId*/, 
            uint256 /*value*/) internal virtual override {
            if (address(transferValidator) != address(0)) {
                transferValidator.applyCollectionTransferPolicy(caller, from, to);
            }
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.4;
    enum AllowlistTypes {
        Operators,
        PermittedContractReceivers
    }
    enum ReceiverConstraints {
        None,
        NoCode,
        EOA
    }
    enum CallerConstraints {
        None,
        OperatorWhitelistEnableOTC,
        OperatorWhitelistDisableOTC
    }
    enum StakerConstraints {
        None,
        CallerIsTxOrigin,
        EOA
    }
    enum TransferSecurityLevels {
        Zero,
        One,
        Two,
        Three,
        Four,
        Five,
        Six
    }
    struct TransferSecurityPolicy {
        CallerConstraints callerConstraints;
        ReceiverConstraints receiverConstraints;
    }
    struct CollectionSecurityPolicy {
        TransferSecurityLevels transferSecurityLevel;
        uint120 operatorWhitelistId;
        uint120 permittedContractReceiversId;
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.4;
    import "@openzeppelin/contracts/utils/Context.sol";
    /**
     * @title TransferValidation
     * @author Limit Break, Inc.
     * @notice A mix-in that can be combined with ERC-721 contracts to provide more granular hooks.
     * Openzeppelin's ERC721 contract only provides hooks for before and after transfer.  This allows
     * developers to validate or customize transfers within the context of a mint, a burn, or a transfer.
     */
    abstract contract TransferValidation is Context {
        
        error ShouldNotMintToBurnAddress();
        /// @dev Inheriting contracts should call this function in the _beforeTokenTransfer function to get more granular hooks.
        function _validateBeforeTransfer(address from, address to, uint256 tokenId) internal virtual {
            bool fromZeroAddress = from == address(0);
            bool toZeroAddress = to == address(0);
            if(fromZeroAddress && toZeroAddress) {
                revert ShouldNotMintToBurnAddress();
            } else if(fromZeroAddress) {
                _preValidateMint(_msgSender(), to, tokenId, msg.value);
            } else if(toZeroAddress) {
                _preValidateBurn(_msgSender(), from, tokenId, msg.value);
            } else {
                _preValidateTransfer(_msgSender(), from, to, tokenId, msg.value);
            }
        }
        /// @dev Inheriting contracts should call this function in the _afterTokenTransfer function to get more granular hooks.
        function _validateAfterTransfer(address from, address to, uint256 tokenId) internal virtual {
            bool fromZeroAddress = from == address(0);
            bool toZeroAddress = to == address(0);
            if(fromZeroAddress && toZeroAddress) {
                revert ShouldNotMintToBurnAddress();
            } else if(fromZeroAddress) {
                _postValidateMint(_msgSender(), to, tokenId, msg.value);
            } else if(toZeroAddress) {
                _postValidateBurn(_msgSender(), from, tokenId, msg.value);
            } else {
                _postValidateTransfer(_msgSender(), from, to, tokenId, msg.value);
            }
        }
        /// @dev Optional validation hook that fires before a mint
        function _preValidateMint(address caller, address to, uint256 tokenId, uint256 value) internal virtual {}
        /// @dev Optional validation hook that fires after a mint
        function _postValidateMint(address caller, address to, uint256 tokenId, uint256 value) internal virtual {}
        /// @dev Optional validation hook that fires before a burn
        function _preValidateBurn(address caller, address from, uint256 tokenId, uint256 value) internal virtual {}
        /// @dev Optional validation hook that fires after a burn
        function _postValidateBurn(address caller, address from, uint256 tokenId, uint256 value) internal virtual {}
        /// @dev Optional validation hook that fires before a transfer
        function _preValidateTransfer(address caller, address from, address to, uint256 tokenId, uint256 value) internal virtual {}
        /// @dev Optional validation hook that fires after a transfer
        function _postValidateTransfer(address caller, address from, address to, uint256 tokenId, uint256 value) internal virtual {}
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)
    pragma solidity ^0.8.0;
    import "../utils/Context.sol";
    /**
     * @dev Contract module which provides a basic access control mechanism, where
     * there is an account (an owner) that can be granted exclusive access to
     * specific functions.
     *
     * By default, the owner account will be the one that deploys the contract. This
     * can later be changed with {transferOwnership}.
     *
     * This module is used through inheritance. It will make available the modifier
     * `onlyOwner`, which can be applied to your functions to restrict their use to
     * the owner.
     */
    abstract contract Ownable is Context {
        address private _owner;
        event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
        /**
         * @dev Initializes the contract setting the deployer as the initial owner.
         */
        constructor() {
            _transferOwnership(_msgSender());
        }
        /**
         * @dev Throws if called by any account other than the owner.
         */
        modifier onlyOwner() {
            _checkOwner();
            _;
        }
        /**
         * @dev Returns the address of the current owner.
         */
        function owner() public view virtual returns (address) {
            return _owner;
        }
        /**
         * @dev Throws if the sender is not the owner.
         */
        function _checkOwner() internal view virtual {
            require(owner() == _msgSender(), "Ownable: caller is not the owner");
        }
        /**
         * @dev Leaves the contract without owner. It will not be possible to call
         * `onlyOwner` functions. 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 {
            require(newOwner != address(0), "Ownable: new owner is the zero address");
            _transferOwnership(newOwner);
        }
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`).
         * Internal function without access restriction.
         */
        function _transferOwnership(address newOwner) internal virtual {
            address oldOwner = _owner;
            _owner = newOwner;
            emit OwnershipTransferred(oldOwner, newOwner);
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (interfaces/IERC165.sol)
    pragma solidity ^0.8.0;
    import "../utils/introspection/IERC165.sol";
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC2981.sol)
    pragma solidity ^0.8.0;
    import "../utils/introspection/IERC165.sol";
    /**
     * @dev Interface for the NFT Royalty Standard.
     *
     * A standardized way to retrieve royalty payment information for non-fungible tokens (NFTs) to enable universal
     * support for royalty payments across all NFT marketplaces and ecosystem participants.
     *
     * _Available since v4.5._
     */
    interface IERC2981 is IERC165 {
        /**
         * @dev Returns how much royalty is owed and to whom, based on a sale price that may be denominated in any unit of
         * exchange. The royalty amount is denominated and should be paid in that same unit of exchange.
         */
        function royaltyInfo(
            uint256 tokenId,
            uint256 salePrice
        ) external view returns (address receiver, uint256 royaltyAmount);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.9.3) (metatx/ERC2771Context.sol)
    pragma solidity ^0.8.9;
    import "../utils/Context.sol";
    /**
     * @dev Context variant with ERC2771 support.
     */
    abstract contract ERC2771Context is Context {
        /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
        address private immutable _trustedForwarder;
        /// @custom:oz-upgrades-unsafe-allow constructor
        constructor(address trustedForwarder) {
            _trustedForwarder = trustedForwarder;
        }
        function isTrustedForwarder(address forwarder) public view virtual returns (bool) {
            return forwarder == _trustedForwarder;
        }
        function _msgSender() internal view virtual override returns (address sender) {
            if (isTrustedForwarder(msg.sender) && msg.data.length >= 20) {
                // The assembly code is more direct than the Solidity version using `abi.decode`.
                /// @solidity memory-safe-assembly
                assembly {
                    sender := shr(96, calldataload(sub(calldatasize(), 20)))
                }
            } else {
                return super._msgSender();
            }
        }
        function _msgData() internal view virtual override returns (bytes calldata) {
            if (isTrustedForwarder(msg.sender) && msg.data.length >= 20) {
                return msg.data[:msg.data.length - 20];
            } else {
                return super._msgData();
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol)
    pragma solidity ^0.8.0;
    import "../utils/Context.sol";
    /**
     * @dev Contract module which allows children to implement an emergency stop
     * mechanism that can be triggered by an authorized account.
     *
     * This module is used through inheritance. It will make available the
     * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
     * the functions of your contract. Note that they will not be pausable by
     * simply including this module, only once the modifiers are put in place.
     */
    abstract contract Pausable is Context {
        /**
         * @dev Emitted when the pause is triggered by `account`.
         */
        event Paused(address account);
        /**
         * @dev Emitted when the pause is lifted by `account`.
         */
        event Unpaused(address account);
        bool private _paused;
        /**
         * @dev Initializes the contract in unpaused state.
         */
        constructor() {
            _paused = false;
        }
        /**
         * @dev Modifier to make a function callable only when the contract is not paused.
         *
         * Requirements:
         *
         * - The contract must not be paused.
         */
        modifier whenNotPaused() {
            _requireNotPaused();
            _;
        }
        /**
         * @dev Modifier to make a function callable only when the contract is paused.
         *
         * Requirements:
         *
         * - The contract must be paused.
         */
        modifier whenPaused() {
            _requirePaused();
            _;
        }
        /**
         * @dev Returns true if the contract is paused, and false otherwise.
         */
        function paused() public view virtual returns (bool) {
            return _paused;
        }
        /**
         * @dev Throws if the contract is paused.
         */
        function _requireNotPaused() internal view virtual {
            require(!paused(), "Pausable: paused");
        }
        /**
         * @dev Throws if the contract is not paused.
         */
        function _requirePaused() internal view virtual {
            require(paused(), "Pausable: not paused");
        }
        /**
         * @dev Triggers stopped state.
         *
         * Requirements:
         *
         * - The contract must not be paused.
         */
        function _pause() internal virtual whenNotPaused {
            _paused = true;
            emit Paused(_msgSender());
        }
        /**
         * @dev Returns to normal state.
         *
         * Requirements:
         *
         * - The contract must be paused.
         */
        function _unpause() internal virtual whenPaused {
            _paused = false;
            emit Unpaused(_msgSender());
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.9.0) (token/common/ERC2981.sol)
    pragma solidity ^0.8.0;
    import "../../interfaces/IERC2981.sol";
    import "../../utils/introspection/ERC165.sol";
    /**
     * @dev Implementation of the NFT Royalty Standard, a standardized way to retrieve royalty payment information.
     *
     * Royalty information can be specified globally for all token ids via {_setDefaultRoyalty}, and/or individually for
     * specific token ids via {_setTokenRoyalty}. The latter takes precedence over the first.
     *
     * Royalty is specified as a fraction of sale price. {_feeDenominator} is overridable but defaults to 10000, meaning the
     * fee is specified in basis points by default.
     *
     * IMPORTANT: ERC-2981 only specifies a way to signal royalty information and does not enforce its payment. See
     * https://eips.ethereum.org/EIPS/eip-2981#optional-royalty-payments[Rationale] in the EIP. Marketplaces are expected to
     * voluntarily pay royalties together with sales, but note that this standard is not yet widely supported.
     *
     * _Available since v4.5._
     */
    abstract contract ERC2981 is IERC2981, ERC165 {
        struct RoyaltyInfo {
            address receiver;
            uint96 royaltyFraction;
        }
        RoyaltyInfo private _defaultRoyaltyInfo;
        mapping(uint256 => RoyaltyInfo) private _tokenRoyaltyInfo;
        /**
         * @dev See {IERC165-supportsInterface}.
         */
        function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC165) returns (bool) {
            return interfaceId == type(IERC2981).interfaceId || super.supportsInterface(interfaceId);
        }
        /**
         * @inheritdoc IERC2981
         */
        function royaltyInfo(uint256 tokenId, uint256 salePrice) public view virtual override returns (address, uint256) {
            RoyaltyInfo memory royalty = _tokenRoyaltyInfo[tokenId];
            if (royalty.receiver == address(0)) {
                royalty = _defaultRoyaltyInfo;
            }
            uint256 royaltyAmount = (salePrice * royalty.royaltyFraction) / _feeDenominator();
            return (royalty.receiver, royaltyAmount);
        }
        /**
         * @dev The denominator with which to interpret the fee set in {_setTokenRoyalty} and {_setDefaultRoyalty} as a
         * fraction of the sale price. Defaults to 10000 so fees are expressed in basis points, but may be customized by an
         * override.
         */
        function _feeDenominator() internal pure virtual returns (uint96) {
            return 10000;
        }
        /**
         * @dev Sets the royalty information that all ids in this contract will default to.
         *
         * Requirements:
         *
         * - `receiver` cannot be the zero address.
         * - `feeNumerator` cannot be greater than the fee denominator.
         */
        function _setDefaultRoyalty(address receiver, uint96 feeNumerator) internal virtual {
            require(feeNumerator <= _feeDenominator(), "ERC2981: royalty fee will exceed salePrice");
            require(receiver != address(0), "ERC2981: invalid receiver");
            _defaultRoyaltyInfo = RoyaltyInfo(receiver, feeNumerator);
        }
        /**
         * @dev Removes default royalty information.
         */
        function _deleteDefaultRoyalty() internal virtual {
            delete _defaultRoyaltyInfo;
        }
        /**
         * @dev Sets the royalty information for a specific token id, overriding the global default.
         *
         * Requirements:
         *
         * - `receiver` cannot be the zero address.
         * - `feeNumerator` cannot be greater than the fee denominator.
         */
        function _setTokenRoyalty(uint256 tokenId, address receiver, uint96 feeNumerator) internal virtual {
            require(feeNumerator <= _feeDenominator(), "ERC2981: royalty fee will exceed salePrice");
            require(receiver != address(0), "ERC2981: Invalid parameters");
            _tokenRoyaltyInfo[tokenId] = RoyaltyInfo(receiver, feeNumerator);
        }
        /**
         * @dev Resets royalty information for the token id back to the global default.
         */
        function _resetTokenRoyalty(uint256 tokenId) internal virtual {
            delete _tokenRoyaltyInfo[tokenId];
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC721/ERC721.sol)
    pragma solidity ^0.8.0;
    import "./IERC721.sol";
    import "./IERC721Receiver.sol";
    import "./extensions/IERC721Metadata.sol";
    import "../../utils/Address.sol";
    import "../../utils/Context.sol";
    import "../../utils/Strings.sol";
    import "../../utils/introspection/ERC165.sol";
    /**
     * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
     * the Metadata extension, but not including the Enumerable extension, which is available separately as
     * {ERC721Enumerable}.
     */
    contract ERC721 is Context, ERC165, IERC721, IERC721Metadata {
        using Address for address;
        using Strings for uint256;
        // Token name
        string private _name;
        // Token symbol
        string private _symbol;
        // Mapping from token ID to owner address
        mapping(uint256 => address) private _owners;
        // Mapping owner address to token count
        mapping(address => uint256) private _balances;
        // Mapping from token ID to approved address
        mapping(uint256 => address) private _tokenApprovals;
        // Mapping from owner to operator approvals
        mapping(address => mapping(address => bool)) private _operatorApprovals;
        /**
         * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
         */
        constructor(string memory name_, string memory symbol_) {
            _name = name_;
            _symbol = symbol_;
        }
        /**
         * @dev See {IERC165-supportsInterface}.
         */
        function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
            return
                interfaceId == type(IERC721).interfaceId ||
                interfaceId == type(IERC721Metadata).interfaceId ||
                super.supportsInterface(interfaceId);
        }
        /**
         * @dev See {IERC721-balanceOf}.
         */
        function balanceOf(address owner) public view virtual override returns (uint256) {
            require(owner != address(0), "ERC721: address zero is not a valid owner");
            return _balances[owner];
        }
        /**
         * @dev See {IERC721-ownerOf}.
         */
        function ownerOf(uint256 tokenId) public view virtual override returns (address) {
            address owner = _ownerOf(tokenId);
            require(owner != address(0), "ERC721: invalid token ID");
            return owner;
        }
        /**
         * @dev See {IERC721Metadata-name}.
         */
        function name() public view virtual override returns (string memory) {
            return _name;
        }
        /**
         * @dev See {IERC721Metadata-symbol}.
         */
        function symbol() public view virtual override returns (string memory) {
            return _symbol;
        }
        /**
         * @dev See {IERC721Metadata-tokenURI}.
         */
        function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
            _requireMinted(tokenId);
            string memory baseURI = _baseURI();
            return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";
        }
        /**
         * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
         * token will be the concatenation of the `baseURI` and the `tokenId`. Empty
         * by default, can be overridden in child contracts.
         */
        function _baseURI() internal view virtual returns (string memory) {
            return "";
        }
        /**
         * @dev See {IERC721-approve}.
         */
        function approve(address to, uint256 tokenId) public virtual override {
            address owner = ERC721.ownerOf(tokenId);
            require(to != owner, "ERC721: approval to current owner");
            require(
                _msgSender() == owner || isApprovedForAll(owner, _msgSender()),
                "ERC721: approve caller is not token owner or approved for all"
            );
            _approve(to, tokenId);
        }
        /**
         * @dev See {IERC721-getApproved}.
         */
        function getApproved(uint256 tokenId) public view virtual override returns (address) {
            _requireMinted(tokenId);
            return _tokenApprovals[tokenId];
        }
        /**
         * @dev See {IERC721-setApprovalForAll}.
         */
        function setApprovalForAll(address operator, bool approved) public virtual override {
            _setApprovalForAll(_msgSender(), operator, approved);
        }
        /**
         * @dev See {IERC721-isApprovedForAll}.
         */
        function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
            return _operatorApprovals[owner][operator];
        }
        /**
         * @dev See {IERC721-transferFrom}.
         */
        function transferFrom(address from, address to, uint256 tokenId) public virtual override {
            //solhint-disable-next-line max-line-length
            require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner or approved");
            _transfer(from, to, tokenId);
        }
        /**
         * @dev See {IERC721-safeTransferFrom}.
         */
        function safeTransferFrom(address from, address to, uint256 tokenId) public virtual override {
            safeTransferFrom(from, to, tokenId, "");
        }
        /**
         * @dev See {IERC721-safeTransferFrom}.
         */
        function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public virtual override {
            require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner or approved");
            _safeTransfer(from, to, tokenId, data);
        }
        /**
         * @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.
         *
         * `data` is additional data, it has no specified format and it is sent in call to `to`.
         *
         * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g.
         * implement alternative mechanisms to perform token transfer, such as signature-based.
         *
         * Requirements:
         *
         * - `from` cannot be the zero address.
         * - `to` cannot be the zero address.
         * - `tokenId` token must exist and be owned by `from`.
         * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
         *
         * Emits a {Transfer} event.
         */
        function _safeTransfer(address from, address to, uint256 tokenId, bytes memory data) internal virtual {
            _transfer(from, to, tokenId);
            require(_checkOnERC721Received(from, to, tokenId, data), "ERC721: transfer to non ERC721Receiver implementer");
        }
        /**
         * @dev Returns the owner of the `tokenId`. Does NOT revert if token doesn't exist
         */
        function _ownerOf(uint256 tokenId) internal view virtual returns (address) {
            return _owners[tokenId];
        }
        /**
         * @dev Returns whether `tokenId` exists.
         *
         * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
         *
         * Tokens start existing when they are minted (`_mint`),
         * and stop existing when they are burned (`_burn`).
         */
        function _exists(uint256 tokenId) internal view virtual returns (bool) {
            return _ownerOf(tokenId) != address(0);
        }
        /**
         * @dev Returns whether `spender` is allowed to manage `tokenId`.
         *
         * Requirements:
         *
         * - `tokenId` must exist.
         */
        function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {
            address owner = ERC721.ownerOf(tokenId);
            return (spender == owner || isApprovedForAll(owner, spender) || getApproved(tokenId) == spender);
        }
        /**
         * @dev Safely mints `tokenId` and transfers it to `to`.
         *
         * Requirements:
         *
         * - `tokenId` must not exist.
         * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
         *
         * Emits a {Transfer} event.
         */
        function _safeMint(address to, uint256 tokenId) internal virtual {
            _safeMint(to, tokenId, "");
        }
        /**
         * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
         * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
         */
        function _safeMint(address to, uint256 tokenId, bytes memory data) internal virtual {
            _mint(to, tokenId);
            require(
                _checkOnERC721Received(address(0), to, tokenId, data),
                "ERC721: transfer to non ERC721Receiver implementer"
            );
        }
        /**
         * @dev Mints `tokenId` and transfers it to `to`.
         *
         * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
         *
         * Requirements:
         *
         * - `tokenId` must not exist.
         * - `to` cannot be the zero address.
         *
         * Emits a {Transfer} event.
         */
        function _mint(address to, uint256 tokenId) internal virtual {
            require(to != address(0), "ERC721: mint to the zero address");
            require(!_exists(tokenId), "ERC721: token already minted");
            _beforeTokenTransfer(address(0), to, tokenId, 1);
            // Check that tokenId was not minted by `_beforeTokenTransfer` hook
            require(!_exists(tokenId), "ERC721: token already minted");
            unchecked {
                // Will not overflow unless all 2**256 token ids are minted to the same owner.
                // Given that tokens are minted one by one, it is impossible in practice that
                // this ever happens. Might change if we allow batch minting.
                // The ERC fails to describe this case.
                _balances[to] += 1;
            }
            _owners[tokenId] = to;
            emit Transfer(address(0), to, tokenId);
            _afterTokenTransfer(address(0), to, tokenId, 1);
        }
        /**
         * @dev Destroys `tokenId`.
         * The approval is cleared when the token is burned.
         * This is an internal function that does not check if the sender is authorized to operate on the token.
         *
         * Requirements:
         *
         * - `tokenId` must exist.
         *
         * Emits a {Transfer} event.
         */
        function _burn(uint256 tokenId) internal virtual {
            address owner = ERC721.ownerOf(tokenId);
            _beforeTokenTransfer(owner, address(0), tokenId, 1);
            // Update ownership in case tokenId was transferred by `_beforeTokenTransfer` hook
            owner = ERC721.ownerOf(tokenId);
            // Clear approvals
            delete _tokenApprovals[tokenId];
            unchecked {
                // Cannot overflow, as that would require more tokens to be burned/transferred
                // out than the owner initially received through minting and transferring in.
                _balances[owner] -= 1;
            }
            delete _owners[tokenId];
            emit Transfer(owner, address(0), tokenId);
            _afterTokenTransfer(owner, address(0), tokenId, 1);
        }
        /**
         * @dev Transfers `tokenId` from `from` to `to`.
         *  As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
         *
         * Requirements:
         *
         * - `to` cannot be the zero address.
         * - `tokenId` token must be owned by `from`.
         *
         * Emits a {Transfer} event.
         */
        function _transfer(address from, address to, uint256 tokenId) internal virtual {
            require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner");
            require(to != address(0), "ERC721: transfer to the zero address");
            _beforeTokenTransfer(from, to, tokenId, 1);
            // Check that tokenId was not transferred by `_beforeTokenTransfer` hook
            require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner");
            // Clear approvals from the previous owner
            delete _tokenApprovals[tokenId];
            unchecked {
                // `_balances[from]` cannot overflow for the same reason as described in `_burn`:
                // `from`'s balance is the number of token held, which is at least one before the current
                // transfer.
                // `_balances[to]` could overflow in the conditions described in `_mint`. That would require
                // all 2**256 token ids to be minted, which in practice is impossible.
                _balances[from] -= 1;
                _balances[to] += 1;
            }
            _owners[tokenId] = to;
            emit Transfer(from, to, tokenId);
            _afterTokenTransfer(from, to, tokenId, 1);
        }
        /**
         * @dev Approve `to` to operate on `tokenId`
         *
         * Emits an {Approval} event.
         */
        function _approve(address to, uint256 tokenId) internal virtual {
            _tokenApprovals[tokenId] = to;
            emit Approval(ERC721.ownerOf(tokenId), to, tokenId);
        }
        /**
         * @dev Approve `operator` to operate on all of `owner` tokens
         *
         * Emits an {ApprovalForAll} event.
         */
        function _setApprovalForAll(address owner, address operator, bool approved) internal virtual {
            require(owner != operator, "ERC721: approve to caller");
            _operatorApprovals[owner][operator] = approved;
            emit ApprovalForAll(owner, operator, approved);
        }
        /**
         * @dev Reverts if the `tokenId` has not been minted yet.
         */
        function _requireMinted(uint256 tokenId) internal view virtual {
            require(_exists(tokenId), "ERC721: invalid token ID");
        }
        /**
         * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.
         * The call is not executed if the target address is not a contract.
         *
         * @param from address representing the previous owner of the given token ID
         * @param to target address that will receive the tokens
         * @param tokenId uint256 ID of the token to be transferred
         * @param data bytes optional data to send along with the call
         * @return bool whether the call correctly returned the expected magic value
         */
        function _checkOnERC721Received(
            address from,
            address to,
            uint256 tokenId,
            bytes memory data
        ) private returns (bool) {
            if (to.isContract()) {
                try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, data) returns (bytes4 retval) {
                    return retval == IERC721Receiver.onERC721Received.selector;
                } catch (bytes memory reason) {
                    if (reason.length == 0) {
                        revert("ERC721: transfer to non ERC721Receiver implementer");
                    } else {
                        /// @solidity memory-safe-assembly
                        assembly {
                            revert(add(32, reason), mload(reason))
                        }
                    }
                }
            } else {
                return true;
            }
        }
        /**
         * @dev Hook that is called before any token transfer. This includes minting and burning. If {ERC721Consecutive} is
         * used, the hook may be called as part of a consecutive (batch) mint, as indicated by `batchSize` greater than 1.
         *
         * Calling conditions:
         *
         * - When `from` and `to` are both non-zero, ``from``'s tokens will be transferred to `to`.
         * - When `from` is zero, the tokens will be minted for `to`.
         * - When `to` is zero, ``from``'s tokens will be burned.
         * - `from` and `to` are never both zero.
         * - `batchSize` is non-zero.
         *
         * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
         */
        function _beforeTokenTransfer(address from, address to, uint256 firstTokenId, uint256 batchSize) internal virtual {}
        /**
         * @dev Hook that is called after any token transfer. This includes minting and burning. If {ERC721Consecutive} is
         * used, the hook may be called as part of a consecutive (batch) mint, as indicated by `batchSize` greater than 1.
         *
         * Calling conditions:
         *
         * - When `from` and `to` are both non-zero, ``from``'s tokens were transferred to `to`.
         * - When `from` is zero, the tokens were minted for `to`.
         * - When `to` is zero, ``from``'s tokens were burned.
         * - `from` and `to` are never both zero.
         * - `batchSize` is non-zero.
         *
         * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
         */
        function _afterTokenTransfer(address from, address to, uint256 firstTokenId, uint256 batchSize) internal virtual {}
        /**
         * @dev Unsafe write access to the balances, used by extensions that "mint" tokens using an {ownerOf} override.
         *
         * WARNING: Anyone calling this MUST ensure that the balances remain consistent with the ownership. The invariant
         * being that for any address `a` the value returned by `balanceOf(a)` must be equal to the number of tokens such
         * that `ownerOf(tokenId)` is `a`.
         */
        // solhint-disable-next-line func-name-mixedcase
        function __unsafe_increaseBalance(address account, uint256 amount) internal {
            _balances[account] += amount;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol)
    pragma solidity ^0.8.0;
    import "../IERC721.sol";
    /**
     * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
     * @dev See https://eips.ethereum.org/EIPS/eip-721
     */
    interface IERC721Metadata is IERC721 {
        /**
         * @dev Returns the token collection name.
         */
        function name() external view returns (string memory);
        /**
         * @dev Returns the token collection symbol.
         */
        function symbol() external view returns (string memory);
        /**
         * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
         */
        function tokenURI(uint256 tokenId) external view returns (string memory);
    }
    // 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 (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)
    pragma solidity ^0.8.0;
    /**
     * @title ERC721 token receiver interface
     * @dev Interface for any contract that wants to support safeTransfers
     * from ERC721 asset contracts.
     */
    interface IERC721Receiver {
        /**
         * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
         * by `operator` from `from`, this function is called.
         *
         * It must return its Solidity selector to confirm the token transfer.
         * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
         *
         * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
         */
        function onERC721Received(
            address operator,
            address from,
            uint256 tokenId,
            bytes calldata data
        ) external returns (bytes4);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
    pragma solidity ^0.8.1;
    /**
     * @dev Collection of functions related to the address type
     */
    library Address {
        /**
         * @dev Returns true if `account` is a contract.
         *
         * [IMPORTANT]
         * ====
         * It is unsafe to assume that an address for which this function returns
         * false is an externally-owned account (EOA) and not a contract.
         *
         * Among others, `isContract` will return false for the following
         * types of addresses:
         *
         *  - an externally-owned account
         *  - a contract in construction
         *  - an address where a contract will be created
         *  - an address where a contract lived, but was destroyed
         *
         * Furthermore, `isContract` will also return true if the target contract within
         * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
         * which only has an effect at the end of a transaction.
         * ====
         *
         * [IMPORTANT]
         * ====
         * You shouldn't rely on `isContract` to protect against flash loan attacks!
         *
         * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
         * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
         * constructor.
         * ====
         */
        function isContract(address account) internal view returns (bool) {
            // This method relies on extcodesize/address.code.length, which returns 0
            // for contracts in construction, since the code is only stored at the end
            // of the constructor execution.
            return account.code.length > 0;
        }
        /**
         * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
         * `recipient`, forwarding all available gas and reverting on errors.
         *
         * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
         * of certain opcodes, possibly making contracts go over the 2300 gas limit
         * imposed by `transfer`, making them unable to receive funds via
         * `transfer`. {sendValue} removes this limitation.
         *
         * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
         *
         * IMPORTANT: because control is transferred to `recipient`, care must be
         * taken to not create reentrancy vulnerabilities. Consider using
         * {ReentrancyGuard} or the
         * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
         */
        function sendValue(address payable recipient, uint256 amount) internal {
            require(address(this).balance >= amount, "Address: insufficient balance");
            (bool success, ) = recipient.call{value: amount}("");
            require(success, "Address: unable to send value, recipient may have reverted");
        }
        /**
         * @dev Performs a Solidity function call using a low level `call`. A
         * plain `call` is an unsafe replacement for a function call: use this
         * function instead.
         *
         * If `target` reverts with a revert reason, it is bubbled up by this
         * function (like regular Solidity function calls).
         *
         * Returns the raw returned data. To convert to the expected return value,
         * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
         *
         * Requirements:
         *
         * - `target` must be a contract.
         * - calling `target` with `data` must not revert.
         *
         * _Available since v3.1._
         */
        function functionCall(address target, bytes memory data) internal returns (bytes memory) {
            return functionCallWithValue(target, data, 0, "Address: low-level call failed");
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
         * `errorMessage` as a fallback revert reason when `target` reverts.
         *
         * _Available since v3.1._
         */
        function functionCall(
            address target,
            bytes memory data,
            string memory errorMessage
        ) internal returns (bytes memory) {
            return functionCallWithValue(target, data, 0, errorMessage);
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but also transferring `value` wei to `target`.
         *
         * Requirements:
         *
         * - the calling contract must have an ETH balance of at least `value`.
         * - the called Solidity function must be `payable`.
         *
         * _Available since v3.1._
         */
        function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
            return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
        }
        /**
         * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
         * with `errorMessage` as a fallback revert reason when `target` reverts.
         *
         * _Available since v3.1._
         */
        function functionCallWithValue(
            address target,
            bytes memory data,
            uint256 value,
            string memory errorMessage
        ) internal returns (bytes memory) {
            require(address(this).balance >= value, "Address: insufficient balance for call");
            (bool success, bytes memory returndata) = target.call{value: value}(data);
            return verifyCallResultFromTarget(target, success, returndata, errorMessage);
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but performing a static call.
         *
         * _Available since v3.3._
         */
        function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
            return functionStaticCall(target, data, "Address: low-level static call failed");
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
         * but performing a static call.
         *
         * _Available since v3.3._
         */
        function functionStaticCall(
            address target,
            bytes memory data,
            string memory errorMessage
        ) internal view returns (bytes memory) {
            (bool success, bytes memory returndata) = target.staticcall(data);
            return verifyCallResultFromTarget(target, success, returndata, errorMessage);
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but performing a delegate call.
         *
         * _Available since v3.4._
         */
        function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
            return functionDelegateCall(target, data, "Address: low-level delegate call failed");
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
         * but performing a delegate call.
         *
         * _Available since v3.4._
         */
        function functionDelegateCall(
            address target,
            bytes memory data,
            string memory errorMessage
        ) internal returns (bytes memory) {
            (bool success, bytes memory returndata) = target.delegatecall(data);
            return verifyCallResultFromTarget(target, success, returndata, errorMessage);
        }
        /**
         * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
         * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
         *
         * _Available since v4.8._
         */
        function verifyCallResultFromTarget(
            address target,
            bool success,
            bytes memory returndata,
            string memory errorMessage
        ) internal view returns (bytes memory) {
            if (success) {
                if (returndata.length == 0) {
                    // only check isContract if the call was successful and the return data is empty
                    // otherwise we already know that it was a contract
                    require(isContract(target), "Address: call to non-contract");
                }
                return returndata;
            } else {
                _revert(returndata, errorMessage);
            }
        }
        /**
         * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
         * revert reason or using the provided one.
         *
         * _Available since v4.3._
         */
        function verifyCallResult(
            bool success,
            bytes memory returndata,
            string memory errorMessage
        ) internal pure returns (bytes memory) {
            if (success) {
                return returndata;
            } else {
                _revert(returndata, errorMessage);
            }
        }
        function _revert(bytes memory returndata, string memory errorMessage) private pure {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly
                /// @solidity memory-safe-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
    // 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 v4.4.1 (utils/Counters.sol)
    pragma solidity ^0.8.0;
    /**
     * @title Counters
     * @author Matt Condon (@shrugs)
     * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number
     * of elements in a mapping, issuing ERC721 ids, or counting request ids.
     *
     * Include with `using Counters for Counters.Counter;`
     */
    library Counters {
        struct Counter {
            // This variable should never be directly accessed by users of the library: interactions must be restricted to
            // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
            // this feature: see https://github.com/ethereum/solidity/issues/4637
            uint256 _value; // default: 0
        }
        function current(Counter storage counter) internal view returns (uint256) {
            return counter._value;
        }
        function increment(Counter storage counter) internal {
            unchecked {
                counter._value += 1;
            }
        }
        function decrement(Counter storage counter) internal {
            uint256 value = counter._value;
            require(value > 0, "Counter: decrement overflow");
            unchecked {
                counter._value = value - 1;
            }
        }
        function reset(Counter storage counter) internal {
            counter._value = 0;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
    pragma solidity ^0.8.0;
    import "./IERC165.sol";
    /**
     * @dev Implementation of the {IERC165} interface.
     *
     * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
     * for the additional interface id that will be supported. For example:
     *
     * ```solidity
     * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
     *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
     * }
     * ```
     *
     * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
     */
    abstract contract ERC165 is IERC165 {
        /**
         * @dev See {IERC165-supportsInterface}.
         */
        function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
            return interfaceId == type(IERC165).interfaceId;
        }
    }
    // 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
    // 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
    pragma solidity ^0.8.0;
    import "./interfaces/IGateway.sol";
    import "./interfaces/IBasicERC721.sol";
    import "./management/GatewayGuardedOwnable.sol";
    import "@limitbreak/creator-token-contracts/contracts/erc721c/ERC721C.sol";
    import "@limitbreak/creator-token-contracts/contracts/access/OwnableBasic.sol";
    import "@openzeppelin/contracts/security/Pausable.sol";
    import "@openzeppelin/contracts/utils/Strings.sol";
    import "@openzeppelin/contracts/utils/Counters.sol";
    import "@openzeppelin/contracts/metatx/ERC2771Context.sol";
    /**
     * @title BasicERC721C
     * @author Libeccio Inc.
     * @notice Extension of ERC721C that adds access control through TokenGateway.
     */
    contract BasicERC721C is
        IBasicERC721,
        ERC2771Context,
        ERC721C,
        OwnableBasic,
        GatewayGuardedOwnable,
        Pausable
    {
        using Counters for Counters.Counter;
        uint256 public constant VERSION_BasicERC721C = 20231126;
        Counters.Counter private _tokenIdCounter;
        string private __baseURI;
        /**
         * @param name the NFT contract name
         * @param symbol the NFT contract symbol
         * @param baseURI the base uri for nft meta. Note that the meta uri for the speicied token will be "{baseURI}/{contractAddress}/{tokenId}"
         * @param gateway the NFTGateway contract address
         * @param trustedForwarder the trusted forwarder contract address used for ERC2771
         */
        constructor(
            string memory name,
            string memory symbol,
            string memory baseURI,
            address gateway,
            address trustedForwarder
        )
            ERC2771Context(trustedForwarder)
            ERC721OpenZeppelin(name, symbol)
            GatewayGuarded(gateway)
        {
            __baseURI = baseURI;
            _tokenIdCounter.increment();
        }
        function incTokenIdCounter(uint256 limit) public returns (uint256) {
            uint256 id = _tokenIdCounter.current();
            limit = id + limit; // to avoid out of gas
            while (id < limit) {
                if (!_exists(id)) {
                    return id;
                }
                _tokenIdCounter.increment();
                id = _tokenIdCounter.current();
            }
            return id;
        }
        /**
         * Mint `tokenId` to `to`. If `tokenId` is 0, use auto-increment id.
         */
        function mint(
            address to,
            uint256 tokenId
        ) external override onlyGatewayOrOwner {
            if (tokenId == 0) {
                tokenId = incTokenIdCounter(4096);
            }
            _safeMint(to, tokenId);
        }
        /**
         * Batch mint `tokenId` to `to`.
         */
        function mintBatch(
            address to,
            uint256[] calldata tokenId
        ) external override onlyGatewayOrOwner {
            for (uint256 i = 0; i < tokenId.length; i++) {
                _safeMint(to, tokenId[i]);
            }
        }
        /**
         * @dev Burns `tokenId`. See {ERC721-_burn}.
         *
         * Requirements:
         *
         * - The caller must own `tokenId` or be an approved operator.
         */
        function burn(uint256 tokenId) public virtual {
            require(
                _isApprovedOrOwner(_msgSender(), tokenId),
                "ERC721: caller is not token owner or approved"
            );
            _burn(tokenId);
        }
        function tokenURI(
            uint256 tokenId
        ) public view override returns (string memory) {
            return
                string(
                    abi.encodePacked(
                        __baseURI,
                        "/",
                        Strings.toHexString(uint160(address(this)), 20),
                        "/",
                        Strings.toHexString(tokenId, 32)
                    )
                );
        }
        function contractURI() public view returns (string memory) {
            return
                string(
                    abi.encodePacked(
                        __baseURI,
                        "/",
                        Strings.toHexString(uint160(address(this)), 20)
                    )
                );
        }
        function setURI(
            string calldata newBaseURI
        ) external override onlyGatewayOrOwner {
            __baseURI = newBaseURI;
        }
        function pause() external onlyGatewayOrOwner {
            _pause();
        }
        function unpause() external onlyGatewayOrOwner {
            _unpause();
        }
        function supportsInterface(
            bytes4 interfaceId
        ) public view virtual override returns (bool) {
            return
                interfaceId == type(IBasicERC721).interfaceId ||
                super.supportsInterface(interfaceId);
        }
        function _beforeTokenTransfer(
            address from,
            address to,
            uint256 firstTokenId,
            uint256 batchSize
        ) internal virtual override {
            _requireNotPaused();
            super._beforeTokenTransfer(from, to, firstTokenId, batchSize);
        }
        function _msgSender()
            internal
            view
            virtual
            override(ERC2771Context, Context)
            returns (address sender)
        {
            return ERC2771Context._msgSender();
        }
        function _msgData()
            internal
            view
            virtual
            override(ERC2771Context, Context)
            returns (bytes calldata)
        {
            return ERC2771Context._msgData();
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    import "./BasicERC721C.sol";
    import "@limitbreak/creator-token-contracts/contracts/programmable-royalties/BasicRoyalties.sol";
    /**
     * @title BasicERC721CWithBasicRoyalties
     * @author Libeccio Inc.
     * @notice Extension of BasicERC721C that adds basic royalties support.
     */
    contract BasicERC721CWithBasicRoyalties is BasicERC721C, BasicRoyalties {
        uint256 public constant VERSION_BasicERC721CWithBasicRoyalties = 20231019;
        constructor(
            string memory name,
            string memory symbol,
            string memory baseURI,
            address gateway,
            address trustedForwarder,
            address royaltyReceiver,
            uint96 royaltyFeeNumerator
        )
            BasicERC721C(name, symbol, baseURI, gateway, trustedForwarder)
            BasicRoyalties(royaltyReceiver, royaltyFeeNumerator)
        {}
        function supportsInterface(
            bytes4 interfaceId
        ) public view virtual override(BasicERC721C, ERC2981) returns (bool) {
            return super.supportsInterface(interfaceId);
        }
        function setDefaultRoyalty(
            address receiver,
            uint96 feeNumerator
        ) external onlyGatewayOrOwner {
            super._setDefaultRoyalty(receiver, feeNumerator);
        }
        function setTokenRoyalty(
            uint256 tokenId,
            address receiver,
            uint96 feeNumerator
        ) external onlyGatewayOrOwner {
            super._setTokenRoyalty(tokenId, receiver, feeNumerator);
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    interface IBasicERC721 {
        function mint(address to, uint256 tokenId) external;
        function mintBatch(address to, uint256[] calldata tokenId) external;
        function setURI(string calldata newBaseURI) external;
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    interface IERC1155Gateway {
        /********************************************************************
         *                        ERC1155 interfaces                        *
         ********************************************************************/
        /**
         * @dev Mint ERC1155 tokens.
         * @param account receiver of the minted tokens
         * @param id id of tokens to be minted
         * @param amount amount of tokens to be minted
         */
        function ERC1155_mint(
            address nftContract,
            address account,
            uint256 id,
            uint256 amount,
            bytes memory data
        ) external;
        /**
         * @dev Mint a batch of ERC1155 tokens.
         *
         * See {ERC1155_mint}
         */
        function ERC1155_mintBatch(
            address nftContract,
            address to,
            uint256[] memory ids,
            uint256[] memory amounts,
            bytes memory data
        ) external;
        /**
         * @dev Sets a new URI for all token types, by relying on the token type ID
         * substitution mechanism
         * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP].
         *
         * By this mechanism, any occurrence of the `\\{id\\}` substring in either the
         * URI or any of the amounts in the JSON file at said URI will be replaced by
         * clients with the token type ID.
         *
         * For example, the `https://token-cdn-domain/\\{id\\}.json` URI would be
         * interpreted by clients as
         * `https://token-cdn-domain/000000000000000000000000000000000000000000000000000000000004cce0.json`
         * for token type ID 0x4cce0.
         *
         * See {uri}.
         *
         * Because these URIs cannot be meaningfully represented by the {URI} event,
         * this function emits no events.
         */
        function ERC1155_setURI(address nftContract, string memory newuri) external;
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    interface IERC20Gateway {
        /********************************************************************
         *                         ERC20 interfaces                         *
         ********************************************************************/
        /**
         * @dev Mint some ERC20 tokens to the recipient address.
         * @notice Only gateway contract is authorized to mint.
         * @param recipient The recipient of the minted ERC20 tokens.
         * @param amount The amount to be minted.
         */
        function ERC20_mint(
            address erc20Contract,
            address recipient,
            uint256 amount
        ) external;
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    interface IERC721Gateway {
        /********************************************************************
         *                        ERC721 interfaces                         *
         ********************************************************************/
        /**
         * @dev Mint an ERC721 token to the given address.
         * @notice Only gateway contract is authorized to mint.
         * @param recipient The recipient of the minted NFT.
         * @param tokenId The tokenId to be minted.
         */
        function ERC721_mint(
            address nftContract,
            address recipient,
            uint256 tokenId
        ) external;
        function ERC721_mintBatch(
            address nftContract,
            address recipient,
            uint256[] calldata tokenId
        ) external;
        /**
         * @dev Set `baseURI` of the ERC721 token. If set, the resulting URI for each
         * token will be the concatenation of the `baseURI` and the `tokenId`.
         */
        function ERC721_setURI(address nftContract, string memory newURI) external;
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    import "./IERC721Gateway.sol";
    import "./IERC1155Gateway.sol";
    import "./IERC20Gateway.sol";
    interface IGateway is IERC721Gateway, IERC1155Gateway, IERC20Gateway {
        function operatorWhitelist(address _operator) external view returns (bool);
        function setManagerOf(address _nftContract, address _manager) external;
        function nftManager(address _nftContract) external view returns (address);
        function isInManagement(
            address _x,
            address _tokenContract
        ) external view returns (bool);
        function pause(address _contract) external;
        function unpause(address _contract) external;
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    /**
     * The management interface exposed to gateway.
     */
    interface IGatewayGuarded {
        /**
         * @dev Set the gateway contract address.
         * @notice Only gateway contract is authorized to set a
         * new gateway address.
         * @notice This function should be rarely used.
         * @param gateway The new gateway address.
         */
        function setGateway(address gateway) external;
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    import "../interfaces/IGatewayGuarded.sol";
    /**
     * The management interface exposed to gateway.
     */
    abstract contract GatewayGuarded is IGatewayGuarded {
        
        address public gateway;
        modifier onlyGateway() {
            _checkGateway();
            _;
        }
        constructor(address _gateway) {
            gateway = _gateway;
        }
        /**
         * @dev Throws if the sender is not the gateway contract.
         */
        function _checkGateway() internal view virtual {
            require(gateway == msg.sender, "GatewayGuarded: caller is not the gateway");
        }
        /**
         * @inheritdoc IGatewayGuarded
         */
        function setGateway(address _gateway) external override onlyGateway {
            gateway = _gateway;
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    import "./GatewayGuarded.sol";
    import "@openzeppelin/contracts/access/Ownable.sol";
    /**
     * The management interface exposed to gateway.
     */
    abstract contract GatewayGuardedOwnable is GatewayGuarded, Ownable {
        
        modifier onlyGatewayOrOwner() {
            _checkGatewayOrOwner();
            _;
        }
        /**
         * @dev Throws if the sender is neither the gateway contract nor the owner.
         */
        function _checkGatewayOrOwner() internal view virtual {
            address sender = _msgSender();
            require(gateway == sender || owner() == sender, "GatewayGuardedOwnable: caller is neither the gateway nor the owner");
        }
        function resetOwner(address _newOwner) external onlyGateway {
            _transferOwnership(_newOwner);
        }
    }
    

    File 2 of 2: CreatorTokenTransferValidator
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.4;
    import "./IEOARegistry.sol";
    import "./ITransferSecurityRegistry.sol";
    import "./ITransferValidator.sol";
    interface ICreatorTokenTransferValidator is ITransferSecurityRegistry, ITransferValidator, IEOARegistry {}// SPDX-License-Identifier: MIT
    pragma solidity ^0.8.4;
    import "@openzeppelin/contracts/utils/introspection/IERC165.sol";
    /**
     * @title IEOARegistry
     * @author Limit Break, Inc.
     * @notice Interface for a registry of verified EOA accounts.
     */
    interface IEOARegistry is IERC165 {
        /// @dev Returns true if the account has been verified as an EOA, false otherwise
        function isVerifiedEOA(address account) external view returns (bool);
    }// SPDX-License-Identifier: MIT
    pragma solidity ^0.8.4;
    interface IOwnable {
        function owner() external view returns (address);
    }// SPDX-License-Identifier: MIT
    pragma solidity ^0.8.4;
    import "../utils/TransferPolicy.sol";
    interface ITransferSecurityRegistry {
        event AddedToAllowlist(AllowlistTypes indexed kind, uint256 indexed id, address indexed account);
        event CreatedAllowlist(AllowlistTypes indexed kind, uint256 indexed id, string indexed name);
        event ReassignedAllowlistOwnership(AllowlistTypes indexed kind, uint256 indexed id, address indexed newOwner);
        event RemovedFromAllowlist(AllowlistTypes indexed kind, uint256 indexed id, address indexed account);
        event SetAllowlist(AllowlistTypes indexed kind, address indexed collection, uint120 indexed id);
        event SetTransferSecurityLevel(address indexed collection, TransferSecurityLevels level);
        function createOperatorWhitelist(string calldata name) external returns (uint120);
        function createPermittedContractReceiverAllowlist(string calldata name) external returns (uint120);
        function reassignOwnershipOfOperatorWhitelist(uint120 id, address newOwner) external;
        function reassignOwnershipOfPermittedContractReceiverAllowlist(uint120 id, address newOwner) external;
        function renounceOwnershipOfOperatorWhitelist(uint120 id) external;
        function renounceOwnershipOfPermittedContractReceiverAllowlist(uint120 id) external;
        function setTransferSecurityLevelOfCollection(address collection, TransferSecurityLevels level) external;
        function setOperatorWhitelistOfCollection(address collection, uint120 id) external;
        function setPermittedContractReceiverAllowlistOfCollection(address collection, uint120 id) external;
        function addOperatorToWhitelist(uint120 id, address operator) external;
        function addPermittedContractReceiverToAllowlist(uint120 id, address receiver) external;
        function removeOperatorFromWhitelist(uint120 id, address operator) external;
        function removePermittedContractReceiverFromAllowlist(uint120 id, address receiver) external;
        function getCollectionSecurityPolicy(address collection) external view returns (CollectionSecurityPolicy memory);
        function getWhitelistedOperators(uint120 id) external view returns (address[] memory);
        function getPermittedContractReceivers(uint120 id) external view returns (address[] memory);
        function isOperatorWhitelisted(uint120 id, address operator) external view returns (bool);
        function isContractReceiverPermitted(uint120 id, address receiver) external view returns (bool);
    }// SPDX-License-Identifier: MIT
    pragma solidity ^0.8.4;
    import "../utils/TransferPolicy.sol";
    interface ITransferValidator {
        function applyCollectionTransferPolicy(address caller, address from, address to) external view;
    }// SPDX-License-Identifier: MIT
    pragma solidity ^0.8.4;
    import "./EOARegistry.sol";
    import "../interfaces/IOwnable.sol";
    import "../interfaces/ICreatorTokenTransferValidator.sol";
    import "@openzeppelin/contracts/access/IAccessControl.sol";
    import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
    /**
     * @title  CreatorTokenTransferValidator
     * @author Limit Break, Inc.
     * @notice The CreatorTokenTransferValidator contract is designed to provide a customizable and secure transfer 
     *         validation mechanism for NFT collections. This contract allows the owner of an NFT collection to configure 
     *         the transfer security level, operator whitelist, and permitted contract receiver allowlist for each 
     *         collection.
     *
     * @dev    <h4>Features</h4>
     *         - Transfer security levels: Provides different levels of transfer security, 
     *           from open transfers to completely restricted transfers.
     *         - Operator whitelist: Allows the owner of a collection to whitelist specific operator addresses permitted
     *           to execute transfers on behalf of others.
     *         - Permitted contract receiver allowlist: Enables the owner of a collection to allow specific contract 
     *           addresses to receive NFTs when otherwise disabled by security policy.
     *
     * @dev    <h4>Benefits</h4>
     *         - Enhanced security: Allows creators to have more control over their NFT collections, ensuring the safety 
     *           and integrity of their assets.
     *         - Flexibility: Provides collection owners the ability to customize transfer rules as per their requirements.
     *         - Compliance: Facilitates compliance with regulations by enabling creators to restrict transfers based on 
     *           specific criteria.
     *
     * @dev    <h4>Intended Usage</h4>
     *         - The CreatorTokenTransferValidator contract is intended to be used by NFT collection owners to manage and 
     *           enforce transfer policies. This contract is integrated with the following varations of creator token 
     *           NFT contracts to validate transfers according to the defined security policies.
     *
     *           - ERC721-C:   Creator token implenting OpenZeppelin's ERC-721 standard.
     *           - ERC721-AC:  Creator token implenting Azuki's ERC-721A standard.
     *           - ERC721-CW:  Creator token implementing OpenZeppelin's ERC-721 standard with opt-in staking to 
     *                         wrap/upgrade a pre-existing ERC-721 collection.
     *           - ERC721-ACW: Creator token implementing Azuki's ERC721-A standard with opt-in staking to 
     *                         wrap/upgrade a pre-existing ERC-721 collection.
     *           - ERC1155-C:  Creator token implenting OpenZeppelin's ERC-1155 standard.
     *           - ERC1155-CW: Creator token implementing OpenZeppelin's ERC-1155 standard with opt-in staking to 
     *                         wrap/upgrade a pre-existing ERC-1155 collection.
     *
     *          <h4>Transfer Security Levels</h4>
     *          - Level 0 (Zero): No transfer restrictions.
     *            - Caller Constraints: None
     *            - Receiver Constraints: None
     *          - Level 1 (One): Only whitelisted operators can initiate transfers, with over-the-counter (OTC) trading enabled.
     *            - Caller Constraints: OperatorWhitelistEnableOTC
     *            - Receiver Constraints: None
     *          - Level 2 (Two): Only whitelisted operators can initiate transfers, with over-the-counter (OTC) trading disabled.
     *            - Caller Constraints: OperatorWhitelistDisableOTC
     *            - Receiver Constraints: None
     *          - Level 3 (Three): Only whitelisted operators can initiate transfers, with over-the-counter (OTC) trading enabled. Transfers to contracts with code are not allowed.
     *            - Caller Constraints: OperatorWhitelistEnableOTC
     *            - Receiver Constraints: NoCode
     *          - Level 4 (Four): Only whitelisted operators can initiate transfers, with over-the-counter (OTC) trading enabled. Transfers are allowed only to Externally Owned Accounts (EOAs).
     *            - Caller Constraints: OperatorWhitelistEnableOTC
     *            - Receiver Constraints: EOA
     *          - Level 5 (Five): Only whitelisted operators can initiate transfers, with over-the-counter (OTC) trading disabled. Transfers to contracts with code are not allowed.
     *            - Caller Constraints: OperatorWhitelistDisableOTC
     *            - Receiver Constraints: NoCode
     *          - Level 6 (Six): Only whitelisted operators can initiate transfers, with over-the-counter (OTC) trading disabled. Transfers are allowed only to Externally Owned Accounts (EOAs).
     *            - Caller Constraints: OperatorWhitelistDisableOTC
     *            - Receiver Constraints: EOA
     */
    contract CreatorTokenTransferValidator is EOARegistry, ICreatorTokenTransferValidator {
        using EnumerableSet for EnumerableSet.AddressSet;
        error CreatorTokenTransferValidator__AddressAlreadyAllowed();
        error CreatorTokenTransferValidator__AddressNotAllowed();
        error CreatorTokenTransferValidator__AllowlistDoesNotExist();
        error CreatorTokenTransferValidator__AllowlistOwnershipCannotBeTransferredToZeroAddress();
        error CreatorTokenTransferValidator__CallerDoesNotOwnAllowlist();
        error CreatorTokenTransferValidator__CallerMustBeWhitelistedOperator();
        error CreatorTokenTransferValidator__CallerMustHaveElevatedPermissionsForSpecifiedNFT();
        error CreatorTokenTransferValidator__ReceiverMustNotHaveDeployedCode();
        error CreatorTokenTransferValidator__ReceiverProofOfEOASignatureUnverified();
        
        bytes32 private constant DEFAULT_ACCESS_CONTROL_ADMIN_ROLE = 0x00;
        TransferSecurityLevels public constant DEFAULT_TRANSFER_SECURITY_LEVEL = TransferSecurityLevels.Zero;
        uint120 private lastOperatorWhitelistId;
        uint120 private lastPermittedContractReceiverAllowlistId;
        mapping (TransferSecurityLevels => TransferSecurityPolicy) public transferSecurityPolicies;
        mapping (address => CollectionSecurityPolicy) private collectionSecurityPolicies;
        mapping (uint120 => address) public operatorWhitelistOwners;
        mapping (uint120 => address) public permittedContractReceiverAllowlistOwners;
        mapping (uint120 => EnumerableSet.AddressSet) private operatorWhitelists;
        mapping (uint120 => EnumerableSet.AddressSet) private permittedContractReceiverAllowlists;
        constructor(address defaultOwner) EOARegistry() {
            transferSecurityPolicies[TransferSecurityLevels.Zero] = TransferSecurityPolicy({
                callerConstraints: CallerConstraints.None,
                receiverConstraints: ReceiverConstraints.None
            });
            transferSecurityPolicies[TransferSecurityLevels.One] = TransferSecurityPolicy({
                callerConstraints: CallerConstraints.OperatorWhitelistEnableOTC,
                receiverConstraints: ReceiverConstraints.None
            });
            transferSecurityPolicies[TransferSecurityLevels.Two] = TransferSecurityPolicy({
                callerConstraints: CallerConstraints.OperatorWhitelistDisableOTC,
                receiverConstraints: ReceiverConstraints.None
            });
            transferSecurityPolicies[TransferSecurityLevels.Three] = TransferSecurityPolicy({
                callerConstraints: CallerConstraints.OperatorWhitelistEnableOTC,
                receiverConstraints: ReceiverConstraints.NoCode
            });
            transferSecurityPolicies[TransferSecurityLevels.Four] = TransferSecurityPolicy({
                callerConstraints: CallerConstraints.OperatorWhitelistEnableOTC,
                receiverConstraints: ReceiverConstraints.EOA
            });
            transferSecurityPolicies[TransferSecurityLevels.Five] = TransferSecurityPolicy({
                callerConstraints: CallerConstraints.OperatorWhitelistDisableOTC,
                receiverConstraints: ReceiverConstraints.NoCode
            });
            transferSecurityPolicies[TransferSecurityLevels.Six] = TransferSecurityPolicy({
                callerConstraints: CallerConstraints.OperatorWhitelistDisableOTC,
                receiverConstraints: ReceiverConstraints.EOA
            });
            uint120 id = ++lastOperatorWhitelistId;
            operatorWhitelistOwners[id] = defaultOwner;
            emit CreatedAllowlist(AllowlistTypes.Operators, id, "DEFAULT OPERATOR WHITELIST");
            emit ReassignedAllowlistOwnership(AllowlistTypes.Operators, id, defaultOwner);
        }
        /**
         * @notice Apply the collection transfer policy to a transfer operation of a creator token.
         *
         * @dev Throws when the receiver has deployed code but is not in the permitted contract receiver allowlist,
         *      if the ReceiverConstraints is set to NoCode.
         * @dev Throws when the receiver has never verified a signature to prove they are an EOA and the receiver
         *      is not in the permitted contract receiver allowlist, if the ReceiverConstraints is set to EOA.
         * @dev Throws when `msg.sender` is not a whitelisted operator, if CallerConstraints is OperatorWhitelistDisableOTC.
         * @dev Throws when `msg.sender` is neither a whitelisted operator nor the 'from' addresses,
         *      if CallerConstraints is OperatorWhitelistEnableOTC.
         *
         * @dev <h4>Postconditions:</h4>
         *      1. Transfer is allowed or denied based on the applied transfer policy.
         *
         * @param caller The address initiating the transfer.
         * @param from   The address of the token owner.
         * @param to     The address of the token receiver.
         */
        function applyCollectionTransferPolicy(address caller, address from, address to) external view override {
            address collection = _msgSender();
            CollectionSecurityPolicy memory collectionSecurityPolicy = collectionSecurityPolicies[collection];
            TransferSecurityPolicy memory transferSecurityPolicy = 
                transferSecurityPolicies[collectionSecurityPolicy.transferSecurityLevel];
            
            if (transferSecurityPolicy.receiverConstraints == ReceiverConstraints.NoCode) {
                if (to.code.length > 0) {
                    if (!isContractReceiverPermitted(collectionSecurityPolicy.permittedContractReceiversId, to)) {
                        revert CreatorTokenTransferValidator__ReceiverMustNotHaveDeployedCode();
                    }
                }
            } else if (transferSecurityPolicy.receiverConstraints == ReceiverConstraints.EOA) {
                if (!isVerifiedEOA(to)) {
                    if (!isContractReceiverPermitted(collectionSecurityPolicy.permittedContractReceiversId, to)) {
                        revert CreatorTokenTransferValidator__ReceiverProofOfEOASignatureUnverified();
                    }
                }
            }
            if (transferSecurityPolicy.callerConstraints != CallerConstraints.None) {
                if(operatorWhitelists[collectionSecurityPolicy.operatorWhitelistId].length() > 0) {
                    if (!isOperatorWhitelisted(collectionSecurityPolicy.operatorWhitelistId, caller)) {
                        if (transferSecurityPolicy.callerConstraints == CallerConstraints.OperatorWhitelistEnableOTC) {
                            if (caller != from) {
                                revert CreatorTokenTransferValidator__CallerMustBeWhitelistedOperator();
                            }
                        } else {
                            revert CreatorTokenTransferValidator__CallerMustBeWhitelistedOperator();
                        }
                    }
                }
            }
        }
        /**
         * @notice Create a new operator whitelist.
         *
         * @dev <h4>Postconditions:</h4>
         *      1. A new operator whitelist with the specified name is created.
         *      2. The caller is set as the owner of the new operator whitelist.
         *      3. A `CreatedAllowlist` event is emitted.
         *      4. A `ReassignedAllowlistOwnership` event is emitted.
         *
         * @param name The name of the new operator whitelist.
         * @return     The id of the new operator whitelist.
         */
        function createOperatorWhitelist(string calldata name) external override returns (uint120) {
            uint120 id = ++lastOperatorWhitelistId;
            operatorWhitelistOwners[id] = _msgSender();
            emit CreatedAllowlist(AllowlistTypes.Operators, id, name);
            emit ReassignedAllowlistOwnership(AllowlistTypes.Operators, id, _msgSender());
            return id;
        }
        /**
         * @notice Create a new permitted contract receiver allowlist.
         * 
         * @dev <h4>Postconditions:</h4>
         *      1. A new permitted contract receiver allowlist with the specified name is created.
         *      2. The caller is set as the owner of the new permitted contract receiver allowlist.
         *      3. A `CreatedAllowlist` event is emitted.
         *      4. A `ReassignedAllowlistOwnership` event is emitted.
         *
         * @param name The name of the new permitted contract receiver allowlist.
         * @return     The id of the new permitted contract receiver allowlist.
         */
        function createPermittedContractReceiverAllowlist(string calldata name) external override returns (uint120) {
            uint120 id = ++lastPermittedContractReceiverAllowlistId;
            permittedContractReceiverAllowlistOwners[id] = _msgSender();
            emit CreatedAllowlist(AllowlistTypes.PermittedContractReceivers, id, name);
            emit ReassignedAllowlistOwnership(AllowlistTypes.PermittedContractReceivers, id, _msgSender());
            return id;
        }
        /**
         * @notice Transfer ownership of an operator whitelist to a new owner.
         *
         * @dev Throws when the new owner is the zero address.
         * @dev Throws when the caller does not own the specified operator whitelist.
         *
         * @dev <h4>Postconditions:</h4>
         *      1. The operator whitelist ownership is transferred to the new owner.
         *      2. A `ReassignedAllowlistOwnership` event is emitted.
         *
         * @param id       The id of the operator whitelist.
         * @param newOwner The address of the new owner.
         */
        function reassignOwnershipOfOperatorWhitelist(uint120 id, address newOwner) external override {
            if(newOwner == address(0)) {
                revert CreatorTokenTransferValidator__AllowlistOwnershipCannotBeTransferredToZeroAddress();
            }
            _reassignOwnershipOfOperatorWhitelist(id, newOwner);
        }
        /**
         * @notice Transfer ownership of a permitted contract receiver allowlist to a new owner.
         *
         * @dev Throws when the new owner is the zero address.
         * @dev Throws when the caller does not own the specified permitted contract receiver allowlist.
         *
         * @dev <h4>Postconditions:</h4>
         *      1. The permitted contract receiver allowlist ownership is transferred to the new owner.
         *      2. A `ReassignedAllowlistOwnership` event is emitted.
         *
         * @param id       The id of the permitted contract receiver allowlist.
         * @param newOwner The address of the new owner.
         */
        function reassignOwnershipOfPermittedContractReceiverAllowlist(uint120 id, address newOwner) external override {
            if(newOwner == address(0)) {
                revert CreatorTokenTransferValidator__AllowlistOwnershipCannotBeTransferredToZeroAddress();
            }
            _reassignOwnershipOfPermittedContractReceiverAllowlist(id, newOwner);
        }
        /**
         * @notice Renounce the ownership of an operator whitelist, rendering the whitelist immutable.
         *
         * @dev Throws when the caller does not own the specified operator whitelist.
         *
         * @dev <h4>Postconditions:</h4>
         *      1. The ownership of the specified operator whitelist is renounced.
         *      2. A `ReassignedAllowlistOwnership` event is emitted.
         *
         * @param id The id of the operator whitelist.
         */
        function renounceOwnershipOfOperatorWhitelist(uint120 id) external override {
            _reassignOwnershipOfOperatorWhitelist(id, address(0));
        }
        /**
         * @notice Renounce the ownership of a permitted contract receiver allowlist, rendering the allowlist immutable.
         *
         * @dev Throws when the caller does not own the specified permitted contract receiver allowlist.
         *
         * @dev <h4>Postconditions:</h4>
         *      1. The ownership of the specified permitted contract receiver allowlist is renounced.
         *      2. A `ReassignedAllowlistOwnership` event is emitted.
         *
         * @param id The id of the permitted contract receiver allowlist.
         */
        function renounceOwnershipOfPermittedContractReceiverAllowlist(uint120 id) external override {
            _reassignOwnershipOfPermittedContractReceiverAllowlist(id, address(0));
        }
        /**
         * @notice Set the transfer security level of a collection.
         *
         * @dev Throws when the caller is neither collection contract, nor the owner or admin of the specified collection.
         *
         * @dev <h4>Postconditions:</h4>
         *      1. The transfer security level of the specified collection is set to the new value.
         *      2. A `SetTransferSecurityLevel` event is emitted.
         *
         * @param collection The address of the collection.
         * @param level      The new transfer security level to apply.
         */
        function setTransferSecurityLevelOfCollection(
            address collection, 
            TransferSecurityLevels level) external override {
            _requireCallerIsNFTOrContractOwnerOrAdmin(collection);
            collectionSecurityPolicies[collection].transferSecurityLevel = level;
            emit SetTransferSecurityLevel(collection, level);
        }
        /**
         * @notice Set the operator whitelist of 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 operator whitelist id does not exist.
         *
         * @dev <h4>Postconditions:</h4>
         *      1. The operator whitelist of the specified collection is set to the new value.
         *      2. A `SetAllowlist` event is emitted.
         *
         * @param collection The address of the collection.
         * @param id         The id of the operator whitelist.
         */
        function setOperatorWhitelistOfCollection(address collection, uint120 id) external override {
            _requireCallerIsNFTOrContractOwnerOrAdmin(collection);
            if (id > lastOperatorWhitelistId) {
                revert CreatorTokenTransferValidator__AllowlistDoesNotExist();
            }
            collectionSecurityPolicies[collection].operatorWhitelistId = id;
            emit SetAllowlist(AllowlistTypes.Operators, collection, id);
        }
        /**
         * @notice Set the permitted contract receiver allowlist of a collection.
         *
         * @dev Throws when the caller does not own the specified collection.
         * @dev Throws when the specified permitted contract receiver allowlist id does not exist.
         *
         * @dev <h4>Postconditions:</h4>
         *      1. The permitted contract receiver allowlist of the specified collection is set to the new value.
         *      2. A `PermittedContractReceiverAllowlistSet` event is emitted.
         *
         * @param collection The address of the collection.
         * @param id         The id of the permitted contract receiver allowlist.
         */
        function setPermittedContractReceiverAllowlistOfCollection(address collection, uint120 id) external override {
            _requireCallerIsNFTOrContractOwnerOrAdmin(collection);
            if (id > lastPermittedContractReceiverAllowlistId) {
                revert CreatorTokenTransferValidator__AllowlistDoesNotExist();
            }
            collectionSecurityPolicies[collection].permittedContractReceiversId = id;
            emit SetAllowlist(AllowlistTypes.PermittedContractReceivers, collection, id);
        }
        /**
         * @notice Add an operator to an operator whitelist.
         *
         * @dev Throws when the caller does not own the specified operator whitelist.
         * @dev Throws when the operator address is already allowed.
         *
         * @dev <h4>Postconditions:</h4>
         *      1. The operator is added to the specified operator whitelist.
         *      2. An `AddedToAllowlist` event is emitted.
         *
         * @param id       The id of the operator whitelist.
         * @param operator The address of the operator to add.
         */
        function addOperatorToWhitelist(uint120 id, address operator) external override {
            _requireCallerOwnsOperatorWhitelist(id);
            if (!operatorWhitelists[id].add(operator)) {
                revert CreatorTokenTransferValidator__AddressAlreadyAllowed();
            }
            emit AddedToAllowlist(AllowlistTypes.Operators, id, operator);
        }
        /**
         * @notice Add a contract address to a permitted contract receiver allowlist.
         *
         * @dev Throws when the caller does not own the specified permitted contract receiver allowlist.
         * @dev Throws when the contract address is already allowed.
         *
         * @dev <h4>Postconditions:</h4>
         *      1. The contract address is added to the specified permitted contract receiver allowlist.
         *      2. An `AddedToAllowlist` event is emitted.
         *
         * @param id              The id of the permitted contract receiver allowlist.
         * @param receiver The address of the contract to add.
         */
        function addPermittedContractReceiverToAllowlist(uint120 id, address receiver) external override {
            _requireCallerOwnsPermittedContractReceiverAllowlist(id);
            if (!permittedContractReceiverAllowlists[id].add(receiver)) {
                revert CreatorTokenTransferValidator__AddressAlreadyAllowed();
            }
            emit AddedToAllowlist(AllowlistTypes.PermittedContractReceivers, id, receiver);
        }
        /**
         * @notice Remove an operator from an operator whitelist.
         *
         * @dev Throws when the caller does not own the specified operator whitelist.
         * @dev Throws when the operator is not in the specified operator whitelist.
         *
         * @dev <h4>Postconditions:</h4>
         *      1. The operator is removed from the specified operator whitelist.
         *      2. A `RemovedFromAllowlist` event is emitted.
         *
         * @param id       The id of the operator whitelist.
         * @param operator The address of the operator to remove.
         */
        function removeOperatorFromWhitelist(uint120 id, address operator) external override {
            _requireCallerOwnsOperatorWhitelist(id);
            if (!operatorWhitelists[id].remove(operator)) {
                revert CreatorTokenTransferValidator__AddressNotAllowed();
            }
            emit RemovedFromAllowlist(AllowlistTypes.Operators, id, operator);
        }
        /**
         * @notice Remove a contract address from a permitted contract receiver allowlist.
         * 
         * @dev Throws when the caller does not own the specified permitted contract receiver allowlist.
         * @dev Throws when the contract address is not in the specified permitted contract receiver allowlist.
         *
         * @dev <h4>Postconditions:</h4>
         *      1. The contract address is removed from the specified permitted contract receiver allowlist.
         *      2. A `RemovedFromAllowlist` event is emitted.
         *
         * @param id       The id of the permitted contract receiver allowlist.
         * @param receiver The address of the contract to remove.
         */
        function removePermittedContractReceiverFromAllowlist(uint120 id, address receiver) external override {
            _requireCallerOwnsPermittedContractReceiverAllowlist(id);
            if (!permittedContractReceiverAllowlists[id].remove(receiver)) {
                revert CreatorTokenTransferValidator__AddressNotAllowed();
            }
            emit RemovedFromAllowlist(AllowlistTypes.PermittedContractReceivers, id, receiver);
        }
        /**
         * @notice Get the security policy of the specified collection.
         * @param collection The address of the collection.
         * @return           The security policy of the specified collection, which includes:
         *                   Transfer security level, operator whitelist id, permitted contract receiver allowlist id
         */
        function getCollectionSecurityPolicy(address collection) 
            external view override returns (CollectionSecurityPolicy memory) {
            return collectionSecurityPolicies[collection];
        }
        /**
         * @notice Get the whitelisted operators in an operator whitelist.
         * @param id The id of the operator whitelist.
         * @return   An array of whitelisted operator addresses.
         */
        function getWhitelistedOperators(uint120 id) external view override returns (address[] memory) {
            return operatorWhitelists[id].values();
        }
        /**
         * @notice Get the permitted contract receivers in a permitted contract receiver allowlist.
         * @param id The id of the permitted contract receiver allowlist.
         * @return   An array of contract addresses is the permitted contract receiver allowlist.
         */
        function getPermittedContractReceivers(uint120 id) external view override returns (address[] memory) {
            return permittedContractReceiverAllowlists[id].values();
        }
        /**
         * @notice Check if an operator is in a specified operator whitelist.
         * @param id       The id of the operator whitelist.
         * @param operator The address of the operator to check.
         * @return         True if the operator is in the specified operator whitelist, false otherwise.
         */
        function isOperatorWhitelisted(uint120 id, address operator) public view override returns (bool) {
            return operatorWhitelists[id].contains(operator);
        }
        /**
         * @notice Check if a contract address is in a specified permitted contract receiver allowlist.
         * @param id       The id of the permitted contract receiver allowlist.
         * @param receiver The address of the contract to check.
         * @return         True if the contract address is in the specified permitted contract receiver allowlist, 
         *                 false otherwise.
         */
        function isContractReceiverPermitted(uint120 id, address receiver) public view override returns (bool) {
            return permittedContractReceiverAllowlists[id].contains(receiver);
        }
        /// @notice ERC-165 Interface Support
        function supportsInterface(bytes4 interfaceId) public view virtual override(EOARegistry, IERC165) returns (bool) {
            return
                interfaceId == type(ITransferValidator).interfaceId ||
                interfaceId == type(ITransferSecurityRegistry).interfaceId ||
                interfaceId == type(ICreatorTokenTransferValidator).interfaceId ||
                super.supportsInterface(interfaceId);
        }
        function _requireCallerIsNFTOrContractOwnerOrAdmin(address tokenAddress) internal view {
            bool callerHasPermissions = false;
            if(tokenAddress.code.length > 0) {
                callerHasPermissions = _msgSender() == tokenAddress;
                if(!callerHasPermissions) {
                    try IOwnable(tokenAddress).owner() returns (address contractOwner) {
                        callerHasPermissions = _msgSender() == contractOwner;
                    } catch {}
                    if(!callerHasPermissions) {
                        try IAccessControl(tokenAddress).hasRole(DEFAULT_ACCESS_CONTROL_ADMIN_ROLE, _msgSender()) 
                            returns (bool callerIsContractAdmin) {
                            callerHasPermissions = callerIsContractAdmin;
                        } catch {}
                    }
                }
            }
            if(!callerHasPermissions) {
                revert CreatorTokenTransferValidator__CallerMustHaveElevatedPermissionsForSpecifiedNFT();
            }
        }
        function _reassignOwnershipOfOperatorWhitelist(uint120 id, address newOwner) private {
            _requireCallerOwnsOperatorWhitelist(id);
            operatorWhitelistOwners[id] = newOwner;
            emit ReassignedAllowlistOwnership(AllowlistTypes.Operators, id, newOwner);
        }
        function _reassignOwnershipOfPermittedContractReceiverAllowlist(uint120 id, address newOwner) private {
            _requireCallerOwnsPermittedContractReceiverAllowlist(id);
            permittedContractReceiverAllowlistOwners[id] = newOwner;
            emit ReassignedAllowlistOwnership(AllowlistTypes.PermittedContractReceivers, id, newOwner);
        }
        function _requireCallerOwnsOperatorWhitelist(uint120 id) private view {
            if (_msgSender() != operatorWhitelistOwners[id]) {
                revert CreatorTokenTransferValidator__CallerDoesNotOwnAllowlist();
            }
        }
        function _requireCallerOwnsPermittedContractReceiverAllowlist(uint120 id) private view {
            if (_msgSender() != permittedContractReceiverAllowlistOwners[id]) {
                revert CreatorTokenTransferValidator__CallerDoesNotOwnAllowlist();
            }
        }
    }// SPDX-License-Identifier: MIT
    pragma solidity ^0.8.4;
    import "../interfaces/IEOARegistry.sol";
    import "@openzeppelin/contracts/utils/Context.sol";
    import "@openzeppelin/contracts/utils/introspection/ERC165.sol";
    import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
    error CallerDidNotSignTheMessage();
    error SignatureAlreadyVerified();
    /**
     * @title EOARegistry
     * @author Limit Break, Inc.
     * @notice A registry that may be used globally by any smart contract that limits contract interactions to verified EOA addresses only.
     * @dev Take care and carefully consider whether or not to use this. Restricting operations to EOA only accounts can break Defi composability, 
     * so if Defi composability is an objective, this is not a good option.  Be advised that in the future, EOA accounts might not be a thing
     * but this is yet to be determined.  See https://eips.ethereum.org/EIPS/eip-4337 for more information.
     */
    contract EOARegistry is Context, ERC165, IEOARegistry {
        /// @dev A pre-cached signed message hash used for gas-efficient signature recovery
        bytes32 immutable private signedMessageHash;
        /// @dev The plain text message to sign for signature verification
        string constant public MESSAGE_TO_SIGN = "EOA";
        /// @dev Mapping of accounts that to signature verification status
        mapping (address => bool) private eoaSignatureVerified;
        /// @dev Emitted whenever a user verifies that they are an EOA by submitting their signature.
        event VerifiedEOASignature(address indexed account);
        constructor() {
            signedMessageHash = ECDSA.toEthSignedMessageHash(bytes(MESSAGE_TO_SIGN));
        }
        /// @notice Allows a user to verify an ECDSA signature to definitively prove they are an EOA account.
        ///
        /// Throws when the caller has already verified their signature.
        /// Throws when the caller did not sign the message.
        ///
        /// Postconditions:
        /// ---------------
        /// The verified signature mapping has been updated to `true` for the caller.
        function verifySignature(bytes calldata signature) external {
            if(eoaSignatureVerified[_msgSender()]) {
                revert SignatureAlreadyVerified();
            }
            if(_msgSender() != ECDSA.recover(signedMessageHash, signature)) {
                revert CallerDidNotSignTheMessage();
            }
            eoaSignatureVerified[_msgSender()] = true;
            emit VerifiedEOASignature(_msgSender());
        }
        /// @notice Allows a user to verify an ECDSA signature to definitively prove they are an EOA account.
        /// This version is passed the v, r, s components of the signature, and is slightly more gas efficient than
        /// calculating the v, r, s components on-chain.
        ///
        /// Throws when the caller has already verified their signature.
        /// Throws when the caller did not sign the message.
        ///
        /// Postconditions:
        /// ---------------
        /// The verified signature mapping has been updated to `true` for the caller.
        function verifySignatureVRS(uint8 v, bytes32 r, bytes32 s) external {
            if(eoaSignatureVerified[msg.sender]) {
                revert SignatureAlreadyVerified();
            }
            if(msg.sender != ECDSA.recover(signedMessageHash, v, r, s)) {
                revert CallerDidNotSignTheMessage();
            }
            eoaSignatureVerified[msg.sender] = true;
            emit VerifiedEOASignature(msg.sender);
        }
        /// @notice Returns true if the specified account has verified a signature on this registry, false otherwise.
        function isVerifiedEOA(address account) public view override returns (bool) {
            return eoaSignatureVerified[account];
        }
        /// @dev ERC-165 interface support
        function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
            return
                interfaceId == type(IEOARegistry).interfaceId ||
                super.supportsInterface(interfaceId);
        }
    }// SPDX-License-Identifier: MIT
    pragma solidity 0.8.9;
    enum AllowlistTypes {
        Operators,
        PermittedContractReceivers
    }
    enum ReceiverConstraints {
        None,
        NoCode,
        EOA
    }
    enum CallerConstraints {
        None,
        OperatorWhitelistEnableOTC,
        OperatorWhitelistDisableOTC
    }
    enum StakerConstraints {
        None,
        CallerIsTxOrigin,
        EOA
    }
    enum TransferSecurityLevels {
        Zero,
        One,
        Two,
        Three,
        Four,
        Five,
        Six
    }
    struct TransferSecurityPolicy {
        CallerConstraints callerConstraints;
        ReceiverConstraints receiverConstraints;
    }
    struct CollectionSecurityPolicy {
        TransferSecurityLevels transferSecurityLevel;
        uint120 operatorWhitelistId;
        uint120 permittedContractReceiversId;
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev External interface of AccessControl declared to support ERC165 detection.
     */
    interface IAccessControl {
        /**
         * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
         *
         * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
         * {RoleAdminChanged} not being emitted signaling this.
         *
         * _Available since v3.1._
         */
        event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
        /**
         * @dev Emitted when `account` is granted `role`.
         *
         * `sender` is the account that originated the contract call, an admin role
         * bearer except when using {AccessControl-_setupRole}.
         */
        event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
        /**
         * @dev Emitted when `account` is revoked `role`.
         *
         * `sender` is the account that originated the contract call:
         *   - if using `revokeRole`, it is the admin role bearer
         *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
         */
        event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
        /**
         * @dev Returns `true` if `account` has been granted `role`.
         */
        function hasRole(bytes32 role, address account) external view returns (bool);
        /**
         * @dev Returns the admin role that controls `role`. See {grantRole} and
         * {revokeRole}.
         *
         * To change a role's admin, use {AccessControl-_setRoleAdmin}.
         */
        function getRoleAdmin(bytes32 role) external view returns (bytes32);
        /**
         * @dev Grants `role` to `account`.
         *
         * If `account` had not been already granted `role`, emits a {RoleGranted}
         * event.
         *
         * Requirements:
         *
         * - the caller must have ``role``'s admin role.
         */
        function grantRole(bytes32 role, address account) external;
        /**
         * @dev Revokes `role` from `account`.
         *
         * If `account` had been granted `role`, emits a {RoleRevoked} event.
         *
         * Requirements:
         *
         * - the caller must have ``role``'s admin role.
         */
        function revokeRole(bytes32 role, address account) external;
        /**
         * @dev Revokes `role` from the calling account.
         *
         * Roles are often managed via {grantRole} and {revokeRole}: this function's
         * purpose is to provide a mechanism for accounts to lose their privileges
         * if they are compromised (such as when a trusted device is misplaced).
         *
         * If the calling account had been granted `role`, emits a {RoleRevoked}
         * event.
         *
         * Requirements:
         *
         * - the caller must be `account`.
         */
        function renounceRole(bytes32 role, address account) external;
    }
    // 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.8.0) (utils/Strings.sol)
    pragma solidity ^0.8.0;
    import "./math/Math.sol";
    /**
     * @dev String operations.
     */
    library Strings {
        bytes16 private constant _SYMBOLS = "0123456789abcdef";
        uint8 private constant _ADDRESS_LENGTH = 20;
        /**
         * @dev Converts a `uint256` to its ASCII `string` decimal representation.
         */
        function toString(uint256 value) internal pure returns (string memory) {
            unchecked {
                uint256 length = Math.log10(value) + 1;
                string memory buffer = new string(length);
                uint256 ptr;
                /// @solidity memory-safe-assembly
                assembly {
                    ptr := add(buffer, add(32, length))
                }
                while (true) {
                    ptr--;
                    /// @solidity memory-safe-assembly
                    assembly {
                        mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                    }
                    value /= 10;
                    if (value == 0) break;
                }
                return buffer;
            }
        }
        /**
         * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
         */
        function toHexString(uint256 value) internal pure returns (string memory) {
            unchecked {
                return toHexString(value, Math.log256(value) + 1);
            }
        }
        /**
         * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
         */
        function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
            bytes memory buffer = new bytes(2 * length + 2);
            buffer[0] = "0";
            buffer[1] = "x";
            for (uint256 i = 2 * length + 1; i > 1; --i) {
                buffer[i] = _SYMBOLS[value & 0xf];
                value >>= 4;
            }
            require(value == 0, "Strings: hex length insufficient");
            return string(buffer);
        }
        /**
         * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
         */
        function toHexString(address addr) internal pure returns (string memory) {
            return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/ECDSA.sol)
    pragma solidity ^0.8.0;
    import "../Strings.sol";
    /**
     * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
     *
     * These functions can be used to verify that a message was signed by the holder
     * of the private keys of a given address.
     */
    library ECDSA {
        enum RecoverError {
            NoError,
            InvalidSignature,
            InvalidSignatureLength,
            InvalidSignatureS,
            InvalidSignatureV // Deprecated in v4.8
        }
        function _throwError(RecoverError error) private pure {
            if (error == RecoverError.NoError) {
                return; // no error: do nothing
            } else if (error == RecoverError.InvalidSignature) {
                revert("ECDSA: invalid signature");
            } else if (error == RecoverError.InvalidSignatureLength) {
                revert("ECDSA: invalid signature length");
            } else if (error == RecoverError.InvalidSignatureS) {
                revert("ECDSA: invalid signature 's' value");
            }
        }
        /**
         * @dev Returns the address that signed a hashed message (`hash`) with
         * `signature` or error string. This address can then be used for verification purposes.
         *
         * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
         * this function rejects them by requiring the `s` value to be in the lower
         * half order, and the `v` value to be either 27 or 28.
         *
         * IMPORTANT: `hash` _must_ be the result of a hash operation for the
         * verification to be secure: it is possible to craft signatures that
         * recover to arbitrary addresses for non-hashed data. A safe way to ensure
         * this is by receiving a hash of the original message (which may otherwise
         * be too long), and then calling {toEthSignedMessageHash} on it.
         *
         * Documentation for signature generation:
         * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
         * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
         *
         * _Available since v4.3._
         */
        function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
            if (signature.length == 65) {
                bytes32 r;
                bytes32 s;
                uint8 v;
                // ecrecover takes the signature parameters, and the only way to get them
                // currently is to use assembly.
                /// @solidity memory-safe-assembly
                assembly {
                    r := mload(add(signature, 0x20))
                    s := mload(add(signature, 0x40))
                    v := byte(0, mload(add(signature, 0x60)))
                }
                return tryRecover(hash, v, r, s);
            } else {
                return (address(0), RecoverError.InvalidSignatureLength);
            }
        }
        /**
         * @dev Returns the address that signed a hashed message (`hash`) with
         * `signature`. This address can then be used for verification purposes.
         *
         * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
         * this function rejects them by requiring the `s` value to be in the lower
         * half order, and the `v` value to be either 27 or 28.
         *
         * IMPORTANT: `hash` _must_ be the result of a hash operation for the
         * verification to be secure: it is possible to craft signatures that
         * recover to arbitrary addresses for non-hashed data. A safe way to ensure
         * this is by receiving a hash of the original message (which may otherwise
         * be too long), and then calling {toEthSignedMessageHash} on it.
         */
        function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
            (address recovered, RecoverError error) = tryRecover(hash, signature);
            _throwError(error);
            return recovered;
        }
        /**
         * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
         *
         * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
         *
         * _Available since v4.3._
         */
        function tryRecover(
            bytes32 hash,
            bytes32 r,
            bytes32 vs
        ) internal pure returns (address, RecoverError) {
            bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
            uint8 v = uint8((uint256(vs) >> 255) + 27);
            return tryRecover(hash, v, r, s);
        }
        /**
         * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
         *
         * _Available since v4.2._
         */
        function recover(
            bytes32 hash,
            bytes32 r,
            bytes32 vs
        ) internal pure returns (address) {
            (address recovered, RecoverError error) = tryRecover(hash, r, vs);
            _throwError(error);
            return recovered;
        }
        /**
         * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
         * `r` and `s` signature fields separately.
         *
         * _Available since v4.3._
         */
        function tryRecover(
            bytes32 hash,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) internal pure returns (address, RecoverError) {
            // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
            // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
            // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
            // signatures from current libraries generate a unique signature with an s-value in the lower half order.
            //
            // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
            // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
            // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
            // these malleable signatures as well.
            if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
                return (address(0), RecoverError.InvalidSignatureS);
            }
            // If the signature is valid (and not malleable), return the signer address
            address signer = ecrecover(hash, v, r, s);
            if (signer == address(0)) {
                return (address(0), RecoverError.InvalidSignature);
            }
            return (signer, RecoverError.NoError);
        }
        /**
         * @dev Overload of {ECDSA-recover} that receives the `v`,
         * `r` and `s` signature fields separately.
         */
        function recover(
            bytes32 hash,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) internal pure returns (address) {
            (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
            _throwError(error);
            return recovered;
        }
        /**
         * @dev Returns an Ethereum Signed Message, created from a `hash`. This
         * produces hash corresponding to the one signed with the
         * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
         * JSON-RPC method as part of EIP-191.
         *
         * See {recover}.
         */
        function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
            // 32 is the length in bytes of hash,
            // enforced by the type signature above
            return keccak256(abi.encodePacked("\\x19Ethereum Signed Message:\
    32", hash));
        }
        /**
         * @dev Returns an Ethereum Signed Message, created from `s`. This
         * produces hash corresponding to the one signed with the
         * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
         * JSON-RPC method as part of EIP-191.
         *
         * See {recover}.
         */
        function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
            return keccak256(abi.encodePacked("\\x19Ethereum Signed Message:\
    ", Strings.toString(s.length), s));
        }
        /**
         * @dev Returns an Ethereum Signed Typed Data, created from a
         * `domainSeparator` and a `structHash`. This produces hash corresponding
         * to the one signed with the
         * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
         * JSON-RPC method as part of EIP-712.
         *
         * See {recover}.
         */
        function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
            return keccak256(abi.encodePacked("\\x19\\x01", domainSeparator, structHash));
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
    pragma solidity ^0.8.0;
    import "./IERC165.sol";
    /**
     * @dev Implementation of the {IERC165} interface.
     *
     * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
     * for the additional interface id that will be supported. For example:
     *
     * ```solidity
     * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
     *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
     * }
     * ```
     *
     * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
     */
    abstract contract ERC165 is IERC165 {
        /**
         * @dev See {IERC165-supportsInterface}.
         */
        function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
            return interfaceId == type(IERC165).interfaceId;
        }
    }
    // 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.8.0) (utils/math/Math.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Standard math utilities missing in the Solidity language.
     */
    library Math {
        enum Rounding {
            Down, // Toward negative infinity
            Up, // Toward infinity
            Zero // Toward zero
        }
        /**
         * @dev Returns the largest of two numbers.
         */
        function max(uint256 a, uint256 b) internal pure returns (uint256) {
            return a > b ? a : b;
        }
        /**
         * @dev Returns the smallest of two numbers.
         */
        function min(uint256 a, uint256 b) internal pure returns (uint256) {
            return a < b ? a : b;
        }
        /**
         * @dev Returns the average of two numbers. The result is rounded towards
         * zero.
         */
        function average(uint256 a, uint256 b) internal pure returns (uint256) {
            // (a + b) / 2 can overflow.
            return (a & b) + (a ^ b) / 2;
        }
        /**
         * @dev Returns the ceiling of the division of two numbers.
         *
         * This differs from standard division with `/` in that it rounds up instead
         * of rounding down.
         */
        function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
            // (a + b - 1) / b can overflow on addition, so we distribute.
            return a == 0 ? 0 : (a - 1) / b + 1;
        }
        /**
         * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
         * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
         * with further edits by Uniswap Labs also under MIT license.
         */
        function mulDiv(
            uint256 x,
            uint256 y,
            uint256 denominator
        ) internal pure returns (uint256 result) {
            unchecked {
                // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
                // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
                // variables such that product = prod1 * 2^256 + prod0.
                uint256 prod0; // Least significant 256 bits of the product
                uint256 prod1; // Most significant 256 bits of the product
                assembly {
                    let mm := mulmod(x, y, not(0))
                    prod0 := mul(x, y)
                    prod1 := sub(sub(mm, prod0), lt(mm, prod0))
                }
                // Handle non-overflow cases, 256 by 256 division.
                if (prod1 == 0) {
                    return prod0 / denominator;
                }
                // Make sure the result is less than 2^256. Also prevents denominator == 0.
                require(denominator > prod1);
                ///////////////////////////////////////////////
                // 512 by 256 division.
                ///////////////////////////////////////////////
                // Make division exact by subtracting the remainder from [prod1 prod0].
                uint256 remainder;
                assembly {
                    // Compute remainder using mulmod.
                    remainder := mulmod(x, y, denominator)
                    // Subtract 256 bit number from 512 bit number.
                    prod1 := sub(prod1, gt(remainder, prod0))
                    prod0 := sub(prod0, remainder)
                }
                // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
                // See https://cs.stackexchange.com/q/138556/92363.
                // Does not overflow because the denominator cannot be zero at this stage in the function.
                uint256 twos = denominator & (~denominator + 1);
                assembly {
                    // Divide denominator by twos.
                    denominator := div(denominator, twos)
                    // Divide [prod1 prod0] by twos.
                    prod0 := div(prod0, twos)
                    // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                    twos := add(div(sub(0, twos), twos), 1)
                }
                // Shift in bits from prod1 into prod0.
                prod0 |= prod1 * twos;
                // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
                // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
                // four bits. That is, denominator * inv = 1 mod 2^4.
                uint256 inverse = (3 * denominator) ^ 2;
                // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
                // in modular arithmetic, doubling the correct bits in each step.
                inverse *= 2 - denominator * inverse; // inverse mod 2^8
                inverse *= 2 - denominator * inverse; // inverse mod 2^16
                inverse *= 2 - denominator * inverse; // inverse mod 2^32
                inverse *= 2 - denominator * inverse; // inverse mod 2^64
                inverse *= 2 - denominator * inverse; // inverse mod 2^128
                inverse *= 2 - denominator * inverse; // inverse mod 2^256
                // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
                // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
                // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
                // is no longer required.
                result = prod0 * inverse;
                return result;
            }
        }
        /**
         * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
         */
        function mulDiv(
            uint256 x,
            uint256 y,
            uint256 denominator,
            Rounding rounding
        ) internal pure returns (uint256) {
            uint256 result = mulDiv(x, y, denominator);
            if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
                result += 1;
            }
            return result;
        }
        /**
         * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
         *
         * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
         */
        function sqrt(uint256 a) internal pure returns (uint256) {
            if (a == 0) {
                return 0;
            }
            // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
            //
            // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
            // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
            //
            // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
            // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
            // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
            //
            // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
            uint256 result = 1 << (log2(a) >> 1);
            // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
            // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
            // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
            // into the expected uint128 result.
            unchecked {
                result = (result + a / result) >> 1;
                result = (result + a / result) >> 1;
                result = (result + a / result) >> 1;
                result = (result + a / result) >> 1;
                result = (result + a / result) >> 1;
                result = (result + a / result) >> 1;
                result = (result + a / result) >> 1;
                return min(result, a / result);
            }
        }
        /**
         * @notice Calculates sqrt(a), following the selected rounding direction.
         */
        function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
            unchecked {
                uint256 result = sqrt(a);
                return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
            }
        }
        /**
         * @dev Return the log in base 2, rounded down, of a positive value.
         * Returns 0 if given 0.
         */
        function log2(uint256 value) internal pure returns (uint256) {
            uint256 result = 0;
            unchecked {
                if (value >> 128 > 0) {
                    value >>= 128;
                    result += 128;
                }
                if (value >> 64 > 0) {
                    value >>= 64;
                    result += 64;
                }
                if (value >> 32 > 0) {
                    value >>= 32;
                    result += 32;
                }
                if (value >> 16 > 0) {
                    value >>= 16;
                    result += 16;
                }
                if (value >> 8 > 0) {
                    value >>= 8;
                    result += 8;
                }
                if (value >> 4 > 0) {
                    value >>= 4;
                    result += 4;
                }
                if (value >> 2 > 0) {
                    value >>= 2;
                    result += 2;
                }
                if (value >> 1 > 0) {
                    result += 1;
                }
            }
            return result;
        }
        /**
         * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
         * Returns 0 if given 0.
         */
        function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
            unchecked {
                uint256 result = log2(value);
                return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
            }
        }
        /**
         * @dev Return the log in base 10, rounded down, of a positive value.
         * Returns 0 if given 0.
         */
        function log10(uint256 value) internal pure returns (uint256) {
            uint256 result = 0;
            unchecked {
                if (value >= 10**64) {
                    value /= 10**64;
                    result += 64;
                }
                if (value >= 10**32) {
                    value /= 10**32;
                    result += 32;
                }
                if (value >= 10**16) {
                    value /= 10**16;
                    result += 16;
                }
                if (value >= 10**8) {
                    value /= 10**8;
                    result += 8;
                }
                if (value >= 10**4) {
                    value /= 10**4;
                    result += 4;
                }
                if (value >= 10**2) {
                    value /= 10**2;
                    result += 2;
                }
                if (value >= 10**1) {
                    result += 1;
                }
            }
            return result;
        }
        /**
         * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
         * Returns 0 if given 0.
         */
        function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
            unchecked {
                uint256 result = log10(value);
                return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0);
            }
        }
        /**
         * @dev Return the log in base 256, rounded down, of a positive value.
         * Returns 0 if given 0.
         *
         * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
         */
        function log256(uint256 value) internal pure returns (uint256) {
            uint256 result = 0;
            unchecked {
                if (value >> 128 > 0) {
                    value >>= 128;
                    result += 16;
                }
                if (value >> 64 > 0) {
                    value >>= 64;
                    result += 8;
                }
                if (value >> 32 > 0) {
                    value >>= 32;
                    result += 4;
                }
                if (value >> 16 > 0) {
                    value >>= 16;
                    result += 2;
                }
                if (value >> 8 > 0) {
                    result += 1;
                }
            }
            return result;
        }
        /**
         * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
         * Returns 0 if given 0.
         */
        function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
            unchecked {
                uint256 result = log256(value);
                return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0);
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.8.0) (utils/structs/EnumerableSet.sol)
    // This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
    pragma solidity ^0.8.0;
    /**
     * @dev Library for managing
     * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
     * types.
     *
     * Sets have the following properties:
     *
     * - Elements are added, removed, and checked for existence in constant time
     * (O(1)).
     * - Elements are enumerated in O(n). No guarantees are made on the ordering.
     *
     * ```
     * contract Example {
     *     // Add the library methods
     *     using EnumerableSet for EnumerableSet.AddressSet;
     *
     *     // Declare a set state variable
     *     EnumerableSet.AddressSet private mySet;
     * }
     * ```
     *
     * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
     * and `uint256` (`UintSet`) are supported.
     *
     * [WARNING]
     * ====
     * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
     * unusable.
     * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
     *
     * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
     * array of EnumerableSet.
     * ====
     */
    library EnumerableSet {
        // To implement this library for multiple types with as little code
        // repetition as possible, we write it in terms of a generic Set type with
        // bytes32 values.
        // The Set implementation uses private functions, and user-facing
        // implementations (such as AddressSet) are just wrappers around the
        // underlying Set.
        // This means that we can only create new EnumerableSets for types that fit
        // in bytes32.
        struct Set {
            // Storage of set values
            bytes32[] _values;
            // Position of the value in the `values` array, plus 1 because index 0
            // means a value is not in the set.
            mapping(bytes32 => uint256) _indexes;
        }
        /**
         * @dev Add a value to a set. O(1).
         *
         * Returns true if the value was added to the set, that is if it was not
         * already present.
         */
        function _add(Set storage set, bytes32 value) private returns (bool) {
            if (!_contains(set, value)) {
                set._values.push(value);
                // The value is stored at length-1, but we add 1 to all indexes
                // and use 0 as a sentinel value
                set._indexes[value] = set._values.length;
                return true;
            } else {
                return false;
            }
        }
        /**
         * @dev Removes a value from a set. O(1).
         *
         * Returns true if the value was removed from the set, that is if it was
         * present.
         */
        function _remove(Set storage set, bytes32 value) private returns (bool) {
            // We read and store the value's index to prevent multiple reads from the same storage slot
            uint256 valueIndex = set._indexes[value];
            if (valueIndex != 0) {
                // Equivalent to contains(set, value)
                // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
                // the array, and then remove the last element (sometimes called as 'swap and pop').
                // This modifies the order of the array, as noted in {at}.
                uint256 toDeleteIndex = valueIndex - 1;
                uint256 lastIndex = set._values.length - 1;
                if (lastIndex != toDeleteIndex) {
                    bytes32 lastValue = set._values[lastIndex];
                    // Move the last value to the index where the value to delete is
                    set._values[toDeleteIndex] = lastValue;
                    // Update the index for the moved value
                    set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
                }
                // Delete the slot where the moved value was stored
                set._values.pop();
                // Delete the index for the deleted slot
                delete set._indexes[value];
                return true;
            } else {
                return false;
            }
        }
        /**
         * @dev Returns true if the value is in the set. O(1).
         */
        function _contains(Set storage set, bytes32 value) private view returns (bool) {
            return set._indexes[value] != 0;
        }
        /**
         * @dev Returns the number of values on the set. O(1).
         */
        function _length(Set storage set) private view returns (uint256) {
            return set._values.length;
        }
        /**
         * @dev Returns the value stored at position `index` in the set. O(1).
         *
         * Note that there are no guarantees on the ordering of values inside the
         * array, and it may change when more values are added or removed.
         *
         * Requirements:
         *
         * - `index` must be strictly less than {length}.
         */
        function _at(Set storage set, uint256 index) private view returns (bytes32) {
            return set._values[index];
        }
        /**
         * @dev Return the entire set in an array
         *
         * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
         * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
         * this function has an unbounded cost, and using it as part of a state-changing function may render the function
         * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
         */
        function _values(Set storage set) private view returns (bytes32[] memory) {
            return set._values;
        }
        // Bytes32Set
        struct Bytes32Set {
            Set _inner;
        }
        /**
         * @dev Add a value to a set. O(1).
         *
         * Returns true if the value was added to the set, that is if it was not
         * already present.
         */
        function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
            return _add(set._inner, value);
        }
        /**
         * @dev Removes a value from a set. O(1).
         *
         * Returns true if the value was removed from the set, that is if it was
         * present.
         */
        function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
            return _remove(set._inner, value);
        }
        /**
         * @dev Returns true if the value is in the set. O(1).
         */
        function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
            return _contains(set._inner, value);
        }
        /**
         * @dev Returns the number of values in the set. O(1).
         */
        function length(Bytes32Set storage set) internal view returns (uint256) {
            return _length(set._inner);
        }
        /**
         * @dev Returns the value stored at position `index` in the set. O(1).
         *
         * Note that there are no guarantees on the ordering of values inside the
         * array, and it may change when more values are added or removed.
         *
         * Requirements:
         *
         * - `index` must be strictly less than {length}.
         */
        function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
            return _at(set._inner, index);
        }
        /**
         * @dev Return the entire set in an array
         *
         * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
         * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
         * this function has an unbounded cost, and using it as part of a state-changing function may render the function
         * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
         */
        function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
            bytes32[] memory store = _values(set._inner);
            bytes32[] memory result;
            /// @solidity memory-safe-assembly
            assembly {
                result := store
            }
            return result;
        }
        // AddressSet
        struct AddressSet {
            Set _inner;
        }
        /**
         * @dev Add a value to a set. O(1).
         *
         * Returns true if the value was added to the set, that is if it was not
         * already present.
         */
        function add(AddressSet storage set, address value) internal returns (bool) {
            return _add(set._inner, bytes32(uint256(uint160(value))));
        }
        /**
         * @dev Removes a value from a set. O(1).
         *
         * Returns true if the value was removed from the set, that is if it was
         * present.
         */
        function remove(AddressSet storage set, address value) internal returns (bool) {
            return _remove(set._inner, bytes32(uint256(uint160(value))));
        }
        /**
         * @dev Returns true if the value is in the set. O(1).
         */
        function contains(AddressSet storage set, address value) internal view returns (bool) {
            return _contains(set._inner, bytes32(uint256(uint160(value))));
        }
        /**
         * @dev Returns the number of values in the set. O(1).
         */
        function length(AddressSet storage set) internal view returns (uint256) {
            return _length(set._inner);
        }
        /**
         * @dev Returns the value stored at position `index` in the set. O(1).
         *
         * Note that there are no guarantees on the ordering of values inside the
         * array, and it may change when more values are added or removed.
         *
         * Requirements:
         *
         * - `index` must be strictly less than {length}.
         */
        function at(AddressSet storage set, uint256 index) internal view returns (address) {
            return address(uint160(uint256(_at(set._inner, index))));
        }
        /**
         * @dev Return the entire set in an array
         *
         * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
         * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
         * this function has an unbounded cost, and using it as part of a state-changing function may render the function
         * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
         */
        function values(AddressSet storage set) internal view returns (address[] memory) {
            bytes32[] memory store = _values(set._inner);
            address[] memory result;
            /// @solidity memory-safe-assembly
            assembly {
                result := store
            }
            return result;
        }
        // UintSet
        struct UintSet {
            Set _inner;
        }
        /**
         * @dev Add a value to a set. O(1).
         *
         * Returns true if the value was added to the set, that is if it was not
         * already present.
         */
        function add(UintSet storage set, uint256 value) internal returns (bool) {
            return _add(set._inner, bytes32(value));
        }
        /**
         * @dev Removes a value from a set. O(1).
         *
         * Returns true if the value was removed from the set, that is if it was
         * present.
         */
        function remove(UintSet storage set, uint256 value) internal returns (bool) {
            return _remove(set._inner, bytes32(value));
        }
        /**
         * @dev Returns true if the value is in the set. O(1).
         */
        function contains(UintSet storage set, uint256 value) internal view returns (bool) {
            return _contains(set._inner, bytes32(value));
        }
        /**
         * @dev Returns the number of values in the set. O(1).
         */
        function length(UintSet storage set) internal view returns (uint256) {
            return _length(set._inner);
        }
        /**
         * @dev Returns the value stored at position `index` in the set. O(1).
         *
         * Note that there are no guarantees on the ordering of values inside the
         * array, and it may change when more values are added or removed.
         *
         * Requirements:
         *
         * - `index` must be strictly less than {length}.
         */
        function at(UintSet storage set, uint256 index) internal view returns (uint256) {
            return uint256(_at(set._inner, index));
        }
        /**
         * @dev Return the entire set in an array
         *
         * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
         * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
         * this function has an unbounded cost, and using it as part of a state-changing function may render the function
         * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
         */
        function values(UintSet storage set) internal view returns (uint256[] memory) {
            bytes32[] memory store = _values(set._inner);
            uint256[] memory result;
            /// @solidity memory-safe-assembly
            assembly {
                result := store
            }
            return result;
        }
    }