ERC-721 NFT Authorization
The ERC-5585 proposal suggests a standard method for artists to designate original and limited-edition prints with signatures on the Ethereum blockchain using ERC-721 compliant contracts. This helps create a more tangible link between NFTs, digital art, owners, and artists. The proposal also includes a user record struct to store authorized user rights and allows for seamless integration with third-party applications. The standard is backwards compatible with ERC-721 and includes security considerations for authorization fees. If a buyer uses ERC-20 tokens to purchase an NFT, they must first call the approve function of the ERC-20 token to grant the NFT contract access to a certain amount of tokens.
Video
Original
Abstract
This EIP separates the ERC-721 NFT's commercial usage rights from its ownership to allow for the independent management of those rights.
Motivation
Most NFTs have a simplified ownership verification mechanism, with a sole owner of an NFT. Under this model, other rights, such as display, or creating derivative works or distribution, are not possible to grant, limiting the value and commercialization of NFTs. Therefore, the separation of an NFT's ownership and user rights can enhance its commercial value.
Commercial right is a broad concept based on the copyright, including the rights of copy, display, distribution, renting, commercial use, modify, reproduce and sublicense etc. With the development of the Metaverse, NFTs are becoming more diverse, with new use cases such as digital collections, virtual real estate, music, art, social media, and digital asset of all kinds. The copyright and authorization based on NFTs are becoming a potential business form.
Specification
The keywords “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY” and “OPTIONAL” in this document are to be interpreted as described in RFC 2119.
Contract Interface
interface IERC5585 { struct UserRecord { address user; string[] rights; uint256 expires; } /// @notice Get all available rights of this NFT project /// @return All the rights that can be authorized to the user function getRights() external view returns(string[]); /// @notice NFT holder authorizes all the rights of the NFT to a user for a specified period of time /// @dev The zero address indicates there is no user /// @param tokenId The NFT which is authorized /// @param user The user to whom the NFT is authorized /// @param duration The period of time the authorization lasts function authorizeUser(uint256 tokenId, address user, uint duration) external; /// @notice NFT holder authorizes specific rights to a user for a specified period of time /// @dev The zero address indicates there is no user. It will throw exception when the rights are not defined by this NFT project /// @param tokenId The NFT which is authorized /// @param user The user to whom the NFT is authorized /// @param rights Rights authorized to the user, such as renting, distribution or display etc /// @param duration The period of time the authorization lasts function authorizeUser(uint256 tokenId, address user, string[] rights, uint duration) external; /// @notice The user of the NFT transfers his rights to the new user /// @dev The zero address indicates there is no user /// @param tokenId The rights of this NFT is transferred to the new user /// @param newUser The new user function transferUserRights(uint256 tokenId, address newUser) external; /// @notice NFT holder extends the duration of authorization /// @dev The zero address indicates there is no user. It will throw exception when the rights are not defined by this NFT project /// @param tokenId The NFT which has been authorized /// @param user The user to whom the NFT has been authorized /// @param duration The new duration of the authorization function extendDuration(uint256 tokenId, address user, uint duration) external; /// @notice NFT holder updates the rights of authorization /// @dev The zero address indicates there is no user /// @param tokenId The NFT which has been authorized /// @param user The user to whom the NFT has been authorized /// @param rights New rights authorized to the user function updateUserRights(uint256 tokenId, address user, string[] rights) external; /// @notice Get the authorization expired time of the specified NFT and user /// @dev The zero address indicates there is no user /// @param tokenId The NFT to get the user expires for /// @param user The user who has been authorized /// @return The authorization expired time function getExpires(uint256 tokenId, address user) external view returns(uint); /// @notice Get the rights of the specified NFT and user /// @dev The zero address indicates there is no user /// @param tokenId The NFT to get the rights /// @param user The user who has been authorized /// @return The rights has been authorized function getUserRights(uint256 tokenId, address user) external view returns(string[]); /// @notice The contract owner can update the number of users that can be authorized per NFT /// @param userLimit The number of users set by operators only function updateUserLimit(uint256 userLimit) external onlyOwner; /// @notice resetAllowed flag can be updated by contract owner to control whether the authorization can be revoked or not /// @param resetAllowed It is the boolean flag function updateResetAllowed(bool resetAllowed) external onlyOwner; /// @notice Check if the token is available for authorization /// @dev Throws if tokenId is not a valid NFT /// @param tokenId The NFT to be checked the availability /// @return true or false whether the NFT is available for authorization or not function checkAuthorizationAvailability(uint256 tokenId) public view returns(bool); /// @notice Clear authorization of a specified user /// @dev The zero address indicates there is no user. The function works when resetAllowed is true and it will throw exception when false /// @param tokenId The NFT on which the authorization based /// @param user The user whose authorization will be cleared function resetUser(uint256 tokenId, address user) external; /// @notice Emitted when the user of a NFT is changed or the authorization expires time is updated /// param tokenId The NFT on which the authorization based /// param indexed user The user to whom the NFT authorized /// @param rights Rights authorized to the user /// @param expires The expires time of the authorization event authorizeUser(uint256 indexed tokenId, address indexed user, string[] rights, uint expires); /// @notice Emitted when the number of users that can be authorized per NFT is updated /// @param userLimit The number of users set by operators only event updateUserLimit(uint256 userLimit); }
The getRights()
function MAY be implemented as pure and view.
The authorizeUser(uint256 tokenId, address user, uint duration)
function MAY be implemented as public
or external
.
The authorizeUser(uint256 tokenId, address user, string[] rights; uint duration)
function MAY be implemented as public
or external
.
The transferUserRights(uint256 tokenId, address newUser)
function MAY be implemented as public
or external
.
The extendDuration(uint256 tokenId, address user, uint duration)
function MAY be implemented as public
or external
.
The updateUserRights(uint256 tokenId, address user, string[] rights)
function MAY be implemented as public
or external
.
The getExpires(uint256 tokenId, address user)
function MAY be implemented as pure
or view
.
The getUserRights(uint256 tokenId, address user)
function MAY be implemented as pure and view.
The updateUserLimit(unit256 userLimit)
function MAY be implemented as public
or external
.
The updateResetAllowed(bool resetAllowed)
function MAY be implemented as public
or external
.
The checkAuthorizationAvailability(uint256 tokenId)
function MAY be implemented as pure
or view
.
The resetUser(uint256 tokenId, address user)
function MAY be implemented as public
or external
.
The authorizeUser
event MUST be emitted when the user of a NFT is changed or the authorization expires time is updated.
The updateUserLimit
event MUST be emitted when the number of users that can be authorized per NFT is updated.
Rationale
First of all, NFT contract owner can set the maximum number of authorized users to each NFT and whether the NFT owner can cancel the authorization at any time to protect the interests of the parties involved.
Secondly, there is a resetAllowed
flag to control the rights between the NFT owner and the users for the contract owner. If the flag is set to true, then the NFT owner can disable usage rights of all authorized users at any time.
Thirdly, the rights within the user record struct is used to store what rights has been authorized to a user by the NFT owner, in other words, the NFT owner can authorize a user with specific rights and update it when necessary.
Finally, this design can be seamlessly integrated with third parties. It is an extension of ERC-721, therefore it can be easily integrated into a new NFT project. Other projects can directly interact with these interfaces and functions to implement their own types of transactions. For example, an announcement platform could use this EIP to allow all NFT owners to make authorization or deauthorization at any time.
Backwards Compatibility
This standard is compatible with ERC-721 since it is an extension of it.
Security Considerations
When the resetAllowed
flag is false, which means the authorization can not be revoked by NFT owner during the period of authorization, users of the EIP need to make sure the authorization fee can be fairly assigned if the NFT was sold to a new holder.
Here is a solution for taking reference: the authorization fee paid by the users can be held in an escrow contract for a period of time depending on the duration of the authorization. For example, if the authorization duration is 12 months and the fee in total is 10 ETH, then if the NFT is transferred after 3 months, then only 2.5 ETH would be sent and the remaining 7.5 ETH would be refunded.
Copyright
Copyright and related rights waived via CC0.
Adopted by projects
Not miss a beat of EIPs' update?
Subscribe EIPs Fun to receive the latest updates of EIPs Good for Buidlers to follow up.
View all