Warp Finance WETH-DAI LP Oracle Manipulation (2020-12-17)

  • Name: Warp Finance WETH-DAI LP oracle manipulation
  • Date: 2020-12-17
  • Protocol: Warp Finance
  • Chain: Ethereum
  • Category: oracle manipulation / LP-token collateral inflation / reserve-state abuse / undercollateralized borrow
  • Loss: about 7.8M of lender funds, primarily DAI and USDC
  • Summary: The attacker assembled roughly 2.9M DAI plus about 344,800 WETH of flash liquidity from dYdX and multiple Uniswap pairs, used a slice of that liquidity to mint about 94,349 WETH-DAI Uniswap V2 LP tokens, then posted those LP tokens as Warp collateral. After the collateral was in place, the attacker rammed roughly 341,000 WETH into the same WETH-DAI pool, making Warp’s LP-pricing code read a much larger reserve-derived pool value. Warp’s oracle used TWAPs for the component asset prices, but it multiplied those prices by manipulable current reserves from UniswapV2Library.getReserves() and divided by LP supply, so the LP mark could still be flash-loan inflated. With the WETH-DAI LP token now priced at roughly 135,470,392 in Warp’s oracle units instead of about 58,815,427, the attacker borrowed about 3.86M DAI and 3.9M USDC, repaid the flash liquidity, and left Warp lenders holding an underwater LP-collateral position.
  • Trigger / entry point:
    • Warp Finance accepted Uniswap V2 LP tokens as collateral in dedicated LP vaults and let users borrow DAI, USDC, and USDT against that collateral.
    • The affected collateral path was the WETH-DAI LP vault 0x13db1CB418573f4c3A2ea36486F0E421bC0D2427.
    • Warp’s oracle contract 0x4A224CD0517f08B26608a2f73bF390b01a6618c8 priced LP tokens by combining TWAP asset prices with live pair reserves.
    • WarpControl 0xBa539B9a5C2d412Cb10e5770435f362094f9541c turned that LP price directly into borrow power.
  • Root cause:
    • Warp did not use a raw spot price for WETH or DAI; the deeper failure was more subtle. UniswapLPOracleFactory.getUnderlyingPrice() fetched TWAP-derived prices for the underlying assets but multiplied those prices by current, flash-loan-malleable pool reserves from UniswapV2Library.getReserves().
    • _calculatePriceOfLP() then computed LP value as (value0 + value1) / totalSupply, so a huge same-transaction swap that shoved WETH into the pool could make total pool value appear far larger even though that state was not durable or economically realizable at size.
    • WarpControl used that inflated LP price inside getTotalAvailableCollateralValue() and borrowSC(), so the manipulated reserve state became immediate stablecoin borrowing power.
    • In other words, Warp mixed robust-enough per-asset TWAPs with non-robust reserve snapshots. The individual component prices were hard to move; the reserve vector used to scale them was not.
  • Exploit path:
    1. The attacker contract 0xdf8bee861227ffc5eea819c332a1c170ae3dbacb executed the main exploit transaction 0x8bb8dc5c7c830bac85fa48acad2505e9300a91c3ff239c9517d0cae33b595090.
    2. It assembled flash liquidity across dYdX and Uniswap totaling about 2.9M DAI and 344,800 WETH.
    3. It deposited the dYdX-funded 2.9M DAI plus about 76,000 WETH into the Uniswap V2 WETH-DAI pool and minted about 94,349 LP tokens.
    4. It posted those LP tokens into Warp’s WETH-DAI LP vault as collateral.
    5. It then used the remaining borrowed WETH — about 341,000 WETH — to buy roughly 47.6M DAI from the same Uniswap pool.
    6. That swap changed the pool from roughly 95,000 WETH / 60.91M DAI to about 436,000 WETH / 13.288M DAI, dramatically increasing the reserve-weighted pool value used by Warp’s LP-pricing logic.
    7. Warp’s oracle now valued the posted LP collateral at about 135,470,392 in its internal pricing units instead of about 58,815,427 before the manipulation.
    8. With that inflated collateral mark, the attacker borrowed about 3.86M DAI and about 3.9M USDC from Warp’s stablecoin vaults.
    9. The attacker then unwound the pool distortion enough to repay the dYdX and Uniswap flash liquidity, leaving Warp with underwater LP collateral and a lender shortfall.
  • What the attacker needed to be true:
    • Warp had to accept the WETH-DAI LP token as collateral without a haircut or cap commensurate with how cheaply the underlying pool state could be distorted.
    • The attacker had to be able to mint LP tokens first, then manipulate the same pool, then borrow before reserve state normalized.
    • Warp’s borrow path had to consume the manipulated LP price in the same transaction window.
    • The stablecoin vaults had to hold materially more trustworthy liquidity than the manipulated collateral mark deserved.
  • Technical anatomy worth remembering:
    • This is a canonical case of reserve-state manipulation rather than plain spot-oracle manipulation. Warp’s per-asset price feeds were TWAP-like; the failure was treating live reserves as trustworthy multipliers for those prices.
    • The vulnerable code path sat in UniswapLPOracleFactory.getUnderlyingPrice(), which called getReserves(), fed those reserve amounts into oracle.consult(...), and then sent the resulting values into _calculatePriceOfLP().
    • The borrow path sat in WarpControl.getTotalAvailableCollateralValue() and borrowSC(), so the manipulated LP token mark directly enlarged the attacker’s stablecoin credit line.
    • The attacker did not need to exit with the LP collateral itself. Leaving the position underwater was enough because Warp had already released stronger assets against the printed collateral value.
    • Warp’s later recovery efforts underline the balance-sheet failure: the protocol could recover the locked ETH-DAI LP collateral, but that collateral was still worth only about 73%-75% of what lenders were owed.
  • Representative transactions and addresses:
    • Main exploit transaction: 0x8bb8dc5c7c830bac85fa48acad2505e9300a91c3ff239c9517d0cae33b595090
    • Attacker contract: 0xdf8bee861227ffc5eea819c332a1c170ae3dbacb
    • Warp oracle: 0x4A224CD0517f08B26608a2f73bF390b01a6618c8
    • WarpControl: 0xBa539B9a5C2d412Cb10e5770435f362094f9541c
    • Affected WETH-DAI LP vault: 0x13db1CB418573f4c3A2ea36486F0E421bC0D2427
    • Stablecoin vaults highlighted by REKT: DAI vault 0x6046c3Ab74e6cE761d218B9117d5c63200f4b406, USDC vault 0xae465FD39B519602eE28F062037F7B9c41FDc8cF
  • Response / mitigation:
    • Warp paused the protocol and later recovered the attacker’s locked ETH-DAI LP collateral rather than the borrowed stablecoins themselves.
    • On 2020-12-22 the team distributed recovered ETH-DAI LP tokens worth about $5.688M, or roughly 73% of lost funds, to affected users.
    • The reusable lesson was not merely use TWAPs. LP pricing also has to neutralize reserve-state manipulation, for example with fair-reserve style calculations, deeper haircuts, market caps, or by refusing thin LP collateral altogether.

