forked from DecentralizedClimateFoundation/DCIPs
187 lines
10 KiB
Markdown
187 lines
10 KiB
Markdown
|
---
|
||
|
eip: 6672
|
||
|
title: Multi-redeemable NFTs
|
||
|
description: An extension of ERC-721 which enables an NFT to be redeemed in multiple scenarios for either a physical or digital object
|
||
|
author: RE:DREAMER Lab <dev@redreamer.io>, Archie Chang (@ArchieR7) <archie@redreamer.io>, Kai Yu (@chihkaiyu) <kai@redreamer.io>, Yonathan Randyanto (@Randyanto) <randy@redreamer.io>
|
||
|
discussions-to: https://ethereum-magicians.org/t/eip-6672-multi-redeemable-nfts/13276
|
||
|
status: Draft
|
||
|
type: Standards Track
|
||
|
category: ERC
|
||
|
created: 2023-02-21
|
||
|
requires: 721
|
||
|
---
|
||
|
|
||
|
## Abstract
|
||
|
|
||
|
This EIP proposes an extension to the [ERC-721](./eip-721.md) standard for Non-Fungible Tokens (NFTs) to enable multi-redeemable NFTs. Redemption provides a means for NFT holders to demonstrate ownership and eligibility of their NFT, which in turn enables them to receive a physical or digital item. This extension would allow an NFT to be redeemed in multiple scenarios and maintain a record of its redemption status on the blockchain.
|
||
|
|
||
|
## Motivation
|
||
|
|
||
|
[ERC-5560](./eip-5560.md) enables only one-time redemption of an NFT, which means the same NFT cannot be re-used for another redemption from different campaigns or events. Our proposed multi-redeemable NFT is an improved alternative to the [ERC-5560](./eip-5560.md) redeemable NFT. One use case for an NFT that can be redeemed multiple times in various scenarios is a digital concert ticket. The NFT could be redeemed for access to the online concert and then again for exclusive merchandise, a meet and greet with the artist, or any exclusive commerce status that is bound to the NFT. Each redemption could represent a unique experience or benefit for the NFT holder.
|
||
|
|
||
|
## Specification
|
||
|
|
||
|
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174.
|
||
|
|
||
|
### Redeem and Cancel Functions
|
||
|
|
||
|
An operator SHALL only make an update to the redemption created by itself. Therefore, the `redeem()` and `cancel()` functions do not have an `_operator` parameter, and the `msg.sender` address MUST be used as the `_operator`.
|
||
|
|
||
|
### Redemption Flag Key-Value Pairs
|
||
|
|
||
|
The combination of `_operator`, `_tokenId`, and `_redemptionId` MUST be used as the key in the redemption flag key-value pairs, whose value can be accessed from the `isRedeemed()` function.
|
||
|
|
||
|
**Every contract compliant with this EIP MUST implement `ERC6672` and `ERC721` interfaces.**
|
||
|
|
||
|
```solidity
|
||
|
pragma solidity ^0.8.16;
|
||
|
|
||
|
/// @title ERC-6672 Multi-Redeemable NFT Standard
|
||
|
/// @dev See https://eips.ethereum.org/EIPS/eip-6672
|
||
|
interface IERC6672 /* is IERC721 */ {
|
||
|
/// @dev This event emits when an NFT is redeemed.
|
||
|
event Redeem(
|
||
|
address indexed _operator,
|
||
|
uint256 indexed _tokenId,
|
||
|
address redeemer,
|
||
|
bytes32 _redemptionId,
|
||
|
string _memo
|
||
|
);
|
||
|
|
||
|
/// @dev This event emits when a redemption is canceled.
|
||
|
event Cancel(
|
||
|
address indexed _operator,
|
||
|
uint256 indexed _tokenId,
|
||
|
bytes32 _redemptionId,
|
||
|
string _memo
|
||
|
);
|
||
|
|
||
|
/// @notice Check whether an NFT is already used for redemption or not.
|
||
|
/// @dev
|
||
|
/// @param _operator The address of the operator of the redemption platform.
|
||
|
/// @param _redemptionId The identifier for a redemption.
|
||
|
/// @param _tokenId The identifier for an NFT.
|
||
|
/// @return Whether an NFT is already redeemed or not.
|
||
|
function isRedeemed(address _operator, bytes32 _redemptionId, uint256 _tokenId) external view returns (bool);
|
||
|
|
||
|
/// @notice List the redemptions created by the given operator for the given NFT.
|
||
|
/// @dev
|
||
|
/// @param _operator The address of the operator of the redemption platform.
|
||
|
/// @param _tokenId The identifier for an NFT.
|
||
|
/// @return List of redemptions of speficic `_operator` and `_tokenId`.
|
||
|
function getRedemptionIds(address _operator, uint256 _tokenId) external view returns (bytes32[]);
|
||
|
|
||
|
/// @notice Redeem an NFT
|
||
|
/// @dev
|
||
|
/// @param _redemptionId The identifier created by the operator for a redemption.
|
||
|
/// @param _tokenId The NFT to redeem.
|
||
|
/// @param _memo
|
||
|
function redeem(bytes32 _redemptionId, uint256 _tokenId, string _memo) external;
|
||
|
|
||
|
/// @notice Cancel a redemption
|
||
|
/// @dev
|
||
|
/// @param _redemptionId The redemption to cancel.
|
||
|
/// @param _tokenId The NFT to cancel the redemption.
|
||
|
/// @param _memo
|
||
|
function cancel(bytes32 _redemptionId, uint256 _tokenId, string _memo) external;
|
||
|
}
|
||
|
```
|
||
|
|
||
|
### Metadata Extension
|
||
|
|
||
|
The key format for the `redemptions` key-value pairs MUST be standardized as `operator-tokenId-redemptionId`, where `operator` is the operator wallet address, `tokenId` is the identifier of the token that has been redeemed, and `redemptionId` is the redemption identifier. The value of the key `operator-tokenId-redemptionId` is an object that contains the `status` and `description` of the redemption.
|
||
|
|
||
|
- Redemption status, i.e. `status`
|
||
|
|
||
|
The redemption status can have a more granular level, rather than just being a flag with a `true` or `false` value. For instance, in cases of physical goods redemption, we may require the redemption status to be either `redeemed`, `paid`, or `shipping`. It is RECOMMENDED to use a string enum that is comprehensible by both the operator and the marketplace or any other parties that want to exhibit the status of the redemption.
|
||
|
|
||
|
- Description of the redemption, i.e. `description`
|
||
|
|
||
|
The `description` SHOULD be used to provide more details about the redemption, such as information about the concert ticket, a detailed description of the action figures, and more.
|
||
|
|
||
|
The **metadata extension** is OPTIONAL for [ERC-6672](./eip-6672.md) smart contracts (see "caveats", below). This allows your smart contract to be interrogated for its name and for details about the assets which your NFTs represent.
|
||
|
|
||
|
```solidity
|
||
|
/// @title ERC-6672 Multi-Redeemable Token Standard, optional metadata extension
|
||
|
/// @dev See https://eips.ethereum.org/EIPS/eip-6672
|
||
|
interface IERC6672Metadata /* is IERC721Metadata */ {
|
||
|
/// @notice A distinct Uniform Resource Identifier (URI) for a given asset.
|
||
|
/// @dev Throws if `_tokenId` is not a valid NFT. URIs are defined in RFC
|
||
|
/// 3986. The URI may point to a JSON file that conforms to the "ERC-6672
|
||
|
/// Metadata JSON Schema".
|
||
|
function tokenURI(uint256 _tokenId) external view returns (string);
|
||
|
}
|
||
|
```
|
||
|
|
||
|
This is the "[ERC-6672](./eip-6672.md) Metadata JSON Schema" referenced above.
|
||
|
|
||
|
```json
|
||
|
{
|
||
|
"title": "Asset Metadata",
|
||
|
"type": "object",
|
||
|
"properties": {
|
||
|
"name": {
|
||
|
"type": "string",
|
||
|
"description": "Identifies the asset to which this NFT represents"
|
||
|
},
|
||
|
"description": {
|
||
|
"type": "string",
|
||
|
"description": "Describes the asset to which this NFT represents"
|
||
|
},
|
||
|
"image": {
|
||
|
"type": "string",
|
||
|
"description": "A URI pointing to a resource with mime type image/* representing the asset to which this NFT represents. Consider making any images at a width between 320 and 1080 pixels and aspect ratio between 1.91:1 and 4:5 inclusive."
|
||
|
}
|
||
|
},
|
||
|
"redemptions": {
|
||
|
"operator-tokenId-redemptionId": {
|
||
|
"status": {
|
||
|
"type": "string",
|
||
|
"description": "The status of a redemption. Enum type can be used to represent the redemption status, such as redeemed, shipping, paid."
|
||
|
},
|
||
|
"description": {
|
||
|
"type": "string",
|
||
|
"description": "Describes the object that has been redeemed for an NFT, such as the name of an action figure series name or the color of the product."
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
## Rationale
|
||
|
|
||
|
### Key Choices for Redemption Flag and Status
|
||
|
|
||
|
The combination of `_operator`, `_tokenId`, and `_redemptionId` is chosen as the key because it provides a clear and unique identifier for each redemption transaction.
|
||
|
|
||
|
- Operator wallet address, i.e. `_operator`
|
||
|
|
||
|
It's possible that there are more than one party who would like to use the same NFT for redemption. For example, MisterPunks NFTs are eligible to be redemeed for both Event-X and Event-Y tickets, and each event's ticket redemption is handled by a different operator.
|
||
|
|
||
|
- Token identifier, i.e. `_tokenId`
|
||
|
|
||
|
Each NFT holder will have different redemption records created by the same operator. Therefore, it's important to use token identifier as one of the keys.
|
||
|
|
||
|
- Redemption identifier, i.e. `_redemptionId`
|
||
|
|
||
|
Using `_redemptionId` as one of the keys enables NFT holders to redeem the same NFT to the same operator in multiple campaigns. For example, Operator-X has 2 campaigns, i.e. campaign A and campaign B, and both campaigns allow for MisterPunks NFTs to be redemeed for physical action figures. Holder of MisterPunk #7 is eligible for redemption in both campaigns and each redemption is recorded with the same `_operator` and `_tokenId`, but with different `_redemptionId`.
|
||
|
|
||
|
## Backwards Compatibility
|
||
|
|
||
|
This standard is compatible with [ERC-721](./eip-721.md).
|
||
|
|
||
|
## Reference Implementation
|
||
|
|
||
|
The reference implementation of Multi-Redeemable NFT can be found [here](../assets/eip-6672/contracts/ERC6672.sol).
|
||
|
|
||
|
|
||
|
## Security Considerations
|
||
|
|
||
|
An incorrect implementation of [ERC-6672](./eip-6672.md) could potentially allow an unauthorized operator to access redemption flags owned by other operators, creating a security risk. As a result, an unauthorized operator could cancel the redemption process managed by other operators. Therefore, it is crucial for [ERC-6672](./eip-6672.md) implementations to ensure that only the operator who created the redemption, identified using `msg.sender`, can update the redemption flag using the `redeem()` and `cancel()` functions. It is also recommended to isolate the `redeem()` and `cancel()` functions from [ERC-721](./eip-721.md) approval models.
|
||
|
|
||
|
This [ERC-6672](./eip-6672.md) token is compatible with [ERC-721](./eip-721.md), so wallets and smart contracts capable of storing and handling standard [ERC-721](./eip-721.md) tokens will not face the risk of asset loss caused by incompatible standard implementations.
|
||
|
|
||
|
## Copyright
|
||
|
|
||
|
Copyright and related rights waived via [CC0](../LICENSE.md).
|