EIP-8295 - State Tiering by Periods

Created 2026-06-10
Status Draft
Category Core
Type Standards Track
Authors
Requires

Abstract

This proposal introduces renewal-age pricing for Ethereum state writes. It builds on the last_written_block write-age metadata defined in EIP-8188, deriving a coarse period from each item's last-write block number. The gas cost of state-writing operations depends on whether the target state is in the Active or Inactive tier, determined by how many periods have elapsed since its last write. Writing to Inactive state is more expensive than writing to Active state. Read costs are unchanged.

This creates an economic incentive for state hygiene without mandating deletion, complementing the storage-tiering signal that EIP-8188 already provides to clients.

Motivation

State access costs are not uniform in practice, yet the gas schedule prices all writes the same regardless of how recently the target was mutated:

  1. Renewal age is unpriced: Writing to state that has not been mutated in years costs the same as writing to state mutated recently, despite different resource profiles. Recently mutated state is more likely to stay in the performance-critical mutable working set, while older state more often falls onto slower paths with deeper database lookups and higher write overhead. A flat write cost overcharges for the active set and undercharges mutations of long-unmodified state.

  2. Abandoned state imposes uncompensated costs: Once state is written, it persists indefinitely. The ongoing storage, indexing, and lookup costs fall on node operators with no continuing contribution from the party that created it. A renewal-age-based surcharge introduces a continuing cost signal for long-unmutated state without requiring explicit in-protocol expiry or deletion.

  3. A client-local signal cannot be priced: Clients could track write-age locally, but without a consensus price an attacker can cheaply mutate deeply write-inactive state to inflate every node's commit and storage costs. Pricing the surcharge in consensus closes this, and the EIP-8188 metadata it reads is canonically verified and syncable, unlike a local heuristic that each client would recompute and could not trust from peers.

EIP-8188 records when each piece of state was last written but does not change gas costs. This proposal turns that metadata into a price signal, charging more to mutate state that has been write-inactive for a long time.

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.

This proposal requires the last_written_block field defined in EIP-8188. EIP-8188 owns the encoding and the rules that update last_written_block. This proposal only reads that field to determine write pricing.

Parameters

Name Value Description
PERIOD_LENGTH TBD Blocks per period
PERIOD_START_BLOCK derived Number of the fork activation block at which this EIP takes effect
INACTIVE_MIN_AGE TBD Periods of write-inactivity before state enters the Inactive tier

Tier Gas Constants

Constant Description
INACTIVE_ACCOUNT_WRITE_SURCHARGE Additional gas charged when mutating an Inactive account
INACTIVE_STORAGE_WRITE_SURCHARGE Additional gas charged when mutating an Inactive storage slot

Each surcharge is added on top of the normal gas for the operation, not a replacement for it (see Tiered write costs). Active mutations are not repriced. Both surcharges MUST be greater than zero.

Period Definition

A period is a coarse bucket of consecutive block numbers, PERIOD_LENGTH blocks wide. The global current_period and an item's last_written_period are derived from a block number the same way:

