126 lines
4.3 KiB
Markdown
126 lines
4.3 KiB
Markdown
|
---
|
||
|
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).
|