This EIP proposes to reduce the gas costs for transient storage operations (TLOAD and TSTORE) and warm storage loads (SLOAD) by implementing constant pricing. To prevent denial-of-service attacks through excessive memory allocation, a transaction-global limit on transient storage slots is introduced. This approach provides lower costs for common use cases while maintaining security against resource exhaustion attacks.
Motivation
EIP-1153 introduced transient storage with gas costs equivalent to warm storage operations (100 gas). The current pricing model presents several limitations:
Reentrancy Protection Cost: At 100 gas per operation, implementing reentrancy locks by default remains expensive enough to discourage universal adoption at the language level, leaving contracts vulnerable to one of the most common attack vectors.
Underutilization: The high cost is still punishing to developers who wish to use transient storage for other legitimate use cases such as temporary approvals, callback metadata, and cross-frame communication within transactions.
Pricing Inconsistency: Transient storage fundamentally requires fewer resources than persistent storage (no disk I/O, no state root updates), yet is priced identically to warm storage operations.
This EIP addresses these issues by implementing constant, lower pricing for transient storage operations while introducing a transaction-global limit to prevent denial-of-service attacks. It also makes the cost of warm storage cheaper. This provides lower costs and enables broader adoption of transient storage, while also providing hard resource limits for clients.
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.
Parameters
This EIP introduces the following parameters:
GAS_TLOAD = 5 (tent.)
GAS_TSTORE = 12 (tent.)
GAS_WARM_SLOAD = 5 (tent.)
MAX_TRANSIENT_SLOTS = 131072 (tent.)
GAS_TSTORE_ALLOCATE = 24 (tent.)
Gas Cost Changes
The gas cost for TLOAD (opcode 0x5c) is reduced from 100 to GAS_TLOAD.
The base gas cost for TSTORE (opcode 0x5d) is reduced from 100 to GAS_TSTORE.
The gas cost for warm SLOAD (opcode 0x54) is reduced from 100 to GAS_WARM_SLOAD.
Transaction-Global Transient Storage Limit
A transaction-global counter tracks the number of unique transient storage slots written across all contracts during transaction execution:
At the beginning of each transaction, initialize a counter transient_slots_used to 0.
When TSTORE is executed:
If the slot has not been written to during this transaction (across any contract), increment transient_slots_used.
If transient_slots_used exceeds MAX_TRANSIENT_SLOTS, the transaction MUST exceptionally halt.
The counter persists across all message calls in the transaction.
The counter is reset to 0 at the end of the transaction.
Implementation Note
Implementations MUST track transient storage allocated across all contracts. A slot is considered unique based on the tuple (contract_address, storage_key). Writing to the same slot multiple times within a transaction does not generally increment the counter after the first write (unless the slot gets deallocated with a revert).
Rationale
Constant Pricing with Hard Limit
This EIP implements constant pricing with a hard limit for several reasons:
A hard limit provides guarantees on the total resource consumption which are easier to reason about for clients, rather than needing to perform a calculation as a function of current gas limits to find out what total memory consumption could be.
Common use cases (e.g., reentrancy locks using 1-2 slots) are not penalized for worst case resource usage (like in DOS attacks).
Clients can safely pre-reserve all memory which could be used by transient storage up-front at the beginning of a transaction.
Gas Cost Selection
GAS_TLOAD (5 gas): Transient storage reads require only memory access without disk I/O.
GAS_WARM_SLOAD (5 gas): Warm storage loads from cache have similar performance characteristics to transient storage reads.
GAS_TSTORE (12 gas): Transient storage writes require memory allocation and journaling for revert support.
GAS_TSTORE_ALLOCATE (24 gas): Writes to fresh slots require memory allocation, which is more expensive than writing to an existing slot.
Hard Limit Selection
MAX_TRANSIENT_SLOTS of 131072 allows:
Sufficient slots for typical user applications
Memory usage bounded to approximately 8 MB per transaction (131072 slots * 64 bytes), which prevents OOM-based denial-of-service attacks
Design Alternatives Considered
Per-Contract Limits: Increased complexity in reasoning about resource consumption based on the shape of the call stack.
Superlinear Pricing: Adds complexity, and still punishes “common” (non-DOS) use cases.
No Limit: May allow memory-based DOS attack if transaction-level gas limits change, or if pricing changes in the future.
The benefit of a hard limit is that the resource consumption is bounded in a predictable way even in the presence of other parameter changes in the protocol.
Backwards Compatibility
No known issues, besides the gas cost of existing operations being cheaper.
Test Cases
TBD
Reference Implementation
# Pseudo-code for transaction execution with global transient storage limit
GAS_TLOAD=5GAS_TSTORE=12GAS_TSTORE_ALLOCATE=24GAS_WARM_SLOAD=5MAX_TRANSIENT_SLOTS=131072classTransactionContext:def__init__(self):self.transient_storage={}# (address, key) -> value
self.unique_slots=set()# set of (address, key) tuples
self.transient_slots_used=0deftload(self,address:Address,key:Bytes32)->Bytes32:# Charge gas
self.charge_gas(GAS_TLOAD)# Return value or zero
returnself.transient_storage.get((address,key),Bytes32(0))deftstore(self,address:Address,key:Bytes32,value:Bytes32):# Charge gas
self.charge_gas(GAS_TSTORE)# Check if this is a new unique slot
slot_id=(address,key)ifslot_idnotinself.unique_slots:self.charge_gas(GAS_TSTORE_ALLOCATE)self.unique_slots.add(slot_id)# Check limit
iflen(self.unique_slots)>MAX_TRANSIENT_SLOTS:raiseExceptionalHalt("Transient storage limit exceeded")# Store value
self.transient_storage[slot_id]=valuedefwarm_sload(self,address:Address,key:Bytes32)->Bytes32:# Charge reduced gas for warm storage
self.charge_gas(GAS_WARM_SLOAD)# Load from persistent storage (implementation depends on client)
returnself.load_from_storage(address,key)
Security Considerations
With MAX_TRANSIENT_SLOTS = 131072, maximum memory allocation is bounded to 8MB per transaction (131072 * 64 bytes). Compared to limits under current pricing (100 gas), a 30M gas transaction can allocate up to 300,000 slots (19.2 MB). This EIP reduces the maximum allocated amount by 56%.
Charles Cooper (@charles-cooper), Ben Adams (@benaadams), "EIP-7971: Hard Limits for Transient Storage [DRAFT]," Ethereum Improvement Proposals, no. 7971, June 2025. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-7971.