ERC-5501: Rental & Delegation NFT - EIP-721 Extension
Adds a conditional time-limited user role to EIP-721. This role can be delegated or borrowed.
Abstract
The following standard proposes an additional user
role for EIP-721. This role grants the permission to use the NFT with no ability to transfer or set users. It has an expiry and a flag if the token is borrowed or not. Owner
can delegate the NFT for usage to hot wallets or lend the NFT. If the token is borrowed, not even the owner can change the user until the status expires or both parties agree to terminate. This way, it is possible to keep both roles active at the same time.
Motivation
Collectibles, gaming assets, metaverse, event tickets, music, video, domains, real item representation are several among many NFT use cases. With EIP-721 only the owner can reap the benefits. However, with most of the utilities it would be beneficial to distinguish between the token owner and its user. For instance music or movies could be rented. Metaverse lands could be delegated for usage.
The two reasons why to set the user are:
- delegation - Assign user to your hot wallet to interact with applications securely. In this case, the owner can change the user at any time.
- renting - This use case comes with additional requirements. It is needed to terminate the loan once the established lending period is over. This is provided by
expires
of the user. It is also necessary to protect the borrower against resetting their status by the owner. Thus,isBorrowed
check must be implemented to disable the option to set the user before the contract expires.
The most common use cases for having an additional user role are:
- delegation - For security reasons.
- gaming - Would you like to try a game (or particular gaming assets) but are you unsure whether or not you will like it? Rent assets first.
- guilds - Keep the owner of the NFTs as the multisig wallet and set the user to a hot wallet with shared private keys among your guild members.
- events - Distinguish between
ownerOf
anduserOf
. Each role has a different access. - social - Differentiate between roles for different rooms. For example owner has read + write access while userOf has read access only.
This proposal is a follow up on EIP-4400 and EIP-4907 and introduces additional upgrades for lending and borrowing which include:
- NFT stays in owner's wallet during rental period
- Listing and sale of NFT without termination of the rent
- Claiming owner benefits during rental period
Building the standard with additional isBorrowed check now allows to create rental marketplaces which can set the user of NFT without the necessary staking mechanism. With current standards if a token is not staked during the rental period, the owner can simply terminate the loan by setting the user repeatedly. This is taken care of by disabling the function if the token is borrowed which in turn is providing the owner additional benefits. They can keep the token tied to their wallet, meaning they can still receive airdrops, claim free mints based on token ownership or otherwise use the NFT provided by third-party services for owners. They can also keep the NFT listed for sale. Receiving airdrops or free mints was previously possible but the owner was completely reliant on the implementation of rental marketplaces and their discretion.
Decentralized applications can now differentiate between ownerOf and userOf while both statuses can coexist.
Specification
The key words “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.
Every compliant contract MUST implement the IERC5501
interface. This extension is OPTIONAL for EIP-721 contracts.
Every contract implementing the IERC5501
interface is free to define the permissions of a user
. However, user MUST NOT be considered an owner
. They MUST NOT be able to execute transfers and approvals. Furthermore, setUser
MUST be blocked from executing if userIsBorrowed
returns true
and userExpires
is larger than or equal to block.timestamp
.
The UpdateUser
event MUST be emitted when a user
is changed.
The setUser(uint256 _tokenId, address _user, uint64 _expires, bool _isBorrowed)
function SHOULD revert
unless the msg.sender
is the owner
or an approved operator. It MUST revert if a token is borrowed and status has not expired yet. It MAY be public
or external
.
The userOf(uint256 _tokenId)
function SHOULD revert if user
is not set or expired.
The userExpires(uint256 _tokenId)
function returns a timestamp when user status expires.
The userIsBorrowed(uint256 _tokenId)
function returns whether NFT is borrowed or not.
The supportsInterface
function MUST return true
when called with 0xf808ec37
.
On every transfer
, the user
MUST be reset if the token is not borrowed. If the token is borrowed the user
MUST stay the same.
The Balance extension is OPTIONAL. This gives the option to query the number of tokens a user
has.
The userBalanceOf(address _user)
function SHOULD revert
for zero address.
The Enumerable extension is OPTIONAL. This allows to iterate over user balance.
The tokenOfUserByIndex(address _user, uint256 _index)
function SHOULD revert
for zero address and throw
if the index is larger than or equal to user
balance.
The Terminable extension is OPTIONAL. This allows terminating the rent early if both parties agree.
The AgreeToTerminateBorrow
event MUST be emitted when either the lender or borrower agrees to terminate the rent.
The ResetTerminationAgreements
event MUST be emitted when a token is borrowed and transferred or setUser
and terminateBorrow
functions are called.
The TerminateBorrow
event MUST be emitted when the rent is terminated.
The setBorrowTermination(uint256 _tokenId)
. It MUST set an agreement from either party whichever calls the function. If the lender and borrower are the same address, it MUST assign an agreement for both parties at once.
The getBorrowTermination(uint256 _tokenId)
returns if agreements from both parties are true
or false
.
The terminateBorrow(uint256 _tokenId)
function MAY be called by anyone. It MUST revert
if both agreements to terminate are not true
. This function SHOULD change the isBorrowed
flag from true
to false
.
On every transfer
, the termination agreements from either party MUST be reset if the token is borrowed.
Rationale
The main factors influencing this standard are:
- EIP-4400 and EIP-4907
- Allow lending and borrowing without the necessary stake or overcollateralization while owner retains ownership
- Leave the delegation option available
- Keep the number of functions in the interfaces to a minimum while achieving desired functionality
- Modularize additional extensions to let developers choose what they need for their project
Name
The name for the additional role has been chosen to fit the purpose and to keep compatibility with EIP-4907.
Ownership retention
Many collections offer their owners airdrops or free minting of various tokens. This is essentially broken if the owner is lending a token by staking it into a contract (unless the contract is implementing a way to claim at least airdropped tokens). Applications can also provide different access and benefits to owner and user roles in their ecosystem.
Balance and Enumerable extensions
These have been chosen as OPTIONAL extensions due to the complexity of implementation based on the fact that balance is less once user status expires and there is no immediate on-chain transaction to evaluate that. In both userBalanceOf
and tokenOfUserByIndex
functions there must be a way to determine whether or not user status has expired.
Terminable extension
If the owner mistakenly sets a user with borrow status and expires to a large value they would essentially be blocked from setting the user ever again. The problem is addressed by this extension if both parties agree to terminate the user status.
Security
Once applications adopt the user role, it is possible to delegate ownership to hot wallet and interact with them with no fear of connecting to malicious websites.
Backwards Compatibility
This standard is compatible with current EIP-721 by adding an extension function set. The new functions introduced are similar to existing functions in EIP-721 which guarantees easy adoption by developers and applications. This standard also shares similarities to EIP-4907 considering user role and its expiry which means applications will be able to determine the user if either of the standards is used.
Test Cases
Test cases can be found in the reference implementation:
- Main contract
- Balance extension
- Enumerable extension
- Terminable extension
- Scenario combined of all extensions
Reference Implementation
The reference implementation is available here:
- Main contract
- Balance extension
- Enumerable extension
- Terminable extension
- Solution combined of all extensions
Security Considerations
Developers implementing this standard and applications must consider all the permissions they give to users and owners. Since owner and user are both active roles at the same time, double-spending problem must be avoided. Balance extension must be implemented in such a way which will not cause any gas problems. Marketplaces should let users know if a token listed for sale is borrowed or not.
Copyright
Copyright and related rights waived via CC0.