Cheese Bank CHEESE-ETH LP Oracle Manipulation (2020-11-06)

  • Name: Cheese Bank CHEESE-ETH LP oracle manipulation
  • Date: 2020-11-06
  • Protocol: Cheese Bank
  • Chain: Ethereum
  • Category: oracle manipulation / LP-token collateral inflation / AMM reserve-manipulation / undercollateralized borrow
  • Loss: about $3.3M, including roughly 2M USDC, 1.23M USDT, and 87K DAI drained from Cheese Bank
  • Summary: The attacker flash-borrowed 21,000 ETH from dYdX, used a small initial CHEESE/ETH liquidity position to obtain Uniswap V2 CHEESE-ETH LP tokens, then used those LP tokens as Cheese Bank collateral. After that setup, the attacker spent about 20,000 ETH to ram the CHEESE price up on Uniswap, called Cheese Bank’s oracle refresh path, and let the protocol revalue the CHEESE-ETH LP collateral off the manipulated WETH-heavy pool state. With the LP token now marked far above realizable value, the attacker borrowed out essentially all available USDC, USDT, and DAI, unwound the CHEESE price pump, repaid the flash loan, and kept the stablecoins.
  • Trigger / entry point:
    • Cheese Bank accepted UNI_V2-CHEESE-ETH as collateral via its sUSD_V2 market, so a thin CHEESE venue could become direct borrowing power against deeper stablecoin pools.
    • CheesePriceOracle::refresh() let the attacker force a same-transaction repricing after they distorted Uniswap state.
    • PeckShield’s walkthrough shows the decisive LP-pricing path was CheesePriceOracle::fetchLPAnchorPrice(), which read the live WETH balance of the UNI_V2-CHEESE-ETH pair, multiplied it by 2, multiplied again by an ETH price derived from the UNI_V2-USDT-ETH pool, and then divided by LP total supply.
    • That meant the oracle was not measuring realizable unwind value; it was measuring attacker-shaped spot reserves inside one transaction and immediately turning them into collateral truth.
  • Root cause:
    • Cheese Bank priced the UNI_V2-CHEESE-ETH LP token from instantaneous Uniswap reserve state, especially the live WETH side of the pool, as if that reserve composition reflected durable, realizable collateral value.
    • The oracle logic effectively treated totalValue = wEthBalance * 2 * ethPrice, with ethPrice itself coming from another Uniswap V2 pool (UNI_V2-USDT-ETH), and then divided by LP total supply.
    • A flash-loan-funded shove of ETH into the CHEESE pool therefore increased the collateral mark exactly where Cheese Bank looked for solvency, even though the same attacker could unwind that pool distortion immediately after borrowing.
    • The protocol then extended real USDC, USDT, and DAI credit against that printed LP valuation.
    • This was not just a flash-loan issue; it was a solvency-design failure where a manipulable LP-token mark became borrow power.
  • Exploit path:
    1. The attacker contract 0x9e0259437804c7bf175421a451bc80611a0b93c3 initiated the main exploit transaction 0x600a869aa3a259158310a233b815ff67ca41eab8961a49918c2031297a02f1cc.
    2. It borrowed 21,000 ETH from dYdX in a flash loan.
    3. It swapped 50 ETH for about 107,000 CHEESE on Uniswap V2.
    4. It added roughly 107,000 CHEESE plus about 78 ETH as liquidity to the CHEESE-ETH pool and received UNI_V2-CHEESE-ETH LP tokens.
    5. It deposited those LP tokens into Cheese Bank’s sUSD_V2 market, minting the position token used as collateral for later borrowing.
    6. It then spent about 20,000 ETH to buy about 288,000 CHEESE on Uniswap V2, sharply repricing the CHEESE-ETH pool and inflating the WETH-heavy reserve picture the oracle relied on.
    7. The attacker explicitly called CheesePriceOracle::refresh() so Cheese Bank would recompute the LP-token price from the manipulated pool state.
    8. With the LP collateral freshly marked up, the attacker checked Cheese Bank’s available balances and borrowed essentially the exact stablecoin liquidity on hand: about 2M USDC, 1.23M USDT, and 87K DAI.
    9. The attacker then sold the acquired CHEESE back for about 19,980 ETH, swapped about 58K USDC for roughly 132 ETH to top up the ETH needed for flash-loan repayment and fees, transferred proceeds onward, and repaid the 21,000 ETH flash loan.
  • What the attacker needed to be true:
    • Cheese Bank had to accept the CHEESE-ETH LP token as collateral without a severe haircut matched to the market depth of CHEESE.
    • The oracle had to let a same-transaction AMM state change become the new collateral truth.
    • The borrowable stablecoin reserves had to be much deeper and more trustworthy than the CHEESE market used to value the collateral.
    • The attacker had to be able to refresh the oracle before borrowing and unwind after borrowing without the transaction reverting.
  • Technical anatomy worth remembering:
    • This is a clean LP-token valuation exploit, not just a generic price pump. The target asset was the LP token itself, and the protocol treated that LP token as money-good collateral.
    • The key oracle shortcut was treating the pool’s live WETH reserve as a trustworthy anchor for total pool value, while sourcing ETH/USD from another Uniswap V2 pool (UNI_V2-USDT-ETH) in the same spot-oracle family. That looked like an extra cross-check, but it was not real source diversification: both legs still came from flash-moveable Uniswap V2 reserve state. In a manipulated CHEESE-ETH pool, that assumption is wrong exactly when it matters most.
    • PeckShield’s walkthrough shows the borrower also queried Cheese Bank balances before calling borrow(), which made the drain path operationally simple: once the LP mark was high enough, the attacker just emptied the stablecoin inventory the protocol advertised as available.
    • The attacker did not need Cheese Bank to accept CHEESE directly as collateral. Accepting the LP wrapper was enough because the LP oracle inherited the same manipulable market.
    • The exploit shows how an LP token can hide a thin, protocol-adjacent asset inside a wrapper that looks more diversified or safer than it really is.
    • The protocol’s real mistake was crossing two balance sheets: a shallow CHEESE-ETH AMM printed the collateral mark, while Cheese Bank paid out hard stablecoins against that mark.
  • Representative transactions and addresses:
    • Main exploit transaction: 0x600a869aa3a259158310a233b815ff67ca41eab8961a49918c2031297a02f1cc
    • Attacker wallet identified by Cheese Bank: 0x882d72aaae187f54e85c7a0cb19dfec5316cd9aa
    • Attacker contract: 0x9e0259437804c7bf175421a451bc80611a0b93c3
    • Recipient / stolen-assets deposition address mentioned by Cheese Bank and PeckShield: 0x02b7165d0916e373f0235056a7e6fccdb82d2255
    • Oracle function highlighted by PeckShield: CheesePriceOracle::refresh()
    • LP pricing path highlighted by PeckShield: CheesePriceOracle::fetchLPAnchorPrice()
  • Response / mitigation:
    • Cheese Bank said it temporarily paused deposits, notified major exchanges, and worked with security firms and blockchain-analysis providers while tracing funds.
    • In its public follow-up, the team said it patched the LP-pricing path by moving toward a Uniswap moving-average price, adding short-window price-change thresholds, and temporarily restricting refresh authority so the team could reset prices under special circumstances.
    • That response is useful mainly as a record of what teams reach for under pressure: smoothing and thresholding can raise manipulation cost, but the proposed temporary admin-controlled refresh path also shows how quickly an oracle incident can mutate into a live operator-trust surface.
    • The practical fix is not merely use a flash-loan-resistant oracle in the abstract. Protocols also need collateral policy that recognizes how easy it is to print temporary value in thin LP positions.
    • More robust defenses would have included manipulation-resistant pricing, tighter haircuts or caps on thin LP collateral, delayed oracle updates, and depth-aware borrow limits.

