---
eip: 8125
title: Temporary Contract Storage
description: Adds bounded-lifetime contract storage via TMPLOAD/TMPSTORE opcodes that is automatically cleared on a fixed schedule.
author: Wei Han Ng (@weiihann)
discussions-to: https://ethereum-magicians.org/t/eip8125-temporary-contract-storage/27440
status: Draft
type: Standards Track
category: Core
created: 2026-01-14
requires: 2200, 2929
---

## Abstract

This EIP introduces *temporary storage*: a new contract-accessible key-value store that persists across transactions and blocks, but is automatically cleared at a protocol-defined schedule. Two new opcodes are added:

- `TMPSTORE(key, value)` to write temporary storage for the executing contract.
- `TMPLOAD(key)` to read temporary storage for the executing contract.

Temporary storage is intended for data that does not need indefinite retention, providing a safer alternative to using permanent state for ephemeral data and enabling bounded growth of this class of state.

## Motivation

![](../assets/eip-8125/1.png)

*Figure 1: Over 60% of the storage slots are written once and never accessed again onchain.*

Permanent contract storage is costly because it increases long-term node resource requirements (disk, I/O, state maintenance). However, many applications write data that is only valuable for a bounded time window.

This EIP provides:

- **Bounded lifetime semantics** without requiring explicit deletes.
- **Predictable clearing** that can be relied upon at the application layer.
- **A building block** for reducing state growth created by ephemeral use cases.

## 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](https://www.rfc-editor.org/rfc/rfc2119) and [RFC 8174](https://www.rfc-editor.org/rfc/rfc8174).

### Constants and parameters

|Name|Value|Description|
|-|:-:|-|
| `FORK_BLOCK` | TBD | Activation block of this EIP |
| `TEMP_STORAGE_PERIOD` | TBD | The number of blocks that the temporary storage holds for a given period |
| `TEMP_STORAGE_SYSTEM_ADDRESS_0` | TBD | First reserved address used to store temporary storage |
| `TEMP_STORAGE_SYSTEM_ADDRESS_1` | TBD | Second reserved address used to store temporary storage |
| `TMPLOAD_GAS` | TBD | The gas cost of a warm read at temporary storage slot |
| `COLD_TMPLOAD_COST` | TBD | The gas cost surchage of a cold access at the temporary storage |
| `TMPSTORE_SET_GAS` | TBD | The gas cost of setting a slot from zero to non-zero at the temporary storage |
| `TMPSTORE_RESET_GAS` | TBD | The gas cost of changing a non-zero slot to another value at the temporary storage |

### Temporary storage data model

Temporary storage is backed by **two reserved system accounts** at `TEMP_STORAGE_SYSTEM_ADDRESS_0` and `TEMP_STORAGE_SYSTEM_ADDRESS_1`.

The temporary storage keyspace is a mapping of `(contract_address, key) -> value`, where:

```
derived_slot_key = keccak256(contract_address || key)
```

Temporary storage values are stored in the regular storage tries of the system accounts using `derived_slot_key` as the slot key.

### Periodic rollover (storage reset)

Temporary storage is organized into periods, each of length `TEMP_STORAGE_PERIOD` blocks after activation.

For blocks with `block.number >= FORK_BLOCK`, define:

```
period_index(block_number) = floor((block_number - FORK_BLOCK) / TEMP_STORAGE_PERIOD)
```

Define the current and previous system accounts for a block `B` (where `B.number >= FORK_BLOCK`) as:

```
current_index = period_index(B.number) % 2

if current_index == 0:
    CURRENT_SYSADDR = TEMP_STORAGE_SYSTEM_ADDRESS_0
    PREVIOUS_SYSADDR = TEMP_STORAGE_SYSTEM_ADDRESS_1
else if current_index == 1:
    CURRENT_SYSADDR = TEMP_STORAGE_SYSTEM_ADDRESS_1
    PREVIOUS_SYSADDR = TEMP_STORAGE_SYSTEM_ADDRESS_0
```

At the first block of each new period, the protocol MUST clear the storage of the system account that becomes the current.

When processing a block `B` (with parent `P`) where both are `>= FORK_BLOCK`, if `period_index(B.number) > period_index(P.number)`, then before executing transactions in `B`, the protocol MUST perform:

