Redeemable NFTs

Makes an NFT redeemable for a physical object
StagnantStandards Track: ERC
Created: 2022-08-30
Requires: EIP-165, EIP-721
Olivier Fernandez (@fernandezOli), Frédéric Le Coidic (@FredLC29), Julien Béranger (@julienbrg)
DiscussionsOriginal linkEdit
1 min read

ERC-5560 proposes an extension to the ERC-721 standard for Non-Fungible Tokens (NFTs) to enable multi-redeemable NFTs. This means that an NFT can be redeemed in multiple scenarios for either a physical or digital object, and maintain a record of its redemption status on the blockchain. The motivation behind this proposed standard is to provide a more versatile and flexible solution compared to existing standards, allowing for new possibilities for commerce use cases and breaking the limitation of one-time redemption per NFT. One use case for an NFT that can be redeemed multiple times in various scenarios is a digital concert ticket, where each redemption could represent a unique experience or benefit for the NFT holder. The specification includes details on the redeem and cancel functions, redemption flag key-value pairs, and metadata extension. The proposal also addresses backwards compatibility and security considerations.

Anyone may contribute to propose contents.
Go propose


The EIP is a Redeemable NFT extension which adds a redeem function to EIP-721. It can be implemented when an NFT issuer wants his/her NFT to be redeemed for a physical object.


An increasing amount of NFT issuers such as artists, fine art galeries, auction houses, brands and others want to offer a physical object to the holder of a given NFT. This standard allows EIP-721 NFTs to signal reedemability.


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.

EIP-721 compliant contracts MAY implement this EIP to provide a standard method of receiving information on redeemability.

The NFT issuer MUST decide who is allowed to redeem the NFT, and restrict access to the redeem() function accordingly.

Anyone MAY access the isRedeemable() function to check the redeemability status: it returns true when the NFT redeemable, and false when already redeemed.

Third-party services that support this standard MAY use the Redeem event to listen to changes on the redeemable status of the NFT.

Implementers of this standard MUST have all of the following functions:

import '@openzeppelin/contracts/utils/introspection/ERC165.sol'; /** * @dev Implementation of Redeemable for ERC-721s * */ interface IRedeemable is ERC165 { /* * ERC165 bytes to add to interface array - set in parent contract implementing this standard * * bytes4 private constant _INTERFACE_ID_ERC721REDEEM = 0x2f8ca953; */ /// @dev This event emits when a token is redeemed. event Redeem(address indexed from, uint256 indexed tokenId); /// @notice Returns the redeem status of a token /// @param tokenId Identifier of the token. function isRedeemable(uint256 _tokenId) external view returns (bool); /// @notice Redeeem a token /// @param tokenId Identifier of the token to redeeem function redeem(uint256 _tokenId) external; }

The Redeem event is emitted when the redeem() function is called.

The supportsInterface method MUST return true when called with 0x2f8ca953.


When the NFT contract is deployed, the isRedeemable() function returns true by default.

By default, the redeem() function visibility is public, so anyone can trigger it. It is RECOMMENDED to add a require to restrict the access:

require(ownerOf(tokenId) == msg.sender, "ERC721Redeemable: You are not the owner of this token");

After the redeem() function is triggered, isRedeemable() function returns false.

Redeem event

When the redeem() function is triggered, the following event MUST be emitted:

event Redeem(address indexed from, uint256 indexed tokenId);

Backwards Compatibility

This standard is compatible with EIP-721.

Reference Implementation

Here's an example of an EIP-721 that includes the Redeemable extension:

contract ERC721Redeemable is ERC721, Redeemable { constructor(string memory name, string memory symbol) ERC721(name, symbol) { } function isRedeemable(uint256 tokenId) public view virtual override returns (bool) { require(_exists(tokenId), "ERC721Redeemable: Redeem query for nonexistent token"); return super.isRedeemable(tokenId); } function redeem(uint256 tokenId) public virtual override { require(_exists(tokenId), "ERC721Redeemable: Redeem query for nonexistent token"); require(ownerOf(tokenId) == msg.sender, "ERC721Redeemable: You are not the owner of this token"); super.redeem(tokenId); } function supportsInterface(bytes4 interfaceId) public view override(ERC721, Redeemable) returns (bool) { return super.supportsInterface(interfaceId); } }

Security Considerations

Needs discussion.

Copyright and related rights waived via CC0.

Further reading
Anyone may contribute to propose contents.
Go propose
Adopted by projects
Anyone may contribute to propose contents.
Go propose

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
Serve Ethereum Builders, Scale the Community.
Supported by