- Contract name:
- RolesRegistry
- Optimization enabled
- false
- Compiler version
- v0.8.9+commit.e5eed63a
- Verified at
- 2023-11-13T12:56:33.200118Z
contracts/RolesRegistry.sol
// SPDX-License-Identifier: CC0-1.0 pragma solidity 0.8.9; import { IERC7432 } from "./interfaces/IERC7432.sol"; import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import { ERC165Checker } from "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol"; contract RolesRegistry is IERC7432 { // grantee => tokenAddress => tokenId => role => struct(expirationDate, data) mapping(address => mapping(address => mapping(uint256 => mapping(bytes32 => RoleData)))) public roleAssignments; // tokenAddress => tokenId => role => grantee mapping(address => mapping(uint256 => mapping(bytes32 => address))) public latestGrantees; // grantor => tokenAddress => operator => isApproved mapping(address => mapping(address => mapping(address => bool))) public tokenApprovals; modifier validExpirationDate(uint64 _expirationDate) { require(_expirationDate > block.timestamp, "RolesRegistry: expiration date must be in the future"); _; } modifier onlyOwnerOrApproved( address _tokenAddress, uint256 _tokenId, address _account ) { address _tokenOwner = IERC721(_tokenAddress).ownerOf(_tokenId); require( msg.sender == _tokenOwner || (isRoleApprovedForAll(_tokenAddress, _account, msg.sender) && _account == _tokenOwner), "RolesRegistry: sender must be token owner or approved" ); _; } modifier isTokenOwner( address _tokenAddress, uint256 _tokenId, address _account ) { require(_account == IERC721(_tokenAddress).ownerOf(_tokenId), "RolesRegistry: account must be token owner"); _; } function grantRoleFrom( RoleAssignment calldata _roleAssignment ) external override onlyOwnerOrApproved(_roleAssignment.tokenAddress, _roleAssignment.tokenId, _roleAssignment.grantor) { _grantRole(_roleAssignment, false); } function grantRevocableRoleFrom( RoleAssignment calldata _roleAssignment ) external override onlyOwnerOrApproved(_roleAssignment.tokenAddress, _roleAssignment.tokenId, _roleAssignment.grantor) { _grantRole(_roleAssignment, true); } function _grantRole( RoleAssignment calldata _roleAssignment, bool _revocable ) internal validExpirationDate(_roleAssignment.expirationDate) { address _lastGrantee = latestGrantees[_roleAssignment.tokenAddress][_roleAssignment.tokenId][ _roleAssignment.role ]; RoleData memory _roleData = roleAssignments[_lastGrantee][_roleAssignment.tokenAddress][ _roleAssignment.tokenId ][_roleAssignment.role]; bool _hasActiveAssignment = _roleData.expirationDate > block.timestamp; if (_hasActiveAssignment) { // only unique roles can be revocable require(_roleData.revocable, "RolesRegistry: role is not revocable"); } roleAssignments[_roleAssignment.grantee][_roleAssignment.tokenAddress][_roleAssignment.tokenId][ _roleAssignment.role ] = RoleData(_roleAssignment.expirationDate, _revocable, _roleAssignment.data); latestGrantees[_roleAssignment.tokenAddress][_roleAssignment.tokenId][_roleAssignment.role] = _roleAssignment .grantee; emit RoleGranted( _roleAssignment.role, _roleAssignment.tokenAddress, _roleAssignment.tokenId, _roleAssignment.grantor, _roleAssignment.grantee, _roleAssignment.expirationDate, _revocable, _roleAssignment.data ); } function revokeRoleFrom( bytes32 _role, address _tokenAddress, uint256 _tokenId, address _revoker, address _grantee ) external override isTokenOwner(_tokenAddress, _tokenId, _revoker) { address _caller = msg.sender == _revoker || msg.sender == _grantee ? msg.sender : _getApprovedCaller(_tokenAddress, _revoker, _grantee); _revokeRole(_role, _tokenAddress, _tokenId, _revoker, _grantee, _caller); } function _getApprovedCaller( address _tokenAddress, address _revoker, address _grantee ) internal view returns (address) { if (isRoleApprovedForAll(_tokenAddress, _grantee, msg.sender)) { return _grantee; } else if (isRoleApprovedForAll(_tokenAddress, _revoker, msg.sender)) { return _revoker; } else { revert("RolesRegistry: sender must be approved"); } } function _revokeRole( bytes32 _role, address _tokenAddress, uint256 _tokenId, address _revoker, address _grantee, address _caller ) internal { require( _caller == _grantee || roleAssignments[_grantee][_tokenAddress][_tokenId][_role].revocable, "RolesRegistry: Role is not revocable or caller is not the grantee" ); delete roleAssignments[_grantee][_tokenAddress][_tokenId][_role]; delete latestGrantees[_tokenAddress][_tokenId][_role]; emit RoleRevoked(_role, _tokenAddress, _tokenId, _revoker, _grantee); } function hasNonUniqueRole( bytes32 _role, address _tokenAddress, uint256 _tokenId, address _grantor, // not used, but needed for compatibility with ERC7432 address _grantee ) external view returns (bool) { return roleAssignments[_grantee][_tokenAddress][_tokenId][_role].expirationDate > block.timestamp; } function hasRole( bytes32 _role, address _tokenAddress, uint256 _tokenId, address _grantor, // not used, but needed for compatibility with ERC7432 address _grantee ) external view returns (bool) { return latestGrantees[_tokenAddress][_tokenId][_role] == _grantee && roleAssignments[_grantee][_tokenAddress][_tokenId][_role].expirationDate > block.timestamp; } function roleData( bytes32 _role, address _tokenAddress, uint256 _tokenId, address _grantor, // not used, but needed for compatibility with ERC7432 address _grantee ) external view returns (RoleData memory) { return roleAssignments[_grantee][_tokenAddress][_tokenId][_role]; } function roleExpirationDate( bytes32 _role, address _tokenAddress, uint256 _tokenId, address _grantor, // not used, but needed for compatibility with ERC7432 address _grantee ) external view returns (uint64 expirationDate_) { return roleAssignments[_grantee][_tokenAddress][_tokenId][_role].expirationDate; } function supportsInterface(bytes4 interfaceId) external view virtual override returns (bool) { return interfaceId == type(IERC7432).interfaceId; } function setRoleApprovalForAll(address _tokenAddress, address _operator, bool _isApproved) external override { tokenApprovals[msg.sender][_tokenAddress][_operator] = _isApproved; emit RoleApprovalForAll(_tokenAddress, _operator, _isApproved); } function isRoleApprovedForAll( address _tokenAddress, address _grantor, address _operator ) public view override returns (bool) { return tokenApprovals[_grantor][_tokenAddress][_operator]; } function lastGrantee( bytes32 _role, address _tokenAddress, uint256 _tokenId, address _grantor // not used, but needed for compatibility with ERC7432 ) public view override returns (address) { return latestGrantees[_tokenAddress][_tokenId][_role]; } }
@openzeppelin/contracts/token/ERC721/IERC721.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC721/IERC721.sol) pragma solidity ^0.8.0; import "../../utils/introspection/IERC165.sol"; /** * @dev Required interface of an ERC721 compliant contract. */ interface IERC721 is IERC165 { /** * @dev Emitted when `tokenId` token is transferred from `from` to `to`. */ event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); /** * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token. */ event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); /** * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets. */ event ApprovalForAll(address indexed owner, address indexed operator, bool approved); /** * @dev Returns the number of tokens in ``owner``'s account. */ function balanceOf(address owner) external view returns (uint256 balance); /** * @dev Returns the owner of the `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function ownerOf(uint256 tokenId) external view returns (address owner); /** * @dev Safely transfers `tokenId` token from `from` to `to`. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external; /** * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients * are aware of the ERC721 protocol to prevent tokens from being forever locked. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function safeTransferFrom(address from, address to, uint256 tokenId) external; /** * @dev Transfers `tokenId` token from `from` to `to`. * * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721 * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must * understand this adds an external call which potentially creates a reentrancy vulnerability. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * * Emits a {Transfer} event. */ function transferFrom(address from, address to, uint256 tokenId) external; /** * @dev Gives permission to `to` to transfer `tokenId` token to another account. * The approval is cleared when the token is transferred. * * Only a single account can be approved at a time, so approving the zero address clears previous approvals. * * Requirements: * * - The caller must own the token or be an approved operator. * - `tokenId` must exist. * * Emits an {Approval} event. */ function approve(address to, uint256 tokenId) external; /** * @dev Approve or remove `operator` as an operator for the caller. * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller. * * Requirements: * * - The `operator` cannot be the caller. * * Emits an {ApprovalForAll} event. */ function setApprovalForAll(address operator, bool approved) external; /** * @dev Returns the account approved for `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function getApproved(uint256 tokenId) external view returns (address operator); /** * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`. * * See {setApprovalForAll} */ function isApprovedForAll(address owner, address operator) external view returns (bool); }
@openzeppelin/contracts/utils/introspection/ERC165Checker.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/introspection/ERC165Checker.sol) pragma solidity ^0.8.0; import "./IERC165.sol"; /** * @dev Library used to query support of an interface declared via {IERC165}. * * Note that these functions return the actual result of the query: they do not * `revert` if an interface is not supported. It is up to the caller to decide * what to do in these cases. */ library ERC165Checker { // As per the EIP-165 spec, no interface should ever match 0xffffffff bytes4 private constant _INTERFACE_ID_INVALID = 0xffffffff; /** * @dev Returns true if `account` supports the {IERC165} interface. */ function supportsERC165(address account) internal view returns (bool) { // Any contract that implements ERC165 must explicitly indicate support of // InterfaceId_ERC165 and explicitly indicate non-support of InterfaceId_Invalid return supportsERC165InterfaceUnchecked(account, type(IERC165).interfaceId) && !supportsERC165InterfaceUnchecked(account, _INTERFACE_ID_INVALID); } /** * @dev Returns true if `account` supports the interface defined by * `interfaceId`. Support for {IERC165} itself is queried automatically. * * See {IERC165-supportsInterface}. */ function supportsInterface(address account, bytes4 interfaceId) internal view returns (bool) { // query support of both ERC165 as per the spec and support of _interfaceId return supportsERC165(account) && supportsERC165InterfaceUnchecked(account, interfaceId); } /** * @dev Returns a boolean array where each value corresponds to the * interfaces passed in and whether they're supported or not. This allows * you to batch check interfaces for a contract where your expectation * is that some interfaces may not be supported. * * See {IERC165-supportsInterface}. * * _Available since v3.4._ */ function getSupportedInterfaces( address account, bytes4[] memory interfaceIds ) internal view returns (bool[] memory) { // an array of booleans corresponding to interfaceIds and whether they're supported or not bool[] memory interfaceIdsSupported = new bool[](interfaceIds.length); // query support of ERC165 itself if (supportsERC165(account)) { // query support of each interface in interfaceIds for (uint256 i = 0; i < interfaceIds.length; i++) { interfaceIdsSupported[i] = supportsERC165InterfaceUnchecked(account, interfaceIds[i]); } } return interfaceIdsSupported; } /** * @dev Returns true if `account` supports all the interfaces defined in * `interfaceIds`. Support for {IERC165} itself is queried automatically. * * Batch-querying can lead to gas savings by skipping repeated checks for * {IERC165} support. * * See {IERC165-supportsInterface}. */ function supportsAllInterfaces(address account, bytes4[] memory interfaceIds) internal view returns (bool) { // query support of ERC165 itself if (!supportsERC165(account)) { return false; } // query support of each interface in interfaceIds for (uint256 i = 0; i < interfaceIds.length; i++) { if (!supportsERC165InterfaceUnchecked(account, interfaceIds[i])) { return false; } } // all interfaces supported return true; } /** * @notice Query if a contract implements an interface, does not check ERC165 support * @param account The address of the contract to query for support of an interface * @param interfaceId The interface identifier, as specified in ERC-165 * @return true if the contract at account indicates support of the interface with * identifier interfaceId, false otherwise * @dev Assumes that account contains a contract that supports ERC165, otherwise * the behavior of this method is undefined. This precondition can be checked * with {supportsERC165}. * * Some precompiled contracts will falsely indicate support for a given interface, so caution * should be exercised when using this function. * * Interface identification is specified in ERC-165. */ function supportsERC165InterfaceUnchecked(address account, bytes4 interfaceId) internal view returns (bool) { // prepare call bytes memory encodedParams = abi.encodeWithSelector(IERC165.supportsInterface.selector, interfaceId); // perform static call bool success; uint256 returnSize; uint256 returnValue; assembly { success := staticcall(30000, account, add(encodedParams, 0x20), mload(encodedParams), 0x00, 0x20) returnSize := returndatasize() returnValue := mload(0x00) } return success && returnSize >= 0x20 && returnValue > 0; } }
@openzeppelin/contracts/utils/introspection/IERC165.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }
contracts/interfaces/IERC7432.sol
// SPDX-License-Identifier: CC0-1.0 pragma solidity 0.8.9; import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; /// @title ERC-7432 Non-Fungible Token Roles /// @dev See https://eips.ethereum.org/EIPS/eip-7432 /// Note: the ERC-165 identifier for this interface is 0x04984ac8. interface IERC7432 is IERC165 { struct RoleData { uint64 expirationDate; bool revocable; bytes data; } struct RoleAssignment { bytes32 role; address tokenAddress; uint256 tokenId; address grantor; address grantee; uint64 expirationDate; bytes data; } /** Events **/ /// @notice Emitted when a role is granted. /// @param _role The role identifier. /// @param _tokenAddress The token address. /// @param _tokenId The token identifier. /// @param _grantor The user assigning the role. /// @param _grantee The user receiving the role. /// @param _expirationDate The expiration date of the role. /// @param _revocable Whether the role is revocable or not. /// @param _data Any additional data about the role. event RoleGranted( bytes32 indexed _role, address indexed _tokenAddress, uint256 indexed _tokenId, address _grantor, address _grantee, uint64 _expirationDate, bool _revocable, bytes _data ); /// @notice Emitted when a role is revoked. /// @param _role The role identifier. /// @param _tokenAddress The token address. /// @param _tokenId The token identifier. /// @param _revoker The user revoking the role. /// @param _grantee The user that receives the role revocation. event RoleRevoked( bytes32 indexed _role, address indexed _tokenAddress, uint256 indexed _tokenId, address _revoker, address _grantee ); /// @notice Emitted when a user is approved to manage any role on behalf of another user. /// @param _tokenAddress The token address. /// @param _operator The user approved to grant and revoke roles. /// @param _isApproved The approval status. event RoleApprovalForAll( address indexed _tokenAddress, address indexed _operator, bool _isApproved ); /** External Functions **/ /// @notice Grants a role on behalf of a user. /// @param _roleAssignment The role assignment data. function grantRoleFrom(RoleAssignment calldata _roleAssignment) external; /// @notice Grants a role on behalf of a user. /// @param _roleAssignment The role assignment data. function grantRevocableRoleFrom(RoleAssignment calldata _roleAssignment) external; /// @notice Revokes a role on behalf of a user. /// @param _role The role identifier. /// @param _tokenAddress The token address. /// @param _tokenId The token identifier. /// @param _revoker The user revoking the role. /// @param _grantee The user that receives the role revocation. function revokeRoleFrom( bytes32 _role, address _tokenAddress, uint256 _tokenId, address _revoker, address _grantee ) external; /// @notice Approves operator to grant and revoke any roles on behalf of another user. /// @param _tokenAddress The token address. /// @param _operator The user approved to grant and revoke roles. /// @param _approved The approval status. function setRoleApprovalForAll( address _tokenAddress, address _operator, bool _approved ) external; /** View Functions **/ /// @notice Checks if a user has a role. /// @param _role The role identifier. /// @param _tokenAddress The token address. /// @param _tokenId The token identifier. /// @param _grantor The user that assigned the role. /// @param _grantee The user that received the role. function hasNonUniqueRole( bytes32 _role, address _tokenAddress, uint256 _tokenId, address _grantor, address _grantee ) external view returns (bool); /// @notice Checks if a user has a unique role. /// @param _role The role identifier. /// @param _tokenAddress The token address. /// @param _tokenId The token identifier. /// @param _grantor The user that assigned the role. /// @param _grantee The user that received the role. function hasRole( bytes32 _role, address _tokenAddress, uint256 _tokenId, address _grantor, address _grantee ) external view returns (bool); /// @notice Returns the custom data of a role assignment. /// @param _role The role identifier. /// @param _tokenAddress The token address. /// @param _tokenId The token identifier. /// @param _grantor The user that assigned the role. /// @param _grantee The user that received the role. function roleData( bytes32 _role, address _tokenAddress, uint256 _tokenId, address _grantor, address _grantee ) external view returns (RoleData memory data_); /// @notice Returns the expiration date of a role assignment. /// @param _role The role identifier. /// @param _tokenAddress The token address. /// @param _tokenId The token identifier. /// @param _grantor The user that assigned the role. /// @param _grantee The user that received the role. function roleExpirationDate( bytes32 _role, address _tokenAddress, uint256 _tokenId, address _grantor, address _grantee ) external view returns (uint64 expirationDate_); /// @notice Checks if the grantor approved the operator for all NFTs. /// @param _tokenAddress The token address. /// @param _grantor The user that approved the operator. /// @param _operator The user that can grant and revoke roles. function isRoleApprovedForAll( address _tokenAddress, address _grantor, address _operator ) external view returns (bool); /// @notice Returns the last grantee of a role. /// @param _role The role. /// @param _tokenAddress The token address. /// @param _tokenId The token ID. /// @param _grantor The user that granted the role. function lastGrantee( bytes32 _role, address _tokenAddress, uint256 _tokenId, address _grantor ) external view returns (address); }
Contract ABI
[{"type":"event","name":"RoleApprovalForAll","inputs":[{"type":"address","name":"_tokenAddress","internalType":"address","indexed":true},{"type":"address","name":"_operator","internalType":"address","indexed":true},{"type":"bool","name":"_isApproved","internalType":"bool","indexed":false}],"anonymous":false},{"type":"event","name":"RoleGranted","inputs":[{"type":"bytes32","name":"_role","internalType":"bytes32","indexed":true},{"type":"address","name":"_tokenAddress","internalType":"address","indexed":true},{"type":"uint256","name":"_tokenId","internalType":"uint256","indexed":true},{"type":"address","name":"_grantor","internalType":"address","indexed":false},{"type":"address","name":"_grantee","internalType":"address","indexed":false},{"type":"uint64","name":"_expirationDate","internalType":"uint64","indexed":false},{"type":"bool","name":"_revocable","internalType":"bool","indexed":false},{"type":"bytes","name":"_data","internalType":"bytes","indexed":false}],"anonymous":false},{"type":"event","name":"RoleRevoked","inputs":[{"type":"bytes32","name":"_role","internalType":"bytes32","indexed":true},{"type":"address","name":"_tokenAddress","internalType":"address","indexed":true},{"type":"uint256","name":"_tokenId","internalType":"uint256","indexed":true},{"type":"address","name":"_revoker","internalType":"address","indexed":false},{"type":"address","name":"_grantee","internalType":"address","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"grantRevocableRoleFrom","inputs":[{"type":"tuple","name":"_roleAssignment","internalType":"struct IERC7432.RoleAssignment","components":[{"type":"bytes32","name":"role","internalType":"bytes32"},{"type":"address","name":"tokenAddress","internalType":"address"},{"type":"uint256","name":"tokenId","internalType":"uint256"},{"type":"address","name":"grantor","internalType":"address"},{"type":"address","name":"grantee","internalType":"address"},{"type":"uint64","name":"expirationDate","internalType":"uint64"},{"type":"bytes","name":"data","internalType":"bytes"}]}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"grantRoleFrom","inputs":[{"type":"tuple","name":"_roleAssignment","internalType":"struct IERC7432.RoleAssignment","components":[{"type":"bytes32","name":"role","internalType":"bytes32"},{"type":"address","name":"tokenAddress","internalType":"address"},{"type":"uint256","name":"tokenId","internalType":"uint256"},{"type":"address","name":"grantor","internalType":"address"},{"type":"address","name":"grantee","internalType":"address"},{"type":"uint64","name":"expirationDate","internalType":"uint64"},{"type":"bytes","name":"data","internalType":"bytes"}]}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"hasNonUniqueRole","inputs":[{"type":"bytes32","name":"_role","internalType":"bytes32"},{"type":"address","name":"_tokenAddress","internalType":"address"},{"type":"uint256","name":"_tokenId","internalType":"uint256"},{"type":"address","name":"_grantor","internalType":"address"},{"type":"address","name":"_grantee","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"hasRole","inputs":[{"type":"bytes32","name":"_role","internalType":"bytes32"},{"type":"address","name":"_tokenAddress","internalType":"address"},{"type":"uint256","name":"_tokenId","internalType":"uint256"},{"type":"address","name":"_grantor","internalType":"address"},{"type":"address","name":"_grantee","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isRoleApprovedForAll","inputs":[{"type":"address","name":"_tokenAddress","internalType":"address"},{"type":"address","name":"_grantor","internalType":"address"},{"type":"address","name":"_operator","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"lastGrantee","inputs":[{"type":"bytes32","name":"_role","internalType":"bytes32"},{"type":"address","name":"_tokenAddress","internalType":"address"},{"type":"uint256","name":"_tokenId","internalType":"uint256"},{"type":"address","name":"_grantor","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"latestGrantees","inputs":[{"type":"address","name":"","internalType":"address"},{"type":"uint256","name":"","internalType":"uint256"},{"type":"bytes32","name":"","internalType":"bytes32"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"revokeRoleFrom","inputs":[{"type":"bytes32","name":"_role","internalType":"bytes32"},{"type":"address","name":"_tokenAddress","internalType":"address"},{"type":"uint256","name":"_tokenId","internalType":"uint256"},{"type":"address","name":"_revoker","internalType":"address"},{"type":"address","name":"_grantee","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint64","name":"expirationDate","internalType":"uint64"},{"type":"bool","name":"revocable","internalType":"bool"},{"type":"bytes","name":"data","internalType":"bytes"}],"name":"roleAssignments","inputs":[{"type":"address","name":"","internalType":"address"},{"type":"address","name":"","internalType":"address"},{"type":"uint256","name":"","internalType":"uint256"},{"type":"bytes32","name":"","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple","name":"","internalType":"struct IERC7432.RoleData","components":[{"type":"uint64","name":"expirationDate","internalType":"uint64"},{"type":"bool","name":"revocable","internalType":"bool"},{"type":"bytes","name":"data","internalType":"bytes"}]}],"name":"roleData","inputs":[{"type":"bytes32","name":"_role","internalType":"bytes32"},{"type":"address","name":"_tokenAddress","internalType":"address"},{"type":"uint256","name":"_tokenId","internalType":"uint256"},{"type":"address","name":"_grantor","internalType":"address"},{"type":"address","name":"_grantee","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint64","name":"expirationDate_","internalType":"uint64"}],"name":"roleExpirationDate","inputs":[{"type":"bytes32","name":"_role","internalType":"bytes32"},{"type":"address","name":"_tokenAddress","internalType":"address"},{"type":"uint256","name":"_tokenId","internalType":"uint256"},{"type":"address","name":"_grantor","internalType":"address"},{"type":"address","name":"_grantee","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setRoleApprovalForAll","inputs":[{"type":"address","name":"_tokenAddress","internalType":"address"},{"type":"address","name":"_operator","internalType":"address"},{"type":"bool","name":"_isApproved","internalType":"bool"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"supportsInterface","inputs":[{"type":"bytes4","name":"interfaceId","internalType":"bytes4"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"tokenApprovals","inputs":[{"type":"address","name":"","internalType":"address"},{"type":"address","name":"","internalType":"address"},{"type":"address","name":"","internalType":"address"}]}]
Deployed ByteCode
0x608060405234801561001057600080fd5b50600436106100ea5760003560e01c806353f8a7ce1161008c578063a34adf0a11610066578063a34adf0a146102a7578063a9c39982146102c3578063d5ced376146102df578063fe78cf281461030f576100ea565b806353f8a7ce1461022b5780638767c67e1461025b5780639760a9e11461028b576100ea565b806310c10939116100c857806310c109391461017f5780631e3337c6146101af57806344c722cf146101df5780634ea538a2146101fb576100ea565b806301ffc9a7146100ef578063040b0cb51461011f5780630f1df0071461014f575b600080fd5b61010960048036038101906101049190611c27565b610341565b6040516101169190611c6f565b60405180910390f35b61013960048036038101906101349190611d54565b6103ab565b6040516101469190611c6f565b60405180910390f35b61016960048036038101906101649190611d54565b610479565b6040516101769190611df2565b60405180910390f35b61019960048036038101906101949190611e0d565b61053b565b6040516101a69190611c6f565b60405180910390f35b6101c960048036038101906101c49190611d54565b610577565b6040516101d69190611f67565b60405180910390f35b6101f960048036038101906101f49190611fad565b610711565b005b61021560048036038101906102109190611ff6565b610892565b604051610222919061206c565b60405180910390f35b61024560048036038101906102409190611d54565b610920565b6040516102529190611c6f565b60405180910390f35b61027560048036038101906102709190612087565b610aa6565b604051610282919061206c565b60405180910390f35b6102a560048036038101906102a09190611fad565b610af5565b005b6102c160048036038101906102bc9190612106565b610c76565b005b6102dd60048036038101906102d89190611d54565b610db1565b005b6102f960048036038101906102f49190611e0d565b610f42565b6040516103069190611c6f565b60405180910390f35b61032960048036038101906103249190612159565b611014565b6040516103389392919061220a565b60405180910390f35b60007f04984ac8000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916149050919050565b6000426000808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000868152602001908152602001600020600088815260200190815260200160002060000160009054906101000a900467ffffffffffffffff1667ffffffffffffffff1611905095945050505050565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000858152602001908152602001600020600087815260200190815260200160002060000160009054906101000a900467ffffffffffffffff16905095945050505050565b6002602052826000526040600020602052816000526040600020602052806000526040600020600092509250509054906101000a900460ff1681565b61057f611ab5565b6000808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600085815260200190815260200160002060008781526020019081526020016000206040518060600160405290816000820160009054906101000a900467ffffffffffffffff1667ffffffffffffffff1667ffffffffffffffff1681526020016000820160089054906101000a900460ff1615151515815260200160018201805461068490612277565b80601f01602080910402602001604051908101604052809291908181526020018280546106b090612277565b80156106fd5780601f106106d2576101008083540402835291602001916106fd565b820191906000526020600020905b8154815290600101906020018083116106e057829003601f168201915b505050505081525050905095945050505050565b80602001602081019061072491906122a9565b816040013582606001602081019061073c91906122a9565b60008373ffffffffffffffffffffffffffffffffffffffff16636352211e846040518263ffffffff1660e01b815260040161077791906122e5565b60206040518083038186803b15801561078f57600080fd5b505afa1580156107a3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107c79190612315565b90508073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614806108415750610809848333610f42565b801561084057508073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16145b5b610880576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610877906123c5565b60405180910390fd5b61088b85600161110e565b5050505050565b6000600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000848152602001908152602001600020600086815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050949350505050565b60008173ffffffffffffffffffffffffffffffffffffffff16600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000868152602001908152602001600020600088815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16148015610a9b5750426000808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000868152602001908152602001600020600088815260200190815260200160002060000160009054906101000a900467ffffffffffffffff1667ffffffffffffffff16115b905095945050505050565b6001602052826000526040600020602052816000526040600020602052806000526040600020600092509250509054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b806020016020810190610b0891906122a9565b8160400135826060016020810190610b2091906122a9565b60008373ffffffffffffffffffffffffffffffffffffffff16636352211e846040518263ffffffff1660e01b8152600401610b5b91906122e5565b60206040518083038186803b158015610b7357600080fd5b505afa158015610b87573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bab9190612315565b90508073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161480610c255750610bed848333610f42565b8015610c2457508073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16145b5b610c64576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610c5b906123c5565b60405180910390fd5b610c6f85600061110e565b5050505050565b80600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fa9f861543e61f98894ecc9e3edeb6ca82ac424611eb0d8943a84bb89a2eb1d0b83604051610da49190611c6f565b60405180910390a3505050565b8383838273ffffffffffffffffffffffffffffffffffffffff16636352211e836040518263ffffffff1660e01b8152600401610ded91906122e5565b60206040518083038186803b158015610e0557600080fd5b505afa158015610e19573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e3d9190612315565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614610eaa576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610ea190612457565b60405180910390fd5b60008573ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161480610f1157508473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16145b610f2557610f20888787611761565b610f27565b335b9050610f378989898989866117d5565b505050505050505050565b6000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1690509392505050565b60006020528360005260406000206020528260005260406000206020528160005260406000206020528060005260406000206000935093505050508060000160009054906101000a900467ffffffffffffffff16908060000160089054906101000a900460ff169080600101805461108b90612277565b80601f01602080910402602001604051908101604052809291908181526020018280546110b790612277565b80156111045780601f106110d957610100808354040283529160200191611104565b820191906000526020600020905b8154815290600101906020018083116110e757829003601f168201915b5050505050905083565b8160a001602081019061112191906124a3565b428167ffffffffffffffff161161116d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161116490612542565b60405180910390fd5b60006001600085602001602081019061118691906122a9565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008560400135815260200190815260200160002060008560000135815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905060008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600086602001602081019061126291906122a9565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000866040013581526020019081526020016000206000866000013581526020019081526020016000206040518060600160405290816000820160009054906101000a900467ffffffffffffffff1667ffffffffffffffff1667ffffffffffffffff1681526020016000820160089054906101000a900460ff1615151515815260200160018201805461132e90612277565b80601f016020809104026020016040519081016040528092919081815260200182805461135a90612277565b80156113a75780601f1061137c576101008083540402835291602001916113a7565b820191906000526020600020905b81548152906001019060200180831161138a57829003601f168201915b5050505050815250509050600042826000015167ffffffffffffffff161190508015611412578160200151611411576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611408906125d4565b60405180910390fd5b5b60405180606001604052808760a001602081019061143091906124a3565b67ffffffffffffffff1681526020018615158152602001878060c001906114579190612603565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050508152506000808860800160208101906114b391906122a9565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600088602001602081019061150291906122a9565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008860400135815260200190815260200160002060008860000135815260200190815260200160002060008201518160000160006101000a81548167ffffffffffffffff021916908367ffffffffffffffff16021790555060208201518160000160086101000a81548160ff02191690831515021790555060408201518160010190805190602001906115d1929190611ae2565b509050508560800160208101906115e891906122a9565b600160008860200160208101906115ff91906122a9565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008860400135815260200190815260200160002060008860000135815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555085604001358660200160208101906116b991906122a9565b73ffffffffffffffffffffffffffffffffffffffff1687600001357f87ce9b16c986be2f6c60151d2337ecfee6fd8067d3e3d81d3276cbab55139eab89606001602081019061170891906122a9565b8a608001602081019061171b91906122a9565b8b60a001602081019061172e91906124a3565b8b8d8060c0019061173f9190612603565b604051611751969594939291906126a2565b60405180910390a4505050505050565b600061176e848333610f42565b1561177b578190506117ce565b611786848433610f42565b15611793578290506117ce565b6040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016117c590612770565b60405180910390fd5b9392505050565b8173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614806118b957506000808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000858152602001908152602001600020600087815260200190815260200160002060000160089054906101000a900460ff165b6118f8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016118ef90612828565b60405180910390fd5b6000808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008581526020019081526020016000206000878152602001908152602001600020600080820160006101000a81549067ffffffffffffffff02191690556000820160086101000a81549060ff02191690556001820160006119d59190611b68565b5050600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000858152602001908152602001600020600087815260200190815260200160002060006101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055838573ffffffffffffffffffffffffffffffffffffffff16877fd0fed9028dcedb984cc57c4ddcc971990f107dbff315e3c92db61f2927a3e2b98686604051611aa5929190612848565b60405180910390a4505050505050565b6040518060600160405280600067ffffffffffffffff168152602001600015158152602001606081525090565b828054611aee90612277565b90600052602060002090601f016020900481019282611b105760008555611b57565b82601f10611b2957805160ff1916838001178555611b57565b82800160010185558215611b57579182015b82811115611b56578251825591602001919060010190611b3b565b5b509050611b649190611ba8565b5090565b508054611b7490612277565b6000825580601f10611b865750611ba5565b601f016020900490600052602060002090810190611ba49190611ba8565b5b50565b5b80821115611bc1576000816000905550600101611ba9565b5090565b600080fd5b600080fd5b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b611c0481611bcf565b8114611c0f57600080fd5b50565b600081359050611c2181611bfb565b92915050565b600060208284031215611c3d57611c3c611bc5565b5b6000611c4b84828501611c12565b91505092915050565b60008115159050919050565b611c6981611c54565b82525050565b6000602082019050611c846000830184611c60565b92915050565b6000819050919050565b611c9d81611c8a565b8114611ca857600080fd5b50565b600081359050611cba81611c94565b92915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000611ceb82611cc0565b9050919050565b611cfb81611ce0565b8114611d0657600080fd5b50565b600081359050611d1881611cf2565b92915050565b6000819050919050565b611d3181611d1e565b8114611d3c57600080fd5b50565b600081359050611d4e81611d28565b92915050565b600080600080600060a08688031215611d7057611d6f611bc5565b5b6000611d7e88828901611cab565b9550506020611d8f88828901611d09565b9450506040611da088828901611d3f565b9350506060611db188828901611d09565b9250506080611dc288828901611d09565b9150509295509295909350565b600067ffffffffffffffff82169050919050565b611dec81611dcf565b82525050565b6000602082019050611e076000830184611de3565b92915050565b600080600060608486031215611e2657611e25611bc5565b5b6000611e3486828701611d09565b9350506020611e4586828701611d09565b9250506040611e5686828701611d09565b9150509250925092565b611e6981611dcf565b82525050565b611e7881611c54565b82525050565b600081519050919050565b600082825260208201905092915050565b60005b83811015611eb8578082015181840152602081019050611e9d565b83811115611ec7576000848401525b50505050565b6000601f19601f8301169050919050565b6000611ee982611e7e565b611ef38185611e89565b9350611f03818560208601611e9a565b611f0c81611ecd565b840191505092915050565b6000606083016000830151611f2f6000860182611e60565b506020830151611f426020860182611e6f565b5060408301518482036040860152611f5a8282611ede565b9150508091505092915050565b60006020820190508181036000830152611f818184611f17565b905092915050565b600080fd5b600060e08284031215611fa457611fa3611f89565b5b81905092915050565b600060208284031215611fc357611fc2611bc5565b5b600082013567ffffffffffffffff811115611fe157611fe0611bca565b5b611fed84828501611f8e565b91505092915050565b600080600080608085870312156120105761200f611bc5565b5b600061201e87828801611cab565b945050602061202f87828801611d09565b935050604061204087828801611d3f565b925050606061205187828801611d09565b91505092959194509250565b61206681611ce0565b82525050565b6000602082019050612081600083018461205d565b92915050565b6000806000606084860312156120a05761209f611bc5565b5b60006120ae86828701611d09565b93505060206120bf86828701611d3f565b92505060406120d086828701611cab565b9150509250925092565b6120e381611c54565b81146120ee57600080fd5b50565b600081359050612100816120da565b92915050565b60008060006060848603121561211f5761211e611bc5565b5b600061212d86828701611d09565b935050602061213e86828701611d09565b925050604061214f868287016120f1565b9150509250925092565b6000806000806080858703121561217357612172611bc5565b5b600061218187828801611d09565b945050602061219287828801611d09565b93505060406121a387828801611d3f565b92505060606121b487828801611cab565b91505092959194509250565b600082825260208201905092915050565b60006121dc82611e7e565b6121e681856121c0565b93506121f6818560208601611e9a565b6121ff81611ecd565b840191505092915050565b600060608201905061221f6000830186611de3565b61222c6020830185611c60565b818103604083015261223e81846121d1565b9050949350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168061228f57607f821691505b602082108114156122a3576122a2612248565b5b50919050565b6000602082840312156122bf576122be611bc5565b5b60006122cd84828501611d09565b91505092915050565b6122df81611d1e565b82525050565b60006020820190506122fa60008301846122d6565b92915050565b60008151905061230f81611cf2565b92915050565b60006020828403121561232b5761232a611bc5565b5b600061233984828501612300565b91505092915050565b600082825260208201905092915050565b7f526f6c657352656769737472793a2073656e646572206d75737420626520746f60008201527f6b656e206f776e6572206f7220617070726f7665640000000000000000000000602082015250565b60006123af603583612342565b91506123ba82612353565b604082019050919050565b600060208201905081810360008301526123de816123a2565b9050919050565b7f526f6c657352656769737472793a206163636f756e74206d757374206265207460008201527f6f6b656e206f776e657200000000000000000000000000000000000000000000602082015250565b6000612441602a83612342565b915061244c826123e5565b604082019050919050565b6000602082019050818103600083015261247081612434565b9050919050565b61248081611dcf565b811461248b57600080fd5b50565b60008135905061249d81612477565b92915050565b6000602082840312156124b9576124b8611bc5565b5b60006124c78482850161248e565b91505092915050565b7f526f6c657352656769737472793a2065787069726174696f6e2064617465206d60008201527f75737420626520696e2074686520667574757265000000000000000000000000602082015250565b600061252c603483612342565b9150612537826124d0565b604082019050919050565b6000602082019050818103600083015261255b8161251f565b9050919050565b7f526f6c657352656769737472793a20726f6c65206973206e6f74207265766f6360008201527f61626c6500000000000000000000000000000000000000000000000000000000602082015250565b60006125be602483612342565b91506125c982612562565b604082019050919050565b600060208201905081810360008301526125ed816125b1565b9050919050565b600080fd5b600080fd5b600080fd5b600080833560016020038436030381126126205761261f6125f4565b5b80840192508235915067ffffffffffffffff821115612642576126416125f9565b5b60208301925060018202360383131561265e5761265d6125fe565b5b509250929050565b82818337600083830152505050565b600061268183856121c0565b935061268e838584612666565b61269783611ecd565b840190509392505050565b600060a0820190506126b7600083018961205d565b6126c4602083018861205d565b6126d16040830187611de3565b6126de6060830186611c60565b81810360808301526126f1818486612675565b9050979650505050505050565b7f526f6c657352656769737472793a2073656e646572206d75737420626520617060008201527f70726f7665640000000000000000000000000000000000000000000000000000602082015250565b600061275a602683612342565b9150612765826126fe565b604082019050919050565b600060208201905081810360008301526127898161274d565b9050919050565b7f526f6c657352656769737472793a20526f6c65206973206e6f74207265766f6360008201527f61626c65206f722063616c6c6572206973206e6f7420746865206772616e746560208201527f6500000000000000000000000000000000000000000000000000000000000000604082015250565b6000612812604183612342565b915061281d82612790565b606082019050919050565b6000602082019050818103600083015261284181612805565b9050919050565b600060408201905061285d600083018561205d565b61286a602083018461205d565b939250505056fea2646970667358221220c0ab9a543e48541beeea6483689fdec915032425a82dc477ddbc163b2437655264736f6c63430008090033