(In blockchain, “state” means the current data about all accounts, balances, and smart contracts.)
In blockchain systems, efficiently and securely storing all account and contract state is critical. The StateTrie is a data structure designed for this purpose. Used in Ethereum and many other blockchains, it organizes accounts, balances, and contract data into a tree structure.
Want to See the Code First?
Most articles about state are conceptual. Here, you can check out real TypeScript code — read, run, and modify it yourself. This isn't just theory: every core idea explained below is directly backed by a working implementation.
Or, keep reading to see how StateTrie works, step by step.
What sets this guide apart:
You see not just what a StateTrie is, but exactly how state updates and lookups are done — with actual data flows and visual step-throughs.
Each major concept is paired with both an intuitive explanation and a concrete code example (see GitHub).
Why Do We Need a StateTrie?
Integrity of State: With a single root hash, you can verify the entire system state hasn't been tampered with.
Efficient Partial Updates: When an account changes, only the nodes along that account's path are updated. The rest of the tree remains unchanged and can be reused.
History and Rollbacks: Every version of the tree (and its nodes) is kept in the database, making it easy to restore or compare any past state. This enables rollbacks and historical proofs.
These properties enable reliable state management, fast verification, and support for time-travel (rollback) features in blockchain systems.
How Does StateTrie Work?
State History and Snapshots
Imagine the StateTrie as a tree whose root hash is like a unique fingerprint for the blockchain’s state at a certain time. Every update (like a transaction) makes a new root hash, just like how every new Git commit makes a new commit hash. Each root hash is a snapshot. You can always go back to any previous root hash and query that exact state—just like checking out a specific commit in Git.
A Step-by-Step Look at Set and Get Operations
The StateTrie organizes blockchain account data using a tree structure, where each path through the tree is determined by the hash of the account’s address. Let’s break down exactly what happens when you store (set) or retrieve (get) account data.
1. Storing Data (set): Inserting or Updating an Account
The set operation is how you add or change the data for an account. Here’s how it works, step by step:
a. Hashing and Path Generation
The account address (e.g.
0x0001) is hashed (commonly using SHA-256).The resulting hash is split into hexadecimal digits (nibbles), e.g.
4bf5122f3a4554...→[4, b, f, 5, 1, 2, 2, f, 3, ...]Each nibble decides which branch to follow at each level of the tree.
b. Tree Traversal and Node Creation
Starting from the root, the algorithm follows the path specified by the nibbles.
If a branch does not exist for a given nibble, a new branch node is created.
When the path is exhausted (all nibbles followed), a new leaf node containing the account and value is created.
c. Updating Parent Branches and Root Hash
After inserting or updating a leaf, all parent branch nodes along the path are also updated, because each branch’s hash depends on its children.
Only the nodes along the changed path are new; unchanged branches are reused.
The new root hash represents the entire new state.
Example:
Suppose you insert three accounts, each with a unique address.
Step 1: Insert 0x0001
Root (Branch)
└── [4] → Leaf (key: 0x0001, value: 100)Step 2: Insert 0x0002 (different path)
Root (Branch)
├── [4] → Leaf (key: 0x0001, value: 100)
└── [d] → Leaf (key: 0x0002, value: 200)Step 3: Insert 0x0003 (shared prefix, deeper branch)
Root (Branch)
├── [4] → Branch
│ └── [b] → Branch
│ └── [f] → ...
│ ├── [4] → Leaf (key: 0x0001, value: 100)
│ └── [a] → Leaf (key: 0x0003, value: 300)
└── [d] → Leaf (key: 0x0002, value: 200)Only the path from the root to the changed/inserted leaf needs to be updated; all other paths and nodes are reused, making updates efficient and enabling state history.
Only the path from the root to the changed/inserted leaf needs to be updated; all other paths and nodes are reused, making updates efficient and enabling state history.
What Happens in the Worst and Best Cases?
In the worst case, every account’s key hash is completely different, so each inserted key requires the maximum possible depth—one new branch node for every nibble (hex digit) in the hash. This means the tree can be quite deep (e.g., up to 64 levels for a 256-bit hash).
However, in most real-world cases, many accounts share common prefixes in their hashed keys. This allows multiple accounts to share branch nodes along their path—
only the final part of the path differs, and only that part of the tree is unique to each key. Unchanged “parent paths” are always reused.
Suppose the hashes for two addresses both start with the same nibbles:
Account A: [4, b, f, ...]
Account B: [4, b, f, ...]Their paths will share the [4], [b], [f] branches. Only when their paths diverge (say at the 7th nibble) does the tree branch out to different leaves. This sharing of paths means the Trie is very space-efficient and minimizes the number of new nodes needed for each operation.
In Ethereum’s Full MPT
The real Merkle Patricia Trie used by Ethereum optimizes shared prefixes even further:
Extension nodes compress long sequences of branch nodes with a single child into one node, reducing tree depth and database lookups.
This enables even faster operations and lower storage use for keys with long shared prefixes.
2. Retrieving Data (get): Looking Up an Account
The get operation finds the value for a given key. Here’s how it works:
a. Hashing the Key
The search key is hashed in the same way as for insertion, producing a nibble path.
b. Traversing the Tree
Starting at the root, the algorithm follows each nibble as a branch index.
At each branch node, it checks if the branch exists for the current nibble:
If yes, continue following the path.
If no, the key does not exist in the trie (return null).
c. Reaching a Leaf
Once a leaf node is reached, the algorithm checks whether the key stored in the leaf matches the search key.
If it matches, return the value.
If not, return null.
Example:
Looking for 0x0001
Root
└── [4] → Branch
└── [b] → Branch
└── [f] → ...
└── [4] → Leaf (key: 0x0001, value: 100)Each branch index is determined by a nibble of the hashed key.
If any part of the path is missing, or the final leaf key does not match, the lookup fails.
Edge Cases
Missing Branch: The path for the key doesn’t exist in the trie (returns null).
Mismatched Leaf: The leaf node is reached, but the key in the leaf is different (returns null).
How Is This Used in Blockchains?
Every time the state trie is updated, the root hash changes. This new hash uniquely identifies the blockchain’s state at that exact moment. You can use any previous root hash to "travel back" and query or prove what the state was—just like how you can check out any previous commit in Git to see an old version of your codebase. The StateTrie is thus the engine for both real-time state access and historical, tamper-proof snapshots.
... Root Hash A → Root Hash B → Root Hash C ...
(block 100) (block 101) (block 102)
│ │ │
Trie A Trie B Trie C
│ │ │
[state after [state after [state after
block 100] block 101] block 102]You can always query any past state by starting from its root hash.
Finding an Account’s Value in a Specific State
To find the balance or data for a specific account at a given point in time (a specific block number):
Get the root hash of the state you’re interested in (for example, a certain block number).
Hash the account’s address to generate the path through the StateTrie.
Traverse the tree from the root hash, following each nibble (hex digit) of the hashed key at each branch.
When you reach a leaf node:
If the leaf’s key matches the account you’re looking for, return the value (such as the account balance).
If not, the account does not exist in that state.
At block #123, the state root is: 0xabcdef...
You want to check the balance of address 0x1234... in this state.
1. Use the state root to start the trie traversal.
2. Hash 0x1234... to get the path [4, b, f, ...].
3. Follow this path through the trie, moving from root to each branch node.
4. Reach the leaf node. Check: Is it 0x1234...? If yes, return value. If not, not found.Final Summary
The StateTrie is how blockchains like Ethereum keep track of all account and contract data — securely and efficiently. It turns the entire state into a hashed tree where each update only touches part of the structure, making writes fast and storage lean. Every change creates a new root hash, giving you an exact snapshot of the system at any block. With just that root, you can verify, query, or roll back to any past state. Whether you're updating balances or digging into history, the StateTrie keeps the state consistent, verifiable, and ready for whatever’s next.
Thank you for reading. If you enjoy this post, feel free to share!

Comments
Nothing yet. Say the first thing.
Sign in to join the conversation.