Pattern tags

  • oracle-manipulation
  • lp-token-oracle-mispricing
  • Thin-leg LP collateral inflation / CHEESE-side market weakness hidden behind an LP wrapper and turned into stablecoin borrowing power
  • Same-transaction oracle refresh / reserve-state print promoted directly into debt authority

Three LP-collateral promotions this incident collapsed

  1. A reserve-shaped LP value could be authored

    • After minting and posting the UNI_V2-CHEESE-ETH LP tokens, the attacker could still spend about 20,000 ETH to rewrite the reserve picture inside the same Uniswap V2 pool that Cheese Bank later treated as collateral evidence.
    • That was already enough to fabricate a much richer this LP looks valuable right now story even though the state only existed because the attacker was temporarily stuffing WETH into a thin CHEESE market.
  2. Cheese Bank promoted that authored reserve picture into canonical collateral truth

    • CheesePriceOracle::refresh() and CheesePriceOracle::fetchLPAnchorPrice() treated the live CHEESE-ETH reserve composition — especially the WETH side, doubled into total pool value and combined with an ETH price from another Uniswap pair — as the protocol’s official answer for what the LP collateral was worth.
    • The dangerous promotion was not merely the pool has reserves or ETH has a price; it was this same-transaction reserve state still counts as the one canonical LP-solvency answer.
  3. That published LP truth still inherited immediate stablecoin-spending authority

    • Once the attacker forced the refresh, Cheese Bank let the inflated LP mark enlarge borrow capacity against the sUSD_V2 market’s real USDC, USDT, and DAI inventory in the same execution window.
    • By the time the CHEESE price fell back toward reality, the protocol had already converted the transient reserve story into durable lender-side reserve outflow.

