Skip to content

Commit

Permalink
Update EIP-7432: Move to Review
Browse files Browse the repository at this point in the history
Merged by EIP-Bot.
  • Loading branch information
karacurt committed Sep 20, 2023
1 parent 61397f6 commit f015879
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 32 deletions.
36 changes: 25 additions & 11 deletions EIPS/eip-7432.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ title: Non-Fungible Token Roles
description: Role Management for NFTs. Enables accounts to share the utility of NFTs via expirable role assignments.
author: Ernani São Thiago (@ernanirst), Daniel Lima (@karacurt)
discussions-to: https://ethereum-magicians.org/t/eip-7432-non-fungible-token-roles/15298
status: Last Call
last-call-deadline: 2023-09-26
status: Review
type: Standards Track
category: ERC
created: 2023-07-14
Expand Down Expand Up @@ -42,9 +41,9 @@ Compliant contracts MUST implement the following interface:
```solidity
/// @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 0xd7e151ef.
/// Note: the ERC-165 identifier for this interface is 0x25be10b2.
interface IERC7432 /* is ERC165 */ {
/** Events **/
/// @notice Emitted when a role is granted.
Expand All @@ -54,6 +53,7 @@ interface IERC7432 /* is ERC165 */ {
/// @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,
Expand All @@ -62,6 +62,7 @@ interface IERC7432 /* is ERC165 */ {
address _grantor,
address _grantee,
uint64 _expirationDate,
bool _revocable,
bytes _data
);
Expand Down Expand Up @@ -101,21 +102,23 @@ interface IERC7432 /* is ERC165 */ {
bool _isApproved
);
/** External Functions **/
/** External Functions **/
/// @notice Grants a role to a user.
/// @param _role The role identifier.
/// @param _tokenAddress The token address.
/// @param _tokenId The token identifier.
/// @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.
function grantRole(
bytes32 _role,
address _tokenAddress,
uint256 _tokenId,
address _grantee,
uint64 _expirationDate,
bool _revocable,
bytes calldata _data
) external;
Expand All @@ -138,6 +141,7 @@ interface IERC7432 /* is ERC165 */ {
/// @param _grantor The user assigning the role.
/// @param _grantee The user that receives 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.
function grantRoleFrom(
bytes32 _role,
Expand All @@ -146,6 +150,7 @@ interface IERC7432 /* is ERC165 */ {
address _grantor,
address _grantee,
uint64 _expirationDate,
bool _revocable,
bytes calldata _data
) external;
Expand Down Expand Up @@ -200,7 +205,7 @@ interface IERC7432 /* is ERC165 */ {
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.
Expand All @@ -214,7 +219,7 @@ interface IERC7432 /* is ERC165 */ {
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.
Expand All @@ -228,7 +233,7 @@ interface IERC7432 /* is ERC165 */ {
address _grantor,
address _grantee
) external view returns (bytes memory data_);
/// @notice Returns the expiration date of a role assignment.
/// @param _role The role identifier.
/// @param _tokenAddress The token address.
Expand Down Expand Up @@ -388,8 +393,8 @@ for complex tuple types.
`external`.
* The `grantRoleFrom` function MUST revert if the `_expirationDate` is in the past or if the `msg.sender` is not approved
to grant roles on behalf of the `_grantor`. It MAY be implemented as `public` or `external`.
* The `revokeRole` function MAY be implemented as `public` or `external`.
* The `revokeRoleFrom` function MUST revert if the `msg.sender` is not approved to revoke roles on behalf of the `_revoker`.
* The `revokeRole` and `revokeRoleFrom` functions SHOULD revert if `_revocable` is `false`. They MAY be implemented as `public` or `external`.
* The `revokeRoleFrom` function MUST revert if the `msg.sender` is not approved to revoke roles on behalf of the `_revoker` or the `_grantee`.
It MAY be implemented as `public` or `external`.
* The `setRoleApprovalForAll` function MAY be implemented as `public` or `external`.
* The `approveRole` function MAY be implemented as `public` or `external`.
Expand Down Expand Up @@ -420,7 +425,16 @@ the expiration date, and `hasRole` checks if the role is expired by comparing wi
(`block.timestamp`). Since `uint256` is not natively supported by most programming languages, dates are represented as
`uint64` on this standard. The maximum UNIX timestamp represented by a `uint64` is about the year `584,942,417,355`,
which should be enough to be considered "permanent". For this reason, it's RECOMMENDED using `type(uint64).max` when
calling the `grantRole` function to support use cases that require an assignment never to expire.
calling the `grantRole` function to support use cases that require an assignment never to expire.

### Revocable Roles

In certain scenarios, the grantor may need to revoke a role before its expiration date, while in others, the grantee
requires assurance that the grantor cannot revoke the role. The `_revocable` parameter was introduced to the `grantRole`
function to support both use cases and specify whether the `grantor` can revoke assigned roles.

Regardless of the value of `_revocable`, it's RECOMMENDED always to enable the `grantee` to revoke received roles,
allowing recipients to eliminate undesirable assignments.

### Unique and Non-Unique Roles

Expand Down
57 changes: 40 additions & 17 deletions assets/eip-7432/ERC7432.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ pragma solidity 0.8.9;
import { IERC7432 } from "./interfaces/IERC7432.sol";

contract ERC7432 is IERC7432 {

// grantor => grantee => tokenAddress => tokenId => role => struct(expirationDate, data)
mapping(address => mapping(address => mapping(address => mapping(uint256 => mapping(bytes32 => RoleData)))))
public roleAssignments;
Expand All @@ -16,18 +15,17 @@ contract ERC7432 is IERC7432 {
// grantor => tokenAddress => tokenId => operator => isApproved
mapping(address => mapping(address => mapping(uint256 => mapping(address => bool)))) public tokenIdApprovals;

// grantor => operator => tokenAddress => isApproved
// grantor => tokenAddress => operator => isApproved
mapping(address => mapping(address => mapping(address => bool))) public tokenApprovals;

modifier validExpirationDate(uint64 _expirationDate) {
require(_expirationDate > block.timestamp, "ERC7432: expiration date must be in the future");
_;
}

modifier onlyApproved(address _tokenAddress, uint256 _tokenId, address _grantor) {
modifier onlyApproved(address _tokenAddress, uint256 _tokenId, address _account) {
require(
isRoleApprovedForAll(_tokenAddress, _grantor, msg.sender) ||
getApprovedRole(_tokenAddress, _tokenId, _grantor, msg.sender),
_isRoleApproved(_tokenAddress, _tokenId, _account, msg.sender),
"ERC7432: sender must be approved"
);
_;
Expand All @@ -39,9 +37,10 @@ contract ERC7432 is IERC7432 {
uint256 _tokenId,
address _grantee,
uint64 _expirationDate,
bool _revocable,
bytes calldata _data
) external {
_grantRole(_role, _tokenAddress, _tokenId, msg.sender, _grantee, _expirationDate, _data);
_grantRole(_role, _tokenAddress, _tokenId, msg.sender, _grantee, _expirationDate, _revocable, _data);
}

function grantRoleFrom(
Expand All @@ -51,9 +50,10 @@ contract ERC7432 is IERC7432 {
address _grantor,
address _grantee,
uint64 _expirationDate,
bool _revocable,
bytes calldata _data
) external override onlyApproved(_tokenAddress, _tokenId, _grantor) {
_grantRole(_role, _tokenAddress, _tokenId, _grantor, _grantee, _expirationDate, _data);
_grantRole(_role, _tokenAddress, _tokenId, _grantor, _grantee, _expirationDate, _revocable, _data);
}

function _grantRole(
Expand All @@ -63,15 +63,16 @@ contract ERC7432 is IERC7432 {
address _grantor,
address _grantee,
uint64 _expirationDate,
bool _revocable,
bytes calldata _data
) internal validExpirationDate(_expirationDate) {
roleAssignments[_grantor][_grantee][_tokenAddress][_tokenId][_role] = RoleData(_expirationDate, _data);
roleAssignments[_grantor][_grantee][_tokenAddress][_tokenId][_role] = RoleData(_expirationDate, _revocable, _data);
latestGrantees[_grantor][_tokenAddress][_tokenId][_role] = _grantee;
emit RoleGranted( _role, _tokenAddress, _tokenId, _grantor, _grantee, _expirationDate, _data);
emit RoleGranted(_role, _tokenAddress, _tokenId, _grantor, _grantee, _expirationDate, _revocable, _data);
}

function revokeRole(bytes32 _role, address _tokenAddress, uint256 _tokenId, address _grantee) external {
_revokeRole(_role, _tokenAddress, _tokenId, msg.sender, _grantee);
_revokeRole(_role, _tokenAddress, _tokenId, msg.sender, _grantee, msg.sender);
}

function revokeRoleFrom(
Expand All @@ -80,17 +81,31 @@ contract ERC7432 is IERC7432 {
uint256 _tokenId,
address _revoker,
address _grantee
) external override onlyApproved(_tokenAddress, _tokenId, _revoker) {
_revokeRole(_role, _tokenAddress, _tokenId, _revoker, _grantee);
) external override {
address _caller = _getApprovedCaller(_tokenAddress, _tokenId, _revoker, _grantee);
_revokeRole(_role, _tokenAddress, _tokenId, _revoker, _grantee, _caller);
}

function _getApprovedCaller(address _tokenAddress, uint256 _tokenId, address _revoker, address _grantee) internal view returns (address) {
if (_isRoleApproved(_tokenAddress, _tokenId, _grantee, msg.sender)) {
return _grantee;
} else if(_isRoleApproved(_tokenAddress, _tokenId, _revoker, msg.sender)){
return _revoker;
} else {
revert("ERC7432: sender must be approved");
}
}

function _revokeRole(
bytes32 _role,
address _tokenAddress,
uint256 _tokenId,
address _revoker,
address _grantee
address _grantee,
address _caller
) internal {
bool _isRevocable = roleAssignments[_revoker][_grantee][_tokenAddress][_tokenId][_role].revocable;
require(_isRevocable || _caller == _grantee, "ERC7432: Role is not revocable or caller is not the grantee");
delete roleAssignments[_revoker][_grantee][_tokenAddress][_tokenId][_role];
delete latestGrantees[_revoker][_tokenAddress][_tokenId][_role];
emit RoleRevoked(_role, _tokenAddress, _tokenId, _revoker, _grantee);
Expand All @@ -113,8 +128,7 @@ contract ERC7432 is IERC7432 {
address _grantor,
address _grantee
) external view returns (bool) {
return latestGrantees[_grantor][_tokenAddress][_tokenId][_role] == _grantee && roleAssignments[_grantor][_grantee][_tokenAddress][_tokenId][_role].expirationDate >
block.timestamp;
return latestGrantees[_grantor][_tokenAddress][_tokenId][_role] == _grantee && roleAssignments[_grantor][_grantee][_tokenAddress][_tokenId][_role].expirationDate > block.timestamp;
}

function roleData(
Expand All @@ -128,13 +142,13 @@ contract ERC7432 is IERC7432 {
return (_roleData.data);
}

function roleExpirationDate(
function roleExpirationDate(
bytes32 _role,
address _tokenAddress,
uint256 _tokenId,
address _grantor,
address _grantee
) external view returns (uint64 expirationDate_){
) external view returns (uint64 expirationDate_) {
RoleData memory _roleData = roleAssignments[_grantor][_grantee][_tokenAddress][_tokenId][_role];
return (_roleData.expirationDate);
}
Expand Down Expand Up @@ -178,4 +192,13 @@ contract ERC7432 is IERC7432 {
) public view override returns (bool) {
return tokenIdApprovals[_grantor][_tokenAddress][_tokenId][_operator];
}

function _isRoleApproved(
address _tokenAddress,
uint256 _tokenId,
address _grantor,
address _operator
) internal view returns (bool) {
return isRoleApprovedForAll(_tokenAddress, _grantor, _operator) || getApprovedRole(_tokenAddress, _tokenId, _grantor, _operator);
}
}
13 changes: 9 additions & 4 deletions assets/eip-7432/interfaces/IERC7432.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ 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 0xd7e151ef.
/// Note: the ERC-165 identifier for this interface is 0x25be10b2.
interface IERC7432 is IERC165 {
struct RoleData {
uint64 expirationDate;
bool revocable;
bytes data;
}

Expand All @@ -23,6 +23,7 @@ interface IERC7432 is IERC165 {
/// @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,
Expand All @@ -31,6 +32,7 @@ interface IERC7432 is IERC165 {
address _grantor,
address _grantee,
uint64 _expirationDate,
bool _revocable,
bytes _data
);

Expand Down Expand Up @@ -78,13 +80,15 @@ interface IERC7432 is IERC165 {
/// @param _tokenId The token identifier.
/// @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.
function grantRole(
bytes32 _role,
address _tokenAddress,
uint256 _tokenId,
address _grantee,
uint64 _expirationDate,
bool _revocable,
bytes calldata _data
) external;

Expand All @@ -107,6 +111,7 @@ interface IERC7432 is IERC165 {
/// @param _grantor The user assigning the role.
/// @param _grantee The user that receives 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.
function grantRoleFrom(
bytes32 _role,
Expand All @@ -115,6 +120,7 @@ interface IERC7432 is IERC165 {
address _grantor,
address _grantee,
uint64 _expirationDate,
bool _revocable,
bytes calldata _data
) external;

Expand Down Expand Up @@ -233,5 +239,4 @@ interface IERC7432 is IERC165 {
address _grantor,
address _operator
) external view returns (bool);

}
}

0 comments on commit f015879

Please sign in to comment.