Skip to main content
The migration is checkable by anyone, not only by the team that runs it. Every account that existed on the legacy L1 at the snapshot block has a counterpart on the L2, and the comparison is a matter of reading both chains over JSON-RPC and diffing the result. The tooling that does it is published, so you can reproduce the check rather than take the outcome on trust.
Migration applies to Mainnet only and has not happened yet, so there is nothing to reconcile today. This page describes the procedure and the tooling. Whitechain Sepolia started from its own genesis, so it carries no legacy state.

What you need

Verification depends on a legacy L1 archive endpoint that answers at the snapshot block. That endpoint goes away at L1 decommissioning. Run the checks you care about, and export what you need, while the legacy read only endpoints are still online. See L1 sunset stages.

The layers

Each layer gates the next. A failure at a lower number invalidates everything above it. A fourth tool, tokensync, ships alongside these. It backfills token metadata into the explorer database from the legacy explorer API. It changes explorer data rather than chain state, so it is a migration step, not a verification step, and a clean tokensync run proves nothing about the chain. The published runbook, docs/post-migration-verification.md, numbers its layers differently: it carries tokensync as layer 4 and adds functional QA as layer 5. Functional QA is the acceptance testing that follows reconciliation, not a check on the migrated state, so it sits in the migration phases rather than here.

Layer 0: chain sanity

Run this first. It is quick and it fails loudly.
Confirm that the chain ID is the published L2 value, that the block number advances at the configured block time, and that a test transaction is charged in WBT.

Layer 1: account reconciliation

staterecon walks every account on the legacy L1 with debug_accountRange, page by page, and compares each one against the L2. Results stream to CSV as it goes, so it holds no full copy of the state in memory.
Compared per account: Code hash and storage hash are compared for contract accounts only. The CSV carries address, status, field, l1_value, l2_value and error per row. A clean run has zero rows with status=mismatch and zero rows with status=error. An error row is not a pass: it means the comparison did not happen, usually an RPC timeout, and it has to be rerun rather than waved through. Resume an interrupted walk with --account-range-start, and tune throughput with --page-size, --batch-size, --rpc-batch-size and --rpc-timeout. Two expected differences are not findings. The OP Stack predeploys exist only on the L2 and have no L1 counterpart. Empty accounts pruned while the allocation was built are absent on the L2 by design, so the account count differs by that number.

Layer 2: token and NFT reconciliation

tokenrecon calls view functions on both chains for the contracts you list, compares the raw return data, and additionally compares raw storage slots with eth_getStorageAt.
Fill in the config with the contracts you want checked:
config/config.yml
What each rule covers: A clean run has zero mismatch and zero error rows, totalSupply equal on both chains for every token, and matching roles and owners. The check is only as complete as the inventory you give it, so a token absent from the config is a token nobody verified.

Layer 3: SoulDrop and Soul registries

soul-verification is a set of RPC tests that diff the pre-migration deployment against the post-migration one, including storage layout checks, which is why it needs the contract sources compiled first.
Pin a block number on both sides so a moving head cannot produce a false diff. For a fast signal on a subset rather than the full run, use the sample variant:

What counts as verified

The last row is what makes the rest reproducible later. The checksums of the published artifacts are verifiable independently at any time: see Network artifacts.

Where the tooling lives

Both tools are in whitechain-labs/whitechain-state-migration, under staterecon/, with the full verification runbook in docs/post-migration-verification.md. Building them needs Go 1.25 or later. soul-verification needs Node.js and the SoulDrop contract sources.