Nomad Bridge Hack Explained: How $186M Was Drained

Share
Nomad Bridge Hack Explained: How $186M Was Drained

What happened, in two sentences: On August 1, 2022, a June 2022 upgrade to Nomad's Replica contract left unproven messages counting as already "proven." Within about two hours, hundreds of wallets copied the first attacker's transaction, swapped in their own address, and drained roughly $186 million from the bridge.

That's the short version of the Nomad bridge hack. The longer version is more useful. Nomad's verification design was reasonable on paper, and the sources point to a contract logic flaw rather than stolen keys. The bridge broke because Solidity's default value for an unset mapping entry, bytes32(0), was accidentally marked as a trusted Merkle root. Below we go through the contract logic, rebuild the attack pattern, account for who lost what, and finish with a checklist for judging any bridge before you trust it with funds.

Key Takeaways:The Nomad bridge exploit began on August 1, 2022, and took the bridge's balance from $190.3M to $11,815, according to Fortune via Yahoo Finance.The root cause was a Replica contract upgrade that, as commonly described in public post-mortems, set confirmAt[0x00] = 1, so the Solidity default value for any unproven message passed the "proven" check.Nomad's optimistic fraud window couldn't catch the attack because no fraudulent root was ever submitted, which left watchers with nothing to challenge.Nomad had to rely on an engineer on a plane relaying code snippets over chat and could not pause the bridge until it was empty, according to the FTC complaint reported by Decrypt.The cheapest defenses Nomad lacked were operational: a fast pause, per-asset withdrawal rate limits, and invariant tests asserting that unproven messages always revert.

Table of Contents

How Was the Nomad Bridge Supposed to Work?

Nomad was an optimistic cross-chain messaging protocol, and a lock-and-mint token bridge ran on top of it. "Optimistic" means the system accepts state updates as valid by default unless someone challenges them within a fixed fraud window. Rather than having a committee co-sign each transfer, Nomad needed only one honest watcher to spot a bad update within that window.

Three components did the work, per a University of Athens security lecture that reproduces the contract code:

  • Home contract (origin chain): every outbound message is stored as a leaf in a Merkle tree. A Merkle tree hashes pairs of leaves upward until one 32-byte root commits to every message in it.
  • Replica contract (one per destination chain): accepts roots and records when each becomes valid in a mapping, confirmAt[root]. A message is processed only if its root is acceptable.
  • Updaters and watchers: bonded parties sign new roots, and watchers can flag fraudulent roots during the optimistic window.

Delivering a message on the destination chain took two calls:

  1. prove(leaf, proof, index) rebuilds the Merkle root from the leaf and its sibling hashes. If that root is acceptable, it stores messages[leaf] = root.
  2. process(message) hashes the message, looks up the root stored for it, checks that the root is acceptable, marks the message as processed, and calls the recipient (here, the BridgeRouter, which releases the locked tokens).

The model's security rests on one assumption. A message can only be processed if it was first proven against a root that the Updater signed and the fraud window has cleared. In Nomad's case, a single initialization value broke that assumption.

What Was the Bug Behind the Nomad Bridge $186 Million Exploit?

Here are the two functions involved, simplified from the Replica contract as reproduced in the lecture slides:

Flow showing an unproven message reading 0x00, which Nomad's confirmAt mapping treated as valid, passing the proven check.
One default value turned the proven check into a pass for every message.
function process(bytes memory _message) public returns (bool _success) {
    bytes32 _messageHash = keccak256(_message);
    // ...
    require(acceptableRoot(messages[_messageHash]), "!proven");
    messages[_messageHash] = LEGACY_STATUS_PROCESSED;
    // ... dispatch to recipient (BridgeRouter releases tokens)
}

function acceptableRoot(bytes32 _root) public view returns (bool) {
    uint256 _time = confirmAt[_root];
    if (_time == 0) {
        return false;
    }
    return block.timestamp >= _time;
}

Taken on its own, this code is correct. A root nobody has submitted has confirmAt[root] == 0 and is rejected. A root still inside its fraud window has a future timestamp and is also rejected.