Why the exploit worked

  • Cheese Bank’s LP valuation path never asked the liquidation question that mattered: what could a CHEESE-ETH LP position actually be unwound for after the attacker stopped supporting the manipulated pool state? fetchLPAnchorPrice() treated current WETH reserves as if they were durable collateral truth, then doubled that reserve-side value into total LP NAV.
  • The protocol also mistook anchor asset present for wrapper safe enough. The WETH side looked like a sturdier measuring stick than CHEESE, but the borrowable object was still an LP token whose weakest leg was a thin, attacker-movable CHEESE market.
  • Same-transaction publication and same-transaction borrowing were fused. Once the attacker pumped the pool and called CheesePriceOracle::refresh(), the inflated LP mark immediately became USDC, USDT, and DAI credit instead of first passing through a delay, haircut, or borrow-side quarantine.
  • The balance-sheet asymmetry did the monetization. Cheese Bank honored a reserve-shaped paper NAV from a shallow CHEESE-ETH pool, but paid out much stronger stablecoin liquidity from a different balance sheet.
  • The attack therefore was not just Uniswap price manipulation. It was wrapper worth was inferred from attacker-shaped reserve composition and then treated as finished collateral truth before any executable-unwind check could fail it closed.

Detection and audit pivots

  • Reconstruct the LP pricing path end to end instead of stopping at the oracle uses ETH price. Ask which exact variables still come from attacker-shaped pool state: WETH reserve, LP total supply, same-block reserve ratios, or a sibling AMM pair treated as anchor confirmation.
  • Compare the quoted LP mark against weakest-leg unwind value at the protocol’s actual collateral size. If the protocol can only defend the price by assuming the manipulated CHEESE-ETH pool stays cooperative during liquidation, the collateral model is already overstating solvency.
  • Simulate the exact exploit order: mint the LP position, deposit it, shove ETH into the pool, force refresh(), then borrow before arbitrage normalizes reserves. Many reviews test only can reserves move? and miss the more important can the borrower publish and consume the move in one window? question.
  • Treat same AMM family provides both the LP anchor and the ETH price input as a weak independence story, not as robust oracle diversification. Cheese Bank’s UNI_V2-USDT-ETH cross-check still lived inside the same flash-movable Uniswap reserve world.
  • Audit whether borrow-side controls survive oracle failure. LP-specific caps, delayed activation, isolation, and conservative collateral factors should still limit damage even if one refresh publishes a wildly wrong wrapper mark.

Failure-prevention checklist

  • Do not let anchor reserve * 2 / totalSupply style shortcuts become collateral truth for thin LP tokens. If the pool’s reserve composition is attacker-authorable for one block, the resulting LP mark is not a safe lending input.
  • Treat LP wrappers as exposures to their weakest underlying market, not as automatic diversification. CHEESE-ETH collateral should have inherited CHEESE’s thin-market risk rather than being promoted to near-stable borrow authority.
  • Prevent same-transaction oracle publication from becoming immediate borrow power. If a borrower can pump the pool, call refresh(), and borrow before reversion, the protocol is effectively letting the borrower write the oracle.
  • Haircut LP collateral to executable unwind value at real liquidation size, not to paper NAV at current pool reserves. If the quoted LP value disappears once the attacker stops supporting the pool, that value should never back stablecoin debt.
  • Demand real source independence for collateral valuation. Reading the LP’s WETH reserve and then pricing WETH through another Uniswap pool is still one AMM family speaking twice, not two independent trust roots.
  • Bind LP listing, oracle-helper choice, collateral factor, and market borrow cap into one risk decision. Thin-asset LP collateral should not reach full borrow enablement through a sequence of individually plausible but jointly unsafe settings.
  • Keep lender-side blast-radius limits even when the oracle seems acceptable: isolated markets, strict LP-specific debt caps, and pause authority should bound losses if one wrapper mark fails high.
  • Prefer manipulation-resistant LP valuation methods or reject the collateral entirely when the only available mark is same-block reserve composition.

