creation_sourcecode: {{
    "language": "Solidity",
    "sources": {
      "src/AuthorizedTransferSecurityRegistry.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.24;\n\nimport {\n    ListTypes,\n    IAuthorizedTransferSecurityRegistry\n} from \"./interfaces/IAuthorizedTransferSecurityRegistry.sol\";\n\nimport {\n    ICreatorTokenTransferValidator\n} from \"./interfaces/ICreatorTokenTransferValidator.sol\";\n\nimport { IOwnable } from \"./interfaces/IOwnable.sol\";\nimport { IAccessControl } from \"@openzeppelin/contracts/access/IAccessControl.sol\";\nimport { EnumerableSet } from \"@openzeppelin/contracts/utils/structs/EnumerableSet.sol\";\nimport { ERC165 } from \"@openzeppelin/contracts/utils/introspection/ERC165.sol\";\n\nimport { Tstorish } from \"tstorish/Tstorish.sol\";\n\nimport {\n    AuthorizedTransferSecurityRegistryExtraViewFns\n} from \"./AuthorizedTransferSecurityRegistryExtraViewFns.sol\";\n\n/// @title AuthorizedTransferSecurityRegistry\n/// @dev Implementation of a simplified version of the Transfer Security Registry that only\n///      supports authorizers and whitelisted operators, and assumes a security level of\n///      OperatorWhitelistEnableOTC + authorizers for all collections that use it. Note that\n///      a number of view functions on collections that add this validator will not work.\ncontract AuthorizedTransferSecurityRegistry is Tstorish, IAuthorizedTransferSecurityRegistry, ERC165 {\n    using EnumerableSet for EnumerableSet.AddressSet;\n\n    /**\n     * @dev This struct is used internally to represent an enumerable list of accounts.\n     */\n    struct AccountList {\n        EnumerableSet.AddressSet enumerableAccounts;\n        mapping (address => bool) nonEnumerableAccounts;\n    }\n\n    /**\n     * @dev This struct is used internally for the storage of authorizer + operator lists.\n     */\n    struct List {\n        address owner;\n        AccountList authorizers;\n        AccountList operators;\n    }\n    \n    /// @dev The default admin role value for contracts that implement access control.\n    bytes32 private constant DEFAULT_ACCESS_CONTROL_ADMIN_ROLE = 0x00;\n\n    /// @notice Keeps track of the most recently created list id.\n    uint120 public lastListId;\n\n    /// @dev Mapping of list ids to list settings\n    mapping (uint120 => List) private lists;\n\n    /// @dev Mapping of collection addresses to list ids\n    mapping (address => uint120) private listIdsByCollection;\n\n    // TSTORE slot: scope ++ 8 empty bytes ++ collection\n    bytes4 private constant _AUTHORIZED_OPERATOR_SCOPE = 0x596a397a;\n\n    // TSTORE slot: keccak256(scope ++ identifier ++ collection)\n    bytes4 private constant _AUTHORIZED_IDENTIFIER_SCOPE = 0x7e746c61;\n\n    // TSTORE slot: keccak256(scope ++ identifier ++ collection)\n    bytes4 private constant _AUTHORIZED_AMOUNT_SCOPE = 0x71836d45;\n\n    address private immutable _EXTRA_VIEW_FUNCTIONS;\n\n    /**\n     * @dev This modifier restricts a function call to the owner of the list `id`.\n     * @dev Throws when the caller is not the list owner.\n     */\n    modifier onlyListOwner(uint120 id) {\n        _requireCallerOwnsList(id);\n        _;\n    }\n\n    /**\n     * @dev This modifier reverts a transaction if the supplied array has a zero length.\n     * @dev Throws when the array parameter has a zero length.\n     */\n    modifier notZero(uint256 value) {\n        if (value == 0) {\n            revert AuthorizedTransferSecurityRegistry__ArrayLengthCannotBeZero();\n        }\n        _;\n    }\n\n    constructor(address defaultOwner) {\n        uint120 id = 0;\n\n        lists[id].owner = defaultOwner;\n\n        emit CreatedList(id, \"DEFAULT LIST\");\n        emit ReassignedListOwnership(id, defaultOwner);\n\n        // Deploy a contract containing legacy view functions.\n        _EXTRA_VIEW_FUNCTIONS = address(new AuthorizedTransferSecurityRegistryExtraViewFns());\n    }\n\n    // Delegatecall to contract with legacy view functions in the fallback.\n    fallback() external {\n        address target = _EXTRA_VIEW_FUNCTIONS;\n        assembly {\n            calldatacopy(0, 0, calldatasize())\n            let status := delegatecall(gas(), target, 0, calldatasize(), 0, 0)\n            returndatacopy(0, 0, returndatasize())\n            switch status\n            case 0 {\n                revert(0, returndatasize())\n            }\n            default {\n                return(0, returndatasize())\n            }\n        }\n    }\n\n    /// Manage lists of authorizers & operators that can be applied to collections\n    function createList(string calldata name) external returns (uint120) {\n        uint120 id = ++lastListId;\n\n        lists[id].owner = msg.sender;\n\n        emit CreatedList(id, name);\n        emit ReassignedListOwnership(id, msg.sender);\n\n        return id;\n    }\n\n    function createListCopy(string calldata name, uint120 sourceListId) external override returns (uint120) {\n        uint120 id = ++lastListId;\n\n        unchecked {\n            if (sourceListId > id - 1) {\n                revert AuthorizedTransferSecurityRegistry__ListDoesNotExist();\n            }\n        }\n        List storage sourceList = lists[sourceListId];\n        List storage targetList = lists[id];\n\n        targetList.owner = msg.sender;\n\n        emit CreatedList(id, name);\n        emit ReassignedListOwnership(id, msg.sender);\n\n\n        _copyAddressSet(ListTypes.AuthorizerList, id, sourceList.authorizers, targetList.authorizers);\n        _copyAddressSet(ListTypes.OperatorList, id, sourceList.operators, targetList.operators);\n\n        return id;\n    }\n\n    function reassignOwnershipOfList(uint120 id, address newOwner) external onlyListOwner(id) {\n        if (newOwner == address(0)) {\n            revert AuthorizedTransferSecurityRegistry__ListOwnershipCannotBeTransferredToZeroAddress();\n        }\n\n        lists[id].owner = newOwner;\n\n        emit ReassignedListOwnership(id, newOwner);\n    }\n\n    function renounceOwnershipOfList(uint120 id) external onlyListOwner(id) {\n        lists[id].owner = address(0);\n\n        emit ReassignedListOwnership(id, address(0));\n    }\n\n    function applyListToCollection(address collection, uint120 id) external {\n        _requireCallerIsNFTOrContractOwnerOrAdmin(collection);\n\n        if (id > lastListId) {\n            revert AuthorizedTransferSecurityRegistry__ListDoesNotExist();\n        }\n\n        listIdsByCollection[collection] = id;\n\n        emit AppliedListToCollection(collection, id);\n    }\n\n    function listOwners(uint120 id) external view returns (address) {\n        return lists[id].owner;\n    }\n\n    /// Manage and query for authorizers on lists\n    function addAuthorizers(uint120 id, address[] calldata accounts) external onlyListOwner(id) notZero(accounts.length) {\n        _addAccounts(id, accounts, lists[id].authorizers, ListTypes.AuthorizerList);\n    }\n\n    function removeAuthorizers(uint120 id, address[] calldata accounts) external onlyListOwner(id) notZero(accounts.length) {\n        _removeAccounts(id, accounts, lists[id].authorizers, ListTypes.AuthorizerList);\n    }\n    \n    function getAuthorizers(uint120 id) external view returns (address[] memory) {\n        return lists[id].authorizers.enumerableAccounts.values();\n    }\n\n    function isAuthorizer(uint120 id, address account) external view returns (bool) {\n        return lists[id].authorizers.nonEnumerableAccounts[account];\n    }\n\n    function getAuthorizersByCollection(address collection) external view returns (address[] memory) {\n        return lists[listIdsByCollection[collection]].authorizers.enumerableAccounts.values();\n    }\n\n    function isAuthorizerByCollection(address collection, address account) external view returns (bool) {\n        return lists[listIdsByCollection[collection]].authorizers.nonEnumerableAccounts[account];\n    }\n\n    function _ensureCallerIsCollectionAuthorizer(address collection) internal view {\n        if (!lists[listIdsByCollection[collection]].authorizers.nonEnumerableAccounts[msg.sender]) {\n            revert AuthorizedTransferSecurityRegistry__CallerIsNotValidAuthorizer();\n        }\n    }\n\n    /// Manage and query for operators on lists\n    function addOperators(uint120 id, address[] calldata accounts) external onlyListOwner(id) notZero(accounts.length) {\n        _addAccounts(id, accounts, lists[id].operators, ListTypes.OperatorList);\n    }\n\n    function removeOperators(uint120 id, address[] calldata accounts) external onlyListOwner(id) notZero(accounts.length) {\n        _removeAccounts(id, accounts, lists[id].operators, ListTypes.OperatorList);\n    }\n    \n    function getOperators(uint120 id) external view returns (address[] memory) {\n        return lists[id].operators.enumerableAccounts.values();\n    }\n\n    function isOperator(uint120 id, address account) external view returns (bool) {\n        return lists[id].operators.nonEnumerableAccounts[account];\n    }\n    function getOperatorsByCollection(address collection) external view returns (address[] memory) {\n        return lists[listIdsByCollection[collection]].operators.enumerableAccounts.values();\n    }\n\n    function isOperatorByCollection(address collection, address account) external view returns (bool) {\n        return lists[listIdsByCollection[collection]].operators.nonEnumerableAccounts[account];\n    }\n\n    /// Ensure that a specific operator has been authorized to transfer tokens\n    function validateTransfer(address caller, address from, address) external view {\n        _validateTransfer(caller, from);\n    }\n\n    /// Ensure that a transfer has been authorized for a specific tokenId\n    function validateTransfer(address caller, address from, address, uint256 tokenId) external view {\n        _validateTransferByIdentifer(caller, from, tokenId);\n    }\n\n    /// Ensure that a transfer has been authorized for a specific amount of a specific tokenId, and\n    /// reduce the transferable amount remaining\n    function validateTransfer(address caller, address from, address, uint256 tokenId, uint256 amount) external {\n        _validateTransferByAmount(caller, from, tokenId, amount);\n    }\n\n    /// Legacy alias for validateTransfer (address caller, address from, address to)\n    function applyCollectionTransferPolicy(address caller, address from, address) external view {\n        _validateTransfer(caller, from);\n    }\n\n    /// Temporarily assign a specific allowed operator for a given collection\n    function beforeAuthorizedTransfer(address operator, address token) external {\n        _ensureCallerIsCollectionAuthorizer(token);\n\n        _setTstorish(\n            _getAuthorizedOperatorSlot(token),\n            uint256(uint160(operator))\n        );\n    }\n\n    /// Clear assignment of a specific allowed operator for a given collection\n    function afterAuthorizedTransfer(address token) external {\n        _ensureCallerIsCollectionAuthorizer(token);\n\n        _clearTstorish(_getAuthorizedOperatorSlot(token));\n    }\n\n    /// Temporarily allow a specific tokenId from a given collection to be transferred\n    function beforeAuthorizedTransfer(address token, uint256 tokenId) external {\n        _ensureCallerIsCollectionAuthorizer(token);\n\n        _setTstorish(\n            _getAuthorizedIdentifierSlot(token, tokenId),\n            1\n        );\n    }\n\n    /// Clear assignment of an specific tokenId's transfer allowance\n    function afterAuthorizedTransfer(address token, uint256 tokenId) external {\n        _ensureCallerIsCollectionAuthorizer(token);\n\n        _clearTstorish(_getAuthorizedIdentifierSlot(token, tokenId));\n    }\n\n    /// Temporarily allow a specific amount of a specific tokenId from a given collection to be transferred\n    function beforeAuthorizedTransferWithAmount(address token, uint256 tokenId, uint256 amount) external {\n        _ensureCallerIsCollectionAuthorizer(token);\n\n        uint256 slot = _getAuthorizedAmountSlot(token, tokenId);\n\n        uint256 currentAmount = _getTstorish(slot);\n\n        uint256 newAmount = currentAmount + amount;\n\n        _setTstorish(slot, newAmount);\n    }\n\n    /// Clear assignment of a tokenId's transfer allowance for a specific amount\n    function afterAuthorizedTransferWithAmount(address token, uint256 tokenId) external {\n        _ensureCallerIsCollectionAuthorizer(token);\n\n        _clearTstorish(_getAuthorizedAmountSlot(token, tokenId));\n    }\n\n    /// @notice ERC-165 Interface Support\n    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165) returns (bool) {\n        return\n            interfaceId == type(ICreatorTokenTransferValidator).interfaceId ||\n            interfaceId == type(IAuthorizedTransferSecurityRegistry).interfaceId ||\n            super.supportsInterface(interfaceId);\n    }\n\n    /**\n     * @notice Copies all addresses in `ptrFromList` to `ptrToList`.\n     * \n     * @dev    This function will copy all addresses from one list to another list.\n     * @dev    Note: If used to copy adddresses to an existing list the current list contents will not be\n     * @dev    deleted before copying. New addresses will be appeneded to the end of the list and the\n     * @dev    non-enumerable mapping key value will be set to true.\n     * \n     * @dev <h4>Postconditions:</h4>\n     *      1. Addresses in from list that are not already present in to list are added to the to list.\n     *      2. Emits an `AddedAccountToList` event for each address copied to the list.\n     * \n     * @param  listType          The type of list addresses are being copied from and to.\n     * @param  destinationListId The id of the list being copied to.\n     * @param  ptrFromList       The storage pointer for the list being copied from.\n     * @param  ptrToList         The storage pointer for the list being copied to.\n     */\n    function _copyAddressSet(\n        ListTypes listType,\n        uint120 destinationListId,\n        AccountList storage ptrFromList,\n        AccountList storage ptrToList\n    ) private {\n        EnumerableSet.AddressSet storage ptrFromSet = ptrFromList.enumerableAccounts;\n        EnumerableSet.AddressSet storage ptrToSet = ptrToList.enumerableAccounts;\n        mapping (address => bool) storage ptrToNonEnumerableSet = ptrToList.nonEnumerableAccounts;\n        uint256 sourceLength = ptrFromSet.length();\n        address account;\n        for (uint256 i = 0; i < sourceLength;) {\n            account = ptrFromSet.at(i); \n            if (ptrToSet.add(account)) {\n                emit AddedAccountToList(listType, destinationListId, account);\n                ptrToNonEnumerableSet[account] = true;\n            }\n\n            unchecked {\n                ++i;\n            }\n        }\n    }\n\n    /**\n     * @notice Requires the caller to be the owner of list `id`.\n     * \n     * @dev    Throws when the caller is not the owner of the list.\n     */\n    function _requireCallerOwnsList(uint120 id) private view {\n        if (msg.sender != lists[id].owner) {\n            revert AuthorizedTransferSecurityRegistry__CallerDoesNotOwnList();\n        }\n    }\n\n    /**\n     * @notice Reverts the transaction if the caller is not the owner or assigned the default\n     * @notice admin role of the contract at `tokenAddress`.\n     *\n     * @dev    Throws when the caller is neither owner nor assigned the default admin role.\n     * \n     * @param tokenAddress The contract address of the token to check permissions for.\n     */\n    function _requireCallerIsNFTOrContractOwnerOrAdmin(address tokenAddress) internal view {\n        if (msg.sender == tokenAddress) {\n            return;\n        }\n\n        if (msg.sender == _safeOwner(tokenAddress)) {\n            return;\n        }\n\n        if (!_safeHasRole(tokenAddress)) {\n            revert AuthorizedTransferSecurityRegistry__CallerMustHaveElevatedPermissionsForSpecifiedNFT();\n        }\n    }\n\n    /**\n     * @dev A gas efficient, and fallback-safe way to call the owner function on a token contract.\n     *      This will get the owner if it exists - and when the function is unimplemented, the\n     *      presence of a fallback function will not result in halted execution.\n     */\n    function _safeOwner(\n        address tokenAddress\n    ) internal view returns(address owner) {\n        assembly {\n            mstore(0x00, 0x8da5cb5b)\n            let status := staticcall(gas(), tokenAddress, 0x1c, 0x04, 0x00, 0x20)\n            if and(not(lt(returndatasize(), 0x20)), status) {\n                owner := mload(0x00)\n            }\n        }\n    }\n    \n    /**\n     * @dev A gas efficient, and fallback-safe way to call the hasRole function on a token contract.\n     *      This will check if the account `hasRole` if `hasRole` exists - and when the function is unimplemented, the\n     *      presence of a fallback function will not result in halted execution.\n     */\n    function _safeHasRole(\n        address tokenAddress\n    ) internal view returns(bool hasRole) {\n        assembly {\n            let ptr := mload(0x40)\n            mstore(0x40, add(ptr, 0x60))\n            mstore(ptr, 0x91d14854)\n            mstore(add(0x20, ptr), DEFAULT_ACCESS_CONTROL_ADMIN_ROLE)\n            mstore(add(0x40, ptr), caller())\n            let status := staticcall(gas(), tokenAddress, add(ptr, 0x1c), 0x44, 0x00, 0x20)\n            if and(not(lt(returndatasize(), 0x20)), status) {\n                hasRole := mload(0x00)\n            }\n        }\n    }\n\n\n    /**\n     * @dev Internal function used to efficiently retrieve the code length of `account`.\n     * \n     * @param account The address to get the deployed code length for.\n     * \n     * @return length The length of deployed code at the address.\n     */\n    function _getCodeLengthAsm(address account) internal view returns (uint256 length) {\n        assembly { length := extcodesize(account) }\n    }\n\n    function _addAccounts(\n        uint120 id,\n        address[] calldata accounts,\n        AccountList storage accountList,\n        ListTypes listType\n    ) internal {\n        address account;\n        for (uint256 i = 0; i < accounts.length;) {\n            account = accounts[i];\n\n            if (account == address(0)) {\n                revert AuthorizedTransferSecurityRegistry__ZeroAddressNotAllowed();\n            }\n\n            if (accountList.enumerableAccounts.add(account)) {\n                emit AddedAccountToList(listType, id, account);\n                accountList.nonEnumerableAccounts[account] = true;\n            }\n\n            unchecked {\n                ++i;\n            }\n        }\n    }\n\n    function _removeAccounts(\n        uint120 id,\n        address[] calldata accounts,\n        AccountList storage accountList,\n        ListTypes listType\n    ) internal {\n        address account;\n        for (uint256 i = 0; i < accounts.length;) {\n            account = accounts[i];\n\n            if (accountList.enumerableAccounts.remove(account)) {\n                emit RemovedAccountFromList(listType, id, account);\n                delete accountList.nonEnumerableAccounts[account];\n            }\n\n            unchecked {\n                ++i;\n            }\n        }\n    }\n\n    function _validateTransfer(address operator, address from) internal view {\n        if (operator == from) {\n            return;\n        }\n\n        uint256 slot = _getAuthorizedOperatorSlot(msg.sender);\n\n        if (operator == address(uint160(_getTstorish(slot)))) {\n            return;\n        }\n\n        if (!lists[listIdsByCollection[msg.sender]].operators.nonEnumerableAccounts[operator]) {\n            revert AuthorizedTransferSecurityRegistry__UnauthorizedTransfer();\n        }\n    }\n\n    function _validateTransferByIdentifer(address operator, address from, uint256 identifier) internal view {\n        if (operator == from) {\n            return;\n        }\n\n        uint256 slot = _getAuthorizedIdentifierSlot(msg.sender, identifier);\n\n        uint256 authorizedIdentifier = _getTstorish(slot);\n\n        if (authorizedIdentifier != 0) {\n            return;\n        }\n\n        if (!lists[listIdsByCollection[msg.sender]].operators.nonEnumerableAccounts[operator]) {\n            revert AuthorizedTransferSecurityRegistry__UnauthorizedTransfer();\n        }\n    }\n\n    function _validateTransferByAmount(address operator, address from, uint256 identifier, uint256 amount) internal {\n        if (operator == from) {\n            return;\n        }\n\n        uint256 slot = _getAuthorizedAmountSlot(msg.sender, identifier);\n\n        uint256 authorizedAmount = _getTstorish(slot);\n        if (authorizedAmount >= amount) {\n            unchecked {\n                _setTstorish(slot, authorizedAmount - amount);\n            }\n\n            return;\n        }\n\n        if (!lists[listIdsByCollection[msg.sender]].operators.nonEnumerableAccounts[operator]) {\n            revert AuthorizedTransferSecurityRegistry__UnauthorizedTransfer();\n        }\n    }\n\n    function _getAuthorizedOperatorSlot(\n        address collection\n    ) internal pure returns (uint256 slot) {\n        bytes4 authorizedOperatorScope = _AUTHORIZED_OPERATOR_SCOPE;\n        assembly {\n            slot := or(\n                authorizedOperatorScope,\n                and(collection, 0xffffffffffffffffffffffffffffffffffffffff)\n            )\n        }\n    }\n\n    function _getAuthorizedIdentifierSlot(\n        address collection,\n        uint256 identifier\n    ) internal pure returns (uint256 slot) {\n        bytes4 authorizedIdentifierScope = _AUTHORIZED_IDENTIFIER_SCOPE;\n        assembly {\n            mstore(0x0, authorizedIdentifierScope)\n            mstore(0x18, collection)\n            mstore(0x04, identifier)\n            slot := keccak256(0x0, 0x38)\n        }\n    }\n\n    function _getAuthorizedAmountSlot(\n        address collection,\n        uint256 identifier\n    ) internal pure returns (uint256 slot) {\n        bytes4 authorizedAmountScope = _AUTHORIZED_AMOUNT_SCOPE;\n        assembly {\n            mstore(0x0, authorizedAmountScope)\n            mstore(0x18, collection)\n            mstore(0x04, identifier)\n            slot := keccak256(0x0, 0x38)\n        }\n    }\n}\n"
      },
      "src/interfaces/IAuthorizedTransferSecurityRegistry.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nenum ListTypes {\n    AuthorizerList,\n    OperatorList\n}\n\n/// @title IAuthorizedTransferSecurityRegistry\n/// @dev Interface for the Authorized Transfer Security Registry, a simplified version of the Transfer\n///      Security Registry that only supports authorizers and whitelisted operators, and assumes a\n///      security level of OperatorWhitelistEnableOTC + authorizers for all collections that use it.\n///      Note that a number of view functions on collections that add this validator will not work.\ninterface IAuthorizedTransferSecurityRegistry {\n    event CreatedList(uint256 indexed id, string name);\n    event AppliedListToCollection(address indexed collection, uint120 indexed id);\n    event ReassignedListOwnership(uint256 indexed id, address indexed newOwner);\n    event AddedAccountToList(ListTypes indexed kind, uint256 indexed id, address indexed account);\n    event RemovedAccountFromList(ListTypes indexed kind, uint256 indexed id, address indexed account);\n\n    error AuthorizedTransferSecurityRegistry__ListDoesNotExist();\n    error AuthorizedTransferSecurityRegistry__CallerDoesNotOwnList();\n    error AuthorizedTransferSecurityRegistry__ArrayLengthCannotBeZero();\n    error AuthorizedTransferSecurityRegistry__CallerMustHaveElevatedPermissionsForSpecifiedNFT();\n    error AuthorizedTransferSecurityRegistry__ListOwnershipCannotBeTransferredToZeroAddress();\n    error AuthorizedTransferSecurityRegistry__ZeroAddressNotAllowed();\n    error AuthorizedTransferSecurityRegistry__UnauthorizedTransfer();\n    error AuthorizedTransferSecurityRegistry__CallerIsNotValidAuthorizer();\n\n    /// Manage lists of authorizers & operators that can be applied to collections\n    function createList(string calldata name) external returns (uint120);\n    function createListCopy(string calldata name, uint120 sourceListId) external returns (uint120);\n    function reassignOwnershipOfList(uint120 id, address newOwner) external;\n    function renounceOwnershipOfList(uint120 id) external;\n    function applyListToCollection(address collection, uint120 id) external;\n    function listOwners(uint120 id) external view returns (address);\n\n    /// Manage and query for authorizers on lists\n    function addAuthorizers(uint120 id, address[] calldata accounts) external;\n    function removeAuthorizers(uint120 id, address[] calldata accounts) external;\n    function getAuthorizers(uint120 id) external view returns (address[] memory);\n    function isAuthorizer(uint120 id, address account) external view returns (bool);\n    function getAuthorizersByCollection(address collection) external view returns (address[] memory);\n    function isAuthorizerByCollection(address collection, address account) external view returns (bool);\n\n    /// Manage and query for operators on lists\n    function addOperators(uint120 id, address[] calldata accounts) external;\n    function removeOperators(uint120 id, address[] calldata accounts) external;\n    function getOperators(uint120 id) external view returns (address[] memory);\n    function isOperator(uint120 id, address account) external view returns (bool);\n    function getOperatorsByCollection(address collection) external view returns (address[] memory);\n    function isOperatorByCollection(address collection, address account) external view returns (bool);\n\n    /// Ensure that a specific operator has been authorized to transfer tokens\n    function validateTransfer(address caller, address from, address to) external view;\n\n    /// Ensure that a transfer has been authorized for a specific tokenId\n    function validateTransfer(address caller, address from, address to, uint256 tokenId) external view;\n\n    /// Ensure that a transfer has been authorized for a specific amount of a specific tokenId, and\n    /// reduce the transferable amount remaining\n    function validateTransfer(address caller, address from, address to, uint256 tokenId, uint256 amount) external;\n\n    /// Legacy alias for validateTransfer (address caller, address from, address to)\n    function applyCollectionTransferPolicy(address caller, address from, address to) external view;\n\n    /// Temporarily assign a specific allowed operator for a given collection\n    function beforeAuthorizedTransfer(address operator, address token) external;\n\n    /// Clear assignment of a specific allowed operator for a given collection\n    function afterAuthorizedTransfer(address token) external;\n\n    /// Temporarily allow a specific tokenId from a given collection to be transferred\n    function beforeAuthorizedTransfer(address token, uint256 tokenId) external;\n\n    /// Clear assignment of an specific tokenId's transfer allowance\n    function afterAuthorizedTransfer(address token, uint256 tokenId) external;\n\n    /// Temporarily allow a specific amount of a specific tokenId from a given collection to be transferred\n    function beforeAuthorizedTransferWithAmount(address token, uint256 tokenId, uint256 amount) external;\n\n    /// Clear assignment of a tokenId's transfer allowance for a specific amount\n    function afterAuthorizedTransferWithAmount(address token, uint256 tokenId) external;\n}"
      },
      "src/interfaces/ICreatorTokenTransferValidator.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport \"./IEOARegistry.sol\";\nimport \"./ITransferSecurityRegistry.sol\";\nimport \"./ITransferValidator.sol\";\n\ninterface ICreatorTokenTransferValidator is ITransferSecurityRegistry, ITransferValidator, IEOARegistry {}"
      },
      "src/interfaces/IOwnable.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\ninterface IOwnable {\n    function owner() external view returns (address);\n}"
      },
      "lib/openzeppelin-contracts/contracts/access/IAccessControl.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev External interface of AccessControl declared to support ERC165 detection.\n */\ninterface IAccessControl {\n    /**\n     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`\n     *\n     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite\n     * {RoleAdminChanged} not being emitted signaling this.\n     *\n     * _Available since v3.1._\n     */\n    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);\n\n    /**\n     * @dev Emitted when `account` is granted `role`.\n     *\n     * `sender` is the account that originated the contract call, an admin role\n     * bearer except when using {AccessControl-_setupRole}.\n     */\n    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);\n\n    /**\n     * @dev Emitted when `account` is revoked `role`.\n     *\n     * `sender` is the account that originated the contract call:\n     *   - if using `revokeRole`, it is the admin role bearer\n     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)\n     */\n    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);\n\n    /**\n     * @dev Returns `true` if `account` has been granted `role`.\n     */\n    function hasRole(bytes32 role, address account) external view returns (bool);\n\n    /**\n     * @dev Returns the admin role that controls `role`. See {grantRole} and\n     * {revokeRole}.\n     *\n     * To change a role's admin, use {AccessControl-_setRoleAdmin}.\n     */\n    function getRoleAdmin(bytes32 role) external view returns (bytes32);\n\n    /**\n     * @dev Grants `role` to `account`.\n     *\n     * If `account` had not been already granted `role`, emits a {RoleGranted}\n     * event.\n     *\n     * Requirements:\n     *\n     * - the caller must have ``role``'s admin role.\n     */\n    function grantRole(bytes32 role, address account) external;\n\n    /**\n     * @dev Revokes `role` from `account`.\n     *\n     * If `account` had been granted `role`, emits a {RoleRevoked} event.\n     *\n     * Requirements:\n     *\n     * - the caller must have ``role``'s admin role.\n     */\n    function revokeRole(bytes32 role, address account) external;\n\n    /**\n     * @dev Revokes `role` from the calling account.\n     *\n     * Roles are often managed via {grantRole} and {revokeRole}: this function's\n     * purpose is to provide a mechanism for accounts to lose their privileges\n     * if they are compromised (such as when a trusted device is misplaced).\n     *\n     * If the calling account had been granted `role`, emits a {RoleRevoked}\n     * event.\n     *\n     * Requirements:\n     *\n     * - the caller must be `account`.\n     */\n    function renounceRole(bytes32 role, address account) external;\n}\n"
      },
      "lib/openzeppelin-contracts/contracts/utils/structs/EnumerableSet.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/structs/EnumerableSet.sol)\n// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Library for managing\n * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive\n * types.\n *\n * Sets have the following properties:\n *\n * - Elements are added, removed, and checked for existence in constant time\n * (O(1)).\n * - Elements are enumerated in O(n). No guarantees are made on the ordering.\n *\n * ```\n * contract Example {\n *     // Add the library methods\n *     using EnumerableSet for EnumerableSet.AddressSet;\n *\n *     // Declare a set state variable\n *     EnumerableSet.AddressSet private mySet;\n * }\n * ```\n *\n * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)\n * and `uint256` (`UintSet`) are supported.\n *\n * [WARNING]\n * ====\n * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure\n * unusable.\n * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.\n *\n * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an\n * array of EnumerableSet.\n * ====\n */\nlibrary EnumerableSet {\n    // To implement this library for multiple types with as little code\n    // repetition as possible, we write it in terms of a generic Set type with\n    // bytes32 values.\n    // The Set implementation uses private functions, and user-facing\n    // implementations (such as AddressSet) are just wrappers around the\n    // underlying Set.\n    // This means that we can only create new EnumerableSets for types that fit\n    // in bytes32.\n\n    struct Set {\n        // Storage of set values\n        bytes32[] _values;\n        // Position of the value in the `values` array, plus 1 because index 0\n        // means a value is not in the set.\n        mapping(bytes32 => uint256) _indexes;\n    }\n\n    /**\n     * @dev Add a value to a set. O(1).\n     *\n     * Returns true if the value was added to the set, that is if it was not\n     * already present.\n     */\n    function _add(Set storage set, bytes32 value) private returns (bool) {\n        if (!_contains(set, value)) {\n            set._values.push(value);\n            // The value is stored at length-1, but we add 1 to all indexes\n            // and use 0 as a sentinel value\n            set._indexes[value] = set._values.length;\n            return true;\n        } else {\n            return false;\n        }\n    }\n\n    /**\n     * @dev Removes a value from a set. O(1).\n     *\n     * Returns true if the value was removed from the set, that is if it was\n     * present.\n     */\n    function _remove(Set storage set, bytes32 value) private returns (bool) {\n        // We read and store the value's index to prevent multiple reads from the same storage slot\n        uint256 valueIndex = set._indexes[value];\n\n        if (valueIndex != 0) {\n            // Equivalent to contains(set, value)\n            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in\n            // the array, and then remove the last element (sometimes called as 'swap and pop').\n            // This modifies the order of the array, as noted in {at}.\n\n            uint256 toDeleteIndex = valueIndex - 1;\n            uint256 lastIndex = set._values.length - 1;\n\n            if (lastIndex != toDeleteIndex) {\n                bytes32 lastValue = set._values[lastIndex];\n\n                // Move the last value to the index where the value to delete is\n                set._values[toDeleteIndex] = lastValue;\n                // Update the index for the moved value\n                set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex\n            }\n\n            // Delete the slot where the moved value was stored\n            set._values.pop();\n\n            // Delete the index for the deleted slot\n            delete set._indexes[value];\n\n            return true;\n        } else {\n            return false;\n        }\n    }\n\n    /**\n     * @dev Returns true if the value is in the set. O(1).\n     */\n    function _contains(Set storage set, bytes32 value) private view returns (bool) {\n        return set._indexes[value] != 0;\n    }\n\n    /**\n     * @dev Returns the number of values on the set. O(1).\n     */\n    function _length(Set storage set) private view returns (uint256) {\n        return set._values.length;\n    }\n\n    /**\n     * @dev Returns the value stored at position `index` in the set. O(1).\n     *\n     * Note that there are no guarantees on the ordering of values inside the\n     * array, and it may change when more values are added or removed.\n     *\n     * Requirements:\n     *\n     * - `index` must be strictly less than {length}.\n     */\n    function _at(Set storage set, uint256 index) private view returns (bytes32) {\n        return set._values[index];\n    }\n\n    /**\n     * @dev Return the entire set in an array\n     *\n     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n     * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n     */\n    function _values(Set storage set) private view returns (bytes32[] memory) {\n        return set._values;\n    }\n\n    // Bytes32Set\n\n    struct Bytes32Set {\n        Set _inner;\n    }\n\n    /**\n     * @dev Add a value to a set. O(1).\n     *\n     * Returns true if the value was added to the set, that is if it was not\n     * already present.\n     */\n    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n        return _add(set._inner, value);\n    }\n\n    /**\n     * @dev Removes a value from a set. O(1).\n     *\n     * Returns true if the value was removed from the set, that is if it was\n     * present.\n     */\n    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n        return _remove(set._inner, value);\n    }\n\n    /**\n     * @dev Returns true if the value is in the set. O(1).\n     */\n    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {\n        return _contains(set._inner, value);\n    }\n\n    /**\n     * @dev Returns the number of values in the set. O(1).\n     */\n    function length(Bytes32Set storage set) internal view returns (uint256) {\n        return _length(set._inner);\n    }\n\n    /**\n     * @dev Returns the value stored at position `index` in the set. O(1).\n     *\n     * Note that there are no guarantees on the ordering of values inside the\n     * array, and it may change when more values are added or removed.\n     *\n     * Requirements:\n     *\n     * - `index` must be strictly less than {length}.\n     */\n    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {\n        return _at(set._inner, index);\n    }\n\n    /**\n     * @dev Return the entire set in an array\n     *\n     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n     * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n     */\n    function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {\n        bytes32[] memory store = _values(set._inner);\n        bytes32[] memory result;\n\n        /// @solidity memory-safe-assembly\n        assembly {\n            result := store\n        }\n\n        return result;\n    }\n\n    // AddressSet\n\n    struct AddressSet {\n        Set _inner;\n    }\n\n    /**\n     * @dev Add a value to a set. O(1).\n     *\n     * Returns true if the value was added to the set, that is if it was not\n     * already present.\n     */\n    function add(AddressSet storage set, address value) internal returns (bool) {\n        return _add(set._inner, bytes32(uint256(uint160(value))));\n    }\n\n    /**\n     * @dev Removes a value from a set. O(1).\n     *\n     * Returns true if the value was removed from the set, that is if it was\n     * present.\n     */\n    function remove(AddressSet storage set, address value) internal returns (bool) {\n        return _remove(set._inner, bytes32(uint256(uint160(value))));\n    }\n\n    /**\n     * @dev Returns true if the value is in the set. O(1).\n     */\n    function contains(AddressSet storage set, address value) internal view returns (bool) {\n        return _contains(set._inner, bytes32(uint256(uint160(value))));\n    }\n\n    /**\n     * @dev Returns the number of values in the set. O(1).\n     */\n    function length(AddressSet storage set) internal view returns (uint256) {\n        return _length(set._inner);\n    }\n\n    /**\n     * @dev Returns the value stored at position `index` in the set. O(1).\n     *\n     * Note that there are no guarantees on the ordering of values inside the\n     * array, and it may change when more values are added or removed.\n     *\n     * Requirements:\n     *\n     * - `index` must be strictly less than {length}.\n     */\n    function at(AddressSet storage set, uint256 index) internal view returns (address) {\n        return address(uint160(uint256(_at(set._inner, index))));\n    }\n\n    /**\n     * @dev Return the entire set in an array\n     *\n     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n     * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n     */\n    function values(AddressSet storage set) internal view returns (address[] memory) {\n        bytes32[] memory store = _values(set._inner);\n        address[] memory result;\n\n        /// @solidity memory-safe-assembly\n        assembly {\n            result := store\n        }\n\n        return result;\n    }\n\n    // UintSet\n\n    struct UintSet {\n        Set _inner;\n    }\n\n    /**\n     * @dev Add a value to a set. O(1).\n     *\n     * Returns true if the value was added to the set, that is if it was not\n     * already present.\n     */\n    function add(UintSet storage set, uint256 value) internal returns (bool) {\n        return _add(set._inner, bytes32(value));\n    }\n\n    /**\n     * @dev Removes a value from a set. O(1).\n     *\n     * Returns true if the value was removed from the set, that is if it was\n     * present.\n     */\n    function remove(UintSet storage set, uint256 value) internal returns (bool) {\n        return _remove(set._inner, bytes32(value));\n    }\n\n    /**\n     * @dev Returns true if the value is in the set. O(1).\n     */\n    function contains(UintSet storage set, uint256 value) internal view returns (bool) {\n        return _contains(set._inner, bytes32(value));\n    }\n\n    /**\n     * @dev Returns the number of values in the set. O(1).\n     */\n    function length(UintSet storage set) internal view returns (uint256) {\n        return _length(set._inner);\n    }\n\n    /**\n     * @dev Returns the value stored at position `index` in the set. O(1).\n     *\n     * Note that there are no guarantees on the ordering of values inside the\n     * array, and it may change when more values are added or removed.\n     *\n     * Requirements:\n     *\n     * - `index` must be strictly less than {length}.\n     */\n    function at(UintSet storage set, uint256 index) internal view returns (uint256) {\n        return uint256(_at(set._inner, index));\n    }\n\n    /**\n     * @dev Return the entire set in an array\n     *\n     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n     * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n     */\n    function values(UintSet storage set) internal view returns (uint256[] memory) {\n        bytes32[] memory store = _values(set._inner);\n        uint256[] memory result;\n\n        /// @solidity memory-safe-assembly\n        assembly {\n            result := store\n        }\n\n        return result;\n    }\n}\n"
      },
      "lib/openzeppelin-contracts/contracts/utils/introspection/ERC165.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IERC165.sol\";\n\n/**\n * @dev Implementation of the {IERC165} interface.\n *\n * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check\n * for the additional interface id that will be supported. For example:\n *\n * ```solidity\n * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);\n * }\n * ```\n *\n * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.\n */\nabstract contract ERC165 is IERC165 {\n    /**\n     * @dev See {IERC165-supportsInterface}.\n     */\n    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n        return interfaceId == type(IERC165).interfaceId;\n    }\n}\n"
      },
      "lib/tstorish/src/Tstorish.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.24;\n\ncontract Tstorish {\n    // Declare a storage variable indicating if TSTORE support has been\n    // activated post-deployment.\n    bool private _tstoreSupport;\n\n    /*\n     * ------------------------------------------------------------------------+\n     * Opcode      | Mnemonic         | Stack              | Memory            |\n     * ------------------------------------------------------------------------|\n     * 60 0x02     | PUSH1 0x02       | 0x02               |                   |\n     * 60 0x1e     | PUSH1 0x1e       | 0x1e 0x02          |                   |\n     * 61 0x3d5c   | PUSH2 0x3d5c     | 0x3d5c 0x1e 0x02   |                   |\n     * 3d          | RETURNDATASIZE   | 0 0x3d5c 0x1e 0x02 |                   |\n     *                                                                         |\n     * :: store deployed bytecode in memory: (3d) RETURNDATASIZE (5c) TLOAD :: |\n     * 52          | MSTORE           | 0x1e 0x02          | [0..0x20): 0x3d5c |\n     * f3          | RETURN           |                    | [0..0x20): 0x3d5c |\n     * ------------------------------------------------------------------------+\n     */\n    uint256 constant _TLOAD_TEST_PAYLOAD = 0x6002_601e_613d5c_3d_52_f3;\n    uint256 constant _TLOAD_TEST_PAYLOAD_LENGTH = 0x0a;\n    uint256 constant _TLOAD_TEST_PAYLOAD_OFFSET = 0x16;\n\n    // Declare an immutable variable to store the initial TSTORE support status.\n    bool private immutable _tstoreInitialSupport;\n\n    // Declare an immutable variable to store the tstore test contract address.\n    address private immutable _tloadTestContract;\n\n    // Declare a few custom revert error types.\n    error TStoreAlreadyActivated();\n    error TStoreNotSupported();\n    error TloadTestContractDeploymentFailed();\n    error OnlyDirectCalls();\n\n    /**\n     * @dev Determine TSTORE availability during deployment. This involves\n     *      attempting to deploy a contract that utilizes TLOAD as part of the\n     *      contract construction bytecode, and configuring initial support for\n     *      using TSTORE in place of SSTORE based on the result.\n     */\n    constructor() {\n        // Deploy the contract testing TLOAD support and store the address.\n        address tloadTestContract = _prepareTloadTest();\n\n        // Ensure the deployment was successful.\n        if (tloadTestContract == address(0)) {\n            revert TloadTestContractDeploymentFailed();\n        }\n\n        // Determine if TSTORE is supported.\n        bool tstoreInitialSupport = _testTload(tloadTestContract);\n\n        // Store the result as an immutable.\n        _tstoreInitialSupport = tstoreInitialSupport;\n\n        // Set the address of the deployed TLOAD test contract as an immutable.\n        _tloadTestContract = tloadTestContract;\n    }\n\n    /**\n     * @dev External function to activate TSTORE usage. Does not need to be\n     *      called if TSTORE is supported from deployment, and only needs to be\n     *      called once. Reverts if TSTORE has already been activated or if the\n     *      opcode is not available. Note that this must be called directly from\n     *      an externally-owned account to avoid potential reentrancy issues.\n     */\n    function __activateTstore() external {\n        // Ensure this function is triggered from an externally-owned account.\n        if (msg.sender != tx.origin) {\n            revert OnlyDirectCalls();\n        }\n\n        // Determine if TSTORE can potentially be activated.\n        if (_tstoreInitialSupport || _tstoreSupport) {\n            revert TStoreAlreadyActivated();\n        }\n\n        // Determine if TSTORE can be activated and revert if not.\n        if (!_testTload(_tloadTestContract)) {\n            revert TStoreNotSupported();\n        }\n\n        // Mark TSTORE as activated.\n        _tstoreSupport = true;\n    }\n\n    /**\n     * @dev Internal function to set a TSTORISH value.\n     *\n     * @param storageSlot The slot to write the TSTORISH value to.\n     * @param value       The value to write to the given storage slot.\n     */\n    function _setTstorish(uint256 storageSlot, uint256 value) internal {\n        if (_tstoreInitialSupport) {\n            assembly {\n                tstore(storageSlot, value)\n            }\n        } else if (_tstoreSupport) {\n            assembly {\n                tstore(storageSlot, value)\n            }\n        } else {\n            assembly {\n                sstore(storageSlot, value)\n            }\n        }\n    }\n\n    /**\n     * @dev Internal function to read a TSTORISH value.\n     *\n     * @param storageSlot The slot to read the TSTORISH value from.\n     *\n     * @return value The TSTORISH value at the given storage slot.\n     */\n    function _getTstorish(\n        uint256 storageSlot\n    ) internal view returns (uint256 value) {\n        if (_tstoreInitialSupport) {\n            assembly {\n                value := tload(storageSlot)\n            }\n        } else if (_tstoreSupport) {\n            assembly {\n                value := tload(storageSlot)\n            }\n        } else {\n            assembly {\n                value := sload(storageSlot)\n            }\n        }\n    }\n\n    /**\n     * @dev Internal function to clear a TSTORISH value.\n     *\n     * @param storageSlot The slot to clear the TSTORISH value for.\n     */\n    function _clearTstorish(uint256 storageSlot) internal {\n        if (_tstoreInitialSupport) {\n            assembly {\n                tstore(storageSlot, 0)\n            }\n        } else if (_tstoreSupport) {\n            assembly {\n                tstore(storageSlot, 0)\n            }\n        } else {\n            assembly {\n                sstore(storageSlot, 0)\n            }\n        }\n    }\n\n    /**\n     * @dev Private function to deploy a test contract that utilizes TLOAD as\n     *      part of its fallback logic.\n     */\n    function _prepareTloadTest() private returns (address contractAddress) {\n        // Utilize assembly to deploy a contract testing TLOAD support.\n        assembly {\n            // Write the contract deployment code payload to scratch space.\n            mstore(0, _TLOAD_TEST_PAYLOAD)\n\n            // Deploy the contract.\n            contractAddress := create(\n                0,\n                _TLOAD_TEST_PAYLOAD_OFFSET,\n                _TLOAD_TEST_PAYLOAD_LENGTH\n            )\n        }\n    }\n\n    /**\n     * @dev Private view function to determine if TSTORE/TLOAD are supported by\n     *      the current EVM implementation by attempting to call the test\n     *      contract, which utilizes TLOAD as part of its fallback logic.\n     */\n    function _testTload(\n        address tloadTestContract\n    ) private view returns (bool ok) {\n        // Call the test contract, which will perform a TLOAD test. If the call\n        // does not revert, then TLOAD/TSTORE is supported. Do not forward all\n        // available gas, as all forwarded gas will be consumed on revert.\n        (ok, ) = tloadTestContract.staticcall{ gas: gasleft() / 10 }(\"\");\n    }\n}"
      },
      "src/AuthorizedTransferSecurityRegistryExtraViewFns.sol": {
        "content": "\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.24;\n\nimport { EnumerableSet } from \"@openzeppelin/contracts/utils/structs/EnumerableSet.sol\";\n\nimport { Tstorish } from \"tstorish/Tstorish.sol\";\n\nenum TransferSecurityLevels {\n    UNUSED_Recommended,\n    UNUSED_One,\n    UNUSED_Two,\n    Three,\n    UNUSED_Four,\n    UNUSED_Five,\n    UNUSED_Six,\n    UNUSED_Seven,\n    UNUSED_Eight\n}\n\nstruct CollectionSecurityPolicy {\n    TransferSecurityLevels transferSecurityLevel;\n    uint120 operatorWhitelistId;\n    uint120 permittedContractReceiversId;\n}\n\n/// @title AuthorizedTransferSecurityRegistryExtraViewFns\n/// @dev Additional view functions, called by AuthorizedTransferSecurityRegistry\n///      via delegatecall in the fallback.\ncontract AuthorizedTransferSecurityRegistryExtraViewFns is Tstorish {\n    using EnumerableSet for EnumerableSet.AddressSet;\n\n    error AuthorizedTransferSecurityRegistry__NotImplemented();\n\n    struct AccountList {\n        EnumerableSet.AddressSet enumerableAccounts;\n        mapping (address => bool) nonEnumerableAccounts;\n    }\n\n    struct List {\n        address owner;\n        AccountList authorizers;\n        AccountList operators;\n    }\n\n    uint120 private UNUSED_lastListId;\n\n    mapping (uint120 => List) private lists;\n\n    mapping (address => uint120) private listIdsByCollection;\n\n    // view functions from other transfer security registries, included for completeness\n    function getBlacklistedAccounts(uint120) external pure returns (address[] memory) {}\n    function getWhitelistedAccounts(uint120 id) external view returns (address[] memory) {\n        return lists[id].operators.enumerableAccounts.values();\n    }\n    function getBlacklistedCodeHashes(uint120) external pure returns (bytes32[] memory) {}\n    function getWhitelistedCodeHashes(uint120) external pure returns (bytes32[] memory) {}\n    function isAccountBlacklisted(uint120, address) external pure returns (bool) {\n        return false;\n    }\n    function isAccountWhitelisted(uint120 id, address account) external view returns (bool) {\n        return lists[id].operators.nonEnumerableAccounts[account];\n    }\n    function isCodeHashBlacklisted(uint120, bytes32) external pure returns (bool) {\n        return false;\n    }\n    function isCodeHashWhitelisted(uint120, bytes32) external pure returns (bool) {\n        return false;\n    }\n    function getBlacklistedAccountsByCollection(address) external pure returns (address[] memory) {}\n    function getWhitelistedAccountsByCollection(address collection) external view returns (address[] memory) {\n        return lists[listIdsByCollection[collection]].operators.enumerableAccounts.values();\n    }\n    function getBlacklistedCodeHashesByCollection(address) external pure returns (bytes32[] memory) {}\n    function getWhitelistedCodeHashesByCollection(address) external pure returns (bytes32[] memory) {}\n    function isAccountBlacklistedByCollection(address, address) external pure returns (bool) {\n        return false;\n    }\n    function isAccountWhitelistedByCollection(\n        address collection, address account\n    ) external view returns (bool) {\n        return lists[listIdsByCollection[collection]].operators.nonEnumerableAccounts[account];\n    }\n    function isCodeHashBlacklistedByCollection(address, bytes32) external pure returns (bool) {\n        return false;\n    }\n    function isCodeHashWhitelistedByCollection(address, bytes32) external pure returns (bool) {\n        return false;\n    }\n    function getCollectionSecurityPolicy(\n        address collection\n    ) external view returns (CollectionSecurityPolicy memory) {\n        return CollectionSecurityPolicy({\n            transferSecurityLevel: TransferSecurityLevels.Three,\n            operatorWhitelistId: listIdsByCollection[collection],\n            permittedContractReceiversId: 0\n        });\n    }\n    function getWhitelistedOperators(uint120 id) external view returns (address[] memory) {\n        return lists[id].operators.enumerableAccounts.values();\n    }\n    function getPermittedContractReceivers(uint120) external pure returns (address[] memory) {}\n    function isOperatorWhitelisted(uint120 id, address operator) external view returns (bool) {\n        return lists[id].operators.nonEnumerableAccounts[operator];\n    }\n    function isContractReceiverPermitted(uint120, address) external pure returns (bool) {\n        return true;\n    }\n    fallback() external {\n        revert AuthorizedTransferSecurityRegistry__NotImplemented();\n    }\n}"
      },
      "src/interfaces/IEOARegistry.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport \"@openzeppelin/contracts/utils/introspection/IERC165.sol\";\n\ninterface IEOARegistry is IERC165 {\n    function isVerifiedEOA(address account) external view returns (bool);\n}"
      },
      "src/interfaces/ITransferSecurityRegistry.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport \"../utils/TransferPolicy.sol\";\n\ninterface ITransferSecurityRegistry {\n    event AddedToAllowlist(AllowlistTypes indexed kind, uint256 indexed id, address indexed account);\n    event CreatedAllowlist(AllowlistTypes indexed kind, uint256 indexed id, string indexed name);\n    event ReassignedAllowlistOwnership(AllowlistTypes indexed kind, uint256 indexed id, address indexed newOwner);\n    event RemovedFromAllowlist(AllowlistTypes indexed kind, uint256 indexed id, address indexed account);\n    event SetAllowlist(AllowlistTypes indexed kind, address indexed collection, uint120 indexed id);\n    event SetTransferSecurityLevel(address indexed collection, TransferSecurityLevels level);\n\n    function createOperatorWhitelist(string calldata name) external returns (uint120);\n    function createPermittedContractReceiverAllowlist(string calldata name) external returns (uint120);\n    function reassignOwnershipOfOperatorWhitelist(uint120 id, address newOwner) external;\n    function reassignOwnershipOfPermittedContractReceiverAllowlist(uint120 id, address newOwner) external;\n    function renounceOwnershipOfOperatorWhitelist(uint120 id) external;\n    function renounceOwnershipOfPermittedContractReceiverAllowlist(uint120 id) external;\n    function setTransferSecurityLevelOfCollection(address collection, TransferSecurityLevels level) external;\n    function setOperatorWhitelistOfCollection(address collection, uint120 id) external;\n    function setPermittedContractReceiverAllowlistOfCollection(address collection, uint120 id) external;\n    function addOperatorToWhitelist(uint120 id, address operator) external;\n    function addPermittedContractReceiverToAllowlist(uint120 id, address receiver) external;\n    function removeOperatorFromWhitelist(uint120 id, address operator) external;\n    function removePermittedContractReceiverFromAllowlist(uint120 id, address receiver) external;\n    function getCollectionSecurityPolicy(address collection) external view returns (CollectionSecurityPolicy memory);\n    function getWhitelistedOperators(uint120 id) external view returns (address[] memory);\n    function getPermittedContractReceivers(uint120 id) external view returns (address[] memory);\n    function isOperatorWhitelisted(uint120 id, address operator) external view returns (bool);\n    function isContractReceiverPermitted(uint120 id, address receiver) external view returns (bool);\n}"
      },
      "src/interfaces/ITransferValidator.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport \"../utils/TransferPolicy.sol\";\n\ninterface ITransferValidator {\n    function applyCollectionTransferPolicy(address caller, address from, address to) external view;\n}"
      },
      "lib/openzeppelin-contracts/contracts/utils/introspection/IERC165.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC165 standard, as defined in the\n * https://eips.ethereum.org/EIPS/eip-165[EIP].\n *\n * Implementers can declare support of contract interfaces, which can then be\n * queried by others ({ERC165Checker}).\n *\n * For an implementation, see {ERC165}.\n */\ninterface IERC165 {\n    /**\n     * @dev Returns true if this contract implements the interface defined by\n     * `interfaceId`. See the corresponding\n     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]\n     * to learn more about how these ids are created.\n     *\n     * This function call must use less than 30 000 gas.\n     */\n    function supportsInterface(bytes4 interfaceId) external view returns (bool);\n}\n"
      },
      "src/utils/TransferPolicy.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\n/** \n * @dev Used in events to indicate the list type that an account or \n * @dev codehash is being added to or removed from.\n * \n * @dev Used in Creator Token Standards V2.\n */\nenum ListTypes {\n    // 0: List type that will block a matching address/codehash that is on the list.\n    Blacklist,\n\n    // 1: List type that will block any matching address/codehash that is not on the list.\n    Whitelist\n}\n\n/** \n * @dev Used in events to indicate the list type that event relates to.\n * \n * @dev Used in Creator Token Standards V1.\n */\nenum AllowlistTypes {\n    // 0: List type that defines the allowed operator addresses.\n    Operators,\n\n    // 1: List type that defines the allowed contract receivers.\n    PermittedContractReceivers\n}\n\n/**\n @dev Defines the constraints that will be applied for receipt of tokens.\n */\nenum ReceiverConstraints {\n    // 0: Any address may receive tokens.\n    None,\n\n    // 1: Address must not have deployed bytecode.\n    NoCode,\n\n    // 2: Address must verify a signature with the EOA Registry to prove it is an EOA.\n    EOA\n}\n\n/**\n * @dev Defines the constraints that will be applied to the transfer caller.\n */\nenum CallerConstraints {\n    // 0: Any address may transfer tokens.\n    None,\n\n    // 1: Addresses and codehashes not on the blacklist may transfer tokens.\n    OperatorBlacklistEnableOTC,\n\n    // 2: Addresses and codehashes on the whitelist and the owner of the token may transfer tokens.\n    OperatorWhitelistEnableOTC,\n\n    // 3: Addresses and codehashes on the whitelist may transfer tokens.\n    OperatorWhitelistDisableOTC\n}\n\n/**\n * @dev Defines constraints for staking tokens in token wrapper contracts.\n */\nenum StakerConstraints {\n    // 0: No constraints applied to staker.\n    None,\n\n    // 1: Transaction originator must be the address that will receive the wrapped tokens.\n    CallerIsTxOrigin,\n\n    // 2: Address that will receive the wrapped tokens must be a verified EOA.\n    EOA\n}\n\n/**\n * @dev Used in both Creator Token Standards V1 and V2.\n * @dev Levels may have different transfer restrictions in V1 and V2. Refer to the \n * @dev Creator Token Transfer Validator implementation for the version being utilized\n * @dev to determine the effect of the selected level.\n */\nenum TransferSecurityLevels {\n    Recommended,\n    One,\n    Two,\n    Three,\n    Four,\n    Five,\n    Six,\n    Seven,\n    Eight\n}\n\n/**\n * @dev Defines the caller and receiver constraints for a transfer security level.\n * @dev Used in Creator Token Standards V1.\n * \n * @dev **callerConstraints**: The restrictions applied to the transfer caller.\n * @dev **receiverConstraints**: The restrictions applied to the transfer recipient.\n */\nstruct TransferSecurityPolicy {\n    CallerConstraints callerConstraints;\n    ReceiverConstraints receiverConstraints;\n}\n\n/**\n * @dev Defines the security policy for a token collection in Creator Token Standards V1.\n * \n * @dev **transferSecurityLevel**: The transfer security level set for the collection.\n * @dev **operatorWhitelistId**: The list id for the operator whitelist.\n * @dev **permittedContractReceiversId: The list id for the contracts that are allowed to receive tokens.\n */\nstruct CollectionSecurityPolicy {\n    TransferSecurityLevels transferSecurityLevel;\n    uint120 operatorWhitelistId;\n    uint120 permittedContractReceiversId;\n}\n\n/**\n * @dev Defines the security policy for a token collection in Creator Token Standards V2.\n * \n * @dev **transferSecurityLevel**: The transfer security level set for the collection.\n * @dev **listId**: The list id that contains the blacklist and whitelist to apply to the collection.\n */\nstruct CollectionSecurityPolicyV2 {\n    TransferSecurityLevels transferSecurityLevel;\n    uint120 listId;\n}\n\n/** \n * @dev Used internally in the Creator Token Base V2 contract to pack transfer validator configuration.\n * \n * @dev **isInitialized**: If not initialized by the collection owner or admin the default validator will be used.\n * @dev **version**: The transfer validator version.\n * @dev **transferValidator**: The address of the transfer validator to use for applying collection security settings.\n */\nstruct TransferValidatorReference {\n    bool isInitialized;\n    uint16 version;\n    address transferValidator;\n}"
      }
    },
    "settings": {
      "remappings": [
        "seaport-core/=lib/seaport-core/",
        "seaport-types/=lib/seaport-types/",
        "seaport-sol/=lib/seaport-sol/src/",
        "seaport-deploy/=lib/seaport-deploy/src/",
        "solady/=lib/solady/",
        "@openzeppelin/=lib/openzeppelin-contracts/",
        "@limitbreak/creator-token-standards/=lib/creator-token-standards/src/",
        "@rari-capital/solmate/=lib/seaport-sol/lib/seaport/lib/solmate/",
        "ERC721A/=lib/creator-token-standards/lib/ERC721A/contracts/",
        "creator-token-standards/=lib/creator-token-standards/",
        "ds-test/=lib/forge-std/lib/ds-test/src/",
        "erc4626-tests/=lib/seaport-sol/lib/seaport/lib/openzeppelin-contracts/lib/erc4626-tests/",
        "erc721a/=lib/creator-token-standards/lib/ERC721A/",
        "forge-std/=lib/forge-std/src/",
        "murky/=lib/creator-token-standards/lib/murky/",
        "openzeppelin-contracts/=lib/openzeppelin-contracts/",
        "seaport/=lib/seaport-sol/lib/seaport/contracts/",
        "solarray/=lib/seaport-sol/lib/solarray/src/",
        "solmate/=lib/seaport-sol/lib/seaport/lib/solmate/src/",
        "tstorish/=lib/tstorish/src/"
      ],
      "optimizer": {
        "enabled": true,
        "runs": 9999999
      },
      "metadata": {
        "useLiteralContent": false,
        "bytecodeHash": "ipfs",
        "appendCBOR": true
      },
      "outputSelection": {
        "*": {
          "*": [
            "evm.bytecode",
            "evm.deployedBytecode",
            "abi"
          ]
        }
      },
      "evmVersion": "cancun",
      "viaIR": false,
      "libraries": {}
    }
  }}