EIP-8297 Zoned Binary State Tree (Partitioned Binary Tree)

No call decision · Scaling & data · EIP status Draft · created 2026-06-11

Replaces Ethereum's state storage structure, the hexary Merkle Patricia Trie, with a single binary tree. The first byte of a key distinguishes the account header, code, and storage zones, and contract code is also split into chunks and placed in the tree.

Why it is needed

Ethereum's long-term goal is to verify blocks with validity proofs, making chain verification as light and fast as possible. That requires proving the state read during EVM execution, and today's Merkle Patricia Trie (MPT) is not suited to this. It uses RLP encoding and the Keccak hash, which is expensive to prove, it has a "tree within a tree" structure in which a separate storage tree hangs under the account tree, and it cannot prove only part of a contract's code. Proof sizes are also large. With an account tree depth of about 12, one branch is 5760 bytes, and in the worst case, if a block's 60 million gas is spent entirely on touching 1 byte each of different accounts' code, about 1.8GB is needed. This is because, if code is not split into chunks, proving even 1 byte requires revealing the entire 64KiB of code. A binary tree with a branching factor of 2 minimizes Merkle proof size, and switching to a proof-friendly hash also improves circuit performance.

What changes

Today

After EIP-8297

Who is affected

In depth

Tree structure: two node types and a unique canonical form

The tree stores entries whose keys are variable-length byte strings of at least 1 byte and at most MAX_KEY_LENGTH (8192 bytes) and whose values are 32 bytes. No key may be a prefix of another key (prefix-free). There are only two node types. A LeafNode holds the full key and value, and a BranchNode holds a prefix (bit string), left, and right. There is no separate extension node, and the BranchNode's prefix holds the bit range shared by all keys beneath it. Because a BranchNode must have two non-empty children, the prefix is exactly the shared range, and exactly one valid tree exists for each key/value set. A LeafNode commits to the full key rather than a suffix relative to its position in the tree, so the hash of an unrelated leaf does not change when a branch is split or merged elsewhere.

Merkleization rules and the undecided hash function

A leaf is hashed as H(LEAF_TAG || key || value), and a branch as H(BRANCH_TAG || encode_bit_prefix(prefix) || left_hash || right_hash). LEAF_TAG is 0x00, BRANCH_TAG is 0x01, and the hash of an empty tree is 32 zero bytes. encode_bit_prefix writes the bit count as 2 bytes in big-endian, then appends the bits padded with zeros to a byte boundary, and MAX_KEY_LENGTH = 8192 bytes comes from this 2-byte limit (65535 bits). The hash function has not been decided yet. The reference implementation uses BLAKE3, and the candidates are BLAKE3 (good native performance), Keccak (already used in Ethereum, low proving efficiency), and Poseidon2 (strong in-circuit performance, with the EF cryptography initiative carrying out a security analysis). Because the tree depends only on a hash function and uses no elliptic curves, it is also secure against quantum attacks. Verkle's curve-based design is not, and NIST guidance calls for retiring elliptic curve cryptography by 2030.

Zones and key embedding

The first byte of every key is the zone identifier Z. 0x00 is the account header, 0x01 is a code chunk, 0xFF is storage, and 0x02 to 0xFE are reserved for future categories. A key is assembled from the zone byte, a hash-determined tree position, and a 1-byte sub-index, and the zone byte and tree position together are called the stem. One stem has STEM_SUBTREE_WIDTH = 256 sub-indexes. Key lengths are fixed per zone: ACCOUNT_KEY_LENGTH and CODE_KEY_LENGTH are 34 bytes, and STORAGE_KEY_LENGTH is 66 bytes. Fixing a single length within a zone guarantees prefix-free, and keys in different zones differ from the first byte. Addresses are passed as Address32, the 20-byte address prefixed with 12 zero bytes.

Account header and delegation leaf

The account header stem sits in ACCOUNT_ZONE at key_hash(address), one per account. A single BASIC_DATA_LEAF_KEY(0) leaf packs version (offset 0, 1 byte), code_size (4, 4 bytes), nonce (8, 8 bytes), and balance (16, 16 bytes) in big-endian, reducing branches that would need to be opened three or four times to one. CODE_HASH_LEAF_KEY(1) holds the Keccak code hash, and an account without code has the Keccak hash of empty bytecode. An EIP-7702 delegated account stores the 23-byte designator 0xef0100 || target followed by 9 zero bytes at DELEGATION_LEAF_KEY(2), and code_size is 23. A delegated account has no code_hash leaf and no CODE_ZONE leaves, and an existing account has exactly one of the code_hash and delegation leaves. Storage slots 0 to 63 are also placed in the header stem, starting at sub-index 64.

Code chunking and content addressing

Code is split into 31-byte pieces and placed in CODE_ZONE. Byte 0 of each chunk records how many leading bytes are PUSHDATA, and bytes 1 to 31 are the code piece. The key is built as key_hash(code_hash || tree_index), so code is addressed by code hash rather than by account. Thousands of contracts deployed from the same factory therefore share code leaves, and a shared chunk enters the block witness only once. When an account is deleted, its code leaves are removed only if no account with the same code_hash remains in the resulting state. This check can be made from the transaction alone. An account with code can be deleted only by SELFDESTRUCT within the transaction that created it (EIP-6780), so code leaves that existed before the transaction always remain, and only leaves newly added by the transaction are decided within that transaction. A reference count over the whole state is not needed.

Storage layout and buckets

Slots 64 and above go to the storage zone. The key is 0xFF || key_hash(address) || key_hash(address || tree_index) || sub_index, with tree_index = slot // 256 and sub_index = slot % 256. The first hash, key_hash(address), gathers an account's entire overflow storage under one bucket, and the second hash spreads groups evenly within the bucket. The address is included in the second hash to confine grinding attacks to the attacker's own bucket. Adjacent slots fall into the same group, so access to slots that sit together, such as mappings and arrays, shares a path. Group 0 is an exception: slots 0 to 63 are in the header, so the storage zone has only slots 64 to 255 there. The bucket becomes the unit that later state expiry or partial state holding designs cut off or sync.

Zero values are deletions, and deletion restores the canonical form

The state transition function must treat writing 32 zero bytes as a deletion. The tree has no keys with zero values, and reading a missing key returns zero, so zero and "absent" are the same state and produce the same root. The root becomes a function of the state alone rather than the write history, so a tree converted from the MPT and a tree maintained incrementally match. A branch left with only one child after a deletion must be replaced by that child to restore the canonical form, and this merge finishes in one step and undoes the split that the insertion performed. The MPT checked the EIP-7610 condition (checking for non-empty storage before contract creation) using storage_root, but this tree has no such node, so it decides by whether a leaf exists anywhere in the header's storage sub-indexes or in the storage bucket.

Security, status, and schedule

All hash components of a key are full 256-bit digests, so producing a collision costs about 2^128 in a birthday attack, and keys in different zones differ from the first byte, so they cannot collide at all. Even if grinding collects digests that share k leading bits, prefix compression folds that range into a BranchNode prefix of about k/8 bytes, so adding d real nodes requires about 2^d work. This EIP does not change the gas schedule and is invisible to the EVM, but code inside contracts that verified MPT state proofs must switch to the new proof format. Activation and the migration of existing MPT state are specified separately by EIP-8347, and the conversion happens on a path independent of consensus, so the new tree becomes the canonical state commitment in a single hard fork. The source status is Draft, so the content may change, and the hash function is also undecided. According to the call record, it was discussed in AMA #15 on 09.16, and no decision was made.

Original EIP · ← EIPs