THORChain ETH Router Fake-Deposit Refund Drain (2021-07-22)
- Name: THORChain ETH Router fake-deposit refund drain
- Date: 2021-07-22
- Protocol: THORChain / Bifrost / ETH Router
- Chain: Ethereum + THORChain
- Category: bridge deposit-signal mismatch / event ambiguity / memo-triggered refund drain / unbacked outbound release
- Loss: about $8M, including roughly 1.67M USDC, 990,137 USDT, 20.87M XRUNE, 56,104 SUSHI, 966.62 ALCX, and 6.91 YFI
- Summary: THORChain’s second July 2021 ETH-router exploit worked because Bifrost accepted a deposit-looking router event as proof that real user assets had arrived when the underlying call path had not created the economic state Bifrost assumed. THORChain’s own postmortem says the attacker created a fake router setup, called
returnVaultAssets()with a small amount of ETH while that router path was defined as an Asgard vault, and thereby caused the real Router to forward ETH and emit what Bifrost interpreted as a normal deposit carrying a malicious memo. The healthy boundary should have beenonly the canonical router-authored deposit artifact that proves user custody change may author inbound truth; instead, a router-adjacent vault-return path was still able to mint deposit meaning. THORChain then followed its ordinarybad memo -> refundlogic and released real ERC-20 assets from vault inventory even though the inbound deposit premise was fake. - Trigger / entry point:
- THORChain’s Ethereum-side Router and Bifrost listener had to agree on what counted as a real inbound deposit.
- Bifrost consumed router-side event data and memo semantics as if they reliably represented real user deposit intent and custody change.
- A router-adjacent
returnVaultAssets()path remained reachable through an attacker-controlled contract arrangement that the system did not model as adversarial deposit fabrication. - THORChain’s normal memo handling would refund a transaction when the memo was malformed or otherwise invalid.
- Root cause:
- The core failure was not stolen keys or forged validator signatures. It was source-side authenticity drift:
event looked like a depositwas treated as equivalent touser really deposited assets under the expected path. - Bifrost did not fully account for how smart-contract event context could be manipulated when the router interacted with attacker-controlled contracts.
- THORChain’s postmortem preserves the sharper mechanical boundary: a
returnVaultAssets()-style path, with the router defined as an Asgard-vault-like destination, could still produce a deposit-looking event even though the surrounding call path was not the canonical user-deposit lane that should have been allowed to author inbound truth. - In other words, THORChain let a router-adjacent event stand in for the one thing that mattered: a router-owned custody artifact proving that user value had actually arrived under the intended deposit path.
- Once Bifrost accepted that event and memo as genuine, THORChain’s refund logic turned a fake inbound into a real outbound.
- The core failure was not stolen keys or forged validator signatures. It was source-side authenticity drift:
- Exploit path:
- The attacker funded EOA
0x8c1944fac705ef172f21f905b5523ae260f76d62, reportedly via Tornado Cash, and deployed attack contract0x700196e226283671a3de6704ebcdb37a76658805. - The attacker arranged the contract to sit
behindTHORChain’s Ethereum Router0xc145990e84155416144c532e31f89b840ca8c2cerather than acting as a simple front-end caller. - The attacker invoked
returnVaultAssets()with a small amount of ETH while the malicious contract was treated in the relevant path as an Asgard-vault-style destination. - The Router forwarded ETH to the attacker-controlled contract path and emitted a deposit-looking event containing attacker-chosen memo data.
- Bifrost intercepted that event and treated it as a normal THORChain deposit instead of recognizing it as a manipulated router/vault-return flow.
- The memo was deliberately malformed so THORChain followed its ordinary automatic refund path.
- Because the source-side event had already been accepted as authentic inbound value, the refund path released real assets from vault inventory to the attacker.
- The attacker repeated the pattern across multiple transactions, draining the economically significant ERC-20 balances from the affected Ethereum-side vaults.
- The attacker funded EOA
- What the attacker needed to be true:
- Bifrost had to treat router event shape plus memo as sufficient proof that a real deposit had occurred.
- The Router had to expose a path where attacker-controlled contract context could still produce a deposit-looking event.
- THORChain’s refund semantics had to release real assets on a
bad memopath without independently re-establishing that the original inbound value claim was genuine. - The hot vaults had to hold liquid ERC-20 inventory worth stealing once the spoofed deposit crossed the bridge-authenticity boundary.
- Technical anatomy worth remembering:
- This was a very sharp
deposit signal != locked valuebridge failure. The outbound side mostly did what the protocol said to do after an invalid memo; the real lie was earlier, when source-side event handling claimed a deposit had happened. - THORChain’s memo system mattered because the exploit was not just
fake deposit, immediate mint. The attacker used a forged inbound plus ordinary refund handling to turn spoofed intent into real asset release. - The cleaner healthy-side lesson is not merely
router events can be spoofed; it isdeposit actions should consume only the router's own custody-proving deposit artifact, while router-adjacent vault-return or upgrade rails must never keep authority to author ordinary inbound truth. - The incident also shows how smart-contract composability expands the bridge trust boundary: if event interpretation depends on call context, an attacker-controlled contract behind or beside the router becomes part of the bridge verifier.
- No separate price-oracle bug or lending-market mispricing was needed. Once Bifrost accepted the fake deposit, the vault inventory itself became the payout source.
- This was a very sharp
Three deposit-truth promotions this incident collapsed
-
Some router-adjacent ETH movement and deposit-looking event existed
- The attacker really did get the THORChain Router to forward ETH into the fake Asgard-style path, and the Router really did emit an event that looked deposit-shaped to Bifrost.
- That only proved
ETH moved somewhere through a router-adjacent helper pathplusan event with deposit-like shape was emitted, not that a user had entered the canonical THORChain deposit lane with redeemable value.
-
That helper-path artifact still counted as canonical inbound deposit truth
- Bifrost still promoted the event into ordinary deposit semantics even though THORChain’s own postmortem describes the path as a fake-router /
returnVaultAssets()construction where the Router was acting against an attacker-authored Asgard-style context rather than the canonical user-deposit path. - The dangerous jump was not merely that an event existed; it was that one router-adjacent maintenance-style path retained the authority to publish the same inbound truth the canonical deposit lane was supposed to own exclusively.
- Bifrost still promoted the event into ordinary deposit semantics even though THORChain’s own postmortem describes the path as a fake-router /
-
That accepted inbound truth still deserved immediate refund-release authority
- Once the forged inbound crossed Bifrost’s authenticity boundary, THORChain let the deliberately bad memo steer the system into its normal refund machinery and spend real ERC-20 inventory from live vault balances.
- The same source-side lie therefore inherited ordinary refund cash-out authority before any narrower lane budget, route-local quarantine, or provenance re-check interrupted it.
- Representative transactions and contracts:
- Ethereum Router:
0xc145990e84155416144c532e31f89b840ca8c2ce - Affected vault highlighted in public writeups:
0xf56cba49337a624e94042e325ad6bc864436e370 - Attack contract:
0x700196e226283671a3de6704ebcdb37a76658805 - Attack EOA:
0x8c1944fac705ef172f21f905b5523ae260f76d62 - Representative exploit transactions from THORChain’s postmortem:
0x10352e6ec052771a92f05f93e037e066873f64bb502d4488726697987f054595,0x1c6ef4d5122f1287085097ac37df076d43f389bfc62a1bdfcd3163254b5fe94a,0x0b39a878f1bf3daa78be149222dd8e5fa3e37b54d0451872b3d7a2cbf7f070e2,0x01f38f17ba838b54350f8e2a2bd54fcdd9cf3c45fa1d2a735f5311507671be9e,0xa89d90f300fed8d1d987dec86d0d628f31ffd2d8aefdd857ad90151084531d4c,0x9db403ad39d3fe78de378af0b49f03d244326662f7fee230db87d12a624f564b
- Ethereum Router:
- Response / mitigation:
- THORChain halted Ethereum activity and later kept ETH disabled until the router and Bifrost logic could be reworked and audited.
- The team stated that LPs would be made whole from treasury resources and recovery financing.
- Post-incident changes emphasized stricter event handling, broader audits, a bug-bounty program, active red-team review, solvency checks, node-triggerable timeouts, and outbound throttling.
Pattern tags
- deposit-signal-locked-value-mismatch
- message-source-authentication-failure
- Router-adjacent vault-return path retained authority to emit deposit-looking bridge truth
- Counterfeit inbound accepted as refund-worthy value claim against live ERC-20 vault inventory
Why the exploit worked
- THORChain let two unequal source-side objects speak too similarly about ETH-side deposit truth: the canonical router deposit path and a router-adjacent
returnVaultAssets()flow. Once the latter could still emit a deposit-looking artifact that Bifrost parsed as ordinary inbound value, the bridge had already split its own source-side authenticity model. - The decisive break was not merely
bad memo caused refund. The attacker first forged the upstream fact that a normal refundable deposit had happened at all. After that, THORChain’s ordinary memo-validation and refund machinery simply spent against a fiction the bridge had already accepted. - The healthy-side boundary was narrower than
router emitted something. The system neededrouter-authored event from the canonical custody-changing deposit lane, notany router-adjacent event shape that resembled deposit intent while ETH moved somewhere in the surrounding call path. - This is also why the incident pairs so tightly with the July 15 exploit while still preserving a distinct lesson. The first exploit let wrapper context outside the router redefine what outer
tx.valuemeant; the second let a path behind or beside the router keep deposit-authorship authority it should have lost. - THORChain also lacked a meaningful containment break between
one fake inbound admittedandlive ERC-20 inventory released on refund. Once Bifrost promoted the counterfeit source-side fact into normal inbound accounting, the refund path could cash it out immediately.
Detection and audit pivots
- Enumerate every router or bridge-core path that can emit deposit-looking artifacts, not just the public
deposit()entrypoint. Refund helpers, vault-return flows, migrations, upgrade shims, and recovery rails should be reviewed as potential alternate authors of inbound truth. - For each bridge-watched event, ask whether the emitter path itself proves the same custody change ordinary deposits are supposed to prove. If a watcher would still treat the event as fresh deposit authority even when it came from a helper branch with different semantics, treat that as a live bridge-authentication bug.
- Trace memo and refund logic backward from payout to source-side proof. The key review question is whether
invalid memo -> refundcan ever release assets against an inbound record whose authenticity was inherited from a weaker event surface than the canonical deposit lane. - Review composable router topologies adversarially: contracts in front of, behind, or called by the router can all become part of the bridge verifier if Bifrost or relayers infer deposit truth from surrounding call context rather than from one canonical custody artifact.
- Compare accepted inbound facts with route-local release limits. If one forged router event can still command multi-asset refunds from hot-vault inventory before a narrower throttle, lane budget, or quarantine applies, the bridge is missing a second line of defense even after the source-side bug exists.
Failure-prevention checklist
- Restrict ordinary inbound authorship to one canonical router deposit artifact. Vault-return helpers, recovery flows, upgrade shims, and
returnVaultAssets()-style paths should never emit or relay the same deposit truth ordinary user deposits use unless they also satisfy the exact same custody proof. - Bind Bifrost to path kind as well as event shape. A deposit-looking event is not enough; relayers should know whether it came from the canonical user-deposit lane or from a helper path with different semantics.
- Separate refund-worthiness from memo syntax alone. Before
bad memo -> refundspends live vault inventory, the refund path should still be able to point to the original canonical deposit object that proved user value really arrived. - Treat behind-the-router contracts as part of the bridge verifier surface. If an attacker-controlled contract can influence whether the router emits a deposit-looking artifact, that contract topology needs the same adversarial review as the relayer parser itself.
- Deauthorize special-purpose routes once they no longer need to speak with deposit authority. A vault-return or migration helper that retains ordinary inbound semantics after the intended maintenance window is already latent counterfeit-deposit surface.
- Keep hot-vault refund authority bounded. One accepted inbound should not immediately unlock large multi-asset refunds without route-local caps, staged release, or fast halt levers.
- Reconcile refund-triggering inbound records against canonical router custody and route provenance quickly enough to detect
deposit-shaped event did not come from the canonical custody lanebefore hot-vault inventory is spent.
Audit prompts
- Which exact router functions can emit artifacts Bifrost interprets as fresh inbound deposits, and which of them are supposed to represent ordinary user custody change versus maintenance or vault-return semantics?
- When Bifrost sees a deposit-looking event, what concrete fields or call-path predicates prove that the event came from the canonical user-deposit lane rather than from
returnVaultAssets()or another helper rail? - Can a contract behind, beside, or called by the router still cause the canonical Router to emit the same deposit-shaped event without creating the same economic state a normal deposit would create?
- If THORChain decides a memo is invalid and initiates a refund, where is the original inbound’s custody provenance re-checked before vault assets are released?
- Which legacy recovery, migration, or upgrade helpers still retain the authority to mint refund-worthy inbound truth, and how would operators know that those special paths remain live?
- What route-local limits stop one forged deposit artifact from immediately draining the hot-vault ERC-20 inventory through repeated refund cycles?
- How quickly can operators distinguish
normal deposit event from canonical lanefromhelper-path event with deposit-like shapewhen Ethereum-side router traffic spikes or contracts interact compositionally?
Lessons that should already be standard
Router emitted a deposit-looking eventis not the same thing asa user made a canonical deposit. Event shape alone is too weak when helper paths and adversarial contracts can still reach the same emitter.- Refund logic is not operational glue; it is spend logic. Once a fake inbound crosses the authenticity boundary,
bad memo -> refundbecomes a direct cash-out rail against hot-vault inventory. - Special-purpose maintenance flows are part of bridge authentication if relayers cannot distinguish them from ordinary deposit authors. Helpers do not stay harmless just because they were introduced for recovery or vault-management reasons.
- Contracts behind or beside the router can be verifier-adjacent even when the bridge team thinks only the public deposit entrypoint matters. Composability widens the source-side speaker set unless the protocol narrows it explicitly.
- Deauthorization matters as much as initial design. A route that once needed to move assets for vault-return or migration purposes should lose ordinary inbound authority as soon as that narrower job is complete.
- Bounded refund release should already be standard. One forged inbound plus one ordinary invalid-memo path should not have had line-of-sight to the vault’s economically significant ERC-20 inventory.
Why this incident matters
- It is one of the cleanest bridge cases where the relayer / watcher stack was not malicious; it was faithfully honoring a source-side signal whose semantics were weaker than the protocol believed.
- It extends the usual bridge-failure lesson beyond
forged proofandkey compromise: source-chain event design and contract-context assumptions are themselves verifier code. - It preserves the sharper deposit-authorship lesson too: not every router-adjacent event that looks like a deposit should retain the right to author refund-worthy inbound truth. That authority had to stay pinned to the canonical deposit artifact and custody path.
- It is also a memorable example of how refund logic can become the cash-out path once a fake inbound deposit crosses the authenticity boundary.
- It also preserves the separate bounded-release lesson: one forged inbound plus one ordinary
bad memo -> refundpath still controlled too much immediately spendable hot-vault inventory before operators could isolate the route. For that blast-radius question, pair this incident with bridge-bounded-release-design.
Internal linkages
- More specific recurring pattern: deposit-signal-locked-value-mismatch.
- Adjacent source-authentication lens: message-source-authentication-failure.
- Parent bridge-wide framing: bridge-verification-failure.
- Positive-design companion: bridge-deposit-truth-binding — the sharper framing for why only the canonical router-authored custody artifact should have been allowed to author refund-worthy inbound truth.
- Positive-design companion: bridge-source-provenance-bundle-and-authoring-surface-discipline — the sharper framing for why a router-adjacent
returnVaultAssets()path should never have retained authority to emit ordinary inbound truth to Bifrost. - Positive-design companion: bridge-canonical-path-exclusivity-and-stale-authoring-surface-deauthorization — the sharper framing for why router-adjacent vault-return flows should have lost any ability to publish ordinary inbound ETH truth once THORChain had a narrower canonical deposit lane.
- Positive-design companion: bridge-bounded-release-design — the cleaner framing for why one fake inbound plus one ordinary refund path still reached so much live vault inventory.
- Useful predecessor on the same router / Bifrost surface: thorchain-eth-router-msg-value-spoof-fake-deposit-swap-refund-drain-2021-07-15.
Control surface
-
The leverage sat in the ETH Router’s event semantics, Bifrost’s interpretation of which router paths were allowed to author deposit truth, the memo / refund state machine, and the hot-vault inventory that those signals were allowed to release.
-
The clean design boundary is worth preserving explicitly: ordinary deposits should have consumed only the router’s canonical custody-proving deposit artifact, while router-adjacent vault-return or upgrade rails should have remained narrower special-purpose paths rather than alternate authors of inbound truth.
-
The loss size also depended on containment: once the forged deposit crossed the authenticity boundary, the route could immediately convert that one bad source-side fact into real outbound inventory instead of hitting a narrower lane budget, staged release, or route-local brake first.
-
THORChain did not mainly fail because refunds are dangerous in isolation. It failed because the protocol accepted a fake deposit premise and then let perfectly ordinary refund logic spend real assets against that fiction.
-
Sources:
-
Last reviewed: 2026-08-02 UTC