EIP-8253 Raising the nonce of nonce-0 storage accounts (Bump nonce of zero-nonce storage accounts)

Hegota Considered for Inclusion · EVM & execution · EIP status Draft · created 2026-05-05

At the start of the fork block, raises to 1 the nonce of 28 mainnet accounts that have no code and nonce 0 but non-empty storage. This prevents a new contract from being created at these addresses under the EIP-684 rule alone.

Why it is needed

When Ethereum creates a new contract, it checks only that the destination address has nonce 0 (nonce: the number of transactions or contract creations an account has made) and no code (EIP-684). The rule assumes that if the nonce is 0 and there is no code, the storage is also empty. However, before EIP-161 (the Spurious Dragon fork), the nonce was not incremented before the init code ran when a contract was created. So if a constructor wrote values to storage and returned no code, an account with nonce 0, no code and non-empty storage was left behind. This form is the only case in which that assumption breaks. If a new contract is created at such an address, it inherits the old storage as is. One option would be to additionally check that storage is empty on every contract creation, but that would make every contract creation pay a check cost and a disk read forever to protect a few addresses that will not grow in number. This EIP raises the nonce of those accounts to 1 once, removing the problematic account form itself.

What changes

Today

After EIP-8253

Who is affected

In depth

Conditions for target accounts and the list

The list is a chain configuration parameter and is managed the same way as system contract addresses. A chain's list MUST contain exactly the accounts that satisfy all three of the following conditions in the pre-state just before the fork block: nonce == 0, codeHash == keccak256("") (EmptyCodeHash), storageRoot != keccak256(RLP("")) (EmptyRootHash). Such accounts can only be created by contract creation before EIP-161 or by genesis allocation. A chain that has had EIP-161 enabled since genesis and did not put such accounts in genesis has an empty list, and this EIP does nothing. Reusing one chain's list without re-extracting it against another chain's state is prohibited (MUST NOT).

The 28 mainnet accounts and public materials

The mainnet target accounts are published in targeted-accounts.json as 28 entries. Each entry holds a 20-byte address and its keccak256 hash. The file also lists each account's nonzero storage slots (slot key, key hash, value), so the storage root can be recomputed from the file alone and compared with the reported root. Slot information is not used in the state transition. The procedure used to build the list and the eth_getProof (latest-based) verification are described in methodology.md. The 224-entry pre-EIP-161-deletion superset at the Spurious Dragon block is published alongside as zero-nonce-matches.jsonl. Of the 28, 26 were created by contract-creation transactions sent by 12 externally owned accounts, and the other 2 were created by a factory contract using CREATE at nonce 0. The published list MUST be byte-for-byte identical to the result of running the same procedure against mainnet at the fork block height.

State transition rules

At the start of the fork block, before system contract calls such as EIP-2935, EIP-4788, EIP-7002 and EIP-7251, and before any transaction, the nonce of each account A in the list MUST be set to 1. balance, codeHash and storageRoot are not changed. The transition is unconditional. It does not read A first to check a condition; it simply writes. Because the list condition guarantees the nonce is 0, this is in practice the same as raising it from 0 to 1. If the list is empty, nothing happens. This transition creates no transaction, receipt or log, and uses no gas. The reference implementation is a loop that, for each entry, asserts nonce == 0 and then calls set_nonce(address, 1). An assert failure is a list configuration error, not a situation the transition handles.

Integration with the EIP-7928 Block Access List (BAL)

If EIP-7928 is enabled at the fork block, the BAL MUST record this change at block_access_index = 0. This is the same index as the pre-execution system contract calls. For each target account A, add only one NonceChange [0, 1] (block_access_index = 0, new_nonce = 1) to nonce_changes in its AccountChanges entry. Nothing is added to storage_changes, storage_reads, balance_changes or code_changes. If nothing else touches A in the same block, the entry is [A, [], [], [], [[0, 1]], []]. Transactions and system contract calls in the same block append their own changes to the same entry. If the list is empty, nothing is added to the BAL. Reapplying the BAL to the state root just before the fork block MUST yield the state root after the fork block. Because the nonce change is only one NonceChange per account, a block builder can emit this change at index 0 without re-executing the irregular transition.

Why raise the nonce instead of a runtime check or storage deletion