Pattern tags

  • oracle-manipulation
  • lp-token-oracle-mispricing
  • The preserved shape is sharper than generic TWAP oracle manipulation: Warp combined sturdy-enough component TWAPs with flash-loan-malleable live reserves, so the exploitable object was the LP wrapper mark rather than the raw WETH or DAI price feed by itself.
  • This was also a clean same-window reserve publication became borrow power failure. The attacker did not need the distorted pool state to persist; they only needed Warp to convert that transient LP mark into DAI and USDC credit before liquidation reality caught up.

Three LP-solvency promotions this incident collapsed

  1. A reserve-shaped LP state could be authored

    • After minting and posting the WETH-DAI LP tokens, the attacker could still use flash liquidity to shove roughly 341,000 WETH through the same pool and rewrite the reserve vector that Warp later read with UniswapV2Library.getReserves().
    • This was already enough to fabricate a very different LP looks valuable right now story even though the underlying state was not durable unwind value.
  2. Warp promoted that authored reserve state into canonical collateral truth

    • UniswapLPOracleFactory.getUnderlyingPrice() treated the current reserve amounts as trustworthy scale inputs for the component TWAPs and _calculatePriceOfLP() then published the resulting LP mark as if it described money-good collateral value.
    • In practice the dangerous promotion was not WETH had a price or DAI had a price; it was this same-window reserve composition still counts as the one canonical LP-solvency answer.
  3. That published LP truth still inherited immediate stablecoin-spending authority

    • WarpControl consumed the inflated LP mark inside getTotalAvailableCollateralValue() and borrowSC(), so the freshly published wrapper price immediately authorized DAI and USDC borrowing against much stronger lender-side assets.
    • By the time liquidation reality reappeared, the attacker had already converted the transient reserve story into durable reserve outflow.

