Skip to content

Commit

Permalink
Update EIP-7432: Several enhancements
Browse files Browse the repository at this point in the history
Merged by EIP-Bot.
  • Loading branch information
karacurt authored Oct 11, 2023
1 parent bb1ca48 commit b08f4e4
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 289 deletions.
207 changes: 76 additions & 131 deletions EIPS/eip-7432.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,23 @@ 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 0x25be10b2.
/// Note: the ERC-165 identifier for this interface is 0x04984ac8.
interface IERC7432 /* is ERC165 */ {
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 **/
Expand Down Expand Up @@ -90,69 +105,15 @@ interface IERC7432 /* is ERC165 */ {
bool _isApproved
);
/// @notice Emitted when a user is approved to manage the roles of an NFT on behalf of another user.
/// @param _tokenAddress The token address.
/// @param _tokenId The token identifier.
/// @param _operator The user approved to grant and revoke roles.
/// @param _isApproved The approval status.
event RoleApproval(
address indexed _tokenAddress,
uint256 indexed _tokenId,
address _operator,
bool _isApproved
);
/** 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;
/// @notice Revokes a role from a user.
/// @param _role The role identifier.
/// @param _tokenAddress The token address.
/// @param _tokenId The token identifier.
/// @param _grantee The user that receives the role revocation.
function revokeRole(
bytes32 _role,
address _tokenAddress,
uint256 _tokenId,
address _grantee
) external;
/// @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 _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 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,
address _tokenAddress,
uint256 _tokenId,
address _grantor,
address _grantee,
uint64 _expirationDate,
bool _revocable,
bytes calldata _data
) external;
/// @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.
Expand All @@ -178,18 +139,6 @@ interface IERC7432 /* is ERC165 */ {
bool _approved
) external;
/// @notice Approves operator to grant and revoke roles of an NFT on behalf of another user.
/// @param _tokenAddress The token address.
/// @param _tokenId The token identifier.
/// @param _operator The user approved to grant and revoke roles.
/// @param _approved The approval status.
function approveRole(
address _tokenAddress,
uint256 _tokenId,
address _operator,
bool _approved
) external;
/** View Functions **/
/// @notice Checks if a user has a role.
Expand All @@ -198,12 +147,12 @@ interface IERC7432 /* is ERC165 */ {
/// @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
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.
Expand All @@ -212,12 +161,12 @@ interface IERC7432 /* is ERC165 */ {
/// @param _tokenId The token identifier.
/// @param _grantor The user that assigned the role.
/// @param _grantee The user that received the role.
function hasUniqueRole(
bytes32 _role,
address _tokenAddress,
uint256 _tokenId,
address _grantor,
address _grantee
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.
Expand All @@ -227,12 +176,12 @@ interface IERC7432 /* is ERC165 */ {
/// @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 (bytes memory data_);
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.
Expand All @@ -241,35 +190,34 @@ interface IERC7432 /* is ERC165 */ {
/// @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
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
address _tokenAddress,
address _grantor,
address _operator
) external view returns (bool);
/// @notice Checks if the grantor approved the operator for a specific NFT.
/// @notice Returns the last user to receive a role.
/// @param _role The role identifier.
/// @param _tokenAddress The token address.
/// @param _tokenId The token identifier.
/// @param _grantor The user that approved the operator.
/// @param _operator The user approved to grant and revoke roles.
function getApprovedRole(
/// @param _grantor The user that granted the role.
function lastGrantee(
bytes32 _role,
address _tokenAddress,
uint256 _tokenId,
address _grantor,
address _operator
) external view returns (bool);
address _grantor
) external view returns (address);
}
```

Expand Down Expand Up @@ -376,7 +324,7 @@ The following JSON is an example of [ERC-7432](./eip-7432.md) Metadata:

The `roles` array properties are SUGGESTED, and developers should add any other relevant information as necessary (e.g.,
an image for the role). However, it's highly RECOMMENDED to include the `isUniqueRole` property, as this field is
used to determine if the `hasRole` or `hasUniqueRole` function should be called (refer to
used to determine if the `hasRole` or `hasNonUniqueRole` function should be called (refer to
[Unique and Non-Unique Roles](#unique-and-non-unique-roles)).

It's also important to highlight the importance of the `inputs` property. This field describes the parameters that
Expand All @@ -389,34 +337,30 @@ for complex tuple types.
* Compliant contracts MUST implement the `IERC7432` interface.
* A role is represented by a `bytes32`, and it's RECOMMENDED to use the `keccak256` of the role's name for this purpose:
`bytes32 role = keccak256("ROLE_NAME")`.
* The `grantRole` function MUST revert if the `_expirationDate` is in the past, and MAY be implemented as `public` or
`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 `grantRoleFrom` and `grantRevocableRoleFrom` functions 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` 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`.
* The `hasRole` function MAY be implemented as `pure` or `view` and SHOULD only confirm if the role assignment exists
and is not expired (supports simultaneous role assignments).
* The `hasUniqueRole` function MAY be implemented as `pure` or `view` and SHOULD check if the assignment exists, is not
* The `hasNonUniqueRole` function MAY be implemented as `pure` or `view` and SHOULD only confirm if the role assignment
exists and is not expired.
* The `hasRole` function MAY be implemented as `pure` or `view` and SHOULD check if the assignment exists, is not
expired and is the last one granted (does not support simultaneous role assignments).
* The `roleData` function MAY be implemented as `pure` or `view`, and SHOULD return an empty array of bytes if the role
* The `roleData` function MAY be implemented as `pure` or `view`, and SHOULD return an empty struct if the role
assignment does not exist.
* The `roleExpirationDate` function MAY be implemented as `pure` or `view`, and SHOULD return zero if the role
assignment does not exist.
* The `isRoleApprovedForAll` function MAY be implemented as `pure` or `view` and SHOULD only return `true` if the
`_operator` is approved to grant and revoke roles for all NFTs on behalf of the `_grantor`.
* The `getApprovedRole` function MAY be implemented as `pure` or `view` and SHOULD only return `true` if the `_operator`
is approved to grant and revoke roles for the specified NFT on behalf of the `_grantor`.
* Compliant contracts SHOULD support [ERC-165](./eip-165.md).

## Rationale

[ERC-7432](./eip-7432.md) IS NOT an extension of [ERC-721](./eip-721.md) or [ERC-1155](./eip-1155.md). The main reason
behind this decision is to keep the standard agnostic of any NFT implementation. This approach also enables the standard
to be implemented externally or on the same contract as the NFT, and allow dApps to use roles with immutable NFTs.
[ERC-7432](./eip-7432.md) IS NOT an extension of [ERC-721](./eip-721.md). The main reason behind this decision is to
keep the standard agnostic of any NFT implementation. This approach also enables the standard to be implemented
externally or on the same contract as the NFT, and allow dApps to use roles with immutable NFTs.

### Automatic Expiration

Expand All @@ -440,10 +384,9 @@ allowing recipients to eliminate undesirable assignments.

The standard supports both unique and non-unique roles. Unique roles can be assigned to only one account at a time,
while non-unique roles can be granted to multiple accounts simultaneously. The roles extension metadata can be used to
specify if roles are unique or not, and if the contract does not implement it, all roles should be considered
non-unique.
specify if roles are unique or not, and if the contract does not implement it, all roles should be considered unique.

To verify the validity of a unique role, dApps SHOULD use the `hasUniqueRole` function, which also checks if no other
To verify the validity of a unique role, dApps SHOULD use the `hasRole` function, which also checks if no other
assignment was granted afterward. In other words, for unique roles, each new assignment invalidates the previous one,
and only the last one can be valid.

Expand All @@ -457,16 +400,17 @@ developers can integrate this information into their applications, both on-chain
### Role Approval

Similar to [ERC-721](./eip-721.md), this standard enables users to approve other accounts to grant and revoke roles on
its behalf. Users can authorize an account to manage a single NFT or any NFTs in a collection. This functionality was
introduced to allow third-party contracts to interact with ERC-7432 without requiring ownership of the NFTs.
its behalf. This functionality was introduced to allow third-parties to interact with ERC-7432 without requiring NFT
ownership. Compliant contracts MUST implement the functions `setRoleApprovalForAll` and `isRoleApprovedForAll` to
deliver this feature.

## Backwards Compatibility

On all functions and events, the standard requires both the `tokenAddress` and `tokenId` to be provided. This
requirement enables dApps to use a standalone [ERC-7432](./eip-7432.md) contract as the authoritative source for the
roles of immutable NFTs. It also helps with backward compatibility as NFT-specific functions such as `ownerOf` and
`balanceOf` aren't required. Consequently, this design ensures a more straightforward integration with different
implementations of NFTs.
roles of immutable NFTs. It also helps with backward compatibility as NFT-specific functions such as `ownerOf` are no
longer required. Consequently, this design ensures a more straightforward integration with different implementations of
NFTs.

## Reference Implementation

Expand All @@ -480,8 +424,9 @@ Developers integrating the Non-Fungible Token Roles interface should consider th
* Take into account potential attack vectors such as reentrancy and ensure appropriate safeguards are in place.
* Since this standard does not check NFT ownership, it's the responsibility of the dApp to query for the NFT Owner and
pass the correct `_grantor` to the `hasRole` function.
* It's the responsibility of the dApp to check if the role is unique or non-unique. To ensure the role was not assigned
to another account when unique, `hasUniqueRole` should be called instead of `hasRole`.
* It's the responsibility of the dApp to confirm if the role is unique or non-unique. When the role is unique, the dApp
SHOULD use the `hasRole` function to check for the validity of the assignment. `hasNonUniqueRole` SHOULD be used when
the role can be granted to multiple accounts simultaneously (hence, non-unique).

## Copyright

Expand Down
Loading

0 comments on commit b08f4e4

Please sign in to comment.