This standard is an extension for ERC-721, designed to specify users for various contexts with a locking feature and allow temporary ownership delegation without changing the original owner.
This EIP preserves the benefits and rights of the owner while expanding the utility of NFTs across various dApps by adding the concepts of Ownership Delegation and Contexts, which define specific roles: Controller and User, who can use the NFT within defined contexts.
For standard ERC-721 NFTs, there are several use cases in financial applications, including:
Traditionally, these applications require ownership transfers to lock the NFT in contracts. However, other decentralized applications (dApps) recognize token ownership as proof that the token owner is entitled to benefits within their reward systems, such as airdrops or tiered rewards. If token owners have their tokens locked in contracts, they are not eligible to receive benefits from holding these tokens, or the reward systems have to support as many contracts as possible to help these owners.
This is because there is only an Owner role indicating the ownership rights, developing on top of ERC-721 has often posed challenges. This proposal aims to solve these challenges by contextualizing the use case to be handled by controllers and distinguishing ownership rights from other roles at the standard level through an ownership delegation mechanism. Standardizing these measures, dApp developers can more easily construct infrastructure and protocols on top of this standard.
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.
This specification encompasses the following components:
Token Context provides a specified use case of a token. It serves as the association relationship between Tokens and Contexts. Within each unique token context, there exists an allocated user who is authorized to utilize the token within that context. In a specified context, there are two distinct roles:
Ownership Rights of a token are defined to be able to:
Ownership Delegation involves distinguishing between owner and ownership rights by delegating ownership to other accounts for a specific duration. During this period, owners temporarily cede ownership rights until the delegation expires.
| Roles | Explanation / Permission | Quantity per Token |
|---|---|---|
| Owner | • Has Ownership Rights by default • Delegates an account to hold Ownership Rights in a duration | $1$ |
| Ownership Delegatee | • Has Ownership Rights in a delegation duration • Renounces before delegation expires | $1$ |
| Ownership Manager | • Is one who holds Ownership Rights • If not delegated yet, it is referenced to Owner, otherwise it is referenced to Ownership Delegatee | $1$ |
| Context Roles | $n$ | |
| Controller | • Transfers controller • Sets context user • (Un)locks token transfer | $1$ per context |
| User | • Authorized to use token in its context | $1$ per context |
Smart contracts implementing this standard MUST implement all the functions in the IERC7695 interface.
Smart contracts implementing this standard MUST implement the ERC-165 supportsInterface function and MUST return the constant value true if 0x486b6fba is passed through the interfaceID argument.
Enumerable extension
The enumeration extension is OPTIONAL for this standard. This allows your contract to publish its full list of contexts and make them discoverable. When calling the supportsInterface function MUST return the constant value true if 0xcebf44b7 is passed through the interfaceID argument.
Controller Interface
The controller is RECOMMENDED to be a contract and including callback methods to allow callbacks in cases where there are any attachment or detachment requests. When calling the supportsInterface function MUST return the constant value true if 0xad0491f1 is passed through the interfaceID argument.
startDelegateOwnership rules
OwnershipDelegationStarted on success.acceptOwnershipDelegation rules
OwnershipDelegationAccepted on success.stopDelegateOwnership rules
OwnershipDelegationStopped on success.To be more explicit about how token (un)locked, these functions:
attachContext methodsetContextLock function MUST be called by the controller to (un)lockrequestDetachContext and execDetachContext functions MUST be called by the ownership manager and MUST operate with respect to the IERC7695ContextCallback hook functionsA list of scenarios and rules follows.
Scenarios
Scenario#1: Context controller wants to (un)lock a token that is not requested for detachment.
setContextLock MUST be called successfullyScenario#2: Context controller wants to (un)lock a token that is requested for detachment.
setContextLock MUST be revertedScenario#3: Ownership manager wants to (unlock and) detach a locked token and the callback controller implements IERC7695ContextCallback.
requestDetachContext function successfullydetachingDuration in the getContext function)execDetachContext function successfullyrequestDetachContext MUST call the onDetachRequested function despite the call resultexecDetachContext MUST call the onExecDetachContext function despite the call resultScenario#4: Ownership manager wants to (unlock and) detach a locked token and the callback controller does not implement IERC7695ContextCallback.
requestDetachContext function successfullydetachingDuration in the getContext function)execDetachContext function successfullyScenario#5: Ownership manager wants to detach an unlocked token and the callback controller implements IERC7695ContextCallback.
requestDetachContext function successfullyrequestDetachContext MUST call the onExecDetachContext function despite the resultexecDetachContext MUST NOT be calledScenario#6: Ownership manager wants to detach an unlocked token and the callback controller does not implement IERC7695ContextCallback.
requestDetachContext function successfullyexecDetachContext MUST NOT be calledRules
attachContext rules
ContextAttached.onAttached and MUST revert if the call is failed.
data argument provided by the caller MUST be passed with its contents unaltered to the onAttached hook function via its data argument.setContextLock rules
ContextLockUpdated on success.requestDetachContext rules
ContextDetached event. After the above conditions are met, this function MUST check if the controller address is a smart contract (e.g. code size > 0). If so, it MUST call onExecDetachContext and the call result MUST be skipped.
data argument provided by the caller MUST be passed with its contents unaltered to the onExecDetachContext hook function via its data argument.ContextDetachRequested event. After the above conditions are met, this function MUST check if the controller address is a smart contract (e.g. code size > 0). If so, it MUST call onDetachRequested and the call result MUST be skipped.
data argument provided by the caller MUST be passed with its contents unaltered to the onDetachRequested hook function via its data argument.execDetachContext rules
detachingDuration in the getContext function when requesting detachment).ContextDetached event.onExecDetachContext and the call result MUST be skipped.
data argument provided by the caller MUST be passed with its contents unaltered to the onExecDetachContext hook function via its data argument.In addition to extending from ERC-721 for the transfer mechanism when transferring an NFT, the implementation:
detachingDuration in the getContext function when requesting detachment) if the token is locked.onExecDetachContext function (with an empty data argument "") and the call result MUST be skipped.When designing the proposal, we considered the following concerns.
This proposal is centered around Token Context to allow for the creation of distinct contexts tailored to various decentralized applications (dApps). The context controller assumes the role of facilitating (rental or delegation) dApps, by enabling the granting of usage rights to another user without modifying the NFT's owner record. Besides, this proposal provides the lock feature for contexts to ensure trustlessness in performing these dApps, especially staking cases.
By providing an unlock mechanism for owners, this approach allows owners to unlock their tokens independently, without relying on the context controller to initiate the process. This prevents scenarios where, should the controller lose control, owners would be unable to unlock their tokens.
The callback results of the onDetachRequested and onExecDetachContext functions in the Token (Un)lock Rules are skipped because we are intentionally removing the controller's ability to stop detachment, ensuring token detachment is independent of the controller's actions.
Additionally, to retain the permission to reject incoming attachments, the operation reverts if the call to the onAttach function fails.
This feature provides a new approach by separating the owner and ownership. Primarily designed to facilitate delegating for third parties, it enables delegating another account as the manager of ownership, distinct from the owner.
Unlike approve or setApprovalForAll methods, which grant permission to other accounts while maintaining ownership status. Ownership delegation goes beyond simply granting permissions; it involves transferring the owner's rights to the delegatee, with provisions for automatic reversion upon expiration. This mechanism prevents potential abuses, such as requesting mortgages and transfers to alternative accounts if the owner retains ownership rights.
The 2-step delegation process is provided to prevent mistakes in assigning delegatees, it must be done through two steps: offer and confirm. In cases the delegation needs to be canceled before its scheduled expiry, the delegatees can invoke stopOwnershipDelegation method.
As part of the integration with the transfer method, we extended its implicit behavior to include token approval:
OwnershipDelegationStopped event is intentionally not emitted.ContextDetached event is intentionally not emitted.These modifications are to ensure trustlessness and gas efficiency during token transfers, providing a seamless experience for users.
This proposal is backward compatible with ERC-721.
When developing this token standard to serve multiple contexts:
maxDetachingDuration method).This precaution is essential to mitigate the risk of the owner abusing systems by spamming listings and transferring tokens to another owner in a short time.
When initiating a new context, the context controllers should track all other contexts within the NFT contract to prevent duplicated usage.
For example, suppose a scenario where a token is locked for rental purposes within a particular game. If that game introduces another context (e.g. supporting delegation in that game), it could lead to duplicated token usage within the game, despite being intended for different contexts.
In such cases, a shared context for rental and delegation purposes can be considered. Or there must be some restrictions on the new delegation context to prevent reusing that token in the game.
When constructing systems that rely on ownership delegation for product development, it is imperative to incorporate a buffer time (of at least maxDetachingDuration seconds) when requesting ownership delegation. This precaution is essential to mitigate the risk of potential abuse, particularly if one of the associated contexts locks the token until the delegation time expires.
For example, consider a scenario where a mortgage contract is built atop this standard, which has a maximum detaching duration of 7 days, while the required delegation period is only 3 days. In such cases, without an adequate buffer time, the owner could exploit the system by withdrawing funds and invoking the relevant context to lock the token, thus preventing its unlock and transfer.
To enhance security and integrity in interactions between contracts, it is essential to validate the caller of any callback function while implementing the IERC7695ContextCallback. This validation ensures that the msg.sender of the callback is indeed the expected contract address, typically the token contract or a designated controller contract. Such checks are crucial for preventing unauthorized actions that could be executed by malicious entities pretending to be a legitimate contract.
Rental
This is a typical use case for rentals, supposing A(owner) owns a token and wants to list his/her token for rent, and B(user) wants to rent the token to play in a certain game.
Mortgage
When constructing collateral systems, it is recommended to support token owners who wish to rent out their tokens while using them for collateral lending. This approach enhances the appeal of mortgage systems, creating a more attractive and versatile financial ecosystem that meets many different needs.
This is a typical use case for mortgages, supposing A(owner) owns a token and wants to mortgage, and B(lender) wants to earn interest by lending their funds to A.
Phishing attacks
It is crucial to note that the owner role has the ability to delegate ownership to another account, allowing it to authorize transfers out of the respective wallet. Consequently, some malicious actors could deceive the token owner into delegating them as a delegatee by invoking the startDelegateOwnership method. This risk can be considered the same as the approve or setApprovalForAll methods.
Ownership rights loss
When interacting with a contract system (e.g. mortgage), where owners have to delegate their ownership rights to the smart contract, it's imperative to:
Copyright and related rights waived via CC0.