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 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, which adds a runtime storage check on every contract creation.
Motivation
EIP-684 requires zero nonce and zero code length at a CREATE destination. Pre-EIP-161, 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, 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.
Mainnet account list
The targeted Mainnet accounts are published as 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. The 224-entry pre-EIP-161-deletion superset at the Spurious Dragon block is published alongside, as 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-4788, EIP-7002, EIP-7251) and before any transactions, for each account A in the list:
The nonce of A MUST be set to 1.
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 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);
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 (because nonce != 0) rather than under EIP-7610’s storage check; the externally-observable result is the same.
Test Cases
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.
CREATE reverts. A CREATE/CREATE2 targeting A reverts under EIP-684 (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.
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).
Non-targeted accounts unaffected. Accounts with code, non-zero nonce, or not in the list are untouched.
BAL correctness (if EIP-7928 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
defapply_irregular_state_transition(state,account_list):forentryinaccount_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. 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; 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.