ERC-7589: Semi-Fungible Token Roles
Role Management for Semi-Fungible Tokens (SFTs). Enables accounts to share the utility of SFTs via expirable role assignments.
Abstract
This standard introduces role management for SFTs (Semi-Fungible Tokens). Each role assignment is granted to a single
user (grantee) and expires automatically. Roles are defined as bytes32
and feature a custom _data
field of
arbitrary size to allow customization.
Motivation
ERC-1155 has significantly contributed to the tokenization capabilities of Ethereum by enabling developers to create fungible and non-fungible tokens with a single contract. While ERC-1155 excels at tracking ownership, it focuses solely on token balances, overlooking the nuanced aspects of how these tokens can be utilized.
An essential aspect of token utility is access control, which determines who has permission to spend or use these tokens. In some cases, the owner has complete control over its balance. Nevertheless, in many others, the utility can be delegated (or granted) to other users, allowing for more complex use cases to be implemented.
One example is in gaming, in-game assets can be issued with a single ERC-1155 contract and rented out via a secure role management interface.
Specification
The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174.
Compliant contracts MUST implement the following interface:
Single Transaction Extension
Granting roles is a two-step process that requires two transactions. The first is to commit tokens, and the second is to grant the role. This extension allows users to commit tokens and grant a role in one transaction, which is desirable for some use cases.
Role Balance Extension
The core interface allows for querying a token commitment's balance but not for a specific user's balance. To determine the total amount of tokens granted to a user, the implementation needs to sum up all the roles granted to that user while filtering out any expired roles.
This function was included in an optional extension because it's not always necessary and will likely make the implementation much more complex (increasing smart contract risk).
Metadata Extension
The Roles Metadata extension extends the traditional JSON-based metadata schema of SFTs. Therefore, DApps supporting this feature MUST also implement the metadata extension of ERC-1155. This JSON extension is optional and allows developers to provide additional information on roles.
Updated JSON Schema:
The following code snipped is an example of the additional fields described above:
The properties of the roles
array are SUGGESTED, and developers should add any other relevant information for their
use case (e.g., an image representing the role).
It's also important to highlight the significance of the inputs
property. This field describes the parameters that
should be encoded and passed to the grantRole
function, and can include the properties type
and components
to
represent the format of the data. It's RECOMMENDED to use the properties type
and components
as defined on the
Solidity ABI Specification.
Caveats
- Compliant contracts MUST implement the
IERC7589
interface. - Every role is represented by a
bytes32
identifier. It's RECOMMENDED to use the keccak256 hash of the role name and its arguments (if any) as the identifier. E.g.,keccak256("Player(uint256)")
. - The
commitTokens
function MUST revert if the_tokenAmount
is zero or themsg.sender
was not approved by the_grantor
. It MAY be implemented as public or external. - The
grantRole
function MUST revert if the_expirationDate
is in the past or if themsg.sender
is not approved to grant roles on behalf of the grantor. It MAY be implemented as public or external, and it is RECOMMENDED usingtype(uint64).max
for a permanent roles. - The
revokeRole
function SHOULD always allow the grantee to revoke roles and MAY be implemented as public or external, and MUST revert if:- The role assignment is not found (no role was granted).
- The
msg.sender
was not approved by the grantor or the grantee. - The
msg.sender
is the grantor or was approved by the grantor, but the role is not revocable or expired.
- The
releaseTokens
function MAY be implemented as public or external and MUST revert if:- The commitment is not found (no tokens were committed).
- The
msg.sender
is not and was not approved by the grantor. - The commitment has at least one non-revocable role that didn't expire.
- The
setRoleApprovalForAll
function MAY be implemented as public or external. - The
grantorOf
function MAY be implemented as pure or view and MUST return the owner of the committed tokens. - The
tokenAddressOf
function MAY be implemented as pure or view and MUST return the address of the committed tokens. - The
tokenIdOf
function MAY be implemented as pure or view and MUST return the identifier of the committed tokens. - The
tokenAmountOf
function MAY be implemented as pure or view and MUST return the token amount committed. - The
roleData
function MAY be implemented as pure or view and MUST return the custom data of the role assignment. - The
roleExpirationDate
function MAY be implemented as pure or view and MUST return the expiration date of the role assignment. - The
isRoleRevocable
function MAY be implemented as pure or view and MUST return whether the grantor can end the role assignment before its expiration date. - The
isRoleApprovedForAll
function MAY be implemented as pure or view and MUST return whether the_operator
is allowed to grant and revoke roles on behalf of the_grantor
.
Please note that "approval" refers to allowing users to commit tokens and grant/revoke roles on one's behalf. An approved user either received the role approval or is the target user. Role approvals are not to be confused with ERC-1155 approvals. More information can be found in the Role Approvals section.
Rationale
The concept of "token commitments" as an abstraction serves as a powerful tool for users looking to delegate the control of their SFTs. A token commitment represents either a frozen balance or tokens deposited into a contract, offering a standardized and secure way for SFT owners to delegate the use of their assets. Through ERC-7589, users gain a versatile mechanism to abstract the complexities of secure delegation, enhancing the utility and interoperability of semi-fungible tokens.
ERC-7589 IS NOT an extension of ERC-1155. The main reason behind this decision is to keep the standard agnostic of any implementation. This approach enables the standard to be implemented externally or on the same contract as the SFT and allows dApps to use roles with immutable SFTs.
Role Approvals
Like ERC-1155, ERC-7589 allows users to approve operators to grant and revoke roles on
their behalf. This feature is crucial for interoperability, as it enables third-party applications to manage user roles
without custody-level approvals. Role approvals are part of the core interface, and compliant contracts must implement
the setRoleApprovalForAll
and isRoleApprovedForAll
functions.
Automatic Expiration
Automatic expiration is implemented to save users gas. To end a role assignment, instead of requiring users always to
call revokeRole
, applications should call the roleExpirationDate
and compare it to the current timestamp to check if
the role is still valid.
In the context of ERC-7589, dates are represented as uint64
. The maximum UNIX timestamp represented
by a uint64
is about the year 584 billion, which should be enough to be considered "permanent". For this reason, using
type(uint64).max
in an assignment represents that it never expires.
Revocable Roles
In certain scenarios, the grantor might need to revoke a role before its expiration. While in others, the grantee
requires assurance that the role can't be prematurely revoked (e.g. when the grantee pays tokens to utilize them). The
_revocable
parameter was included in the grantRole
function for this exact reason, and it specifies whether the
grantor can revoke the role prior to the expiration date. Regardless of the _revocable
value, the grantee will always
be able to revoke roles, allowing recipients to eliminate undesirable assignments.
Custom Data
The grantRole
function's _data
parameter is critical for the standardization of this EIP. SFTs have different use
cases, and it's impractical to attempt to cover all of them on a solidity-level interface. Therefore, a generic
parameter of type bytes
was incorporated, allowing users to pass any custom information when granting a role.
For example, it's common for web3 games to introduce a profit-share when delegating NFTs to players, which is
represented by a uint256
. Using ERC-7589, one could simply encode the uint256
as bytes and pass it
to thegrantRole
function. Data validation can happen on-chain or off-chain, and other contracts can query this
information using the roleData
function.
Backwards Compatibility
Many SFTs are deployed as immutable contracts, which imposes the following challenge: How can one enable role management
for SFTs that can't be modified? This proposal solves this problem by requiring the tokenAddress
parameter
when committing tokens. This requirement ensures that dApps can either implement ERC-7589 inside the
SFT contract or use a standalone external contract as the authoritative source for the roles of immutable SFTs.
Reference Implementation
See ERC7589.sol
.
Security Considerations
Developers integrating with Semi-Fungible Token Roles should consider the points below on their implementations:
- Ensure proper access control is in place to prevent unauthorized role assignments or revocations. This is especially
important in
commitTokens
andreleaseTokens
, as they might freeze or transfer balances. - Consider potential attack vectors such as reentrancy and ensure appropriate safeguards are in place.
- Always check the expiration date before allowing users to utilize a role assignment.
Copyright
Copyright and related rights waived via CC0.