The bug was in how the contract was initialized.

When a Replica is deployed after its Home contract already holds messages, the deployer can start it at a _committedRoot and set confirmAt[_committedRoot] = 1. That skips the need to replay old updates, and the value 1 is a Unix timestamp from 1970, so the root is valid immediately. The trouble is the case where Home and Replica start fresh. An empty tree has a root of bytes32(0), so initializing with the "current root" stored confirmAt[0x00] = 1.

The FTC says a June 2022 code update "introduced a critical vulnerability" into the Replica, and public post-mortems describe it as this zero-root initialization. The exact commit and code diff are not confirmed in our sources. Now combine it with how Solidity handles mappings:

  1. In Solidity, every unset mapping key returns zero. For a message that was never proven, messages[_messageHash] returns 0x00.
  2. process() passes that 0x00 into acceptableRoot().
  3. confirmAt[0x00] now returns 1, which is not zero, so the first guard passes.
  4. block.timestamp >= 1 is always true, so the function returns true.
  5. The "!proven" requirement is satisfied for every message that had never been proven.

In practice, the "has this message been proven?" check turned into "has this message not been processed yet?" Merkle proofs no longer mattered. Anyone could write a message and have the bridge execute it.

The general lesson: In any language where storage defaults to zero, the zero value must never mean "authorized." If a sentinel, default, or uninitialized value can pass an authorization check, the check is broken regardless of how strong the cryptography behind it is.

How Did the Attack Unfold, Step by Step?

The attack took no cryptography and no stolen keys. It needed calldata. Ledger's on-chain reconstruction found that the first attackers planned at least a day ahead and tested the bug with small amounts before draining larger ones.

The original exploit transaction

  1. Craft a message. The attacker builds a Nomad-formatted message addressed to the Ethereum BridgeRouter, with a body that says "transfer N units of token T to address A."
  2. Skip prove() entirely. No Merkle proof is submitted. The message hash has never been stored, so messages[hash] == 0x00.
  3. Call process(message) directly. acceptableRoot(0x00) returns true, the message is marked processed, and the BridgeRouter sends real tokens from Nomad's escrow to the attacker.
  4. Repeat with a new hash. A processed message can't be replayed, but changing the amount, recipient, or nonce produces a fresh hash that again maps to 0x00.

The copycat phase

This is the part that set Nomad apart from other bridge hacks. Once the first exploit transactions landed on Ethereum, anyone reading Etherscan could open the input data, replace the recipient address with their own, and send it again. The class action complaint calls the technique "spoofing," and public on-chain visibility made it easy to replicate. A lecture deck claims more than 300 attackers across about 960 transactions, though we could not independently verify those counts.

It moved fast. The Defiant reported $45M drained by 7:15 PM ET on August 1, and Fortune reported that the bridge emptied "in a matter of two hours on Monday evening." The class action says the drain finished by the end of August 2. That fits an initial exploit on August 1 followed by a copycat tail into the next day.

Who Lost What and What Happened Next?

The losses fell on users and protocols whose assets were held in the Nomad bridge. Per the FTC, the assets taken included Ethereum, USDC, DAI, and WBTC.

Bar comparison of Nomad bridge balance: $190.3M before the drain and $11,815 after.
The bridge went from $190.3M to $11,815.
MetricFigureSource
Headline loss~$186MLedger
Bridge balance before drain$190.3MFortune/Yahoo
Bridge balance after drain$11,815Fortune/Yahoo
Assets takenEthereum, USDC, DAI, WBTCFTC via Decrypt
Recovered in following days~$22MFTC via Decrypt
Consumer losses cited by FTC>$100MFTC via Decrypt
Seed round disclosed days earlier$22MThe Defiant/Yahoo

Ethereum was the only chain where funds were drained. Connected chains included Avalanche, Evmos, Milkomeda C1, and Moonbeam, and Moonbeam was paused during the investigation.

