Multi-redeemable NFTs
相关视频
正文
Abstract
This EIP proposes an extension to the ERC-721 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
The motivation behind our proposed NFT standard is to provide a more versatile and flexible solution compared to existing standards, allowing for multi-redeemable NFTs. Our proposed NFT standard enables multi-redeemable NFTs, allowing them to be redeemed in multiple scenarios for different campaigns or events, thus unlocking 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. 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.
pragma solidity ^0.8.16; /// @title ERC-6672 Multi-Redeemable NFT Standard /// @dev See https://eips.ethereum.org/EIPS/eip-6672 /// Note: the ERC-165 identifier for this interface is 0x4dddf83f. 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
orfalse
value. For instance, in cases of physical goods redemption, we may require the redemption status to be eitherredeemed
,paid
, orshipping
. 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 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.
/// @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 Metadata JSON Schema" referenced above.
{ "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 redeemed 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 redeemed 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.
Reference Implementation
The reference implementation of Multi-Redeemable NFT can be found here.
Security Considerations
An incorrect implementation of ERC-6672 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 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 approval models.
This ERC-6672 token is compatible with ERC-721, so wallets and smart contracts capable of storing and handling standard ERC-721 tokens will not face the risk of asset loss caused by incompatible standard implementations.
Copyright
Copyright and related rights waived via CC0.