ERC-4400 ERC-721 Consumer Role Extension (EIP-721 Consumable Extension)
Final · NFTs & multi-token · created 2021-10-30
Adds a standard interface to ERC-721 NFTs for assigning and querying a consumer address, which uses the token without being its owner. It is the basis for services that separate ownership from use, such as NFT rental and staking.
Why it is needed
ERC-721 NFTs define only three roles: owner, operator, and approved address. But an NFT has a use beyond ownership, which is utilization. For example, the owner of a land NFT in a metaverse must be able to delegate scene deployment to another address, and a person who deposits an NFT in a staking contract wants to keep enjoying the token's utility benefits even after ownership moves to the contract. Without a standard, each contract creates its own usage-right role with different names and methods, and wallets or other contracts cannot read or handle that role consistently. As a result, it becomes hard to build services such as a general-purpose NFT rental marketplace that spans many projects.
What to implement
event ConsumerChanged(address indexed owner, address indexed consumer, uint256 indexed tokenId);: An event emitted when the owner changes the consumer of an NFT. A consumer of the zero address means there is no consumer.function consumerOf(uint256 _tokenId) view external returns (address);: Returns the current consumer address of the NFT. Returns the zero address if there is no consumer.function changeConsumer(address _consumer, uint256 _tokenId) external;: Changes or reaffirms the consumer address of an NFT. Only the owner, an operator, or an approved address can call it.
In depth
The IEIP721Consumable interface and the ERC-165 identifier
A contract that follows this extension MUST implement the IEIP721Consumable interface. The extension itself is OPTIONAL for an ERC-721 contract. The ERC-165 identifier of the interface is 0x953c8dfa, and calling supportsInterface with this value MUST return true. This lets external code such as wallets or marketplaces check on-chain in advance whether an NFT contract supports the consumer extension. The interface consists of just one event and two functions.
consumerOf: querying the consumer
consumerOf(uint256 _tokenId) returns the current consumer address of the NFT. The zero address means no consumer is assigned, and execution is aborted (throw) if _tokenId is not a valid NFT. This function MAY be implemented as pure or view. Because it is a read-only function that does not change state, it serves as the standard entry point for external services to check who holds the usage right to a given token.
changeConsumer and the ConsumerChanged event
changeConsumer(address _consumer, uint256 _tokenId) changes or reaffirms the consumer address of the NFT. Execution is aborted if msg.sender is not the current owner, an operator approved by the owner, or an approved address, and it is also aborted if _tokenId is not a valid NFT. Passing the zero address means there is no consumer. The function MAY be implemented as public or external. When the consumer changes, the ConsumerChanged(owner, consumer, tokenId) event MUST be emitted.
The limit on consumer permissions
What a consumer can do within the system is left to each implementing contract. There is one exception. A consumer MUST NOT be treated as an owner, operator, or approved address under the ERC-721 specification, and therefore must not be able to execute transfers or approvals. The original Rationale states the reason for this restriction plainly. Many use cases need a separate role without owner permissions, and an implementation that gives a consumer owner permissions defeats the purpose of this standard.
Consumer reset on transfer
Every time an NFT is transferred, the consumer MUST be changed to the default address, and using the zero address address(0) as the default is RECOMMENDED. The comment on the ConsumerChanged event says the same. When a Transfer event is emitted, the consumer is reset to none even if the NFT had one. In other words, usage rights do not follow ownership, and a new owner who wants a consumer must assign one again with changeConsumer.
Design principles and name choice
The original text lists four factors that influenced the design of the standard: keeping the number of functions to a minimum to avoid bloating contracts, simplicity, gas efficiency, and not reusing or overlaying existing roles such as owner, operator, and approved address. The name operator was also considered for the role, but consumer was chosen because operator is already defined and used in ERC-721. Consumer matches the meaning of an entity that uses a token instance without owning it. This standard has been finalized with Final status, is compatible with existing ERC-721, and no other standard defines a similar role.
Things to watch out for
- Design consumer permissions carefully: Even if the standard is implemented correctly and blocks transfer and burning of the NFT, you can inadvertently grant a consumer permissions that should be allowed only to the original owner. Implementers must thoroughly review the scope of permissions given to consumers and make sure consumers cannot use actions that should be restricted to the owner.