Adding a storage check to every contract creation leaves the problem state in place and pays a cost on every creation forever to protect a closed set of addresses. This EIP instead chooses a single consensus change. It follows the same pattern as EIP-7523, which cleaned up empty accounts. Clearing the storage of the target accounts was also considered, but it is more invasive. It requires deleting at least one and often several slots per account, and the storageRoot must be recomputed. On mainnet, 129 storage entries across the 28 accounts would have to be deleted, while raising the nonce takes 28 nonce changes. Nonce 1, no code, with storage is a shape that can already exist on Ethereum. A conditional transition (raise only if the condition still holds) was also considered and rejected. It would let the mainnet list be applied unchanged to other chains. Those chains would take on BAL entries for addresses that never needed to be touched, and the BAL contents would depend on a runtime check. Keeping the condition on the list side makes the transition and the BAL unconditional, and the check can be verified offline against the pre-fork state.

Security considerations: list correctness and the closed set

Both omissions and wrong entries in the list affect consensus. An omission leaves that account exposed to collision risk. Because the transition is unconditional, adding a nonexistent account creates a new account with nonce 1 (under EIP-161 it is not an empty account, so it stays), and adding an account with a nonzero nonce overwrites that nonce. A list that violates the condition is a chain configuration error, like a wrong system contract address. This EIP does not define behavior in that case. Verification by multiple clients and external parties is needed before the fork is scheduled, and the eth_getProof liveness check SHOULD be rerun against the pre-fork state. The mainnet set is closed. A nonce changes only through a transaction signed by that account, an EIP-7702 authorization signed by that account, or a contract creation landing at that address. The target addresses are contract addresses with no known key, so the first two are impossible. CREATE can only reach them from the original deployer at the original nonce, and that nonce has already passed. CREATE2 cannot reach addresses made by CREATE. The 2 made by the factory would need factory nonce 0, but after EIP-161 contracts start at nonce 1, so this is impossible in principle. With no code they cannot self-destruct, and their balance can only grow. The supply does not change.

Application to other chains and test cases

Chains that inherited mainnet state after Spurious Dragon (including mainnet shadow forks) apply the mainnet list. Chains that had EIP-161 enabled from genesis have an empty list. All current public testnets fall into this category. Any other chain builds a list from its own state with the methodology.md procedure, containing both the address preimages and the hashes. The reason the mainnet list must not be reused is as follows. On a chain where the nonces of the 12 deployers are lower than the values used on mainnet, a key holder can recreate the same address with arbitrary init code. At the time of writing, all 12 deployers on Sepolia are in this situation. Target accounts created by genesis allocation cannot be found by replaying blocks, but they are found by the snapshot scan in the methodology. Tests are run by putting target-shaped accounts in genesis and registering the name in the chain config. mainnet-prestate-alloc.json provides the 28 mainnet accounts (balance, nonce 0, storage) as a genesis alloc fragment. There are 7 test cases: nonce raised, CREATE reverted (first transaction of the fork block), CALL unchanged (no new-account gas), non-target account unchanged, BAL correctness, BAL merge (merged into one entry with a BalanceChange from a transaction in the same block), and empty list.

Status and fork schedule

The source status is Draft. Details of the specification, the list and the published materials may still change. According to the core developer call records, on 08.17 ACDT #92 this EIP was targeted at Hegota (PFI). On 08.20 ACDC #185 a Hegota inclusion proposal was received as a replacement for EIP-7610. On 09.07 ACDT #95 it was pushed to Hegota by rough consensus that it was too late to add to Glamsterdam. On 09.10 ACDE #245 it was settled not to add it late to Glamsterdam, because there was no security urgency. A request for immediate SFI in Hegota was deferred to the scoping block and left as a candidate only. On 09.14 ACDT #96 it was decided to leave BAL collision handling undefined until this EIP is resolved. On 09.24 ACDE #246 it moved to Considered for Inclusion (CFI) in Hegota along with other candidates. On 10.12 ACDT #100 there was only discussion and no decision. On 10.22 ACDE #248 the scope of bogota-devnet-2 was fixed at four EIPs, EIP-8253, EIP-4758, EIP-8298 and EIP-8151, and it was decided to ship it next week with three clients: Nethermind, Geth and ethrex.

Original EIP · ← EIPs