Why the exploit worked

  • Warp asked one oracle path to answer two harder questions at once: what are WETH and DAI worth? and what is this LP token worth right now as collateral? The first question had some TWAP resistance; the second quietly reintroduced spot-state trust by multiplying those prices against live pair reserves.
  • That meant the protocol was not actually lending against durable unwind value. It was lending against a same-transaction reserve composition that the attacker themselves could author with flash liquidity after minting and posting the LP tokens.
  • Borrow-side controls did not create a second distrust boundary. Once getUnderlyingPrice() produced the inflated LP mark, WarpControl immediately turned it into stablecoin borrow power instead of quarantining fresh reserve states, haircutting the collateral to executable unwind value, or capping exposure tightly enough that one manipulated pool could not drain lender-side cash.
  • The balance-sheet asymmetry did the rest: Warp released DAI and USDC that were materially stronger than the distorted LP collateral mark, and liquidation only discovered that mismatch after the attacker had already repaid the flash loans and exited.
  • The durable lesson is therefore broader than use TWAPs for LPs too. Protocols have to prove that wrapper-collateral valuation, reserve-state publication, and same-window borrowing authority are all talking about one economically realizable object.

Detection and audit pivots

  • Reconstruct LP pricing end to end instead of stopping at underlying assets use TWAP. Ask exactly which state variables still come from the live pool: reserves, k, total supply, cached balances, wrapper exchange rate, or one-side redemption assumptions.
  • Simulate the exact attacker order of operations: mint LP shares, post them as collateral, distort the same pool’s reserve vector with flash liquidity, then borrow before the state settles. If the protocol only tests pre-manipulation collateral deposits or post-manipulation liquidation, it will miss the decisive same-window authority gap.
  • Compare the collateral mark against executable unwind value under stress. If a position can only realize its quoted value by selling through the same pool whose reserves the attacker just changed, the protocol should treat the quoted mark as adversarial until delayed snapshots, fair-reserve math, or explicit unwind haircuts narrow the gap.
  • Audit the wrapper layer, not only the price feed layer. LP collateral is unsafe whenever wrapper worth depends on current reserve composition that a borrower can cheaply reshape after minting but before borrowing.
  • Check whether borrow-side blast-radius controls assume oracle correctness instead of surviving oracle failure: per-market debt caps, conservative collateral factors, same-block quarantine, and LP-specific caps should still contain damage if one wrapper mark is briefly wrong.

Failure-prevention checklist

  • Treat LP-collateral valuation as a separate solvency problem from underlying-token pricing. TWAPs on WETH and DAI are not enough if LP NAV still multiplies those prices by flash-loan-malleable live reserves.
  • Keep same-transaction reserve state out of immediate borrowing authority. If reserve composition can change materially inside the same execution window, fresh LP marks should be delayed, smoothed, or quarantined before they count as collateral.
  • Haircut LP collateral to executable unwind value at protocol size, not to paper NAV at current spot reserves. If liquidation would have to sell through the same distorted pool, the protocol should already be lending against the stressed unwind number.
  • Cap LP-backed borrowing by market depth and weakest-leg liquidity rather than by nominal pool TVL alone. The pool can look large while one side of the valuation remains attacker-authored for one block.
  • Couple LP listing, oracle-helper choice, collateral factor, and borrow enablement into one risk decision. If governance can separately admit LP collateral and later discover the valuation path is unsafe, the balance sheet is already exposed.
  • Add reserve-state sanity checks and same-block change limits around LP valuation paths. A collateral mark that doubles because one reserve vector swung violently inside one transaction should fail closed or clamp rather than mint stablecoin credit.
  • Keep lender-side blast-radius controls even if the oracle is thought to be robust: per-LP debt caps, protocol-wide pauses, and isolated markets should bound losses when one wrapper mark is wrong.
  • Prefer fair-reserve or similarly manipulation-aware LP valuation methods where possible, and be willing to reject thin LP collateral entirely when no clean executable-value path exists.

