Synapse Protocol nUSD MetaPool Virtual-Price Normalization Exploit / Bridge-Halt Near Miss (2021-11-06)
- Name: Synapse Protocol nUSD MetaPool virtual-price normalization exploit / bridge-halt near miss
- Date: 2021-11-06
- Protocol: Synapse Protocol
- Chain: Avalanche
- Category: metapool virtual-price normalization failure / LP-token scaling mismatch / bridge-halt near miss
- Loss: Synapse said about
$8.2MofnUSDwas drained from the Avalanche nUSD metapool, but validators halted the bridge before the attacker could mint that value out on the destination chain and LPs were later made whole - Summary: Synapse’s Avalanche
nUSDmetapool used Saddle-derivedMetaSwapmath that paired a plain stablecoin-side asset with the LP token of a base pool. In that design, raw base-pool LP units have to be normalized through the base pool’s virtual price everywhere they enter pricing logic. Synapse’s 2021 postmortem pointed to the same SaddleMetaSwapUtilsomission later preserved in Nerve and Saddle incident writeups:_calculateSwap()’sswap()path omitted the base virtual-price scaling that related paths such as liquidity-removal math andswapUnderlying()still applied. The attacker looped through public metapool transactions that pushed the metapool virtual price down by about12.5%, drained roughly$8.2MinnUSD, and then tried to exit through the same Synapse bridge. Because Synapse paused the metapool, took validators offline, and refused the malicious bridge mint on the destination chain, the incident became a near miss for permanent user loss rather than a completed cash-out. - Trigger / entry point:
- The affected Avalanche contract was the Synapse
MetaSwap/nUSDmetapool at0xF44938b0125A6662f9536281aD2CD6c499F22004. - Synapse’s public postmortem said the bug came from the Saddle
MetaSwapUtilsimplementation the Synapse AMM used, specifically the missing base virtual-price check in_calculateSwap()that it compared to the correctly scaled path at a different line in the same library. - The exploit surface was ordinary public metapool math. It was not a bridge-message forgery or validator-key compromise.
- The attacker addresses highlighted by Synapse were
0x3AB92d06F5f2A33D8F45F836607F8Da68CAb81e8and a smaller linked looping address0xb99c0f7163aace550f06cdd3ba495f143efc2415.
- The affected Avalanche contract was the Synapse
- Root cause:
- A metapool LP token is not a flat
$1stablecoin. Its economic value drifts with the base pool’sgetVirtualPrice()and must be normalized symmetrically across every path that prices it. - Synapse said the live AMM inherited Saddle’s missing virtual-price check in
_calculateSwap(), while nearby logic such as liquidity-removal math andswapUnderlying()still handled the LP token with the proper virtual-price scaling. - That inconsistency meant the metapool could honor two different truths about the same LP token depending on which path the attacker used.
- Once the attacker could repeatedly cycle through those inconsistent paths, the metapool’s own virtual price and reserve accounting became exploitable state rather than an honest summary of backing.
- A metapool LP token is not a flat
- Exploit path:
- The attacker targeted the Avalanche nUSD metapool and repeatedly used only public trading / liquidity paths.
- Synapse’s postmortem described the recurring loop as three linked transactions:
nUSD -> USDCthrough Firebird Router plusMetaSwap.swap()/removeLiquidityOneToken(), thenUSDC -> nUSD-LPthroughMetaSwapDeposit.addLiquidity(), thennUSD-LP -> nUSDthroughMetaSwapDeposit.removeLiquidityOneToken(). - Because the vulnerable path did not preserve the same base virtual-price normalization as the safer path, the attacker could cycle between plain stablecoin and LP-token representations at inconsistent economic values.
- Each loop pushed the metapool virtual price lower rather than merely extracting a one-off favorable quote; Synapse said the attacker drove it down by about
12.5%. - By repeating the loop, the attacker drained about
$8.2MofnUSDfrom the metapool. - The attacker then attempted to move the extracted value through the Synapse bridge to cash out on another chain.
- Synapse validators had already coordinated to go offline and not process the malicious bridge transfer, so the attacker failed to mint the bridged
nUSDon the destination chain. - Synapse later returned the blocked value to affected Avalanche LPs.
- What the attacker needed to be true:
- The live Avalanche metapool had to retain Saddle-derived LP-token normalization drift in the public
swap()path. - Related liquidity and underlying-swap paths had to keep a different, economically correct treatment of the same LP token so the attacker could realize the mismatch through loops.
- The metapool had to stay unpaused long enough for repeated looping transactions to drain a meaningful amount of
nUSD. - The bridge exit had to remain open long enough for a successful cash-out attempt; Synapse’s emergency validator halt is what broke the exploit’s final monetization step.
- The live Avalanche metapool had to retain Saddle-derived LP-token normalization drift in the public
- Technical anatomy worth remembering:
- This was not a bridge-verification failure. The bridge only appears because it became the would-be cash-out rail after the AMM exploit had already created unearned
nUSDoutflow. - Synapse is the earliest preserved public case in this corpus where the missing metapool LP-token virtual-price check was discussed explicitly before the later Nerve and Saddle incidents turned the same family into better-known exploit exemplars.
- The key failure was not
price oracle liedin the usual external-feed sense. The metapool lied to itself about what its LP-token side was worth. - The incident is also a useful reminder that emergency operator intervention can prevent realized loss without changing the underlying exploit primitive. The AMM bug was still real, loopable, and already draining value before the bridge halt contained it.
- This was not a bridge-verification failure. The bridge only appears because it became the would-be cash-out rail after the AMM exploit had already created unearned
- Representative addresses and artifacts:
- Avalanche nUSD
MetaSwap:0xF44938b0125A6662f9536281aD2CD6c499F22004 - Primary attacker address:
0x3AB92d06F5f2A33D8F45F836607F8Da68CAb81e8 - Secondary linked attacker address:
0xb99c0f7163aace550f06cdd3ba495f143efc2415 - Synapse postmortem discussing the vulnerable Saddle code path:
https://medium.com/synapse-protocol/11-06-2021-post-mortem-of-synapse-metapool-exploit-3003b4df4ef4
- Avalanche nUSD
- Response / mitigation:
- Synapse’s timeline says the Avalanche nUSD metapool was paused at
18:22 UTConce abnormal activity was recognized. - By
18:58 UTC, metapools on all six Synapse chains were paused and validators coordinated to go offline to prevent malicious bridge processing. - Synapse and Saddle worked together to isolate the missing
_calculateSwap()virtual-price check and evaluate sibling deployments. - Synapse then chose to redeploy new pools holding all four stable assets directly rather than continue with the broken metapool structure, migrated liquidity, and later returned the blocked
nUSDto affected LPs.
- Synapse’s timeline says the Avalanche nUSD metapool was paused at
Three metapool-and-exit authority promotions this incident collapsed
-
A real metapool LP-token accounting distortion already existed on Avalanche
- Synapse’s own postmortem preserved the local mistake clearly:
_calculateSwap()omitted the base virtual-price scaling at line424, while nearby liquidity-removal logic at line277andswapUnderlying()still handled the same base-pool LP token with the proper normalization. - That meant the first thing the system had on its hands was not yet
bridge fraudor evencross-chain loss; it was a real local metapool state transition where public looping could push the Avalanche nUSD metapool virtual price down to about0.887and drain value from LPs.
- Synapse’s own postmortem preserved the local mistake clearly:
-
Synapse still promoted that distorted local state into canonical withdrawable
nUSDtruth- The dangerous promotion happened inside the AMM before the bridge ever mattered. Because public routes could cycle between stablecoin balances, metapool LP, and base-pool LP at inconsistent economic values, the metapool kept publishing attacker-shaped virtual-price and reserve state as if it were honest
nUSDsolvency truth. - In other words,
some raw LP-token movement happenedandthis metapool now authoritatively owes this much spendable nUSDwere different claims, but the broken path collapsed them into one live accounting story.
- The dangerous promotion happened inside the AMM before the bridge ever mattered. Because public routes could cycle between stablecoin balances, metapool LP, and base-pool LP at inconsistent economic values, the metapool kept publishing attacker-shaped virtual-price and reserve state as if it were honest
-
That published
nUSDtruth still retained bridge-export authority until operators explicitly overruled it- Synapse’s timeline makes the final promotion boundary unusually visible: after about
$8.2MofnUSDhad already been drained from the metapool, the attacker tried to move it through the Synapse bridge, and validators only prevented permanent loss by coordinating to go offline and refusing the malicious destination-chain mint. - That is why this note matters as a near miss instead of only an AMM exploit. The bridge did not create the counterfeit value, but absent operator intervention it was still prepared to honor the AMM-authored
nUSDas exportable cross-chain money-good value.
- Synapse’s timeline makes the final promotion boundary unusually visible: after about
The reusable reviewer split is sharper when written this way: one public path can distort local LP-token accounting, the metapool can still publish that distortion as canonical withdrawable nUSD truth, and the bridge can still treat that published claim as exportable release authority are three different promotions. Synapse only avoided final user loss because humans interrupted the third one.
Pattern tags
same base-pool LP token had two incompatible truths because one public metapool path skipped the virtual-price normalization that related paths still honoredloopable AMM-side LP-token overpayment became real pool drain before the bridge ever entered the storybridge validators prevented final monetization, but only after the AMM had already authored counterfeit exportable value inside the Avalanche metapoolpublic near miss in one Saddle-derived deployment should have been interpreted as herd-level production risk for sibling forks sharing the same MetaSwapUtils lineage
Why the exploit worked
- Synapse’s Avalanche metapool priced the same base-pool LP token two different ways. The vulnerable
_calculateSwap()/swap()branch omitted the base virtual-price normalization that related paths such asswapUnderlying()and liquidity-removal math still applied, so the pool could overpay raw LP-side value on one route while still honoring the more correct economic value on another. - That asymmetry made the metapool lie to itself about solvency. Each loop did not just win one lucky quote; it pushed the metapool virtual price down and turned reserve state plus invariant updates into attacker-steerable accounting fiction.
- The exploit stayed practical because the affected route was entirely public and composable. Firebird routing plus Synapse deposit / withdrawal helpers gave the attacker a repeatable way to move between plain stablecoin balances and LP-token representations until the pool had paid out about
$8.2MinnUSD. - The bridge mattered only as the cash-out rail. The decisive bug had already happened inside AMM math, but Synapse’s validator coordination kept the drained
nUSDfrom becoming final cross-chain loss by refusing to honor the malicious outbound transfer. - The preserved broader lesson is that
contained monetizationis notcontained exploit primitive. Once one live Saddle-derived metapool was shown to disagree with itself about LP-token normalization, every sibling deployment sharing that helper lineage should have been treated as immediately suspect.
Detection and audit pivots
- Diff every path that can touch the base-pool LP token and write down exactly where base virtual-price normalization happens. In Synapse’s case the key question was not
does swap math compile?butwhy does _calculateSwap() disagree with remove-liquidity math and swapUnderlying() about when raw LP units become economic value? - Run a round-trip property test on the public loop Synapse itself published: pool stablecoin → underlying stablecoin via
swap()/removeLiquidityOneToken(), then underlying stablecoin → metapool LP viaaddLiquidity(), then metapool LP → pool stablecoin viaremoveLiquidityOneToken(). Under unchanged market state, that route should lose fees rather than grind the metapool virtual price downward. - Treat sudden metapool virtual-price drift as a solvency alarm, not just a pricing anomaly. Synapse’s own early signal was the Avalanche nUSD virtual price collapsing to about
0.887. - Inventory deployed helper libraries and sibling forks immediately after one public incident. A source-level patch elsewhere or a belief that
our bridge is differentdoes not matter if a live metapool still points at the same vulnerableMetaSwapUtilslineage. - Review whether downstream exit rails can still bound loss after local accounting failure. Synapse preserved a rare example where operators could still refuse malicious bridge processing, but that containment only works if the bridge’s release policy is not fully automatic once the AMM has already emitted spendable-looking assets.
Failure-prevention checklist
- Centralize base-pool LP normalization so
swap(),swapUnderlying(), add/remove-liquidity helpers, quote views, and invariant updates all consume the same conversion routine rather than hand-rolled scaling logic. - Add round-trip tests for the exact economically equivalent route that the attacker used and assert that same-state looping loses fees instead of depressing metapool virtual price.
- Monitor metapool virtual price, LP-token outflows, and repeated loop-shaped trade sequences as first-class solvency telemetry, with pause thresholds defined before production.
- Keep a deployment inventory of every live pool, implementation hash, and linked math library so one public exploit in a sibling fork triggers immediate enumeration and pause decisions rather than ad hoc discovery.
- Treat cross-chain exit paths as bounded-release control surfaces. If an AMM-authored claim becomes bridgeable value, validators or release logic should still be able to quarantine obviously malicious drains before minting on the destination chain.
- Rehearse emergency coordination between AMM operators and bridge operators ahead of time; Synapse’s near miss depended on pausing the metapool quickly and getting validators offline before the outbound mint finalized.
- Prefer simpler pool topologies when the metapool abstraction has already proven fragile. Synapse’s eventual move to four-asset direct pools was a concrete reduction in LP-token normalization complexity, not merely a product migration.
Audit prompts
- Where exactly does this metapool convert raw base-pool LP units into normalized economic value, and do
swap(),swapUnderlying(), and liquidity helpers all reuse that exact rule? - Can any public route cycle between stablecoin balance, metapool LP, and base-pool LP representations in a way that changes virtual price while producing positive PnL under unchanged market state?
- If one path overpays the base-pool LP token, what invariant or monitoring signal would detect the resulting solvency drift before repeated loops drain the liquid side of the pool?
- Which live deployments, forks, and linked libraries would still be exposed if this exact helper bug were rediscovered today?
- If locally drained pool balances are also bridgeable or otherwise exportable, what exact human or automated control can still refuse final release once the local exploit is recognized?
- Does the incident-response plan distinguish between
bug contained before value left the chainandbug fully fixed, or could sibling pools keep running on the same exploitable math after one near miss? - Are pool-complexity savings from a metapool structure still worth the extra normalization surface, or would a flatter pool topology remove a whole class of LP-token accounting drift?
Lessons that should already be standard
- A base-pool LP token is not a stablecoin slot. If one path forgets that it is a drifting receipt token with a virtual price, the metapool will eventually trade against its own reserves.
- Near misses in shared code are production incidents for every sibling deployment, not reassuring evidence that the exploit is hard to monetize.
- Virtual-price drift is a solvency signal. A metapool’s own price collapsing without an external market explanation should be treated like balance-sheet damage, not like harmless pool imbalance.
- Cross-chain containment can reduce realized loss, but it does not soften the underlying AMM lesson. The exploit was already real before the bridge halt saved users from permanent loss.
- Library lineage matters more than repo comfort. Once a shared helper bug becomes public, teams need a live deployment inventory immediately, not a promise to review source later.
- Simplifying the pool design can be a security fix. Replacing a fragile metapool with a direct multi-asset pool may remove the normalization seam entirely rather than merely hardening one branch.
Why this incident matters
- It preserves the
failed exploit monetizationbranch of the metapool-normalization family: the AMM bug succeeded, but the attacker could not convert the pool drain into final bridged assets because operators halted the exit rail. - It is an important chronological anchor for the later Nerve and Saddle incidents, both of which exposed the same broader
MetaSwapUtilsfamily under different execution paths and with different cash-out results. - It also shows how shared metapool code should trigger herd-level response. Once Synapse identified the bug, sibling Saddle-derived deployments were already on borrowed time if they had not paused or audited their own live pools.
Internal linkages
- Core recurring pattern: metapool-virtual-price-normalization-failure.
- Positive-design companion: metapool-lp-normalization-and-path-equivalence-discipline — use this when the sharper follow-up is how a metapool should keep one canonical base-pool LP normalization rule across
swap(),swapUnderlying(), liquidity add/remove helpers, invariant updates, and linked-library rollout before one path can print self-arbitrage. - Sibling incident: nerve-bridge-fusdt-ust-metapool-virtual-price-normalization-exploit-2021-11-15 — same broader metapool LP-token normalization family, but there the attacker fully cashed out through the still-live venue rather than being stopped at the bridge exit rail.
- Sibling incident: saddle-finance-susdv2-metapool-virtual-price-scaling-exploit-2022-04-30 — later redeploy variant where a live pool still pointed at stale vulnerable math and two uses of the vulnerable
swap()path were enough to profit. - Adjacent pattern: amm-invariant-scaling-mismatch — both families are internal pool-math truth failures, but Synapse is specifically about inconsistent treatment of a base-pool LP token whose value drifted with virtual price.
- Adjacent design companion: bridge-bounded-release-design — useful when the sharper question is why the incident stayed a near miss for permanent loss only because the bridge exit path could still be operationally bounded after the AMM bug had already fired.
Control surface
- The leverage sat in the LP-token normalization boundary inside Synapse’s Saddle-derived metapool math: the exact place where raw base-pool LP units were supposed to become normalized economic value before quote, reserve, and virtual-price updates.
- The final loss outcome then depended on a second control surface outside the AMM itself: whether the bridge would keep honoring the drained
nUSDas freely exportable value after operators recognized the exploit.
Sources
- https://medium.com/synapse-protocol/11-06-2021-post-mortem-of-synapse-metapool-exploit-3003b4df4ef4
- https://github.com/saddle-finance/saddle-contract/blob/141a00e7ba0c5e8d51d8018d3c4a170e63c6c7c4/contracts/meta/MetaSwapUtils.sol#L277
- https://github.com/saddle-finance/saddle-contract/blob/141a00e7ba0c5e8d51d8018d3c4a170e63c6c7c4/contracts/meta/MetaSwapUtils.sol#L424
- https://snowtrace.io/address/0xF44938b0125A6662f9536281aD2CD6c499F22004
- https://blocksec.com/blog/the-analysis-of-nerve-bridge-security-incident
Last reviewed
- 2026-08-04 UTC