ERC-7858: Expirable NFTs and SBTs
Non-fungible (NFT) and soulbound (SBT) tokens with expiration, supporting time-limited use cases.
Abstract
Introduces an extension for ERC-721 Non-Fungible Tokens (NFTs) and Soulbound Tokens (SBTs) that adds an expiration mechanism, allowing tokens to become invalid after a predefined period. This additional layer of functionality ensures that the expiration mechanism does not interfere with existing NFTs or SBTs, preserving transferability for NFTs and compatibility with current DApps such as NFT Marketplace. Expiration can be defined using either block height or timestamp, offering flexibility for various use cases.
Motivation
Before this EIP, NFTs and SBTs implemented expiration mechanisms via custom mappings. However, this approach presents challenges, such as inconsistent integration with other smart contracts, the custom ERC-721 implementations might misbehave when used in NFT marketplaces. This EIP addresses these issues by providing a built-in expiration mechanism and interfaces that are compatible with existing infrastructure. It supports both per-token and epoch-based expiry, allowing developers to choose the appropriate expiry type for their use cases, including:
- Access and Authentication
- 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 keywords “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
Behavior Specification
balanceOf
that inherited from ERC-721 MUST return all tokens even if expired; they still exist but are unusable due to the limitation of tracking expired token on-chain.- For NFTs
transferFrom
, andsafeTransferFrom
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
.isTokenExpired
is used for retrieving the status of the giventokenId
the function MUST returntrue
if the token is expired and MUST revert if thetokenId
does not exist for implementation that use custom error SHOULD revert withERC721NonexistentToken
following the relevant ERC-6093 and for implementation that using Solidity version belowv0.8.4
or those preferring to use revert with string error SHOULD revert withNonexistToken
, If thetokenId
exists and is not expired, the function MUST returnfalse
.startTime
andendTime
oftokenId
, 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
has no time-limited. If thetokenId
does not exist MUST revert same as in functionisTokenExpired
.supportInterface
forIERC7858
is0x3ebdfa31
forIERC7858Epoch
is0x8f55b98a
TokenExpiryUpdated
MUST be emitted when the token is minted or when its expiration details (startTime
orendTime
) are updated.
Extension Interface
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
.
unexpiredBalanceOfAtEpoch
MUST return unexpired or usable balance of tokens held by an account at the specifiedepoch
,If the specifiedepoch
is expired, this function MUST return0
. For example, if epoch5
has expired, callingunexpiredBalanceOfAtEpoch(5, address)
returns0
even if there were tokens previously held in that epoch.unexpiredBalanceOf
MUST return only unexpired or usable tokens.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
.
Additional Potential Useful Function
These OPTIONAL functions provide additional functionality that might be useful depending on the specific use case.
Base (default)
getRemainingDurationBeforeTokenExpired
returns the remaining time or blocks before the giventokenId
is expired.
Epoch (extension)
getEpochBalance
returns the amount of tokens stored in a given epoch, even if the epoch has expired.
getEpochInfo
returns both the start and end of the specifiedepoch
.
getNearestExpiryOf
returns the list oftokenId
closest to expiration, along with an estimated expiration block number or timestamp based onepochType
.
getRemainingDurationBeforeEpochChange
returns the remaining time or blocks before the epoch change happens, based on theepochType
.
Rationale
First, do no harm
Introducing expirability as an additional layer of functionality ensures it doesn’t interfere with existing use cases or applications. For non-SBT tokens, transferability remains intact, maintaining compatibility with current systems. Expired tokens are simply flagged as unusable during validity checks, treating expiration as an enhancement rather than a fundamental change.
Expiry Types
Defining expiration by either block height (block.number
) or block timestamp (block.timestamp
) offers flexibility for various use cases. Block-based expiration suits applications that rely on network activity and require precise consistency, while time-based expiration is ideal for networks with variable block intervals.
Backwards Compatibility
This standard is fully compatible with ERC-721, ERC-5484 and other SBTs.
Reference Implementation
You can find our reference implementation here.
Security Considerations
Burn and Re-Mint
Implementation should ensure that burning token and re-minting it with the same tokenId
will not introduce an unauthorized renewal.
Copyright
Copyright and related rights waived via CC0.