Audit prompts

  • Which exact variable creates the LP mark here: live reserve balances, a sibling AMM price, LP total supply, or a cached helper output? Which of those can the borrower reshape before the protocol consumes them?
  • If the attacker first acquires or mints the LP position, can they then distort the same pool and force a refresh() or equivalent update before borrowing?
  • What would liquidation actually recover if the attacker stopped supporting the CHEESE price immediately after the borrow? How far below the quoted collateral mark does executable unwind value fall?
  • Does the protocol confuse contains WETH with deserves WETH-like collateral treatment? Which weakest-leg liquidity assumptions are being smuggled in by the wrapper?
  • Are multiple oracle inputs actually independent, or are they just separate reads from the same AMM / same liquidity family under different labels?
  • If one LP market reprices violently inside one block, what stops the protocol from lending out deeper stablecoin liquidity against that transient mark: delay, haircut, cap, isolation, or nothing?
  • Can the borrower discover and drain the exact available balances once the collateral mark is high enough, as happened here, or is downstream inventory exposure separately bounded?
  • If governance had to justify admitting this LP today, what objective market-depth, source-independence, and unwind-analysis evidence would it show beyond the oracle has a refresh function?

Lessons that should already be standard

  • LP collateral is only as trustworthy as its weakest leg and the timing rules around the reserve read. Wrapping CHEESE with ETH did not make Cheese Bank’s collateral model robust.
  • A reserve anchor is not an oracle if the borrower can stuff the reserve themselves. More WETH in the pool is not the same thing as more realizable collateral value.
  • Same-transaction refresh authority is already borrowing authority. Once Cheese Bank let the attacker publish the new LP price and immediately consume it, the rest of the exploit was just inventory withdrawal.
  • Two Uniswap reads is not source diversification. If both sides of the valuation live inside the same flash-loan-movable reserve world, the protocol still has one trust root.
  • Stablecoin lenders need extra distrust when collateral truth comes from thin protocol-adjacent markets. The dangerous asymmetry is not just price volatility; it is a weak venue being allowed to print claims on stronger assets.
  • Oracle review has to ask the liquidation question directly: what can this collateral really be sold for after the attacker exits? Elegant-looking LP math is irrelevant if unwind reality is much lower.
  • Borrow caps and market isolation are first-class solvency controls, not optional polish. If Cheese Bank had assumed the LP oracle might be wrong, the loss surface could have been materially smaller.
  • Admin special refresh or threshold patches may buy time during incident response, but they do not repair the underlying design error if live reserve state still authors collateral value.

Why this incident matters

  • Cheese Bank is one of the cleanest early examples of LP-token collateral inflation rather than plain spot-token collateral inflation.
  • It shows that wrapping a thin token in an LP token does not remove oracle risk; it can actually obscure it behind reserve-based pricing heuristics.
  • It is also a crisp negative exemplar for LP collateral weakest-leg unwind and same-window quarantine: the protocol treated a thin CHEESE leg as if the LP wrapper made the position money-good, and it let a fresh reserve print become debt authority before anyone could challenge whether that mark was executable at size.
  • It also makes the reusable pattern very explicit: printable AMM reserve state on one side, immediately borrowable blue-chip or stable assets on the other.

Internal linkages

Control surface

  • The leverage sat in LP-collateral admission, oracle-refresh permissions, the fetchLPAnchorPrice() reserve-based valuation shortcut, collateral haircuts, and the protocol’s willingness to lend deep stablecoin liquidity against a thin CHEESE-ETH market.
  • Cheese Bank did not merely have a bad oracle input. It had a bad collateral model: the protocol treated a flash-loan-printable LP mark as if it were realizable solvency.

Sources

Last reviewed

  • 2026-08-04 UTC