Skip to main content

StateRoot

32-byte Merkle Patricia Trie root hash representing global Ethereum state.

Overview

StateRoot represents the root hash of the Ethereum state trie. It uniquely identifies the entire global state at a given block - all account balances, nonces, contract code, and storage. Every block header includes a state root.

Type Definition

type StateRootType = Uint8Array & {
  readonly __tag: "StateRoot";
  readonly length: 32;
};

type StateRootLike = StateRootType | string | Uint8Array;

const SIZE = 32;

Usage

Create StateRoot

import * as StateRoot from './primitives/StateRoot/index.js';

// From hex string
const root = StateRoot.fromHex("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421");

// From Uint8Array
const bytes = new Uint8Array(32);
const root2 = StateRoot.from(bytes);

Convert to Hex

const hex = StateRoot.toHex(root);
// "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"

Compare State Roots

const isEqual = StateRoot.equals(root1, root2);

API Reference

Constructors

FunctionDescription
from(bytes)Create from Uint8Array
fromHex(hex)Create from hex string

Methods

FunctionDescription
toHex(root)Convert to hex string
equals(a, b)Check equality

Constants

ConstantValueDescription
SIZE32State root size in bytes

State Trie Structure

The Ethereum state is a Merkle Patricia Trie mapping addresses to account states:
State Root (32 bytes)
    |
    v
Merkle Patricia Trie
    |
    +-- Address A --> Account State A
    |                   - nonce
    |                   - balance
    |                   - codeHash
    |                   - storageRoot
    |
    +-- Address B --> Account State B
    ...

Block Header Context

Every block header contains a state root:
type BlockHeader = {
  parentHash: Hash;
  stateRoot: StateRoot;    // <-- This represents all state
  transactionsRoot: Hash;
  receiptsRoot: Hash;
  // ...
};

Empty State Root

The empty state root (no accounts) has a known value:
// Empty trie root = keccak256(RLP(null))
const EMPTY_STATE_ROOT = StateRoot.fromHex(
  "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
);

Use Cases

Verify State Against Header

// Get block header (from trusted source)
const header = await provider.getBlock("latest");

// Get state proof
const proof = await provider.send("eth_getProof", [address, [], "latest"]);

// Verify proof against header's state root
const isValid = verifyStateProof(proof, header.stateRoot);

Track State Changes

// Compare state roots between blocks
const block1 = await provider.getBlock(1000000);
const block2 = await provider.getBlock(1000001);

const stateChanged = !StateRoot.equals(
  StateRoot.fromHex(block1.stateRoot),
  StateRoot.fromHex(block2.stateRoot)
);

Light Client Sync

// Light client stores only block headers
const headers = [header1, header2, ...];

// To verify any state claim, check proof against stateRoot
function verifyBalance(address: string, balance: bigint, proof: StateProof): boolean {
  const header = findHeader(proof.blockNumber);
  return verifyProof(proof, header.stateRoot);
}

Specification

Per the Yellow Paper, the state trie encodes mappings from addresses (160-bit identifiers) to account states. The state root is a 32-byte Keccak-256 hash of the root node of the state trie.

See Also