---
eip: 8253
title: Upgrade pre-Spurious-Dragon accounts
description: Set nonce to 1 for accounts with empty code, zero nonce, and non-empty storage via an irregular state transition
author: Jochem Brouwer (@jochem-brouwer)
discussions-to: https://ethereum-magicians.org/t/eip-8253-upgrade-pre-spurious-dragon-accounts/28505
status: Draft
type: Standards Track
category: Core
created: 2026-05-05
requires: 161, 684
---

## Abstract

At the start of the designated fork block, set the nonce to `1` for a fixed list of accounts that have empty code, zero nonce, and non-empty storage. Bumping the nonce makes these accounts fail the [EIP-684](./eip-684.md) precondition for contract creation (`nonce == 0`), so a future `CREATE`/`CREATE2` cannot collide with their storage. This is offered as an alternative to [EIP-7610](./eip-7610.md), which adds a runtime storage check on every contract creation.

## Motivation

[EIP-684](./eip-684.md) requires zero nonce and zero code length at a `CREATE` destination. Pre-[EIP-161](./eip-161.md), `CREATE` did not increment the nonce before init code ran, so a constructor that wrote storage and returned no code left an account with zero nonce and non-empty storage. EIP-7610 fixes the resulting collision risk by additionally requiring empty storage at the destination. This however needs an extra check in runtime code, and for some clients/database layouts also another disk read, which is paid forever, guarding against a non-growing address set after Spurious Dragon.

This EIP defuses the same collision risk with a one-time, per-account nonce bump. After the fork, the targeted accounts have `nonce == 1`, so EIP-684 already rejects any future `CREATE`/`CREATE2` to them: no runtime storage check is required. The motivation parallels [EIP-7523](./eip-7523.md), which similarly cleaned up empty accounts to retire EIP-161's "touch" edge cases from re-execution and test logic. Removing this account shape (zero-nonce, no code, non-empty storage) from future state also retires the analysis and test-case burden it imposes on subsequent EIPs, and ensures that the state does not include the theoretical path where one can create a contract on an address which has zero nonce and no code, but with non-empty storage.

## Specification

