creation_sourcecode: {{
    "language": "Solidity",
    "sources": {
      "@openzeppelin/contracts/access/Ownable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)\n\npragma solidity ^0.8.20;\n\nimport {Context} from \"../utils/Context.sol\";\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * The initial owner is set to the address provided by the deployer. This can\n * later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n    address private _owner;\n\n    /**\n     * @dev The caller account is not authorized to perform an operation.\n     */\n    error OwnableUnauthorizedAccount(address account);\n\n    /**\n     * @dev The owner is not a valid owner account. (eg. `address(0)`)\n     */\n    error OwnableInvalidOwner(address owner);\n\n    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n    /**\n     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.\n     */\n    constructor(address initialOwner) {\n        if (initialOwner == address(0)) {\n            revert OwnableInvalidOwner(address(0));\n        }\n        _transferOwnership(initialOwner);\n    }\n\n    /**\n     * @dev Throws if called by any account other than the owner.\n     */\n    modifier onlyOwner() {\n        _checkOwner();\n        _;\n    }\n\n    /**\n     * @dev Returns the address of the current owner.\n     */\n    function owner() public view virtual returns (address) {\n        return _owner;\n    }\n\n    /**\n     * @dev Throws if the sender is not the owner.\n     */\n    function _checkOwner() internal view virtual {\n        if (owner() != _msgSender()) {\n            revert OwnableUnauthorizedAccount(_msgSender());\n        }\n    }\n\n    /**\n     * @dev Leaves the contract without owner. It will not be possible to call\n     * `onlyOwner` functions. Can only be called by the current owner.\n     *\n     * NOTE: Renouncing ownership will leave the contract without an owner,\n     * thereby disabling any functionality that is only available to the owner.\n     */\n    function renounceOwnership() public virtual onlyOwner {\n        _transferOwnership(address(0));\n    }\n\n    /**\n     * @dev Transfers ownership of the contract to a new account (`newOwner`).\n     * Can only be called by the current owner.\n     */\n    function transferOwnership(address newOwner) public virtual onlyOwner {\n        if (newOwner == address(0)) {\n            revert OwnableInvalidOwner(address(0));\n        }\n        _transferOwnership(newOwner);\n    }\n\n    /**\n     * @dev Transfers ownership of the contract to a new account (`newOwner`).\n     * Internal function without access restriction.\n     */\n    function _transferOwnership(address newOwner) internal virtual {\n        address oldOwner = _owner;\n        _owner = newOwner;\n        emit OwnershipTransferred(oldOwner, newOwner);\n    }\n}\n"
      },
      "@openzeppelin/contracts/token/ERC721/IERC721.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721.sol)\n\npragma solidity ^0.8.20;\n\nimport {IERC165} from \"../../utils/introspection/IERC165.sol\";\n\n/**\n * @dev Required interface of an ERC721 compliant contract.\n */\ninterface IERC721 is IERC165 {\n    /**\n     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.\n     */\n    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);\n\n    /**\n     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.\n     */\n    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);\n\n    /**\n     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.\n     */\n    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);\n\n    /**\n     * @dev Returns the number of tokens in ``owner``'s account.\n     */\n    function balanceOf(address owner) external view returns (uint256 balance);\n\n    /**\n     * @dev Returns the owner of the `tokenId` token.\n     *\n     * Requirements:\n     *\n     * - `tokenId` must exist.\n     */\n    function ownerOf(uint256 tokenId) external view returns (address owner);\n\n    /**\n     * @dev Safely transfers `tokenId` token from `from` to `to`.\n     *\n     * Requirements:\n     *\n     * - `from` cannot be the zero address.\n     * - `to` cannot be the zero address.\n     * - `tokenId` token must exist and be owned by `from`.\n     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.\n     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon\n     *   a safe transfer.\n     *\n     * Emits a {Transfer} event.\n     */\n    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;\n\n    /**\n     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients\n     * are aware of the ERC721 protocol to prevent tokens from being forever locked.\n     *\n     * Requirements:\n     *\n     * - `from` cannot be the zero address.\n     * - `to` cannot be the zero address.\n     * - `tokenId` token must exist and be owned by `from`.\n     * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or\n     *   {setApprovalForAll}.\n     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon\n     *   a safe transfer.\n     *\n     * Emits a {Transfer} event.\n     */\n    function safeTransferFrom(address from, address to, uint256 tokenId) external;\n\n    /**\n     * @dev Transfers `tokenId` token from `from` to `to`.\n     *\n     * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721\n     * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must\n     * understand this adds an external call which potentially creates a reentrancy vulnerability.\n     *\n     * Requirements:\n     *\n     * - `from` cannot be the zero address.\n     * - `to` cannot be the zero address.\n     * - `tokenId` token must be owned by `from`.\n     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transferFrom(address from, address to, uint256 tokenId) external;\n\n    /**\n     * @dev Gives permission to `to` to transfer `tokenId` token to another account.\n     * The approval is cleared when the token is transferred.\n     *\n     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.\n     *\n     * Requirements:\n     *\n     * - The caller must own the token or be an approved operator.\n     * - `tokenId` must exist.\n     *\n     * Emits an {Approval} event.\n     */\n    function approve(address to, uint256 tokenId) external;\n\n    /**\n     * @dev Approve or remove `operator` as an operator for the caller.\n     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.\n     *\n     * Requirements:\n     *\n     * - The `operator` cannot be the address zero.\n     *\n     * Emits an {ApprovalForAll} event.\n     */\n    function setApprovalForAll(address operator, bool approved) external;\n\n    /**\n     * @dev Returns the account approved for `tokenId` token.\n     *\n     * Requirements:\n     *\n     * - `tokenId` must exist.\n     */\n    function getApproved(uint256 tokenId) external view returns (address operator);\n\n    /**\n     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.\n     *\n     * See {setApprovalForAll}\n     */\n    function isApprovedForAll(address owner, address operator) external view returns (bool);\n}\n"
      },
      "@openzeppelin/contracts/utils/Context.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n    function _msgSender() internal view virtual returns (address) {\n        return msg.sender;\n    }\n\n    function _msgData() internal view virtual returns (bytes calldata) {\n        return msg.data;\n    }\n\n    function _contextSuffixLength() internal view virtual returns (uint256) {\n        return 0;\n    }\n}\n"
      },
      "@openzeppelin/contracts/utils/introspection/IERC165.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)\n\npragma solidity ^0.8.20;\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"
      },
      "contracts/tokenAttributes/IERC7508Draft.sol": {
        "content": "// SPDX-License-Identifier: Apache-2.0\n\npragma solidity ^0.8.21;\n\nimport {IERC165} from \"@openzeppelin/contracts/utils/introspection/IERC165.sol\";\n\n/**\n * @title IERC7508Draft\n * @author RMRK team\n * @notice Interface smart contract of the RMRK token properties extension.\n */\ninterface IERC7508Draft is IERC165 {\n    /**\n     * @notice A list of supported access types.\n     * @return The `Issuer` type, where only the issuer can manage the parameter.\n     * @return The `Collaborator` type, where only the collaborators can manage the parameter.\n     * @return The `IssuerOrCollaborator` type, where only the issuer or collaborators can manage the parameter.\n     * @return The `TokenOwner` type, where only the token owner can manage the parameters of their tokens.\n     * @return The `SpecificAddress` type, where only specific addresses can manage the parameter.\n     */\n    enum AccessType {\n        Issuer,\n        Collaborator,\n        IssuerOrCollaborator,\n        TokenOwner,\n        SpecificAddress\n    }\n\n    /**\n     * @notice Structure used to represent a string attribute.\n     * @return key The key of the attribute\n     * @return value The value of the attribute\n     */\n    struct StringAttribute {\n        string key;\n        string value;\n    }\n\n    /**\n     * @notice Structure used to represent an uint attribute.\n     * @return key The key of the attribute\n     * @return value The value of the attribute\n     */\n    struct UintAttribute {\n        string key;\n        uint256 value;\n    }\n\n    /**\n     * @notice Structure used to represent a boolean attribute.\n     * @return key The key of the attribute\n     * @return value The value of the attribute\n     */\n    struct BoolAttribute {\n        string key;\n        bool value;\n    }\n\n    /**\n     * @notice Structure used to represent an address attribute.\n     * @return key The key of the attribute\n     * @return value The value of the attribute\n     */\n    struct AddressAttribute {\n        string key;\n        address value;\n    }\n\n    /**\n     * @notice Structure used to represent a bytes attribute.\n     * @return key The key of the attribute\n     * @return value The value of the attribute\n     */\n    struct BytesAttribute {\n        string key;\n        bytes value;\n    }\n\n    /**\n     * @notice Used to notify listeners that a new collection has been registered to use the repository.\n     * @param collection Address of the collection\n     * @param issuer Address of the issuer of the collection; the addess authorized to manage the access control\n     * @param registeringAddress Address that registered the collection\n     * @param useOwnable A boolean value indicating whether the collection uses the Ownable extension to verify the\n     *  issuer (`true`) or not (`false`)\n     */\n    event AccessControlRegistration(\n        address indexed collection,\n        address indexed issuer,\n        address indexed registeringAddress,\n        bool useOwnable\n    );\n\n    /**\n     * @notice Used to notify listeners that the access control settings for a specific parameter have been updated.\n     * @param collection Address of the collection\n     * @param key The name of the parameter for which the access control settings have been updated\n     * @param accessType The AccessType of the parameter for which the access control settings have been updated\n     * @param specificAddress The specific addresses that has been updated\n     */\n    event AccessControlUpdate(\n        address indexed collection,\n        string key,\n        AccessType accessType,\n        address specificAddress\n    );\n\n    /**\n     * @notice Used to notify listeners that the metadata URI for a collection has been updated.\n     * @param collection Address of the collection\n     * @param attributesMetadataURI The new attributes metadata URI\n     */\n    event MetadataURIUpdated(\n        address indexed collection,\n        string attributesMetadataURI\n    );\n\n    /**\n     * @notice Used to notify listeners that a new collaborator has been added or removed.\n     * @param collection Address of the collection\n     * @param collaborator Address of the collaborator\n     * @param isCollaborator A boolean value indicating whether the collaborator has been added (`true`) or removed\n     *  (`false`)\n     */\n    event CollaboratorUpdate(\n        address indexed collection,\n        address indexed collaborator,\n        bool isCollaborator\n    );\n\n    /**\n     * @notice Used to notify listeners that a string attribute has been updated.\n     * @param collection The collection address\n     * @param tokenId The token ID\n     * @param key The key of the attribute\n     * @param value The new value of the attribute\n     */\n    event StringAttributeUpdated(\n        address indexed collection,\n        uint256 indexed tokenId,\n        string key,\n        string value\n    );\n\n    /**\n     * @notice Used to notify listeners that an uint attribute has been updated.\n     * @param collection The collection address\n     * @param tokenId The token ID\n     * @param key The key of the attribute\n     * @param value The new value of the attribute\n     */\n    event UintAttributeUpdated(\n        address indexed collection,\n        uint256 indexed tokenId,\n        string key,\n        uint256 value\n    );\n\n    /**\n     * @notice Used to notify listeners that a boolean attribute has been updated.\n     * @param collection The collection address\n     * @param tokenId The token ID\n     * @param key The key of the attribute\n     * @param value The new value of the attribute\n     */\n    event BoolAttributeUpdated(\n        address indexed collection,\n        uint256 indexed tokenId,\n        string key,\n        bool value\n    );\n\n    /**\n     * @notice Used to notify listeners that an address attribute has been updated.\n     * @param collection The collection address\n     * @param tokenId The token ID\n     * @param key The key of the attribute\n     * @param value The new value of the attribute\n     */\n    event AddressAttributeUpdated(\n        address indexed collection,\n        uint256 indexed tokenId,\n        string key,\n        address value\n    );\n\n    /**\n     * @notice Used to notify listeners that a bytes attribute has been updated.\n     * @param collection The collection address\n     * @param tokenId The token ID\n     * @param key The key of the attribute\n     * @param value The new value of the attribute\n     */\n    event BytesAttributeUpdated(\n        address indexed collection,\n        uint256 indexed tokenId,\n        string key,\n        bytes value\n    );\n\n    /**\n     * @notice Used to register a collection to use the RMRK token attributes repository.\n     * @dev  If the collection does not implement the Ownable interface, the `useOwnable` value must be set to `false`.\n     * @dev Emits an {AccessControlRegistration} event.\n     * @param collection The address of the collection that will use the RMRK token attributes repository.\n     * @param issuer The address of the issuer of the collection.\n     * @param useOwnable The boolean value to indicate if the collection implements the Ownable interface and whether it\n     *  should be used to validate that the caller is the issuer (`true`) or to use the manually set issuer address\n     *  (`false`).\n     */\n    function registerAccessControl(\n        address collection,\n        address issuer,\n        bool useOwnable\n    ) external;\n\n    /**\n     * @notice Used to manage the access control settings for a specific parameter.\n     * @dev Only the `issuer` of the collection can call this function.\n     * @dev The possible `accessType` values are:\n     *  [\n     *      Issuer,\n     *      Collaborator,\n     *      IssuerOrCollaborator,\n     *      TokenOwner,\n     *      SpecificAddress,\n     *  ]\n     * @dev Emits an {AccessControlUpdated} event.\n     * @param collection The address of the collection being managed.\n     * @param key The key of the attribute\n     * @param accessType The type of access control to be applied to the parameter.\n     * @param specificAddress The address to be added as a specific addresses allowed to manage the given\n     *  parameter.\n     */\n    function manageAccessControl(\n        address collection,\n        string memory key,\n        AccessType accessType,\n        address specificAddress\n    ) external;\n\n    /**\n     * @notice Used to manage the collaborators of a collection.\n     * @dev The `collaboratorAddresses` and `collaboratorAddressAccess` arrays must be of the same length.\n     * @dev Emits a {CollaboratorUpdate} event.\n     * @param collection The address of the collection\n     * @param collaboratorAddresses The array of collaborator addresses being managed\n     * @param collaboratorAddressAccess The array of boolean values indicating if the collaborator address should\n     *  receive the permission (`true`) or not (`false`).\n     */\n    function manageCollaborators(\n        address collection,\n        address[] memory collaboratorAddresses,\n        bool[] memory collaboratorAddressAccess\n    ) external;\n\n    /**\n     * @notice Used to retrieve the attributes metadata URI for a collection, which contains all the information about the collection attributes.\n     * @param collection Address of the collection\n     * @return attributesMetadataURI The URI of the attributes metadata\n     */\n    function getAttributesMetadataURI(\n        address collection\n    ) external view returns (string memory attributesMetadataURI);\n\n    /**\n     * @notice Used to set the metadata URI for a collection, which contains all the information about the collection attributes.\n     * @dev Emits a {MetadataURIUpdated} event.\n     * @param collection Address of the collection\n     * @param attributesMetadataURI The URI of the attributes metadata\n     */\n    function setAttributesMetadataURI(\n        address collection,\n        string memory attributesMetadataURI\n    ) external;\n\n    /**\n     * @notice Used to set a number attribute.\n     * @dev Emits a {UintAttributeUpdated} event.\n     * @param collection Address of the collection receiving the attribute\n     * @param tokenId The token ID\n     * @param key The attribute key\n     * @param value The attribute value\n     */\n    function setUintAttribute(\n        address collection,\n        uint256 tokenId,\n        string memory key,\n        uint256 value\n    ) external;\n\n    /**\n     * @notice Used to set a string attribute.\n     * @dev Emits a {StringAttributeUpdated} event.\n     * @param collection Address of the collection receiving the attribute\n     * @param tokenId The token ID\n     * @param key The attribute key\n     * @param value The attribute value\n     */\n    function setStringAttribute(\n        address collection,\n        uint256 tokenId,\n        string memory key,\n        string memory value\n    ) external;\n\n    /**\n     * @notice Used to set a boolean attribute.\n     * @dev Emits a {BoolAttributeUpdated} event.\n     * @param collection Address of the collection receiving the attribute\n     * @param tokenId The token ID\n     * @param key The attribute key\n     * @param value The attribute value\n     */\n    function setBoolAttribute(\n        address collection,\n        uint256 tokenId,\n        string memory key,\n        bool value\n    ) external;\n\n    /**\n     * @notice Used to set an bytes attribute.\n     * @dev Emits a {BytesAttributeUpdated} event.\n     * @param collection Address of the collection receiving the attribute\n     * @param tokenId The token ID\n     * @param key The attribute key\n     * @param value The attribute value\n     */\n    function setBytesAttribute(\n        address collection,\n        uint256 tokenId,\n        string memory key,\n        bytes memory value\n    ) external;\n\n    /**\n     * @notice Used to set an address attribute.\n     * @dev Emits a {AddressAttributeUpdated} event.\n     * @param collection Address of the collection receiving the attribute\n     * @param tokenId The token ID\n     * @param key The attribute key\n     * @param value The attribute value\n     */\n    function setAddressAttribute(\n        address collection,\n        uint256 tokenId,\n        string memory key,\n        address value\n    ) external;\n\n    /**\n     * @notice Sets multiple string attributes for a token at once.\n     * @dev The `StringAttribute` struct contains the following fields:\n     *  [\n     *      string key,\n     *      string value\n     *  ]\n     * @param collections Addresses of the collections, in the same order as the attributes. If all tokens are from the same collection the array can contain a single element with the collection address.\n     * @param tokenIds IDs of the tokens, in the same order as the attributes. If all attributes are for the same token the array can contain a single element with the token ID.\n     * @param attributes An array of `StringAttribute` structs to be assigned to the given token\n     */\n    function setStringAttributes(\n        address[] memory collections,\n        uint256[] memory tokenIds,\n        StringAttribute[] memory attributes\n    ) external;\n\n    /**\n     * @notice Sets multiple uint attributes for a token at once.\n     * @dev The `UintAttribute` struct contains the following fields:\n     *  [\n     *      string key,\n     *      uint value\n     *  ]\n     * @param collections Addresses of the collections, in the same order as the attributes. If all tokens are from the same collection the array can contain a single element with the collection address.\n     * @param tokenIds IDs of the tokens, in the same order as the attributes. If all attributes are for the same token the array can contain a single element with the token ID.\n     * @param attributes An array of `UintAttribute` structs to be assigned to the given token\n     */\n    function setUintAttributes(\n        address[] memory collections,\n        uint256[] memory tokenIds,\n        UintAttribute[] memory attributes\n    ) external;\n\n    /**\n     * @notice Sets multiple bool attributes for a token at once.\n     * @dev The `BoolAttribute` struct contains the following fields:\n     *  [\n     *      string key,\n     *      bool value\n     *  ]\n     * @param collections Addresses of the collections, in the same order as the attributes. If all tokens are from the same collection the array can contain a single element with the collection address.\n     * @param tokenIds IDs of the tokens, in the same order as the attributes. If all attributes are for the same token the array can contain a single element with the token ID.\n     * @param attributes An array of `BoolAttribute` structs to be assigned to the given token\n     */\n    function setBoolAttributes(\n        address[] memory collections,\n        uint256[] memory tokenIds,\n        BoolAttribute[] memory attributes\n    ) external;\n\n    /**\n     * @notice Sets multiple address attributes for a token at once.\n     * @dev The `AddressAttribute` struct contains the following fields:\n     *  [\n     *      string key,\n     *      address value\n     *  ]\n     * @param collections Addresses of the collections, in the same order as the attributes. If all tokens are from the same collection the array can contain a single element with the collection address.\n     * @param tokenIds IDs of the tokens, in the same order as the attributes. If all attributes are for the same token the array can contain a single element with the token ID.\n     * @param attributes An array of `AddressAttribute` structs to be assigned to the given token\n     */\n    function setAddressAttributes(\n        address[] memory collections,\n        uint256[] memory tokenIds,\n        AddressAttribute[] memory attributes\n    ) external;\n\n    /**\n     * @notice Sets multiple bytes attributes for a token at once.\n     * @dev The `BytesAttribute` struct contains the following fields:\n     *  [\n     *      string key,\n     *      bytes value\n     *  ]\n     * @param collections Addresses of the collections, in the same order as the attributes. If all tokens are from the same collection the array can contain a single element with the collection address.\n     * @param tokenIds IDs of the tokens, in the same order as the attributes. If all attributes are for the same token the array can contain a single element with the token ID.\n     * @param attributes An array of `BytesAttribute` structs to be assigned to the given token\n     */\n    function setBytesAttributes(\n        address[] memory collections,\n        uint256[] memory tokenIds,\n        BytesAttribute[] memory attributes\n    ) external;\n\n    /**\n     * @notice Sets multiple attributes of multiple types for a token at the same time.\n     * @dev Emits a separate event for each attribute set.\n     * @dev The `StringAttribute`, `UintAttribute`, `BoolAttribute`, `AddressAttribute` and `BytesAttribute` structs consists\n     *  to the following fields (where `value` is of the appropriate type):\n     *  [\n     *      key,\n     *      value,\n     *  ]\n     * @param collection The address of the collection\n     * @param tokenId The token ID\n     * @param stringAttributes An array of `StringAttribute` structs containing string attributes to set\n     * @param uintAttributes An array of `UintAttribute` structs containing uint attributes to set\n     * @param boolAttributes An array of `BoolAttribute` structs containing bool attributes to set\n     * @param addressAttributes An array of `AddressAttribute` structs containing address attributes to set\n     * @param bytesAttributes An array of `BytesAttribute` structs containing bytes attributes to set\n     */\n    function setAttributes(\n        address collection,\n        uint256 tokenId,\n        StringAttribute[] memory stringAttributes,\n        UintAttribute[] memory uintAttributes,\n        BoolAttribute[] memory boolAttributes,\n        AddressAttribute[] memory addressAttributes,\n        BytesAttribute[] memory bytesAttributes\n    ) external;\n\n    /**\n     * @notice Used to set the uint attribute on behalf of an authorized account.\n     * @dev Emits a {UintAttributeUpdated} event.\n     * @param setter Address of the account that presigned the attribute change\n     * @param collection Address of the collection receiving the attribute\n     * @param tokenId The ID of the token receiving the attribute\n     * @param key The attribute key\n     * @param value The attribute value\n     * @param deadline The deadline timestamp for the presigned transaction\n     * @param v `v` value of an ECDSA signature of the presigned message\n     * @param r `r` value of an ECDSA signature of the presigned message\n     * @param s `s` value of an ECDSA signature of the presigned message\n     */\n    function presignedSetUintAttribute(\n        address setter,\n        address collection,\n        uint256 tokenId,\n        string memory key,\n        uint256 value,\n        uint256 deadline,\n        uint8 v,\n        bytes32 r,\n        bytes32 s\n    ) external;\n\n    /**\n     * @notice Used to set the string attribute on behalf of an authorized account.\n     * @dev Emits a {StringAttributeUpdated} event.\n     * @param setter Address of the account that presigned the attribute change\n     * @param collection Address of the collection receiving the attribute\n     * @param tokenId The ID of the token receiving the attribute\n     * @param key The attribute key\n     * @param value The attribute value\n     * @param deadline The deadline timestamp for the presigned transaction\n     * @param v `v` value of an ECDSA signature of the presigned message\n     * @param r `r` value of an ECDSA signature of the presigned message\n     * @param s `s` value of an ECDSA signature of the presigned message\n     */\n    function presignedSetStringAttribute(\n        address setter,\n        address collection,\n        uint256 tokenId,\n        string memory key,\n        string memory value,\n        uint256 deadline,\n        uint8 v,\n        bytes32 r,\n        bytes32 s\n    ) external;\n\n    /**\n     * @notice Used to set the bool attribute on behalf of an authorized account.\n     * @dev Emits a {BoolAttributeUpdated} event.\n     * @param setter Address of the account that presigned the attribute change\n     * @param collection Address of the collection receiving the attribute\n     * @param tokenId The ID of the token receiving the attribute\n     * @param key The attribute key\n     * @param value The attribute value\n     * @param deadline The deadline timestamp for the presigned transaction\n     * @param v `v` value of an ECDSA signature of the presigned message\n     * @param r `r` value of an ECDSA signature of the presigned message\n     * @param s `s` value of an ECDSA signature of the presigned message\n     */\n    function presignedSetBoolAttribute(\n        address setter,\n        address collection,\n        uint256 tokenId,\n        string memory key,\n        bool value,\n        uint256 deadline,\n        uint8 v,\n        bytes32 r,\n        bytes32 s\n    ) external;\n\n    /**\n     * @notice Used to set the bytes attribute on behalf of an authorized account.\n     * @dev Emits a {BytesAttributeUpdated} event.\n     * @param setter Address of the account that presigned the attribute change\n     * @param collection Address of the collection receiving the attribute\n     * @param tokenId The ID of the token receiving the attribute\n     * @param key The attribute key\n     * @param value The attribute value\n     * @param deadline The deadline timestamp for the presigned transaction\n     * @param v `v` value of an ECDSA signature of the presigned message\n     * @param r `r` value of an ECDSA signature of the presigned message\n     * @param s `s` value of an ECDSA signature of the presigned message\n     */\n    function presignedSetBytesAttribute(\n        address setter,\n        address collection,\n        uint256 tokenId,\n        string memory key,\n        bytes memory value,\n        uint256 deadline,\n        uint8 v,\n        bytes32 r,\n        bytes32 s\n    ) external;\n\n    /**\n     * @notice Used to set the address attribute on behalf of an authorized account.\n     * @dev Emits a {AddressAttributeUpdated} event.\n     * @param setter Address of the account that presigned the attribute change\n     * @param collection Address of the collection receiving the attribute\n     * @param tokenId The ID of the token receiving the attribute\n     * @param key The attribute key\n     * @param value The attribute value\n     * @param deadline The deadline timestamp for the presigned transaction\n     * @param v `v` value of an ECDSA signature of the presigned message\n     * @param r `r` value of an ECDSA signature of the presigned message\n     * @param s `s` value of an ECDSA signature of the presigned message\n     */\n    function presignedSetAddressAttribute(\n        address setter,\n        address collection,\n        uint256 tokenId,\n        string memory key,\n        address value,\n        uint256 deadline,\n        uint8 v,\n        bytes32 r,\n        bytes32 s\n    ) external;\n\n    /**\n     * @notice Used to check if the specified address is listed as a collaborator of the given collection's parameter.\n     * @param collaborator Address to be checked.\n     * @param collection Address of the collection.\n     * @return isCollaborator_ Boolean value indicating if the address is a collaborator of the given collection's (`true`) or not\n     *  (`false`).\n     */\n    function isCollaborator(\n        address collaborator,\n        address collection\n    ) external view returns (bool isCollaborator_);\n\n    /**\n     * @notice Used to check if the specified address is listed as a specific address of the given collection's\n     *  parameter.\n     * @param specificAddress Address to be checked.\n     * @param collection Address of the collection.\n     * @param key The key of the attribute\n     * @return isSpecificAddress_ Boolean value indicating if the address is a specific address of the given collection's parameter\n     *  (`true`) or not (`false`).\n     */\n    function isSpecificAddress(\n        address specificAddress,\n        address collection,\n        string memory key\n    ) external view returns (bool isSpecificAddress_);\n\n    /**\n     * @notice Used to retrieve the string type token attributes.\n     * @param collection The collection address\n     * @param tokenId The token ID\n     * @param key The key of the attribute\n     * @return attribute The value of the string attribute\n     */\n    function getStringAttribute(\n        address collection,\n        uint256 tokenId,\n        string memory key\n    ) external view returns (string memory attribute);\n\n    /**\n     * @notice Used to retrieve the uint type token attributes.\n     * @param collection The collection address\n     * @param tokenId The token ID\n     * @param key The key of the attribute\n     * @return attribute The value of the uint attribute\n     */\n    function getUintAttribute(\n        address collection,\n        uint256 tokenId,\n        string memory key\n    ) external view returns (uint256 attribute);\n\n    /**\n     * @notice Used to retrieve the bool type token attributes.\n     * @param collection The collection address\n     * @param tokenId The token ID\n     * @param key The key of the attribute\n     * @return attribute The value of the bool attribute\n     */\n    function getBoolAttribute(\n        address collection,\n        uint256 tokenId,\n        string memory key\n    ) external view returns (bool attribute);\n\n    /**\n     * @notice Used to retrieve the address type token attributes.\n     * @param collection The collection address\n     * @param tokenId The token ID\n     * @param key The key of the attribute\n     * @return attribute The value of the address attribute\n     */\n    function getAddressAttribute(\n        address collection,\n        uint256 tokenId,\n        string memory key\n    ) external view returns (address attribute);\n\n    /**\n     * @notice Used to retrieve the bytes type token attributes.\n     * @param collection The collection address\n     * @param tokenId The token ID\n     * @param key The key of the attribute\n     * @return attribute The value of the bytes attribute\n     */\n    function getBytesAttribute(\n        address collection,\n        uint256 tokenId,\n        string memory key\n    ) external view returns (bytes memory attribute);\n\n    /**\n     * @notice Used to retrieve the message to be signed for submitting a presigned uint attribute change.\n     * @param collection The address of the collection smart contract of the token receiving the attribute\n     * @param tokenId The ID of the token receiving the attribute\n     * @param key The attribute key\n     * @param value The attribute value\n     * @param deadline The deadline timestamp for the presigned transaction after which the message is invalid\n     * @return message Raw message to be signed by the authorized account\n     */\n    function prepareMessageToPresignUintAttribute(\n        address collection,\n        uint256 tokenId,\n        string memory key,\n        uint256 value,\n        uint256 deadline\n    ) external view returns (bytes32 message);\n\n    /**\n     * @notice Used to retrieve the message to be signed for submitting a presigned string attribute change.\n     * @param collection The address of the collection smart contract of the token receiving the attribute\n     * @param tokenId The ID of the token receiving the attribute\n     * @param key The attribute key\n     * @param value The attribute value\n     * @param deadline The deadline timestamp for the presigned transaction after which the message is invalid\n     * @return message Raw message to be signed by the authorized account\n     */\n    function prepareMessageToPresignStringAttribute(\n        address collection,\n        uint256 tokenId,\n        string memory key,\n        string memory value,\n        uint256 deadline\n    ) external view returns (bytes32 message);\n\n    /**\n     * @notice Used to retrieve the message to be signed for submitting a presigned bool attribute change.\n     * @param collection The address of the collection smart contract of the token receiving the attribute\n     * @param tokenId The ID of the token receiving the attribute\n     * @param key The attribute key\n     * @param value The attribute value\n     * @param deadline The deadline timestamp for the presigned transaction after which the message is invalid\n     * @return message Raw message to be signed by the authorized account\n     */\n    function prepareMessageToPresignBoolAttribute(\n        address collection,\n        uint256 tokenId,\n        string memory key,\n        bool value,\n        uint256 deadline\n    ) external view returns (bytes32 message);\n\n    /**\n     * @notice Used to retrieve the message to be signed for submitting a presigned bytes attribute change.\n     * @param collection The address of the collection smart contract of the token receiving the attribute\n     * @param tokenId The ID of the token receiving the attribute\n     * @param key The attribute key\n     * @param value The attribute value\n     * @param deadline The deadline timestamp for the presigned transaction after which the message is invalid\n     * @return message Raw message to be signed by the authorized account\n     */\n    function prepareMessageToPresignBytesAttribute(\n        address collection,\n        uint256 tokenId,\n        string memory key,\n        bytes memory value,\n        uint256 deadline\n    ) external view returns (bytes32 message);\n\n    /**\n     * @notice Used to retrieve the message to be signed for submitting a presigned address attribute change.\n     * @param collection The address of the collection smart contract of the token receiving the attribute\n     * @param tokenId The ID of the token receiving the attribute\n     * @param key The attribute key\n     * @param value The attribute value\n     * @param deadline The deadline timestamp for the presigned transaction after which the message is invalid\n     * @return message Raw message to be signed by the authorized account\n     */\n    function prepareMessageToPresignAddressAttribute(\n        address collection,\n        uint256 tokenId,\n        string memory key,\n        address value,\n        uint256 deadline\n    ) external view returns (bytes32 message);\n\n    /**\n     * @notice Used to retrieve multiple token attributes of any type at once.\n     * @dev The `StringAttribute`, `UintAttribute`, `BoolAttribute`, `AddressAttribute` and `BytesAttribute` structs consists\n     *  to the following fields (where `value` is of the appropriate type):\n     *  [\n     *      key,\n     *      value,\n     *  ]\n     * @param collection The collection address\n     * @param tokenId The token ID\n     * @param stringKeys An array of string type attribute keys to retrieve\n     * @param uintKeys An array of uint type attribute keys to retrieve\n     * @param boolKeys An array of bool type attribute keys to retrieve\n     * @param addressKeys An array of address type attribute keys to retrieve\n     * @param bytesKeys An array of bytes type attribute keys to retrieve\n     * @return stringAttributes An array of strings, in the same order as the stringKeys\n     * @return uintAttributes An array of uints, in the same order as the uintKeys\n     * @return boolAttributes An array of bools, in the same order as the boolKeys\n     * @return addressAttributes An array of addresses, in the same order as the addressKeys\n     * @return bytesAttributes An array of bytes, in the same order as the bytesKeys\n     */\n    function getAttributes(\n        address collection,\n        uint256 tokenId,\n        string[] memory stringKeys,\n        string[] memory uintKeys,\n        string[] memory boolKeys,\n        string[] memory addressKeys,\n        string[] memory bytesKeys\n    )\n        external\n        view\n        returns (\n            string[] memory stringAttributes,\n            uint256[] memory uintAttributes,\n            bool[] memory boolAttributes,\n            address[] memory addressAttributes,\n            bytes[] memory bytesAttributes\n        );\n\n    /**\n     * @notice Used to get multiple sting parameter values for a token.\n     * @dev The `StringAttribute` struct contains the following fields:\n     *  [\n     *     string key,\n     *     string value\n     *  ]\n     * @param collections Addresses of the collections, in the same order as the attribute keys. If all tokens are from the same collection the array can contain a single element with the collection address.\n     * @param tokenIds IDs of the tokens, in the same order as the attribute keys. If all attributes are for the same token the array can contain a single element with the token ID.\n     * @param attributeKeys An array of string keys to retrieve\n     * @return attributes An array of strings, in the same order as the attribute keys\n     */\n    function getStringAttributes(\n        address[] memory collections,\n        uint256[] memory tokenIds,\n        string[] memory attributeKeys\n    ) external view returns (string[] memory attributes);\n\n    /**\n     * @notice Used to get multiple uint parameter values for a token.\n     * @dev The `UintAttribute` struct contains the following fields:\n     *  [\n     *     string key,\n     *     uint value\n     *  ]\n     * @param collections Addresses of the collections, in the same order as the attribute keys. If all tokens are from the same collection the array can contain a single element with the collection address.\n     * @param tokenIds IDs of the tokens, in the same order as the attribute keys. If all attributes are for the same token the array can contain a single element with the token ID.\n     * @param attributeKeys An array of uint keys to retrieve\n     * @return attributes An array of uints, in the same order as the attribute keys\n     */\n    function getUintAttributes(\n        address[] memory collections,\n        uint256[] memory tokenIds,\n        string[] memory attributeKeys\n    ) external view returns (uint256[] memory attributes);\n\n    /**\n     * @notice Used to get multiple bool parameter values for a token.\n     * @dev The `BoolAttribute` struct contains the following fields:\n     *  [\n     *     string key,\n     *     bool value\n     *  ]\n     * @param collections Addresses of the collections, in the same order as the attribute keys. If all tokens are from the same collection the array can contain a single element with the collection address.\n     * @param tokenIds IDs of the tokens, in the same order as the attribute keys. If all attributes are for the same token the array can contain a single element with the token ID.\n     * @param attributeKeys An array of bool keys to retrieve\n     * @return attributes An array of bools, in the same order as the attribute keys\n     */\n    function getBoolAttributes(\n        address[] memory collections,\n        uint256[] memory tokenIds,\n        string[] memory attributeKeys\n    ) external view returns (bool[] memory attributes);\n\n    /**\n     * @notice Used to get multiple address parameter values for a token.\n     * @dev The `AddressAttribute` struct contains the following fields:\n     *  [\n     *     string key,\n     *     address value\n     *  ]\n     * @param collections Addresses of the collections, in the same order as the attribute keys. If all tokens are from the same collection the array can contain a single element with the collection address.\n     * @param tokenIds IDs of the tokens, in the same order as the attribute keys. If all attributes are for the same token the array can contain a single element with the token ID.\n     * @param attributeKeys An array of address keys to retrieve\n     * @return attributes An array of addresses, in the same order as the attribute keys\n     */\n    function getAddressAttributes(\n        address[] memory collections,\n        uint256[] memory tokenIds,\n        string[] memory attributeKeys\n    ) external view returns (address[] memory attributes);\n\n    /**\n     * @notice Used to get multiple bytes parameter values for a token.\n     * @dev The `BytesAttribute` struct contains the following fields:\n     *  [\n     *     string key,\n     *     bytes value\n     *  ]\n     * @param collections Addresses of the collections, in the same order as the attribute keys. If all tokens are from the same collection the array can contain a single element with the collection address.\n     * @param tokenIds IDs of the tokens, in the same order as the attribute keys. If all attributes are for the same token the array can contain a single element with the token ID.\n     * @param attributeKeys An array of bytes keys to retrieve\n     * @return attributes An array of bytes, in the same order as the attribute keys\n     */\n    function getBytesAttributes(\n        address[] memory collections,\n        uint256[] memory tokenIds,\n        string[] memory attributeKeys\n    ) external view returns (bytes[] memory attributes);\n}\n"
      },
      "contracts/tokenAttributes/RMRKTokenAttributesRepositoryDraft.sol": {
        "content": "// SPDX-License-Identifier: Apache-2.0\n\npragma solidity ^0.8.21;\n\nimport {Ownable} from \"@openzeppelin/contracts/access/Ownable.sol\";\nimport {IERC721} from \"@openzeppelin/contracts/token/ERC721/IERC721.sol\";\nimport {IERC165} from \"@openzeppelin/contracts/utils/introspection/IERC165.sol\";\nimport {Context} from \"@openzeppelin/contracts/utils/Context.sol\";\nimport {IERC7508Draft} from \"./IERC7508Draft.sol\";\n\n/**\n * @title RMRKTokenAttributesRepository\n * @author RMRK team\n * @notice Smart contract of the RMRK Token property repository module.\n */\ncontract RMRKTokenAttributesRepositoryDraft is IERC7508Draft, Context {\n    bytes32 public immutable DOMAIN_SEPARATOR =\n        keccak256(\n            abi.encode(\n                \"ERC-7508: Public On-Chain NFT Attributes Repository\",\n                \"1\",\n                block.chainid,\n                address(this)\n            )\n        );\n    bytes32 public immutable SET_UINT_ATTRIBUTE_TYPEHASH =\n        keccak256(\n            \"setUintAttribute(address collection,uint256 tokenId,string memory key,uint256 value)\"\n        );\n    bytes32 public immutable SET_STRING_ATTRIBUTE_TYPEHASH =\n        keccak256(\n            \"setStringAttribute(address collection,uint256 tokenId,string memory key,string memory value)\"\n        );\n    bytes32 public immutable SET_BOOL_ATTRIBUTE_TYPEHASH =\n        keccak256(\n            \"setBoolAttribute(address collection,uint256 tokenId,string memory key,bool value)\"\n        );\n    bytes32 public immutable SET_BYTES_ATTRIBUTE_TYPEHASH =\n        keccak256(\n            \"setBytesAttribute(address collection,uint256 tokenId,string memory key,bytes memory value)\"\n        );\n    bytes32 public immutable SET_ADDRESS_ATTRIBUTE_TYPEHASH =\n        keccak256(\n            \"setAddressAttribute(address collection,uint256 tokenId,string memory key,address value)\"\n        );\n\n    mapping(address collection => mapping(uint256 parameterId => AccessType accessType))\n        private _parameterAccessType;\n    mapping(address collection => mapping(uint256 parameterId => address specificAddress))\n        private _parameterSpecificAddress;\n    mapping(address collection => IssuerSetting setting)\n        private _issuerSettings;\n    mapping(address collection => mapping(address collaborator => bool isCollaborator))\n        private _collaborators;\n\n    // For keys, we use a mapping from strings to IDs.\n    // The purpose is to store unique string keys only once, since they are more expensive.\n    mapping(string key => uint256 id) private _keysToIds;\n    uint256 private _nextKeyId;\n\n    mapping(address collection => string attributesMetadataURI)\n        private _attributesMetadataURIs;\n    mapping(address collection => mapping(uint256 => mapping(uint256 => address)))\n        private _addressValues;\n    mapping(address collection => mapping(uint256 => mapping(uint256 => bytes)))\n        private _bytesValues;\n    mapping(address collection => mapping(uint256 => mapping(uint256 => uint256)))\n        private _uintValues;\n    mapping(address collection => mapping(uint256 => mapping(uint256 => bool)))\n        private _boolValues;\n    mapping(address collection => mapping(uint256 => mapping(uint256 => string)))\n        private _stringValues;\n\n    struct IssuerSetting {\n        bool registered;\n        bool useOwnable;\n        address issuer;\n    }\n\n    /// Used to signal that the length of the arrays is not equal.\n    error LengthsMismatch();\n    /// Used to signal that the smart contract interacting with the repository does not implement Ownable pattern.\n    error OwnableNotImplemented();\n    /// Used to signal that the caller is not the issuer of the collection.\n    error NotCollectionIssuer();\n    /// Used to signal that the collaborator and collaborator rights array are not of equal length.\n    error CollaboratorArraysNotEqualLength();\n    /// Used to signal that the collection is not registered in the repository yet.\n    error CollectionNotRegistered();\n    /// Used to signal that the caller is not aa collaborator of the collection.\n    error NotCollectionCollaborator();\n    /// Used to signal that the caller is not the issuer or a collaborator of the collection.\n    error NotCollectionIssuerOrCollaborator();\n    /// Used to signal that the caller is not the owner of the token.\n    error NotTokenOwner();\n    /// Used to signal that the caller is not the specific address allowed to manage the attribute.\n    error NotSpecificAddress();\n    /// Used to signal that the presigned message's signature is invalid.\n    error InvalidSignature();\n    /// Used to signal that the presigned message's deadline has expired.\n    error ExpiredDeadline();\n\n    /**\n     * @inheritdoc IERC7508Draft\n     */\n    function registerAccessControl(\n        address collection,\n        address issuer,\n        bool useOwnable\n    ) external {\n        (bool ownableSuccess, bytes memory ownableReturn) = collection.call(\n            abi.encodeWithSignature(\"owner()\")\n        );\n\n        if (address(uint160(uint256(bytes32(ownableReturn)))) == address(0)) {\n            revert OwnableNotImplemented();\n        }\n        if (\n            ownableSuccess &&\n            address(uint160(uint256(bytes32(ownableReturn)))) != _msgSender()\n        ) {\n            revert NotCollectionIssuer();\n        }\n\n        _issuerSettings[collection] = IssuerSetting({\n            registered: true,\n            issuer: issuer,\n            useOwnable: useOwnable\n        });\n\n        emit AccessControlRegistration(\n            collection,\n            issuer,\n            _msgSender(),\n            useOwnable\n        );\n    }\n\n    /**\n     * @inheritdoc IERC7508Draft\n     */\n    function manageAccessControl(\n        address collection,\n        string memory key,\n        AccessType accessType,\n        address specificAddress\n    ) external onlyRegisteredCollection(collection) onlyIssuer(collection) {\n        uint256 parameterId = _getIdForKey(key);\n\n        _parameterAccessType[collection][parameterId] = accessType;\n        _parameterSpecificAddress[collection][parameterId] = specificAddress;\n\n        emit AccessControlUpdate(collection, key, accessType, specificAddress);\n    }\n\n    /**\n     * @inheritdoc IERC7508Draft\n     */\n    function manageCollaborators(\n        address collection,\n        address[] memory collaboratorAddresses,\n        bool[] memory collaboratorAddressAccess\n    ) external onlyRegisteredCollection(collection) onlyIssuer(collection) {\n        uint256 length = collaboratorAddresses.length;\n        if (length != collaboratorAddressAccess.length) {\n            revert CollaboratorArraysNotEqualLength();\n        }\n        for (uint256 i; i < length; ) {\n            _collaborators[collection][\n                collaboratorAddresses[i]\n            ] = collaboratorAddressAccess[i];\n            emit CollaboratorUpdate(\n                collection,\n                collaboratorAddresses[i],\n                collaboratorAddressAccess[i]\n            );\n            unchecked {\n                ++i;\n            }\n        }\n    }\n\n    /**\n     * @inheritdoc IERC7508Draft\n     */\n    function getAttributesMetadataURI(\n        address collection\n    ) external view returns (string memory attributesMetadataURI) {\n        attributesMetadataURI = _attributesMetadataURIs[collection];\n    }\n\n    /**\n     * @inheritdoc IERC7508Draft\n     */\n    function setAttributesMetadataURI(\n        address collection,\n        string memory attributesMetadataURI\n    ) external onlyIssuer(collection) {\n        _attributesMetadataURIs[collection] = attributesMetadataURI;\n        emit MetadataURIUpdated(collection, attributesMetadataURI);\n    }\n\n    /**\n     * @inheritdoc IERC7508Draft\n     */\n    function isCollaborator(\n        address collaborator,\n        address collection\n    ) external view returns (bool isCollaborator_) {\n        isCollaborator_ = _collaborators[collection][collaborator];\n    }\n\n    /**\n     * @inheritdoc IERC7508Draft\n     */\n    function isSpecificAddress(\n        address specificAddress,\n        address collection,\n        string memory key\n    ) external view returns (bool isSpecificAddress_) {\n        isSpecificAddress_ =\n            _parameterSpecificAddress[collection][_keysToIds[key]] ==\n            specificAddress;\n    }\n\n    /**\n     * @notice Modifier to check if the caller is authorized to call the function.\n     * @dev If the authorization is set to TokenOwner and the tokenId provided is of the non-existent token, the\n     *  execution will revert with `ERC721InvalidTokenId` rather than `NotTokenOwner`.\n     * @dev The tokenId parameter is only needed for the TokenOwner authorization type, other authorization types ignore\n     *  it.\n     * @param collection The address of the collection.\n     * @param key Key of the attribute.\n     * @param tokenId The ID of the token.\n     */\n    modifier onlyAuthorizedCaller(\n        address collection,\n        string memory key,\n        uint256 tokenId\n    ) {\n        _onlyAuthorizedCaller(_msgSender(), collection, key, tokenId);\n        _;\n    }\n\n    /**\n     * @notice Modifier to check if the collection is registered.\n     * @param collection Address of the collection.\n     */\n    modifier onlyRegisteredCollection(address collection) {\n        if (!_issuerSettings[collection].registered) {\n            revert CollectionNotRegistered();\n        }\n        _;\n    }\n\n    /**\n     * @notice Modifier to check if the caller is the issuer of the collection.\n     * @param collection Address of the collection.\n     */\n    modifier onlyIssuer(address collection) {\n        if (_issuerSettings[collection].useOwnable) {\n            if (Ownable(collection).owner() != _msgSender()) {\n                revert NotCollectionIssuer();\n            }\n        } else if (_issuerSettings[collection].issuer != _msgSender()) {\n            revert NotCollectionIssuer();\n        }\n        _;\n    }\n\n    /**\n     * @notice Function to check if the caller is authorized to mamage a given parameter.\n     * @param collection The address of the collection.\n     * @param key Key of the attribute.\n     * @param tokenId The ID of the token.\n     */\n    function _onlyAuthorizedCaller(\n        address caller,\n        address collection,\n        string memory key,\n        uint256 tokenId\n    ) private view {\n        AccessType accessType = _parameterAccessType[collection][\n            _keysToIds[key]\n        ];\n\n        if (\n            accessType == AccessType.Issuer &&\n            ((_issuerSettings[collection].useOwnable &&\n                Ownable(collection).owner() != caller) ||\n                (!_issuerSettings[collection].useOwnable &&\n                    _issuerSettings[collection].issuer != caller))\n        ) {\n            revert NotCollectionIssuer();\n        } else if (\n            accessType == AccessType.Collaborator &&\n            !_collaborators[collection][caller]\n        ) {\n            revert NotCollectionCollaborator();\n        } else if (\n            accessType == AccessType.IssuerOrCollaborator &&\n            ((_issuerSettings[collection].useOwnable &&\n                Ownable(collection).owner() != caller) ||\n                (!_issuerSettings[collection].useOwnable &&\n                    _issuerSettings[collection].issuer != caller)) &&\n            !_collaborators[collection][caller]\n        ) {\n            revert NotCollectionIssuerOrCollaborator();\n        } else if (\n            accessType == AccessType.TokenOwner &&\n            IERC721(collection).ownerOf(tokenId) != caller\n        ) {\n            revert NotTokenOwner();\n        } else if (\n            accessType == AccessType.SpecificAddress &&\n            !(_parameterSpecificAddress[collection][_keysToIds[key]] == caller)\n        ) {\n            revert NotSpecificAddress();\n        }\n    }\n\n    /**\n     * @inheritdoc IERC7508Draft\n     */\n    function getStringAttribute(\n        address collection,\n        uint256 tokenId,\n        string memory key\n    ) public view returns (string memory attribute) {\n        attribute = _stringValues[collection][tokenId][_keysToIds[key]];\n    }\n\n    /**\n     * @inheritdoc IERC7508Draft\n     */\n    function getUintAttribute(\n        address collection,\n        uint256 tokenId,\n        string memory key\n    ) public view returns (uint256 attribute) {\n        attribute = _uintValues[collection][tokenId][_keysToIds[key]];\n    }\n\n    /**\n     * @inheritdoc IERC7508Draft\n     */\n    function getBoolAttribute(\n        address collection,\n        uint256 tokenId,\n        string memory key\n    ) public view returns (bool attribute) {\n        attribute = _boolValues[collection][tokenId][_keysToIds[key]];\n    }\n\n    /**\n     * @inheritdoc IERC7508Draft\n     */\n    function getAddressAttribute(\n        address collection,\n        uint256 tokenId,\n        string memory key\n    ) public view returns (address attribute) {\n        attribute = _addressValues[collection][tokenId][_keysToIds[key]];\n    }\n\n    /**\n     * @inheritdoc IERC7508Draft\n     */\n    function getBytesAttribute(\n        address collection,\n        uint256 tokenId,\n        string memory key\n    ) public view returns (bytes memory attribute) {\n        attribute = _bytesValues[collection][tokenId][_keysToIds[key]];\n    }\n\n    /**\n     * @inheritdoc IERC7508Draft\n     */\n    function getAttributes(\n        address collection,\n        uint256 tokenId,\n        string[] memory stringKeys,\n        string[] memory uintKeys,\n        string[] memory boolKeys,\n        string[] memory addressKeys,\n        string[] memory bytesKeys\n    )\n        external\n        view\n        returns (\n            string[] memory stringAttributes,\n            uint256[] memory uintAttributes,\n            bool[] memory boolAttributes,\n            address[] memory addressAttributes,\n            bytes[] memory bytesAttributes\n        )\n    {\n        address[] memory collections = new address[](1);\n        uint256[] memory tokenIds = new uint256[](1);\n        collections[0] = collection;\n        tokenIds[0] = tokenId;\n\n        stringAttributes = getStringAttributes(\n            collections,\n            tokenIds,\n            stringKeys\n        );\n        uintAttributes = getUintAttributes(collections, tokenIds, uintKeys);\n        boolAttributes = getBoolAttributes(collections, tokenIds, boolKeys);\n        addressAttributes = getAddressAttributes(\n            collections,\n            tokenIds,\n            addressKeys\n        );\n        bytesAttributes = getBytesAttributes(collections, tokenIds, bytesKeys);\n    }\n\n    /**\n     * @inheritdoc IERC7508Draft\n     */\n    function getStringAttributes(\n        address[] memory collections,\n        uint256[] memory tokenIds,\n        string[] memory attributeKeys\n    ) public view returns (string[] memory attributes) {\n        (\n            bool multipleCollections,\n            bool multipleTokens,\n            bool multipleAttributes,\n            uint256 loopLength\n        ) = _checkIfMultipleCollectionsAndTokens(\n                collections,\n                tokenIds,\n                attributeKeys.length\n            );\n\n        attributes = new string[](loopLength);\n\n        for (uint256 i; i < loopLength; ) {\n            attributes[i] = getStringAttribute(\n                multipleCollections ? collections[i] : collections[0],\n                multipleTokens ? tokenIds[i] : tokenIds[0],\n                multipleAttributes ? attributeKeys[i] : attributeKeys[0]\n            );\n            unchecked {\n                ++i;\n            }\n        }\n    }\n\n    /**\n     * @inheritdoc IERC7508Draft\n     */\n    function getUintAttributes(\n        address[] memory collections,\n        uint256[] memory tokenIds,\n        string[] memory attributeKeys\n    ) public view returns (uint256[] memory attributes) {\n        (\n            bool multipleCollections,\n            bool multipleTokens,\n            bool multipleAttributes,\n            uint256 loopLength\n        ) = _checkIfMultipleCollectionsAndTokens(\n                collections,\n                tokenIds,\n                attributeKeys.length\n            );\n\n        attributes = new uint256[](loopLength);\n\n        for (uint256 i; i < loopLength; ) {\n            attributes[i] = getUintAttribute(\n                multipleCollections ? collections[i] : collections[0],\n                multipleTokens ? tokenIds[i] : tokenIds[0],\n                multipleAttributes ? attributeKeys[i] : attributeKeys[0]\n            );\n            unchecked {\n                ++i;\n            }\n        }\n    }\n\n    /**\n     * @inheritdoc IERC7508Draft\n     */\n    function getBoolAttributes(\n        address[] memory collections,\n        uint256[] memory tokenIds,\n        string[] memory attributeKeys\n    ) public view returns (bool[] memory attributes) {\n        (\n            bool multipleCollections,\n            bool multipleTokens,\n            bool multipleAttributes,\n            uint256 loopLength\n        ) = _checkIfMultipleCollectionsAndTokens(\n                collections,\n                tokenIds,\n                attributeKeys.length\n            );\n\n        attributes = new bool[](loopLength);\n\n        for (uint256 i; i < loopLength; ) {\n            attributes[i] = getBoolAttribute(\n                multipleCollections ? collections[i] : collections[0],\n                multipleTokens ? tokenIds[i] : tokenIds[0],\n                multipleAttributes ? attributeKeys[i] : attributeKeys[0]\n            );\n            unchecked {\n                ++i;\n            }\n        }\n    }\n\n    /**\n     * @inheritdoc IERC7508Draft\n     */\n    function getAddressAttributes(\n        address[] memory collections,\n        uint256[] memory tokenIds,\n        string[] memory attributeKeys\n    ) public view returns (address[] memory attributes) {\n        (\n            bool multipleCollections,\n            bool multipleTokens,\n            bool multipleAttributes,\n            uint256 loopLength\n        ) = _checkIfMultipleCollectionsAndTokens(\n                collections,\n                tokenIds,\n                attributeKeys.length\n            );\n\n        attributes = new address[](loopLength);\n\n        for (uint256 i; i < loopLength; ) {\n            attributes[i] = getAddressAttribute(\n                multipleCollections ? collections[i] : collections[0],\n                multipleTokens ? tokenIds[i] : tokenIds[0],\n                multipleAttributes ? attributeKeys[i] : attributeKeys[0]\n            );\n            unchecked {\n                ++i;\n            }\n        }\n    }\n\n    /**\n     * @inheritdoc IERC7508Draft\n     */\n    function getBytesAttributes(\n        address[] memory collections,\n        uint256[] memory tokenIds,\n        string[] memory attributeKeys\n    ) public view returns (bytes[] memory attributes) {\n        (\n            bool multipleCollections,\n            bool multipleTokens,\n            bool multipleAttributes,\n            uint256 loopLength\n        ) = _checkIfMultipleCollectionsAndTokens(\n                collections,\n                tokenIds,\n                attributeKeys.length\n            );\n\n        attributes = new bytes[](loopLength);\n\n        for (uint256 i; i < loopLength; ) {\n            attributes[i] = getBytesAttribute(\n                multipleCollections ? collections[i] : collections[0],\n                multipleTokens ? tokenIds[i] : tokenIds[0],\n                multipleAttributes ? attributeKeys[i] : attributeKeys[0]\n            );\n            unchecked {\n                ++i;\n            }\n        }\n    }\n\n    /**\n     * @inheritdoc IERC7508Draft\n     */\n    function prepareMessageToPresignUintAttribute(\n        address collection,\n        uint256 tokenId,\n        string memory key,\n        uint256 value,\n        uint256 deadline\n    ) public view returns (bytes32 message) {\n        message = keccak256(\n            abi.encode(\n                DOMAIN_SEPARATOR,\n                SET_UINT_ATTRIBUTE_TYPEHASH,\n                collection,\n                tokenId,\n                key,\n                value,\n                deadline\n            )\n        );\n    }\n\n    /**\n     * @inheritdoc IERC7508Draft\n     */\n    function prepareMessageToPresignStringAttribute(\n        address collection,\n        uint256 tokenId,\n        string memory key,\n        string memory value,\n        uint256 deadline\n    ) public view returns (bytes32 message) {\n        message = keccak256(\n            abi.encode(\n                DOMAIN_SEPARATOR,\n                SET_STRING_ATTRIBUTE_TYPEHASH,\n                collection,\n                tokenId,\n                key,\n                value,\n                deadline\n            )\n        );\n    }\n\n    /**\n     * @inheritdoc IERC7508Draft\n     */\n    function prepareMessageToPresignBoolAttribute(\n        address collection,\n        uint256 tokenId,\n        string memory key,\n        bool value,\n        uint256 deadline\n    ) public view returns (bytes32 message) {\n        message = keccak256(\n            abi.encode(\n                DOMAIN_SEPARATOR,\n                SET_BOOL_ATTRIBUTE_TYPEHASH,\n                collection,\n                tokenId,\n                key,\n                value,\n                deadline\n            )\n        );\n    }\n\n    /**\n     * @inheritdoc IERC7508Draft\n     */\n    function prepareMessageToPresignBytesAttribute(\n        address collection,\n        uint256 tokenId,\n        string memory key,\n        bytes memory value,\n        uint256 deadline\n    ) public view returns (bytes32 message) {\n        message = keccak256(\n            abi.encode(\n                DOMAIN_SEPARATOR,\n                SET_BYTES_ATTRIBUTE_TYPEHASH,\n                collection,\n                tokenId,\n                key,\n                value,\n                deadline\n            )\n        );\n    }\n\n    /**\n     * @inheritdoc IERC7508Draft\n     */\n    function prepareMessageToPresignAddressAttribute(\n        address collection,\n        uint256 tokenId,\n        string memory key,\n        address value,\n        uint256 deadline\n    ) public view returns (bytes32 message) {\n        message = keccak256(\n            abi.encode(\n                DOMAIN_SEPARATOR,\n                SET_ADDRESS_ATTRIBUTE_TYPEHASH,\n                collection,\n                tokenId,\n                key,\n                value,\n                deadline\n            )\n        );\n    }\n\n    /**\n     * @inheritdoc IERC7508Draft\n     */\n    function setBoolAttribute(\n        address collection,\n        uint256 tokenId,\n        string memory key,\n        bool value\n    ) external {\n        _setBoolAttribute(_msgSender(), collection, tokenId, key, value);\n    }\n\n    /**\n     * @inheritdoc IERC7508Draft\n     */\n    function setBytesAttribute(\n        address collection,\n        uint256 tokenId,\n        string memory key,\n        bytes memory value\n    ) external {\n        _setBytesAttribute(_msgSender(), collection, tokenId, key, value);\n    }\n\n    /**\n     * @inheritdoc IERC7508Draft\n     */\n    function setAddressAttribute(\n        address collection,\n        uint256 tokenId,\n        string memory key,\n        address value\n    ) external {\n        _setAddressAttribute(_msgSender(), collection, tokenId, key, value);\n    }\n\n    /**\n     * @inheritdoc IERC7508Draft\n     */\n    function setUintAttribute(\n        address collection,\n        uint256 tokenId,\n        string memory key,\n        uint256 value\n    ) external {\n        _setUintAttribute(_msgSender(), collection, tokenId, key, value);\n    }\n\n    /**\n     * @inheritdoc IERC7508Draft\n     */\n    function setStringAttribute(\n        address collection,\n        uint256 tokenId,\n        string memory key,\n        string memory value\n    ) external {\n        _setStringAttribute(_msgSender(), collection, tokenId, key, value);\n    }\n\n    /**\n     * @inheritdoc IERC7508Draft\n     */\n    function setBoolAttributes(\n        address[] memory collections,\n        uint256[] memory tokenIds,\n        BoolAttribute[] memory attributes\n    ) external {\n        (\n            bool multipleCollections,\n            bool multipleTokens,\n            bool multipleAttributes,\n            uint256 loopLength\n        ) = _checkIfMultipleCollectionsAndTokens(\n                collections,\n                tokenIds,\n                attributes.length\n            );\n        for (uint256 i; i < loopLength; ) {\n            BoolAttribute memory attribute = multipleAttributes\n                ? attributes[i]\n                : attributes[0];\n            _setBoolAttribute(\n                _msgSender(),\n                multipleCollections ? collections[i] : collections[0],\n                multipleTokens ? tokenIds[i] : tokenIds[0],\n                attribute.key,\n                attribute.value\n            );\n            unchecked {\n                ++i;\n            }\n        }\n    }\n\n    /**\n     * @inheritdoc IERC7508Draft\n     */\n    function setBytesAttributes(\n        address[] memory collections,\n        uint256[] memory tokenIds,\n        BytesAttribute[] memory attributes\n    ) external {\n        (\n            bool multipleCollections,\n            bool multipleTokens,\n            bool multipleAttributes,\n            uint256 loopLength\n        ) = _checkIfMultipleCollectionsAndTokens(\n                collections,\n                tokenIds,\n                attributes.length\n            );\n        for (uint256 i; i < loopLength; ) {\n            BytesAttribute memory attribute = multipleAttributes\n                ? attributes[i]\n                : attributes[0];\n            _setBytesAttribute(\n                _msgSender(),\n                multipleCollections ? collections[i] : collections[0],\n                multipleTokens ? tokenIds[i] : tokenIds[0],\n                attribute.key,\n                attribute.value\n            );\n            unchecked {\n                ++i;\n            }\n        }\n    }\n\n    /**\n     * @inheritdoc IERC7508Draft\n     */\n    function setStringAttributes(\n        address[] memory collections,\n        uint256[] memory tokenIds,\n        StringAttribute[] memory attributes\n    ) external {\n        (\n            bool multipleCollections,\n            bool multipleTokens,\n            bool multipleAttributes,\n            uint256 loopLength\n        ) = _checkIfMultipleCollectionsAndTokens(\n                collections,\n                tokenIds,\n                attributes.length\n            );\n        for (uint256 i; i < loopLength; ) {\n            StringAttribute memory attribute = multipleAttributes\n                ? attributes[i]\n                : attributes[0];\n            _setStringAttribute(\n                _msgSender(),\n                multipleCollections ? collections[i] : collections[0],\n                multipleTokens ? tokenIds[i] : tokenIds[0],\n                attribute.key,\n                attribute.value\n            );\n            unchecked {\n                ++i;\n            }\n        }\n    }\n\n    /**\n     * @inheritdoc IERC7508Draft\n     */\n    function setUintAttributes(\n        address[] memory collections,\n        uint256[] memory tokenIds,\n        UintAttribute[] memory attributes\n    ) external {\n        (\n            bool multipleCollections,\n            bool multipleTokens,\n            bool multipleAttributes,\n            uint256 loopLength\n        ) = _checkIfMultipleCollectionsAndTokens(\n                collections,\n                tokenIds,\n                attributes.length\n            );\n        for (uint256 i; i < loopLength; ) {\n            UintAttribute memory attribute = multipleAttributes\n                ? attributes[i]\n                : attributes[0];\n            _setUintAttribute(\n                _msgSender(),\n                multipleCollections ? collections[i] : collections[0],\n                multipleTokens ? tokenIds[i] : tokenIds[0],\n                attribute.key,\n                attribute.value\n            );\n            unchecked {\n                ++i;\n            }\n        }\n    }\n\n    /**\n     * @inheritdoc IERC7508Draft\n     */\n    function setAddressAttributes(\n        address[] memory collections,\n        uint256[] memory tokenIds,\n        AddressAttribute[] memory attributes\n    ) external {\n        (\n            bool multipleCollections,\n            bool multipleTokens,\n            bool multipleAttributes,\n            uint256 loopLength\n        ) = _checkIfMultipleCollectionsAndTokens(\n                collections,\n                tokenIds,\n                attributes.length\n            );\n        for (uint256 i; i < loopLength; ) {\n            AddressAttribute memory attribute = multipleAttributes\n                ? attributes[i]\n                : attributes[0];\n            _setAddressAttribute(\n                _msgSender(),\n                multipleCollections ? collections[i] : collections[0],\n                multipleTokens ? tokenIds[i] : tokenIds[0],\n                attribute.key,\n                attribute.value\n            );\n            unchecked {\n                ++i;\n            }\n        }\n    }\n\n    /**\n     * @inheritdoc IERC7508Draft\n     */\n    function setAttributes(\n        address collection,\n        uint256 tokenId,\n        StringAttribute[] memory stringAttributes,\n        UintAttribute[] memory uintAttributes,\n        BoolAttribute[] memory boolAttributes,\n        AddressAttribute[] memory addressAttributes,\n        BytesAttribute[] memory bytesAttributes\n    ) external {\n        uint256 length = stringAttributes.length;\n        for (uint256 i; i < length; ) {\n            _setStringAttribute(\n                _msgSender(),\n                collection,\n                tokenId,\n                stringAttributes[i].key,\n                stringAttributes[i].value\n            );\n            unchecked {\n                ++i;\n            }\n        }\n\n        length = uintAttributes.length;\n        for (uint256 i; i < length; ) {\n            _setUintAttribute(\n                _msgSender(),\n                collection,\n                tokenId,\n                uintAttributes[i].key,\n                uintAttributes[i].value\n            );\n            unchecked {\n                ++i;\n            }\n        }\n\n        length = boolAttributes.length;\n        for (uint256 i; i < length; ) {\n            _setBoolAttribute(\n                _msgSender(),\n                collection,\n                tokenId,\n                boolAttributes[i].key,\n                boolAttributes[i].value\n            );\n            unchecked {\n                ++i;\n            }\n        }\n\n        length = addressAttributes.length;\n        for (uint256 i; i < length; ) {\n            _setAddressAttribute(\n                _msgSender(),\n                collection,\n                tokenId,\n                addressAttributes[i].key,\n                addressAttributes[i].value\n            );\n            unchecked {\n                ++i;\n            }\n        }\n\n        length = bytesAttributes.length;\n        for (uint256 i; i < length; ) {\n            _setBytesAttribute(\n                _msgSender(),\n                collection,\n                tokenId,\n                bytesAttributes[i].key,\n                bytesAttributes[i].value\n            );\n            unchecked {\n                ++i;\n            }\n        }\n    }\n\n    function _checkIfMultipleCollectionsAndTokens(\n        address[] memory collections,\n        uint256[] memory tokenIds,\n        uint256 attributesLength\n    )\n        internal\n        pure\n        returns (\n            bool multipleCollections,\n            bool multipleTokens,\n            bool multipleAttributes,\n            uint256 loopLength\n        )\n    {\n        multipleCollections = collections.length != 1;\n        multipleTokens = tokenIds.length != 1;\n        multipleAttributes = attributesLength != 1;\n        if (\n            (multipleCollections &&\n                multipleAttributes &&\n                collections.length != attributesLength) ||\n            (multipleTokens &&\n                multipleAttributes &&\n                tokenIds.length != attributesLength) ||\n            (multipleCollections &&\n                multipleTokens &&\n                collections.length != tokenIds.length)\n        ) {\n            revert LengthsMismatch();\n        }\n\n        if (multipleCollections) {\n            loopLength = collections.length;\n        } else if (multipleTokens) {\n            loopLength = tokenIds.length;\n        } else {\n            loopLength = attributesLength;\n        }\n    }\n\n    function _setBoolAttribute(\n        address caller,\n        address collection,\n        uint256 tokenId,\n        string memory key,\n        bool value\n    ) internal {\n        _onlyAuthorizedCaller(caller, collection, key, tokenId);\n        _boolValues[collection][tokenId][_getIdForKey(key)] = value;\n        emit BoolAttributeUpdated(collection, tokenId, key, value);\n    }\n\n    function _setBytesAttribute(\n        address caller,\n        address collection,\n        uint256 tokenId,\n        string memory key,\n        bytes memory value\n    ) internal {\n        _onlyAuthorizedCaller(caller, collection, key, tokenId);\n        _bytesValues[collection][tokenId][_getIdForKey(key)] = value;\n        emit BytesAttributeUpdated(collection, tokenId, key, value);\n    }\n\n    function _setAddressAttribute(\n        address caller,\n        address collection,\n        uint256 tokenId,\n        string memory key,\n        address value\n    ) internal {\n        _onlyAuthorizedCaller(caller, collection, key, tokenId);\n        _addressValues[collection][tokenId][_getIdForKey(key)] = value;\n        emit AddressAttributeUpdated(collection, tokenId, key, value);\n    }\n\n    function _setStringAttribute(\n        address caller,\n        address collection,\n        uint256 tokenId,\n        string memory key,\n        string memory value\n    ) internal {\n        _onlyAuthorizedCaller(caller, collection, key, tokenId);\n        _stringValues[collection][tokenId][_getIdForKey(key)] = value;\n        emit StringAttributeUpdated(collection, tokenId, key, value);\n    }\n\n    function _setUintAttribute(\n        address caller,\n        address collection,\n        uint256 tokenId,\n        string memory key,\n        uint256 value\n    ) internal {\n        _onlyAuthorizedCaller(caller, collection, key, tokenId);\n        _uintValues[collection][tokenId][_getIdForKey(key)] = value;\n        emit UintAttributeUpdated(collection, tokenId, key, value);\n    }\n\n    /**\n     * @inheritdoc IERC7508Draft\n     */\n    function presignedSetUintAttribute(\n        address setter,\n        address collection,\n        uint256 tokenId,\n        string memory key,\n        uint256 value,\n        uint256 deadline,\n        uint8 v,\n        bytes32 r,\n        bytes32 s\n    ) external {\n        bytes32 digest = keccak256(\n            abi.encodePacked(\n                \"\\x19Ethereum Signed Message:\\n32\",\n                keccak256(\n                    abi.encode(\n                        DOMAIN_SEPARATOR,\n                        SET_UINT_ATTRIBUTE_TYPEHASH,\n                        collection,\n                        tokenId,\n                        key,\n                        value,\n                        deadline\n                    )\n                )\n            )\n        );\n        _checkDeadlineAndSigner(setter, deadline, digest, v, r, s);\n        _setUintAttribute(setter, collection, tokenId, key, value);\n    }\n\n    /**\n     * @inheritdoc IERC7508Draft\n     */\n    function presignedSetStringAttribute(\n        address setter,\n        address collection,\n        uint256 tokenId,\n        string memory key,\n        string memory value,\n        uint256 deadline,\n        uint8 v,\n        bytes32 r,\n        bytes32 s\n    ) external {\n        bytes32 digest = keccak256(\n            abi.encodePacked(\n                \"\\x19Ethereum Signed Message:\\n32\",\n                keccak256(\n                    abi.encode(\n                        DOMAIN_SEPARATOR,\n                        SET_STRING_ATTRIBUTE_TYPEHASH,\n                        collection,\n                        tokenId,\n                        key,\n                        value,\n                        deadline\n                    )\n                )\n            )\n        );\n        _checkDeadlineAndSigner(setter, deadline, digest, v, r, s);\n        _setStringAttribute(setter, collection, tokenId, key, value);\n    }\n\n    /**\n     * @inheritdoc IERC7508Draft\n     */\n    function presignedSetBoolAttribute(\n        address setter,\n        address collection,\n        uint256 tokenId,\n        string memory key,\n        bool value,\n        uint256 deadline,\n        uint8 v,\n        bytes32 r,\n        bytes32 s\n    ) external {\n        bytes32 digest = keccak256(\n            abi.encodePacked(\n                \"\\x19Ethereum Signed Message:\\n32\",\n                keccak256(\n                    abi.encode(\n                        DOMAIN_SEPARATOR,\n                        SET_BOOL_ATTRIBUTE_TYPEHASH,\n                        collection,\n                        tokenId,\n                        key,\n                        value,\n                        deadline\n                    )\n                )\n            )\n        );\n        _checkDeadlineAndSigner(setter, deadline, digest, v, r, s);\n        _setBoolAttribute(setter, collection, tokenId, key, value);\n    }\n\n    /**\n     * @inheritdoc IERC7508Draft\n     */\n    function presignedSetBytesAttribute(\n        address setter,\n        address collection,\n        uint256 tokenId,\n        string memory key,\n        bytes memory value,\n        uint256 deadline,\n        uint8 v,\n        bytes32 r,\n        bytes32 s\n    ) external {\n        bytes32 digest = keccak256(\n            abi.encodePacked(\n                \"\\x19Ethereum Signed Message:\\n32\",\n                keccak256(\n                    abi.encode(\n                        DOMAIN_SEPARATOR,\n                        SET_BYTES_ATTRIBUTE_TYPEHASH,\n                        collection,\n                        tokenId,\n                        key,\n                        value,\n                        deadline\n                    )\n                )\n            )\n        );\n        _checkDeadlineAndSigner(setter, deadline, digest, v, r, s);\n        _setBytesAttribute(setter, collection, tokenId, key, value);\n    }\n\n    /**\n     * @inheritdoc IERC7508Draft\n     */\n    function presignedSetAddressAttribute(\n        address setter,\n        address collection,\n        uint256 tokenId,\n        string memory key,\n        address value,\n        uint256 deadline,\n        uint8 v,\n        bytes32 r,\n        bytes32 s\n    ) external {\n        bytes32 digest = keccak256(\n            abi.encodePacked(\n                \"\\x19Ethereum Signed Message:\\n32\",\n                keccak256(\n                    abi.encode(\n                        DOMAIN_SEPARATOR,\n                        SET_ADDRESS_ATTRIBUTE_TYPEHASH,\n                        collection,\n                        tokenId,\n                        key,\n                        value,\n                        deadline\n                    )\n                )\n            )\n        );\n        _checkDeadlineAndSigner(setter, deadline, digest, v, r, s);\n        _setAddressAttribute(setter, collection, tokenId, key, value);\n    }\n\n    function _checkDeadlineAndSigner(\n        address setter,\n        uint256 deadline,\n        bytes32 digest,\n        uint8 v,\n        bytes32 r,\n        bytes32 s\n    ) internal view {\n        if (block.timestamp > deadline) {\n            revert ExpiredDeadline();\n        }\n        address signer = ecrecover(digest, v, r, s);\n        if (signer != setter) {\n            revert InvalidSignature();\n        }\n    }\n\n    /**\n     * @notice Used to get the Id for a key. If the key does not exist, a new ID is created.\n     *  IDs are shared among all tokens and types\n     * @dev The ID of 0 is not used as it represents the default value.\n     * @param key The attribute key\n     * @return keyID The ID of the key\n     */\n    function _getIdForKey(string memory key) internal returns (uint256 keyID) {\n        if (_keysToIds[key] == 0) {\n            _nextKeyId++;\n            _keysToIds[key] = _nextKeyId;\n            keyID = _nextKeyId;\n        } else {\n            keyID = _keysToIds[key];\n        }\n    }\n\n    /**\n     * @inheritdoc IERC165\n     */\n    function supportsInterface(\n        bytes4 interfaceId\n    ) public view virtual returns (bool) {\n        return\n            interfaceId == type(IERC7508Draft).interfaceId ||\n            interfaceId == type(IERC165).interfaceId;\n    }\n}\n"
      }
    },
    "settings": {
      "evmVersion": "london",
      "optimizer": {
        "enabled": true,
        "runs": 200
      },
      "outputSelection": {
        "*": {
          "*": [
            "evm.bytecode",
            "evm.deployedBytecode",
            "devdoc",
            "userdoc",
            "metadata",
            "abi"
          ]
        }
      },
      "libraries": {}
    }
  }}