ERC-5496 Multi-privilege management extension for NFTs (Multi-privilege Management NFT Extension)

Last Call · NFTs & multi-token · created 2022-07-30

An EIP-721 extension that lets a single NFT carry multiple privileges, such as voting rights, coupons, or airdrop claim rights, and allows those privileges to be transferred or duplicated and shared separately while NFT ownership itself remains unchanged.

Why it is needed

EIP-721 only records who owns an NFT and the transfer of that ownership. Benefits attached to an NFT, such as discount coupons, voting rights, or airdrop claim rights, are not recorded on chain. As a result, granting such benefits requires issuing a separate new token, which is not bound to the original NFT. For example, if an airline distributes benefit tokens to attract CryptoPunks holders into its club, and the original NFT is later transferred to someone else, the benefit remains with the previous holder and the new holder does not automatically receive it. Conversely, if an NFT holder has no intention of using a benefit and wants to pass it to someone else, there is no way to separate ownership from the benefit, so the entire NFT must be transferred. This standard proposes an interface that binds privileges to an NFT while still letting the holder manage and transfer each privilege independently.

What to implement

In depth

Required Interface IERC5496

Contracts following this standard must implement IERC5496, while this extension itself is optional from the perspective of an EIP-721 contract. setPrivilege designates user as the holder of the privilegeId privilege for tokenId and makes it valid until expires. It must fail if msg.sender is not the owner or an approved address for that token, and the dev comment states that expires must be shorter than 30 days. privilegeExpires returns the expiration timestamp, and hasPrivilege returns whether the holder possesses it. A PrivilegeAssigned event must be emitted when setPrivilege is called, and supportsInterface must return true for 0x076e1bbb. The expiration time is stored as a timestamp of type uint256.

Privilege Count Cap and PrivilegeTotalChanged

Implementing contracts are recommended to set a maximum privilege count before setting privileges, and privilegeId must not exceed this maximum. A PrivilegeTotalChanged event must be emitted whenever the total privilege count of the collection changes. In the reference implementation, this is managed with the privilegeTotal variable and the internal function _setPrivilegeTotal, and setPrivilege checks privId < privilegeTotal.

Shareable Privileges and the Cloneable Extension IERC721Cloneable

If privileges cannot be shared, the number of privilege holders is tied to the number of NFTs. Shareable privileges work not by the original holder transferring their privilege but by copying it and giving it to someone else, which greatly expands the reach of both the privilege and the NFT. The optional extension IERC721Cloneable defines a function clonePrivilege(tokenId, privId, referrer) for this purpose, and a PrivilegeCloned event must be emitted when it is called. Contracts following the spec are also recommended to use EIP-1271 for signature verification.

Referrer Design

The referrer argument of clonePrivilege records the path along which the privilege spread. In the original example, a pizza shop offers a 30% discount coupon and encourages customers to share the coupon with friends; if Tom receives the coupon from the shop and shares it with Alice, Tom becomes Alice's referrer. In some cases Tom can receive additional rewards from the shop, which helps the merchant spread promotion among consumers.

NFT Transfer and unwrap Protection

Transferring NFT ownership to another user does not affect the privilege. However, when trying to extract the original EIP-721 token from a wrapped NFT via unwrap(), an error can occur if a still-valid privilege remains. For this reason, the original proposal's code checks the last expiration time of the privilege in order to protect the privilege holder's rights. unwrap requires that the current block timestamp be at least privilegeBook[tokenId].lastExpiresAt and that the caller be the owner. If these checks pass, the token is burned, the original NFT is returned, and an Unwrap event is emitted.

Behavior of the reference implementation

In the reference implementation, hasPrivilege checks whether the caller is the recorded holder if the privilege's expiresAt is after the current time, and treats the NFT owner as the privilege holder if it has expired. setPrivilege can only be called by the owner, an approved address, or the privilege holder themselves or a delegator they have designated, and expires must be less than block.timestamp + 30 days. expiresAt is updated only when the call comes from the owner side, and lastExpiresAt is raised together with it. setDelegator can turn a delegator on or off. This extension only adds functions and data structures to EIP-721 without touching the original standard, so it is compatible with any NFT that follows EIP-721.

Things to watch out for

Original ERC · ← ERCs