- `CURRENT_SYSADDR.storageRoot = EMPTY_TRIE_ROOT`

where `CURRENT_SYSADDR` is computed from `B.number` as specified above.

No other accounts are modified by the rollover.

**Effect:** After rollover, entries written in the immediately previous period remain readable via `PREVIOUS_SYSADDR` for one additional period, while entries older than one period are removed.

### New opcodes

Add 2 new opcodes as follows:

#### `TMPLOAD(key)`

Compute `derived_slot_key` from `(contract_address, key)`. Then load `derived_slot_key` from `CURRENT_SYSADDR` first. If it doesn't exist (i.e. `value == 0` ), then load `derived_slot_key` from `PREV_SYSADDR`.

#### `TMPSTORE(key,value)`  

Compute `derived_slot_key` from `(contract_address, key)`. Then:

- If `value != 0`, store in the storage of `CURRENT_SYSADDR`.
- If `value == 0`, delete slot from both `CURRENT_SYSADDR` and `PREV_SYSADDR`.

**Rationale:** Because reads fall back from current to previous, clearing only the current store would allow an older previous-period value to be returned. Clearing both ensures `TMPSTORE(key,0)` makes subsequent `TMPLOAD(key)` return `0` (until a new non-zero is written).

The call rules should follow `SSTORE` and `SLOAD`.

The gas rules should follow the conventions in [EIP-2929](./eip-2929.md) and [EIP-2200](./eip-2200.md) referencing `SSTORE` and `SLOAD`, except that:

- The gas costs should be much lower than `SSTORE` and `SLOAD`, but higher than `TSLOAD` and `TSTORE`
- No refunds are given for clearing temporary storage
- `TMPLOAD` MAY perform up to 2 storage reads (current then previous). Gas MUST account for this behaviour.
- Deletion in `TMPSTORE` is more expensive than creation/modification as the storage root of both system accounts are updated. Gas MUST account for this behaviour.

### Activation and reserved address

At `FORK_BLOCK`, the chain MUST treat `TEMP_STORAGE_SYSTEM_ADDRESS_0` and `TEMP_STORAGE_SYSTEM_ADDRESS_1` as reserved addresses:

- If either does not exist in the state trie, it is created with:
  - `nonce = 1`
  - `balance = 0`
  - `codeHash = EMPTY_CODE_HASH`
  - `storageRoot = EMPTY_TRIE_ROOT`

No contract code is deployed at this address.

## Rationale

### Why not just use transient storage (EIP-1153)?

[EIP-1153](./eip-1153.md) (transient storage) is discarded after every transaction, which is ideal for intra-tx operations. This EIP targets a different class of use cases where data must persist across multiple transactions/blocks, but does not need indefinite retention.

### Why two-period rollover?

Two-period rollover provides a stronger usability guarantee:

- An entry is retained for **at least** one full `TEMP_STORAGE_PERIOD` after it is written (it will be readable in the next period via the previous-period system account).
- An entry is retained for **at most** two periods (once the current-period system account is cleared and reused).

This provides a simple mental model for contract developers. That is, if an entry is stored in temporary storage, it will at least last for `TEMP_STORAGE_PERIOD`.

In contrast, a single global rollover makes effective lifetime depend on when data is written within the period: a write near the end of a period could be cleared almost immediately, which may not be developer-friendly.

### Why two system accounts?

Using two reserved system accounts allows constant-time rollover:

- At each period boundary, only the **current-period** system account is reset by setting a single `storageRoot = EMPTY_TRIE_ROOT`.
- The previous-period system account remains intact for one more period.
- Storage older than one period becomes unreachable and can be pruned.

## Backwards Compatibility

This EIP requires a hard fork to implement.

It does not change behavior of any existing opcodes. Therefore, it is backward compatible with all existing contract accounts.

## Security Considerations

- **DoS surface**: Temporary storage writes still create state that must be processed and stored for at least 1 period. Gas costs should be calibrated so that worst-case temp writes do not create new per-block disk I/O DoS attack vectors beyond existing storage writes.
- **Reorg safety**: Clients should retain sufficient history to handle plausible reorg depths.
- **Application safety**: Contracts MUST treat temp storage as ephemeral and handle the case where entries are unexpectedly missing.

## Copyright

Copyright and related rights waived via [CC0](../LICENSE.md).
