--- eip: 5560 title: Redeemable NFTs description: Makes an NFT redeemable for a physical object author: Olivier Fernandez (@fernandezOli), Frédéric Le Coidic (@FredLC29), Julien Béranger (@julienbrg) discussions-to: https://ethereum-magicians.org/t/eip-redeemable-nft-extension/10589 status: Stagnant type: Standards Track category: ERC created: 2022-08-30 requires: 165, 721 --- ## Abstract The EIP is a Redeemable NFT extension which adds a `redeem` function to [EIP-721](./eip-721.md). It can be implemented when an NFT issuer wants his/her NFT to be redeemed for a physical object. ## Motivation 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. ## 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._ `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: ```solidity 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`. ## Rationale 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: ```solidity 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: ```solidity 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: ```solidity 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 Copyright and related rights waived via [CC0](../LICENSE.md).