diff --git a/contracts/RolesRegistry.sol b/contracts/RolesRegistry.sol index 3d13ab5..116373b 100644 --- a/contracts/RolesRegistry.sol +++ b/contracts/RolesRegistry.sol @@ -2,11 +2,11 @@ pragma solidity 0.8.9; -import { IERC7432 } from './interfaces/IERC7432.sol'; +import { IRolesRegistryCustodial } from './interfaces/IRolesRegistryCustodial.sol'; import { IERC721 } from '@openzeppelin/contracts/token/ERC721/IERC721.sol'; import { ERC165Checker } from '@openzeppelin/contracts/utils/introspection/ERC165Checker.sol'; -contract RolesRegistry is IERC7432 { +contract RolesRegistryCustodial is IRolesRegistryCustodial { // grantee => tokenAddress => tokenId => role => struct(expirationDate, data) mapping(address => mapping(address => mapping(uint256 => mapping(bytes32 => RoleData)))) public roleAssignments; @@ -16,6 +16,9 @@ contract RolesRegistry is IERC7432 { // grantor => tokenAddress => operator => isApproved mapping(address => mapping(address => mapping(address => bool))) public tokenApprovals; + // tokenAddress => tokenId => owner + mapping(address => mapping(uint256 => address)) public tokenOwners; + modifier validExpirationDate(uint64 _expirationDate) { require(_expirationDate > block.timestamp, 'RolesRegistry: expiration date must be in the future'); _; @@ -44,24 +47,14 @@ contract RolesRegistry is IERC7432 { _; } - function grantRoleFrom( + function grantRole( 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); + _grantRole(_roleAssignment, _roleAssignment.revocable); } function _grantRole( @@ -97,9 +90,24 @@ contract RolesRegistry is IERC7432 { _revocable, _roleAssignment.data ); + + // Just an example of transfer logic when granting roles + // that would be a separated helper function + address _tokenOwner = tokenOwners[_roleAssignment.tokenAddress][_roleAssignment.tokenId]; + if (_tokenOwner != address(0)) { + require(_tokenOwner == _roleAssignment.grantor, 'RolesRegistry: grantor must be token owner'); + } else { + IERC721(_roleAssignment.tokenAddress).transferFrom( + _roleAssignment.grantor, + address(this), + _roleAssignment.tokenId + ); + tokenOwners[_roleAssignment.tokenAddress][_roleAssignment.tokenId] = _roleAssignment.grantor; + emit Deposit(_roleAssignment.grantor, _roleAssignment.tokenAddress, _roleAssignment.tokenId); + } } - function revokeRoleFrom( + function revokeRole( bytes32 _role, address _tokenAddress, uint256 _tokenId, @@ -143,6 +151,16 @@ contract RolesRegistry is IERC7432 { emit RoleRevoked(_role, _tokenAddress, _tokenId, _revoker, _grantee); } + function withdraw( + address _tokenAddress, + uint256 _tokenId + ) external onlyOwnerOrApproved(_tokenAddress, _tokenId, msg.sender) { + address _tokenOwner = tokenOwners[_tokenAddress][_tokenId]; + IERC721(_tokenAddress).transferFrom(address(this), _tokenOwner, _tokenId); + delete tokenOwners[_tokenAddress][_tokenId]; + emit Withdraw(_tokenOwner, _tokenAddress, _tokenId); + } + function hasNonUniqueRole( bytes32 _role, address _tokenAddress, @@ -171,8 +189,8 @@ contract RolesRegistry is IERC7432 { 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]; + ) external view returns (bytes memory) { + return roleAssignments[_grantee][_tokenAddress][_tokenId][_role].data; } function roleExpirationDate( @@ -186,7 +204,7 @@ contract RolesRegistry is IERC7432 { } function supportsInterface(bytes4 interfaceId) external view virtual override returns (bool) { - return interfaceId == type(IERC7432).interfaceId; + return interfaceId == type(IRolesRegistryCustodial).interfaceId; } function setRoleApprovalForAll(address _tokenAddress, address _operator, bool _isApproved) external override { @@ -202,6 +220,20 @@ contract RolesRegistry is IERC7432 { return tokenApprovals[_grantor][_tokenAddress][_operator]; } + function isRoleRevocable( + bytes32 _role, + address _tokenAddress, + uint256 _tokenId, + address _grantor, + address _grantee + ) public view override returns (bool) { + return roleAssignments[_grantee][_tokenAddress][_tokenId][_role].revocable; + } + + function ownerOf(address _tokenAddress, uint256 _tokenId) external view override returns (address) { + return tokenOwners[_tokenAddress][_tokenId]; + } + function lastGrantee( bytes32 _role, address _tokenAddress, diff --git a/contracts/interfaces/IRolesRegistryCustodial.sol b/contracts/interfaces/IRolesRegistryCustodial.sol index 27a4ae8..eb7fc29 100644 --- a/contracts/interfaces/IRolesRegistryCustodial.sol +++ b/contracts/interfaces/IRolesRegistryCustodial.sol @@ -4,7 +4,7 @@ pragma solidity 0.8.9; import { IERC7432 } from './IERC7432.sol'; -interface IRolesRegistryCustodial { +interface IRolesRegistryCustodial is IERC7432 { /// @notice Emitted when tokens are deposited. /// @param _owner The owner of the NFTs.