Discussion topic for ERC-XXXX: Expirable NFT/SBT
TODO pull request link
Update Log
- 2024-01-04: Initiate Idea, received from @Arvolear #16, #18
- 2024-01-06: Update section Specification
External Reviews
- None as of 2024-01-04
Outstanding Issue
- None as of 2024-01-04
All changes will be managed through pull requests. This topic provides an initial draft version to help those who are not familiar with using GitHub.
Author Opinion
In the past, plenty of ERCs have tried adding an “expiration” feature to NFTs and SBTs as part of their use case. But honestly, most smart contracts or protocols probably just need to know if the token (or contract) can expire—doesn’t matter if it’s for subscriptions, rentals, or synthetic use cases. credit to the original idea ERC-5007 but this proposal focuses more on “expirable” behavior. allowing other use cases to be built on top of this foundational feature.
Simple Summary
An extended interface enables Non-Fungible Tokens (NFTs) and Soulbound Tokens (SBTs) to possess expiration capabilities, allowing them to expire after a predetermined time.
Abstract
Introduces an extension for ERC-721 Non-Fungible Tokens (NFTs) and Soulbound Tokens (SBTs), Through this extension, tokens have a predetermined validity period, after which they become invalid and cannot be used in the smart contract that checks their validity. This functionality is essential for various applications where token expiration is necessary such as access and authentication, contracts, governance, licenses, and policies.
Motivation
Introduces an extension for ERC-721 Non-Fungible Tokens (NFTs) and Soulbound Tokens (SBTs), which facilitates the implementation of an expiration mechanism.
Use cases include:
- Access and Authentication
- Authentication for Identity and Access Management (IAM)
- Membership for Membership Management System (MMS)
- Ticket and Press for Meetings, Incentive Travel, Conventions, and Exhibitions (MICE)
- Subscription-based access for digital platforms.
- Digital Certifications, Contracts, Copyrights, Documents, Licenses, Policies, etc.
- Loyalty Program voucher or coupon
- Governance and Voting Rights
- Financial Product
- Bonds, Loans, Hedge, and Options Contract
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.
Interface
// SPDX-License-Identifier: CC0-1.0
pragma solidity >=0.8.0 <0.9.0;
/**
* @title ERC-XXXX: Expirable NFT/SBT
* @notice unique/granular expiry
*/
// import "./IERC1155.sol";
// import "./IERC721.sol";
import "IERC5007.sol";
interface IERC5007Ext is IERC5007 /** IERC721 or IERC1155 */ {
enum EXPIRY_TYPE {
BLOCK_BASED, // block.number
TIME_BASED // block.timestamp
}
/**
* @dev Returns the type of the expiry.
* @return EXPIRY_TYPE Enum value indicating the unit of an expiry.
*/
function expiryType() external view returns (EXPIRY_TYPE);
/**
* @dev Checks whether a specific token is expired.
* @param Id The identifier representing the token type `Id` (ERC1155) or `tokenId` (ERC721).
* @return bool True if the token is expired, false otherwise.
*/
function isTokenExpired(uint256 Id) external view returns (bool);
// inherit from ERC-5007 return depends on the type `block.timestamp` or `block.number`
// {ERC-5007} return in uint64 MAY not suitable for `block.number` based.
// function startTime(uint256 tokenId) external view returns (uint64);
// function endTime(uint256 tokenId) external view returns (uint64);
}
Function Behavior
balanceOf
orbalanceOfBatch
that inherited from ERC-721 or ERC-1155 MUST return all tokens even if expired it still exists but unusable due to limitation to tracking expire token on-chain.- For Non-SBTs
transferFrom
,safeTransferFrom
, andsafeBatchTransferFrom
MUST allow transferring tokens even if they expired. This ensures that expired tokens remain transferable and tradable, preserving compatibility with existing applications already deployed. However, expired tokens MUST be considered invalid and unusable in contracts that check for token validity. expiryType
MUST return the type of expiry used by the contract, which can be eitherBLOCK
orTIME
.startTime
andendTime
oftokenId
ortokenType
, can beblock.number
orblock.timestamp
depending onexpiryType
. ThestartTime
MUST less thanendTime
and SHOULD except when both are set to0
. AstartTime
andendTime
of0
indicates that thetokenId
ortokenType
has no time-limited.isTokenValid
is used for retrieving the status of the giventokenId
ortokenType
the function MUST returntrue
if the token is still valid otherwisefalse
.supportInterface
forIERC5007Ext
is0x00000000
forIERC5007ExtEpoch
is0x11111111
Extension
Epochs represent a specific period or block range during which certain tokens are valid borrowing concepts from ERC-7818, tokens are grouped under an epoch
and share the same validityDuration
.
// SPDX-License-Identifier: CC0-1.0
pragma solidity >=0.8.0 <0.9.0;
/**
* @title ERC-XXXX: Expirable ERC1155 or ERC721
* @notice epoch expiry
*/
// import "./IERC1155.sol";
// import "./IERC721.sol";
import "./IERC5007Ext.sol";
interface IERC5007ExtEpoch is IERC5007Ext {
/**
* @dev Retrieves the balance of a specific `epoch` owned by an account.
* @param epoch The `epoch for which the balance is checked.
* @param Id The identifier representing the token type `Id` (ERC1155) or `tokenId` (ERC721).
* @param account The address of the account.
* @return uint256 The balance of the specified `epoch`.
* @notice "MUST" return 0 if the specified `epoch` is expired.
*/
function balanceOfAtEpoch(
uint256 epoch,
uint256 Id,
address account
) external view returns (uint256);
/**
* @dev Retrieves the current epoch of the contract.
* @return uint256 The current epoch of the token contract,
* often used for determining active/expired states.
*/
function currentEpoch() external view returns (uint256);
/**
* @dev Retrieves the duration of a single epoch.
* @return uint256 The duration of a single epoch.
* @notice The unit of the epoch length is determined by the `validityPeriodType()` function.
*/
function epochLength() external view returns (uint256);
/**
* @dev Returns the type of the epoch.
* @return EPOCH_TYPE Enum value indicating the unit of an epoch.
*/
function epochType() external view returns (EPOCH_TYPE);
/**
* @dev Retrieves the validity duration of a specific token.
* @param Id The identifier representing the token type `Id` (ERC1155) or `tokenId` (ERC721).
* @return uint256 The validity duration of the token in `epoch` unit.
*/
function validityDuration(uint256 Id) external view returns (uint256);
/**
* @dev Checks whether a specific `epoch` is expired.
* @param epoch The `epoch` to check.
* @return bool True if the token is expired, false otherwise.
* @notice Implementing contracts "MUST" define and document the logic for determining expiration,
* typically by comparing the latest epoch with the given `epoch` value,
* based on the `EPOCH_TYPE` measurement (e.g., block count or time duration).
*/
function isEpochExpired(uint256 epoch) external view returns (bool);
// inherit from ERC-5007 return but return it in `epoch`
// and `epoch` depends on the type `block.timestamp` or `block.number`
// {ERC-5007} return in uint64 MAY not suitable for `epoch` due to `epoch` is abstract
// it's can be short or long depend on implementation.
// function startTime(uint256 tokenId) external view returns (uint64);
// function endTime(uint256 tokenId) external view returns (uint64);
}
balanceOfAtEpoch
MUST returns the balance of tokens held by an account at the specifiedepoch
, even if theepoch
has expired.currentEpoch
MUST return the currentepoch
of the contract.epochLength
MUST return duration betweenepoch
in blocks or time in seconds.epochType
MUST return the type of epoch used by the contract, which can be eitherBLOCKS_BASED
orTIME_BASED
.validityDuration
MUST return the validity duration of tokens in terms ofepoch
counts.isEpochExpired
MUST return true if the givenepoch
is expired, otherwisefalse
.
Rationale
First, do no harm
Introducing expirability as a token behavior in a way that doesn’t interfere with existing use cases or applications. For non-SBT tokens, transferability remains intact, ensuring compatibility with current systems, while expired tokens are simply flagged as unusable when validity checks are needed
Backwards Compatibility
This standard fully ERC-721, ERC-1155, ERC-5484 and SBTs compatible.
Reference Implementation
TODO
Security Considerations
No security considerations were found.
Historical links related to this standard
ERC that mentions “expiration” includes:
ERC-4907: ERC-721 User And Expires Extension (Final)
ERC-5007: ERC-721 Time Extension (Final)
ERC-5334: ERC-721 User And Expires And Level Extension (Draft)
ERC-5643: Subscription NFTs (Stagnant)
ERC-5727: Semi-Fungible Soulbound Token (Draft)
ERC-6036: Subscribable NFT Extension (Draft)
ERC-6785: ERC-721 Utilities extension (Draft)