ERC-5006 Rental NFT User Extension (Rental NFT, NFT User Extension)
Final · NFTs & multi-token · created 2022-04-12
Adds a user role, separate from the owner, to ERC-1155 tokens so that a set amount of tokens can be lent for a set period without transferring ownership.
Why it is needed
ERC-1155 tokens often have real utility, such as game items, and the person who uses a token can differ from the person who owns it. Rental is the typical case. Without a role that separates owner from user, lending a token means sending the token itself to the other party, and the recipient can sell it or move it elsewhere just like an owner. The token was only lent, yet ownership effectively changes hands. Also, if every project builds its own rental scheme, third-party services such as rental platforms have trouble handling usage rights without the NFT issuer's permission or a custom integration for each project. This calls for a common standard that separates the owner and user roles and prevents the user from taking actions such as transferring ownership.
What to implement
event DeleteUserRecord(uint256 recordId): An event emitted when the user record for recordId is deleted.function usableBalanceOf(address account, uint256 tokenId) external view returns (uint256): Returns the amount of tokenId tokens that account can actually use.function frozenBalanceOf(address account, uint256 tokenId) external view returns (uint256): Returns the frozen amount of account's tokens (locked because they were lent out).function userRecordOf(uint256 recordId) external view returns (UserRecord memory): Returns the contents of the user record (UserRecord) for recordId.function deleteUserRecord(uint256 recordId) external: An authorized caller deletes the record for recordId in one step.
In depth
UserRecord struct
Each usage right is recorded as a UserRecord struct. Its fields are tokenId (uint256), the target token type; owner (address), the owner who lent the token; amount (uint64), the lent amount; user (address), the address that received the right; and expiry (uint64), the expiration time. Each record is given a recordId, and its contents are read with userRecordOf(recordId). Because ERC-1155 allows holding multiple tokens of the same type, the structure does not simply designate a single user but records rights split by amount.
Requirements of createUserRecord
createUserRecord(owner, user, tokenId, amount, expiry) comes with the following conditions. If the caller is not the owner, the caller must have been approved through setApprovalForAll to handle the owner's tokens. The owner must hold at least amount of that token type. The user cannot be the zero address, amount must be greater than 0, and expiry must be after the block timestamp. On success, a CreateUserRecord event is emitted and a record number of type uint256 is returned.
Balance queries and record deletion
usableBalanceOf(account, tokenId) returns the amount of tokens that account can actually use, and frozenBalanceOf(account, tokenId) returns the amount of frozen tokens. With these two functions you can check the borrower's usable amount and the lender's locked amount separately. deleteUserRecord(recordId) requires the caller to have allowance, deletes the record atomically (all at once, with no intermediate state), and emits a DeleteUserRecord event.
Interface identifier
A contract implementing this standard MUST return true when supportsInterface is called with 0xc26d96cc. With this single value, a wallet or platform can check in advance which ERC-1155 contracts support the user extension. This standard assumes ERC-165 and ERC-1155 (requires: 165, 1155).
Design intent: separating rights and third-party integration
Having two roles, owner and user, makes it easier to manage what the lender and the borrower can each do. For example, for the permission to transfer ownership, you can check whether the acting address is the owner or a user and reject it if it is a user. Also, in the spirit of permissionless interoperability, once one project adopts the user role, other projects can integrate with this feature directly without permission from the NFT issuer or the existing app. The original text gives an example where the same PFP NFT is listed at the same time on a 30-day rental platform and on a mortgage platform where ownership is bought in installments.
Backwards compatibility and standard status
This standard adds a set of extension functions to the existing ERC-1155, so it does not conflict with ERC-1155. The original text explains that the newly introduced functions look similar to the existing ERC-1155 functions, so developers can adopt them quickly. Test cases and a reference implementation (ERC5006.sol) are provided, and the standard status is Final, meaning the content is settled and will no longer change.
Things to watch out for
- Protecting owner rights: This standard is designed to protect the rights of the owner. The owner can change the user of an NFT, and the user cannot move the NFT elsewhere.