{"id":"ERC-7818","name":"Expirable ERC-20","status":"Final","chain":"both","category":{"id":"defi","name":"DeFi Patterns","description":"Vaults, bonds, and financial primitives"},"journeyStages":[{"id":"asset-display","name":"Asset Discovery & Display","description":"Seeing tokens, NFTs, and balances"},{"id":"management","name":"Asset Management","description":"Managing assets over time"}],"uxImpact":"ERC-7818 adds expiration timestamps to ERC-20 token batches. Applications can show when each batch expires and apply the deployed contract's transfer and spending rules; FIFO selection and burn/recycle behavior are optional implementation choices.","officialUrl":"https://ercs.ethereum.org/ERCS/erc-7818","lastReviewed":"2026-09-18","lastUpdated":"2026-09-18","kind":"proposal","hasDetailedContent":true,"content":{"id":"ERC-7818","summary":"ERC-7818 adds expiration timestamps to ERC-20 token batches. Applications can show when each batch expires and apply the deployed contract's transfer and spending rules; FIFO selection and burn/recycle behavior are optional implementation choices.","specConstraints":[{"id":"erc20-expiry-interface","constraint":"Compatible implementations must inherit the ERC-20 interface and meet this specification's function behavior.","appliesTo":"account-implementation","normativity":"normative","specQuote":"Compatible implementations **MUST** inherit from [ERC-20](./eip-20.md)'s interface and **MUST** have all the following functions and all function behavior **MUST** meet the specification.","specSection":"Specification / Balance Look Back Over Epochs / Example Scenario","specUrl":"https://ercs.ethereum.org/ERCS/erc-7818#balance-look-back-over-epochs","designImplication":"Reuse ERC-20 balance and transfer expectations while making expiration and validity visible as additional account state."},{"id":"usable-balance-excludes-expired","constraint":"balanceOf must include only still-valid tokens and must exclude expired tokens from the usable balance.","appliesTo":"application","normativity":"normative","specQuote":"- `balanceOf` **MUST** return the total balance of tokens held by an account that are still valid (i.e., have not expired). This includes any tokens associated with specific epochs, provided they remain within their validity duration. Expired tokens **MUST NOT** be included in the returned balance, ensuring that only actively usable tokens are reflected in the result.","specSection":"Specification / Behavior Specification","specUrl":"https://ercs.ethereum.org/ERCS/erc-7818#behavior-specification","designImplication":"Separate usable and expired amounts in balances and never let an expired token appear spendable merely because it remains in historical storage."},{"id":"expired-epoch-balance","constraint":"balanceOfAtEpoch must return zero for an expired epoch.","appliesTo":"application","normativity":"normative","specQuote":"- `balanceOfAtEpoch` **MUST** return the balance of tokens held by an account at the specified `epoch`. If the specified epoch is expired, this function **MUST** return `0`.","specSection":"Specification / Behavior Specification","specUrl":"https://ercs.ethereum.org/ERCS/erc-7818#behavior-specification","designImplication":"Show an expired epoch as zero usable balance while preserving any separate historical or audit view."},{"id":"epoch-metadata-views","constraint":"The contract must expose currentEpoch, epochLength, epochType, and validityDuration according to the specified meanings.","appliesTo":"application","normativity":"normative","specQuote":"- `currentEpoch` **MUST** return the current `epoch` of the contract.\n- `epochLength` **MUST** return duration between `epoch` in blocks or time in seconds.\n- `epochType` **MUST** return the type of epoch used by the contract, which can be either `BLOCKS_BASED` or `TIME_BASED`.\n- `validityDuration` **MUST** return the validity duration of tokens in terms of `epoch` counts.","specSection":"Specification / Behavior Specification","specUrl":"https://ercs.ethereum.org/ERCS/erc-7818#behavior-specification","designImplication":"Use the contract's epoch type and validity duration to label expiry dates and avoid presenting block-based and time-based epochs as interchangeable."},{"id":"epoch-expiration-definition","constraint":"isEpochExpired must return whether an epoch is expired, using documented logic based on the epoch measurement type.","appliesTo":"account-implementation","normativity":"normative","specQuote":"- `isEpochExpired` **MUST** return true if the given `epoch` is expired, otherwise `false`.","specSection":"Specification / Behavior Specification","specUrl":"https://ercs.ethereum.org/ERCS/erc-7818#behavior-specification","designImplication":"Read the deployed contract's expiration rule before showing a token as expired, and surface whether the rule is based on blocks or time."},{"id":"nonexpired-transfer-only","constraint":"transfer and transferFrom must transfer only non-expired tokens; expired-token attempts must revert or return false, while earliest-expiring-first selection is optional.","appliesTo":"application","normativity":"normative","specQuote":"- `transfer` and `transferFrom` **MUST** exclusively transfer tokens that remain non-expired at the time of the transaction. Attempting to transfer expired tokens **MUST** revert the transaction or return `false`. Additionally, implementations **MAY** include logic to prioritize the automatic transfer of tokens closest to expiration, ensuring that the earliest expiring tokens are used first, provided they meet the non-expired condition.","specSection":"Specification / Behavior Specification","specUrl":"https://ercs.ethereum.org/ERCS/erc-7818#behavior-specification","designImplication":"Block or explain expired-token sends, and show FIFO selection only when the deployed contract actually implements that optional behavior."},{"id":"epoch-specific-transfer","constraint":"Epoch-specific transfers must transfer the requested amount from the specified epoch and must revert or return false when that epoch has expired.","appliesTo":"application","normativity":"normative","specQuote":"- `transferAtEpoch` and `transferFromAtEpoch` **MUST** transfer the specified number of tokens held by an account at the specified epoch to the recipient, If the epoch has expired, the transaction **MUST** `revert` or return `false`","specSection":"Specification / Behavior Specification","specUrl":"https://ercs.ethereum.org/ERCS/erc-7818#behavior-specification","designImplication":"Show the selected epoch in a transfer confirmation and keep an expired epoch unavailable rather than silently substituting another batch."},{"id":"total-supply-sentinel","constraint":"Implementations should set totalSupply to zero or type(uint256).max because tracking only valid non-expired tokens is difficult.","appliesTo":"account-implementation","normativity":"normative","specQuote":"- `totalSupply` **SHOULD** be set to `0` or `type(uint256).max` due to the challenges of tracking only valid (non-expired) tokens.","specSection":"Specification / Behavior Specification","specUrl":"https://ercs.ethereum.org/ERCS/erc-7818#behavior-specification","designImplication":"Do not infer spendable supply or user value from totalSupply alone; use the deployed contract's usable-balance semantics."},{"id":"transfer-gas-denial","constraint":"Transferring multiple groups of small tokens or looping transfers can run out of gas.","appliesTo":"application","normativity":"security-considerations","specQuote":"Run out of gas problem due to the operation consuming higher gas if transferring multiple groups of small tokens or loop transfer.","specSection":"Security Considerations / Denial Of Service","specUrl":"https://ercs.ethereum.org/ERCS/erc-7818#denial-of-service","designImplication":"Warn before large or fragmented transfers and provide a smaller-batch recovery path when gas estimation becomes unsafe."},{"id":"transfer-block-gas-limit","constraint":"Transfers can exceed the block gas limit when the blockchain limit is lower than the transaction's required gas.","appliesTo":"application","normativity":"security-considerations","specQuote":"Exceeds block gas limit if the blockchain has a block gas limit lower than the gas used in the transaction.","specSection":"Security Considerations / Gas Limit Vulnerabilities","specUrl":"https://ercs.ethereum.org/ERCS/erc-7818#gas-limit-vulnerabilities","designImplication":"Compare estimated gas with the chain's effective block limit and explain when a transfer must be split."},{"id":"epoch-time-halt-risk","constraint":"Using block.timestamp for epochs can freeze time-based logic during rare network halts, risking asset integrity and inconsistent states.","appliesTo":"application","normativity":"security-considerations","specQuote":"if using `block.timestamp` for calculating `epoch` and In rare network halts, block production stops, freezing `block.timestamp` and disrupting time-based logic. This risks asset integrity and inconsistent states.","specSection":"Security Considerations / Block values as a proxy for time","specUrl":"https://ercs.ethereum.org/ERCS/erc-7818#block-values-as-a-proxy-for-time","designImplication":"Show a stale-chain or paused-expiry state when block production stops instead of promising that a wall-clock deadline has advanced."},{"id":"bulk-expiry-fairness","constraint":"Implementations where all tokens in an epoch share an expiration can cause bulk expiration.","appliesTo":"application","normativity":"security-considerations","specQuote":"In a straightforward implementation, where all tokens within the same epoch share the same expiration (e.g., at `epoch`:`x`), bulk expiration occurs.","specSection":"Security Considerations / Fairness Concerns","specUrl":"https://ercs.ethereum.org/ERCS/erc-7818#fairness-concerns","designImplication":"Warn users when an entire batch may expire together and avoid implying a smooth, individually staggered decay without implementation evidence."},{"id":"liquidity-pool-expiry-risk","constraint":"Tokens with expiration dates can expire while deposited in a liquidity pool.","appliesTo":"application","normativity":"security-considerations","specQuote":"When tokens with expiration dates are deposited into liquidity pools (e.g., in DEXs), they may expire while still in the pool.","specSection":"Security Considerations / Risks in Liquidity Pools","specUrl":"https://ercs.ethereum.org/ERCS/erc-7818#risks-in-liquidity-pools","designImplication":"Show expiry risk in pool deposit and position views, and do not label a deposited expiring token as continuously liquid."}],"applicability":{"whenToUse":["Your product addresses: loyalty points that should expire never do on-chain.","Your product addresses: promotional tokens remain valid forever.","The flow should deliver: on-chain expiration with the deployed contract's transfer and spending rules clearly shown.","You are designing a expiration warning display experience with visible states and recovery paths."],"whenToAvoid":["The token has no per-batch expiration data or the deployed contract does not implement ERC-7818.","The product has no balance or transfer flow where expiration changes user decisions.","The implementation does not expose batch selection or a product-defined expiry policy.","No token balances, swaps, lending, or yield flows appear in the product."]},"designerTakeaways":["Design the flow to support on-chain expiration.","Design the flow to support promotion expires, tokens have clear end date.","Design the flow to support \"Use before Jan 31\" creates healthy urgency."],"problemsSolved":[{"problem":"Loyalty points that should expire never do on-chain","oldWay":"Off-chain expiration tracking, inconsistent enforcement","newWay":"On-chain expiration, with transfer and spending behavior determined by the deployed implementation","impact":"critical"},{"problem":"Promotional tokens remain valid forever","oldWay":"One-time rewards circulate indefinitely, diluting value","newWay":"Promotion expires, tokens have clear end date","impact":"high"},{"problem":"No urgency to use rewards","oldWay":"Users hoard rewards, never engage","newWay":"\"Use before Jan 31\" creates healthy urgency","impact":"high"},{"problem":"Complex backend for expiring balances","oldWay":"Track expiration per-user off-chain, sync issues","newWay":"Expiration timestamps are on-chain; query the deployed contract's transfer and spending rules","impact":"medium"}],"uxPatterns":[{"name":"Expiration Warning Display","description":"Show tokens with approaching expiration","userFlow":["User views reward balance","Tokens sorted by expiration when the product chooses that policy","Visual countdown for each batch","Warning highlights urgent expirations","Quick action to use expiring tokens"]},{"name":"Batch Expiration View","description":"Show multiple token batches with different expirations","userFlow":["Query all token batches for user","Group by expiration date","Color-code by urgency","Suggest using the soonest-expiring batch when the implementation supports selection","Auto-select expiring tokens only when the contract and product support that policy"]},{"name":"Spending with Auto-Select","description":"Use the soonest-expiring tokens first when the deployed contract supports that policy","userFlow":["User initiates purchase","System auto-selects expiring tokens first when the deployed contract supports batch selection","Show which batches will be used","Display remaining balance by batch","Confirm transaction"]},{"name":"Expiration Notification","description":"Alert users before tokens expire","userFlow":["System checks for expiring tokens","Send notification before expiration","Show clear deadline","Provide direct link to use tokens","Allow snooze for reminder"]}],"uiComponents":[{"name":"ExpirationBadge","description":"Small indicator showing expiration status","states":["valid","expiring-soon","expired"],"props":["expirationDate","urgencyThreshold"]},{"name":"TokenBatchList","description":"List tokens grouped by expiration date","states":["loading","loaded","empty"],"props":["batches[]","sortBy","onSelectBatch"]},{"name":"ExpirationCountdown","description":"Visual countdown timer to expiration","states":["days","hours","minutes","expired"],"props":["expirationDate","format"]},{"name":"AutoSelectPreview","description":"Show which batches the deployed contract or application will select for spend","states":["calculating","selected","modified"],"props":["amount","batches[]","selectedBatches[]"]}],"antiPatterns":[{"pattern":"Hiding expiration until it's too late","why":"Users feel tricked when tokens suddenly expire","instead":"Show expiration prominently from day one","severity":"critical"},{"pattern":"No notification before expiration","why":"Users may not check balances, lose tokens unknowingly","instead":"Send reminders at 7 days, 3 days, 1 day before","severity":"high"},{"pattern":"Using newest tokens first","why":"Old tokens expire unused while new ones get spent","instead":"Prefer the soonest-expiring batch when supported; otherwise let users choose and explain the policy","severity":"high"},{"pattern":"Showing only total balance without breakdown","why":"Users don't know some of their balance expires soon","instead":"Show balance breakdown by expiration date","severity":"medium"}],"keyTakeaways":["Always show expiration prominently - no surprises","Prefer FIFO when the deployed contract supports batch selection; otherwise disclose the spending policy","Send notifications before tokens expire","Show breakdown by expiration date, not just total"],"technicalNotes":"ERC-7818 extends ERC-20 with expiration timestamps per token batch. The standard requires non-expired transfer behavior and makes nearest-expiry selection MAY; extra epoch/batch functions are optional. FIFO appears in the reference implementation rather than as a universal UI or contract rule. Burn or recycle behavior is also implementation-specific, so query the deployed contract before promising either.","lastUpdated":"2026-09-18","sources":[{"label":"ERC-7818 official specification","url":"https://ercs.ethereum.org/ERCS/erc-7818","type":"official-spec"}],"lastReviewed":"2026-09-18"},"sources":[{"label":"Official specification","url":"https://ercs.ethereum.org/ERCS/erc-7818","type":"official-spec"},{"label":"Discussion search","url":"https://ethereum-magicians.org/search?q=ERC-7818","type":"discussion"}],"urls":{"canonical":"https://www.eipsfordesigners.com/standards/ERC-7818","markdown":"https://www.eipsfordesigners.com/standards/ERC-7818/content.md","agent":"https://www.eipsfordesigners.com/standards/ERC-7818/agent.md","api":"https://www.eipsfordesigners.com/api/standards/ERC-7818","official":"https://ercs.ethereum.org/ERCS/erc-7818","discussion":"https://ethereum-magicians.org/search?q=ERC-7818"},"freshness":{"lastReviewed":"2026-09-18","lastUpdated":"2026-09-18"}}