current_period      = max(0, (block_number - PERIOD_START_BLOCK) // PERIOD_LENGTH)
last_written_period = max(0, (last_written_block - PERIOD_START_BLOCK) // PERIOD_LENGTH)

last_written_block is the field defined in EIP-8188.

Tier Determination and Gas Pricing

Renewal-age tiers

Given a state item's last_written_period, the renewal-age tier is determined by how many periods have elapsed since the last write:

period_age = current_period - last_written_period

if period_age < INACTIVE_MIN_AGE:
    tier = ACTIVE
else:
    tier = INACTIVE

Tiered write costs

The surcharge is charged per Inactive leaf that an operation writes. It is in addition to the normal gas for the operation, which is defined by EIP-8038 and EIP-8037. Reads are unaffected.

The general rule is:

The table below lists, for each state-changing operation, the leaves it can surcharge.

Operation Inactive leaves surcharged
SSTORE, existing slot changed the slot (storage), and the containing account via the storageRoot cascade (account)
SSTORE, new slot (0 to nonzero) the containing account via the cascade (account) only. The new slot is creation and pays no storage surcharge
SSTORE, slot cleared (nonzero to 0) the containing account via the cascade (account) only
CALL / CALLCODE with value the sender and the receiver (account), each if Inactive. A newly created receiver is exempt
Value-transfer transaction the sender and the receiver (account), charged in intrinsic gas
nonce increment the account whose nonce changes (account)
CREATE / CREATE2 the creator account (account). The new contract account, its code, and constructor-set slots are creation and exempt
SELFDESTRUCT each Inactive side whose balance changes, per the EIP-8188 rules. A contract created and destroyed in the same transaction has no leaf
EIP-7702 authorization the authority account (account), charged in intrinsic gas
reads (SLOAD, BALANCE, EXT*, value-less calls) none

The surcharge is regular-gas, not state-gas, because reviving Inactive state is I/O work rather than state growth. It is therefore subject to EIP-7825's TX_MAX_GAS_LIMIT like any other regular-gas, and it counts toward the block's regular-gas under EIP-8037's two-dimensional accounting. Intrinsic surcharges are folded into intrinsic gas and evaluated at transaction validation.

The surcharge is consumed like ordinary regular-gas:

State creation is not tiered

State creation is never classified as Inactive and never pays the surcharge. The exemption applies to the created leaf only: a new account, a new storage slot, or deployed code. An operation that creates one leaf while writing another existing Inactive leaf still surcharges that existing leaf. For example, SSTORE 0 -> x in a dormant contract creates the slot, which pays no storage surcharge, but revives the existing account leaf through the storageRoot cascade, which pays the account surcharge.

State creation is priced by EIP-8037. The inactive surcharge SHOULD be calibrated so that mutating existing Inactive state is not cheaper, in effective block-resource terms, than creating comparable new state under EIP-8037 (see "Relation with state creation cost" in the Rationale). Because creation is priced independently of the tier mechanism, there is no economic cliff at INACTIVE_MIN_AGE: the cost of creating new state does not jump when newly created state would otherwise age into the Inactive tier.

Disambiguation from EIP-2929 warm/cold

Active and Inactive are renewal-age tiers determined by last_written_block. They affect write costs only. EIP-2929's warm/cold distinction, which is a within-transaction first-touch concept, continues to apply to reads as before. For writes, first determine the item's renewal-age tier, then apply the corresponding tiered write cost.

Access List Interaction

Access lists (EIP-2930) pre-warm addresses and storage keys into the EIP-2929 accessed sets, removing transaction-local first-touch read overhead. Since read costs are not tiered, access-list read pricing is unaffected by this proposal.

However, access-list pre-warming MUST NOT erase the item's renewal-age tier for write purposes. If a transaction pre-warms a storage slot via an access list and later writes to it via SSTORE, the write MUST still pay the tiered write cost based on the slot's last_written_block.

Rationale

Why tier-based pricing?

A tier-based model with fixed gas constants per tier is used instead of a multiplier-based model.

Discrete tiers reflect the coarse resolution of the data, which is that state is either recently mutated or not recently mutated. Fixed constants are also easier for gas estimators to work with, such that an estimator only needs to determine whether the state falls into the Active or Inactive tier.

Why derive periods from the block number?

EIP-8188 records the raw block number, which is finer-grained than pricing needs. Bucketing it into fixed-length periods serves two purposes. First, it matches the coarse resolution of the pricing decision: only the Active/Inactive boundary matters, not the exact block of the last write. Second, it makes the tier stable and predictable for gas estimators, since every block within a period shares the same current_period and an item's tier changes only at period boundaries rather than every block.

PERIOD_LENGTH is denominated in blocks rather than wall-clock time. If slot timing changes materially, the number of blocks corresponding to the target period duration (~6 months) shifts, and PERIOD_LENGTH can be re-derived in a later EIP, in the same way EIP-8037 allows cost per state byte (CPSB) to be re-derived. Deriving the period from a block number also keeps it consistent with the field EIP-8188 actually stores, avoiding the need to store a separate timestamp per entry.

Is the Active tier bounded?

The Active tier is not strictly bounded by protocol, but is limited by gas over the renewal window.

For a tier window of W blocks, any Active item must have been written within that window, and each such refresh consumes gas. Therefore the maximum Active cardinality is bounded above by the total gas available in the window divided by the minimum marginal gas needed to refresh one additional item.

This is a worst-case upper bound. It assumes the protocol allows arbitrary refresh of any accounts or storage slots by any party. In practice, this bound is more natural for accounts than for storage slots, because not all storage slots can be refreshed independently by arbitrary actors.

The relevant claim is not that the Active set is strictly bounded, but that it is economically rate-limited and therefore can remain much smaller than the total state under write-renewal semantics.

Renewal gaming

There is an incentive to periodically rewrite state purely to keep it in the Active tier. This is by design: the proposal intends to make state maintenance an ongoing cost borne by the parties who benefit from cheap future writes.

The size of the surcharge should be chosen with this in mind. It must be large enough that letting state go write-inactive carries a real cost, but not so large that it pays to issue mass speculative refresh writes purely to keep state in the Active tier, which would inflate the very set this proposal aims to keep small.

Relation with state creation cost (EIP-8037)

The gas cost of state creation should be at least equal to the cost of mutating Inactive state, which is the normal write cost plus the inactive surcharge, and may be higher.

Creating new state performs no less work than writing to Inactive state. Both operations require traversing and updating the state trie, including recomputing hashes along the modified path. State creation in the worst case can also touch intermediate nodes that belong to existing Inactive state.

For this reason, mutating Inactive state must not be cheaper than creating comparable new state. Otherwise the gas schedule would create an inconsistency where reviving old Inactive state is a cheaper way to consume the same node resources than allocating fresh state.

This gas relation is distinct from EIP-8037's interaction with EIP-8188, which concerns the per-byte state-size parameters rather than write-tier pricing.

Backwards Compatibility

This is a backwards-incompatible gas repricing that requires a scheduled network upgrade. It depends on the EIP-8188 metadata and must activate at or after the same fork.

Wallets and RPC providers must update eth_estimateGas to reflect the surcharge. Estimators must determine each written item's renewal-age tier from its last_written_block and add the inactive surcharge when the item is Inactive.

Test Cases

Security Considerations

Write-inactivity is not read-inactivity

This proposal prices write-age only. A storage slot can be read on every block yet never rewritten, leaving it in the Inactive tier. The tier signal therefore must not be read as "cold" for access purposes. This proposal does not move any state. Only client storage policy does. Clients that relocate Inactive state to slower or denser storage should account for read frequency separately, since placing frequently read Inactive state on a slow path could become a denial-of-service vector. Read frequency is orthogonal to write-age, and reconciling the two is left to client storage policy, which is out of scope for this proposal.

Copyright

Copyright and related rights waived via CC0.