Audit prompts

  • Which exact values in the LP-pricing path are manipulation-resistant and which are still live pool state? In Warp the TWAP inputs were not the problem by themselves; getReserves() was the residual attacker-authored truth surface.
  • Can a borrower mint or acquire the LP position first, then distort the underlying pool, then borrow before the protocol ever forces a delayed or independently validated mark?
  • What liquidation or unwind path would actually realize the quoted LP value at the borrowed size, and how much of that value disappears if the attacker stops supporting the manipulated reserve ratio immediately after borrowing?
  • Does the protocol rely on current reserves / totalSupply style NAV even when those reserves can be shifted by one-sided flash liquidity after the borrower has already posted the LP token?
  • Are same-block or same-transaction reserve swings bounded, ignored, or directly honored as borrow power? If directly honored, what stops one reserve-stuffing transaction from becoming a lender-side bad-debt event?
  • How large can borrowing against one LP market get relative to the stablecoin liquidity on the other side of the balance sheet? Could one manipulated wrapper mark drain assets that are much deeper and more trustworthy than the collateral itself?
  • If the LP oracle failed high for one block, what independent control would still save lenders: haircuts, debt caps, market isolation, delayed activation, or none?
  • Does governance or operations have a way to pause or derecognize LP collateral quickly when reserve-state assumptions break, or is oracle helper already deployed effectively irreversible balance-sheet authority?

Lessons that should already be standard

  • TWAP inputs do not make wrapper collateral safe if the wrapper’s own valuation still trusts live reserves. A protocol can use robust-looking price feeds and still hand borrowers a user-written LP mark.
  • LP collateral should be judged by unwind reality, not mark-to-reserve elegance. If liquidation could not realize the quoted value after the attacker exits, the protocol should never have lent against that number.
  • Same-window reserve publication is already borrowing authority. If the borrower can author the reserve state and consume it before time or depth can challenge it, the oracle is effectively part of the borrow function.
  • Stronger assets should not be released against weaker wrapper truth without extra distrust. Warp’s real failure was balance-sheet asymmetry: DAI and USDC were money-good, the LP mark was not.
  • LP wrappers do not diversify away the manipulability of their underlying venue. One deep-looking pool can still be a thin solvency object when one side of the reserve vector is cheaply distortable.
  • Oracle review has to include the wrapper-construction layer, not just the base-token feed layer. The dangerous question is not only what is WETH worth? but what object is allowed to translate that answer into LP collateral value?
  • Borrow caps and market isolation are not second-class mitigations. When protocols insist on accepting LP collateral, bounded-loss design is what keeps one valuation mistake from becoming lender insolvency.
  • Recovery paths that only claw back leftover collateral are evidence that the protocol lent against a non-executable mark. If the best postmortem fix is we recovered the LP tokens and users still lost ~25%, the pre-attack collateral truth was overstated from the start.

Why this incident matters

  • Warp Finance is one of the clearest demonstrations that TWAP + current reserves can still be exploitable for LP collateral valuation.
  • It separates two often-confused ideas: manipulation-resistant pricing of the underlying assets is not the same thing as manipulation-resistant pricing of the LP token wrapper.
  • It also shows how an exploit can be profitable even when the attacker leaves apparently valuable collateral behind; what matters is whether the protocol released even stronger assets against a mark that could not survive liquidation.

Internal linkages

Control surface

  • The leverage sat in LP-collateral admission, the WETH-DAI vault configuration, UniswapLPOracleFactory.getUnderlyingPrice(), _calculatePriceOfLP(), and WarpControl’s willingness to convert that LP mark into immediately borrowable DAI and USDC.
  • Warp did not fail because it had no oracle. It failed because it treated live AMM reserve composition as a reliable balance-sheet input for collateral valuation.

Sources

Last reviewed

  • 2026-08-02 UTC