The legal fallout followed. The FTC compelled Nomad's operator, Illusory Systems, to repay users. It alleged the company marketed itself as "security-first" while failing to adequately test its contracts or keep an incident-response process, as Decrypt reported. A private class action alleges negligence and "knowingly false promises of security." Israeli authorities reportedly arrested Alexander Gurevich, who is accused of initiating the exploit, at an airport as he tried to flee to Moscow. The allegations are unproven in court according to the sources, and we could not confirm the charges or case status.

Copying an exploit transaction does not make you a "white hat." Treat funds that aren't yours as off limits.

Why Didn't the Optimistic Security Model Catch It?

The optimistic model was never tested, because the attack never touched what it guarded. Nomad's watchers were built to catch a fraudulent root. In this attack, no new root was needed: the messages relied on the root 0x00, which the contract already treated as valid, so watchers had nothing to challenge.

This shows up across the industry. A bridge's stated trust model (multisig, optimistic, light client, ZK) describes how it validates new state. Many big losses come from the plumbing around that model: initialization, upgrades, proof parsing, and message decoding. The Halborn post-mortem of the June 2026 Syscoin bridge exploit makes the same point. An SPV proof parsing error let an attacker mint about 5 billion SYS (around $10M at the time), and the post-mortem says it was not a cryptographic break.

The response gap made it worse. According to the FTC complaint, Nomad "had to rely on an engineer, who was on a plane, to relay code snippets in a chat back and forth with the incident manager on duty," and could not pause the bridge until it had been emptied. An optimistic design is only as good as the people and automation watching it.

How Do Bridge Hacks Happen? Nomad vs. Other Major Exploits

Bridges are attractive targets because a single verification mistake turns into direct withdrawals from a large shared escrow. Ledger notes that bridge hacks account for four of the five largest crypto hacks. For more history, see our biggest crypto bridge hacks breakdown. Here are well-documented incidents grouped by failure category:

BridgeYearLossFailure categoryControl that would have limited it
Ronin2022~$625MKey compromise (validator keys)Independent signers, outflow alerts
Poly Network2021~$610MVerification/access-control flawPrivileged-function isolation
Wormhole2022~$320MVerification logic flawInvariant tests on the signature-check path
Nomad2022~$186MInitialization / default valueRate limits, fast pause, zero-value tests
Horizon2022~$100MKey compromise (validator keys)Higher thresholds, HSMs
SyscoinJun 2026~$10M (reported)SPV proof parsing errorFuzzing of proof decoders

Loss figures for Ronin, Poly, Wormhole, and Horizon come from the University of Athens bridge-security lecture. Syscoin's figure is from Halborn. A reported ~$11.6M exploit of the Verus–Ethereum bridge through a fake cross-chain message, according to 1inch, resembles the Nomad pattern, but details were still developing.

Key compromises produce some of the largest losses. Logic and parsing bugs affect every verification model, including the "trustless" ones.

Which Bridge Verification Models Fail Least Catastrophically?

No verification model makes a bridge safe by itself. What differs is who you trust and how the bridge fails when that trust breaks.

ModelWhat you trustTypical failure modeBlast radius
Multisig / MPC committeeAn honest threshold of signersKey theft (Ronin, Horizon)Entire escrow
Optimistic (Nomad)At least one live, honest watcherBugs outside the fraud-proof pathEntire escrow if no rate limit
Light client / SPVSource chain's consensus (e.g., Bitcoin proof-of-work)Proof-parsing or header-relay bugs (Syscoin)Depends on collateral and caps
ZK light clientProof system and circuit correctnessCircuit bugsEntire escrow if no rate limit

For Bitcoin, the light-client approach removes the signer committee. An SPV (Simplified Payment Verification) proof shows that a transaction belongs to a Bitcoin block by checking a Merkle branch against a block header. TeleSwap uses this model: SPV light clients verify Bitcoin transactions, and TeleBTC is minted 1:1 only after a verified Bitcoin transaction, with custody backed by slashable collateral rather than a multisig. TeleSwap has bridged $539.6M across 571,518 bridge transactions on 13 supported networks, according to TeleSwap network stats. A route such as BTC → WBTC on Ethereum works this way.