The key words "MUST", "MUST NOT", in this document are to be interpreted as described in [RFC 2119](https://www.rfc-editor.org/rfc/rfc2119).

### Mainnet account list

The targeted Mainnet accounts are published as [`targeted-accounts.json`](../assets/eip-8253/targeted-accounts.json) (28 entries). Each entry contains the 20-byte address and its `keccak256` hash. The published file additionally records each account's non-zero storage slots (slot key and its hash), as supporting information for the predicate `storageRoot != EmptyRootHash` as we can construct the storage root ourselves and see that it matches the reported root. The slot information is not used by the state transition.

The construction procedure and verification (against `eth_getProof` on `latest`) are documented in [`methodology.md`](../assets/eip-8253/methodology.md). The 224-entry pre-EIP-161-deletion superset at the Spurious Dragon block is published alongside, as [`zero-nonce-matches.jsonl`](../assets/eip-8253/zero-nonce-matches.jsonl).

### State transition

At the start of the fork block, before any pre-execution system contract calls (e.g. those introduced by [EIP-2935](./eip-2935.md), [EIP-4788](./eip-4788.md), [EIP-7002](./eip-7002.md), [EIP-7251](./eip-7251.md)) and before any transactions, for each account `A` in the list:

1. The nonce of `A` MUST be set to `1`.
2. `balance`, `codeHash`, and `storageRoot` of `A` are not modified.

The transition produces no transaction, receipt, or log, and consumes no gas.

### Application to non-Mainnet chains

Chains forked from Mainnet post-Spurious-Dragon can apply the same list. Other chains have to generate the list for their own state, listing both the address preimages and their hashes. Chains which never had such accounts at genesis and never had a way to create them end up with an empty list and MAY ignore this EIP.

### Interaction with EIP-7928

If [EIP-7928](./eip-7928.md) is active at the fork block, the BAL MUST encode the changes at `block_access_index = 0`, ordered before any pre-execution system contract calls. For each targeted account `A`, the `AccountChanges` entry is:

- `address = A`;
- `nonce_changes`: a single `NonceChange` `[0, 1]` (`block_access_index = 0`, `new_nonce = 1`);
- `storage_changes`, `storage_reads`, `balance_changes`, `code_changes`: empty.

Re-applying the BAL to the pre-fork-block state root MUST reproduce the post-fork-block state root.

## Rationale

**Irregular state transition vs. runtime check.** EIP-7610 leaves the offending state in place and pays a per-creation cost forever to guard a closed address set. This EIP pays a one-time consensus mutation instead, which is the same pattern Ethereum has used for prior cleanups (e.g. EIP-7523).

**Nonce bump vs. storage clear.** Clearing each targeted account's storage was considered. It is more invasive: it deletes at least one, but up to multiple, slots per account. The `storageRoot` has to be re-calculated (and should evaluate to the empty storage root) which is a more complex algorithm to change the value of the account RLP field than simply changing the value (the nonce from 0 to 1). For Mainnet, this would need 129 storage entries on 28 accounts, where the nonce bump only needs 28 nonce bumps for the 28 accounts. Bumping the nonce is the smallest possible change that still defuses the EIP-684 collision risk: a single per-account scalar update. The accounts continue to look like contracts (post-bump they have `nonce == 1`, which is the standard "deployed contract" shape), only without code: a state that is already representable on Ethereum: if one creates a contract and writes to storage, but deposits code of length 1, this account will thus have non-empty storage, nonce 1, and no code.

**Smaller test surface.** Pre-EIP, the protocol had to reason about an account shape (zero nonce, no code, non-empty storage) that no other path can produce post-Spurious-Dragon. Removing this shape from future state retires that edge case from re-execution logic and from EIP test surfaces.

**BAL encoding.** Bumping a nonce is exactly one `NonceChange` per account; no other field of `A` changes. The BAL therefore lets a block builder emit these changes at index 0 for correct state root calculation without re-executing the irregular transition.

## Backwards Compatibility

This is a consensus change and requires a hard fork. Targeted accounts have empty code, so they are not callable as contracts and have no associated key. After the fork they have `nonce == 1`, no code, the same balance, and the same storage. A future `CREATE`/`CREATE2` to such an address now reverts under [EIP-684](./eip-684.md) (because `nonce != 0`) rather than under [EIP-7610](./eip-7610.md)'s storage check; the externally-observable result is the same.

## Test Cases

1. **Nonce bump.** `A` has empty code, zero nonce, balance `b`, and non-zero storage `S`. After the fork block, `A` has nonce `1`, balance `b`, empty code, and storage `S` unchanged.
2. **CREATE reverts.** A `CREATE`/`CREATE2` targeting `A` reverts under [EIP-684](./eip-684.md) (`nonce != 0`). Without this EIP, the same operation would succeed (under EIP-684 alone) or revert under EIP-7610. Tested as the first transaction of the fork block.
3. **CALL unchanged.** A `CALL` to `A` still does not charge new-account gas, since `A` remains in the trie. The call returns success with no observable effect (empty code).
4. **Non-targeted accounts unaffected.** Accounts with code, non-zero nonce, or not in the list are untouched.
5. **BAL correctness** (if [EIP-7928](./eip-7928.md) is active). For each targeted account, the BAL has a single `NonceChange` `[0, 1]` and all other change lists empty. Re-applying the BAL reproduces the post-state root.

## Reference Implementation

```python
def apply_irregular_state_transition(state, account_list):
    for entry in account_list:
        state.set_nonce(entry.address, 1)
        # balance, codeHash, and storageRoot are intentionally not modified.
```

The list construction procedure (predicate, re-execution of pre-Spurious-Dragon blocks, the snapshot scan, `eth_getProof` verification) is described in [`methodology.md`](../assets/eip-8253/methodology.md). The published list MUST be byte-equal to the output of that procedure run against Mainnet at the fork block height.

## Security Considerations

- **List correctness.** Both an omission and a spurious entry are consensus-relevant. The list is reproducible from canonical state via the procedure in [`methodology.md`](../assets/eip-8253/methodology.md); multi-client and external verification before scheduling the fork is required.
- **No supply change.** Balances and storage are preserved; only the nonce is bumped.
- **Genesis cannot contain these accounts.** This is not caught by re-execution. It would be caught by the snapshot scan though.

## Copyright

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