The Syscoin incident shows that SPV code can still fail. Removing trusted signers takes away a major category of loss, but you still need to fuzz the proof decoder, test that zero and default values are rejected, and cap how much value any single bug can reach. For more on this, read Bitcoin bridge security in 2026.

Bridge Security Best Practices: What Should You Do?

If you build or audit bridges

  1. Treat zero as hostile. Write invariant tests asserting that process() reverts for any message hash that was never proven, and run them after every upgrade.
  2. Test the deployed state, not just the code. Explicitly test that zero or default values cannot authorize messages, and replay adversarial messages against the live configuration after each upgrade.
  3. Rate-limit outflows per asset per epoch. A cap on hourly outflows limits how much a single bug can drain.
  4. Pre-authorize a fast pause. Give automated monitors and several on-call keyholders a pause role that doesn't depend on any one person being reachable.
  5. Monitor accounting invariants. Alert on any process() call with no matching prove(), and on released tokens exceeding locked deposits.
  6. Stage upgrades. Use independent audits of initialization diffs and canary deployments with low caps before full liquidity moves over.
  7. Publish a disclosure channel and bug bounty. The FTC criticized Nomad for lacking a clear vulnerability-reporting process.

If you use bridges

  • Ask "who can mint?" before you bridge. Read the docs for the verification model: a committee, a fraud window, or a light client.
  • Check the upgrade path. Look for timelocks and audits on upgrades.
  • Don't park idle value in bridged IOUs. Bridge, use the funds, and redeem to native assets when you're done.
  • Diversify bridge exposure. Spreading large positions across different verification models limits correlated losses.
  • Revoke stale approvals to bridge routers you no longer use.
  • If an exploit is live, don't "rescue" funds yourself. Copying exploit calldata can expose you to legal risk whatever your intent.

Frequently Asked Questions

What caused the Nomad bridge hack?

A June 2022 Replica contract upgrade introduced a flaw that, as commonly described, marked the zero hash (0x00) as a trusted Merkle root, so unproven messages passed the bridge's "proven" check. Solidity returns zero for unset mapping keys, so any new message looked like it was proven against a valid root. Attackers could then craft withdrawal messages without a Merkle proof.

How much was stolen in the Nomad bridge exploit?

About $186 million was drained starting August 1, 2022. Fortune reported the bridge held $190.3M before the attack and $11,815 afterward. About $22M was recovered in the following days, according to the FTC complaint reported by Decrypt.

Why were there so many Nomad attackers?

The exploit could be copied by anyone who replaced the recipient address in a public transaction. It needed no special code, only calldata visible on-chain. One lecture deck claims more than 300 attackers, a figure we could not verify.

Couldn't Nomad's fraud window have stopped the attack?

No, because the attack never submitted a new root for watchers to challenge. Nomad's optimistic model defended against fraudulent root updates. The malicious messages relied on a root that had been accepted since initialization, so the fraud-proof mechanism was never triggered.

Are light-client bridges immune to Nomad-style bugs?

No. Light-client bridges remove trusted signers, but their proof-verification code can still contain bugs. The June 2026 Syscoin bridge exploit came from an SPV proof parsing error, according to Halborn. Rate limits, fuzzing, and fast pauses limit the damage when code fails.

How can I tell if a bridge is safe to use?

Check who can authorize minting, how upgrades are controlled, and whether outflows are rate-limited. Prefer bridges that verify source-chain consensus directly, publish audits, and run a public bug bounty. Hold bridged assets only as long as you need them.

Conclusion

The Nomad bridge hack was not a cryptographic failure. A single default value broke an otherwise reasonable design, and there was no rate limit, pause, or monitoring in place to contain it. Zero values must never authorize anything, a bridge's headline trust model says little about the safety of its upgrade and initialization code, and limiting blast radius matters as much as preventing bugs.

If you're moving Bitcoin across chains, choose a verification model that doesn't depend on a committee, and keep checking the implementation behind it. You can see SPV light-client verification in practice, and track live bridge activity, at teleswap.xyz.