Securityadvancedarticle25 min read

Cream Finance Hack: $130M Flash Loan Attack Analysis | VCC

On October 27, 2021, at block 13,516,868, a single Ethereum transaction borrowed and repaid 500 million DAI, minted and donated about $16 million of Yearn vault assets, and walked away with roughly $130 million from Cream Finance — without ever holding the funds needed to start. The cream fina

Vietcardano teamTeam

Đội ngũ biên tập VietCardanoCommunity — các chuyên gia blockchain và Cardano, cung cấp nội dung giáo dục và nghiên cứu chuyên sâu cho cộng đồng.

Published September 14, 2026

On October 27, 2021, at block 13,516,868, a single Ethereum transaction borrowed and repaid 500 million DAI, minted and donated about $16 million of Yearn vault assets, and walked away with roughly $130 million from Cream Finance — without ever holding the funds needed to start. The cream finance hack was not a stolen private key or a reentrancy bug. It was a flash loan price manipulation that treated a Yearn vault's internal accounting number — pricePerShare — as if it were a market price, inflated that number by simply giving money away, and then used the inflated valuation as collateral to borrow nine lending markets dry. No funds were ever returned. The hole — about $130 million of bad debt backed by $35 million of real collateral — sat on Cream's balance sheet until protocol fees filled it months later. Understanding how $8 million of donated capital conjured $130 million of borrowing power explains why every major lending fork now treats yield-bearing vault tokens as a second, unaudited oracle.

What Cream Finance Does and Why Its Listing Strategy Matters

Cream Finance launched in September 2020 as a Compound fork on Ethereum: a permissionless lending protocol where users supply assets to CToken (CreamToken) markets, earn interest paid by borrowers, and borrow against the value of their supplied collateral as judged by a Comptroller and a PriceOracle. By October 2021, Cream managed roughly $1.6 billion in total value locked across 40+ collateral markets — four times Compound's count — because its strategy was to list aggressively: long-tail assets, LP tokens, and yield-bearing vault receipts that larger protocols considered too exotic to support.

Two design choices made the $130M theft possible. First, any token listed as collateral needs an oracle path — a rule that maps getUnderlyingPrice(cToken) → USD. For stablecoins that path is Chainlink; for the Yearn yUSD vault token that the attacker chose, the path was yUSD.getPricePerShare() yCRV_virtualPrice Chainlink(USDC). Second, Cream shared Compound's Comptroller structure but not its conservatism: yUSD's collateral factor was 75%, meaning $100 of yUSD at oracle price could back $75 of borrowing in ETH, WBTC, or stables. A price error in the first choice therefore became leverage through the second.

The preceding year had already shown that each novel market was a novel attack surface. In February 2021, Alpha Homora + Cream's ironBank lost ~$37.5M via uncollateralized borrow; in August 2021, Cream lost ~$18.8M when the AMP token's ERC777 tokensReceived hook re-entered borrow. Both were CToken edge cases that Compound's original market set happened to avoid. The October cream finance exploit analysis completes the trilogy: a PriceOracle edge case where listing a vault token added not just a token but a second price feed — vault accounting — that no Chainlink feed covered.

Diagram - Cream architecture: Comptroller + CToken markets + PriceOracle reading yUSD pricePerShare from Yearn vault, and the inflated collateral path the attacker exploited

Timeline: One Block That Cost $130M (and Two Earlier Warnings)

Cream's 2021 reads as a slow-motion warning followed by a single-block detonation.

February 13, 2021 — The Alpha Homora exploit used Cream's ironBank (a zero-collateral lending deployment) to borrow without corresponding deposits — ~$37.5M lost. The root cause was a Comptroller assumption that every borrow caller had already supplied collateral through the normal flow; the new ironBank role broke that invariant. Patch: restrict uncollateralized callers.

August 30, 2021 — The AMP token exploit. The attacker called borrow on Cream's crAMP market; AMP's ERC777 hook (tokensReceived) re-entered Cream before borrow had updated its internal accounting, allowing a second borrow against the same collateral — ~$18.8M stolen. Cream had no reentrancy guard on the borrow path for that market. Patch: add guard.

October 27, 2021, ~11:02 UTC (block 13,516,868) — The flash loan price manipulation. The attacker contract, funded with ~9 ETH for gas via Tornado Cash, executed one transaction:

  • Flash loan 500 million DAI from MakerDAO's DssFlash (0-fee flash loan — Aave would have charged ~$450k at 0.09%).
  • Swapped DAI → yCRV → minted yUSD vault shares at fair price (~$8M).
  • Donated ~$8M of yCRV / yPool LP directly to the yUSD vault contract — a raw transfer, not a deposit, so no new shares were minted.
  • yUSD.pricePerShare jumped from ~1.03 to roughly 3–6× fair value within the same block.
  • Deposited the inflated yUSD into Cream's crYUSD market via mint; Cream's PriceOracle read the inflated pricePerShare and credited ~$200M of phantom collateral.
  • Borrowed ~$130M across nine markets (WETH, WBTC, CRV, YFI, LINK, DPI, USDC, USDT, DAI) against that phantom.
  • Repaid the 500M DAI flash loan plus gas, kept the borrowed assets. The inflated yUSD collateral (real value ~$35M) stayed in crYUSD as bad debt.

All of this was atomic — if any step had reverted, the whole transaction would have. It did not revert.

October 27, ~13:00 UTC — Cream called Comptroller._setBorrowPaused(true) on all markets, pausing further borrowing. PeckShield and SlowMist published root-cause threads within hours. CREAM token fell ~30% on the day. No further deposits were at immediate risk because borrowing — not supplying — was the manipulated path.

October 28, 2021 → 2022 — Cream announced that no funds were returned by the attacker and that the protocol would repay affected depositors from treasury and fees. By mid-2022 the bad debt had been absorbed via protocol revenue — not by an attacker return (contrast Poly Network's $611M full return six weeks earlier). Cream's TVL never recovered to its October peak.

Timeline - Three-exploit timeline: Feb Alpha Homora $37.5M, Aug AMP $18.8M, and the Oct 27 flash loan block that stole $130M, followed by pause and fee-funded repayment

The Numbers: Where $130 Million Came From and Why the Collateral Was Phantom

At ETH ~$4,200 and BTC ~$60,000 on October 27, 2021, the borrowed basket tells the attacker's preferences: take the deepest, most liquid assets first so the exit is stablecoin → ETH, not exotic token → slippage.

Borrow marketApprox borrowedUSD at time
WETH3,924 ETH~$16.5M
WBTC476.29 WBTC~$28.5M
CRV~1.6M CRV~$8M
YFI / LINK / DPI / mWETH—~$8–10M combined
Stables (DAI/USDT/USDC) + MKR—remainder
Total9 markets~$130M ($130–131.5M across aggregators; variance is ETH/BTC snapshot)

On the other side of the ledger:

ItemValue
Flash loan principal (MakerDAO DssFlash)500M DAI (0 fee)
Capital deployed to pump price (mint + donation)~$16M (all from flash loan)
Gas seed (attacker EOA funding via Tornado Cash)~9 ETH
Inflated collateral book value credited by Cream~$200M
Collateral capacity at 75% factor~$150M (attacker borrowed $130M, leaving slack)
Real value of yUSD collateral left behind~$35M
Bad debt hole (borrowed minus real collateral)~$130M
Funds returned by attacker$0 (0% recovery)
Who filled the holeCream fees/treasury over 2022

Three subtleties matter. First, the flash loan made the attack capital-free at any scale: the $16M pump and the $130M loot were both inside the same atomic transaction funded by 500M borrowed DAI. The attacker needed no equity beyond 9 ETH for gas — the donation that created the phantom was borrowed money recycled inside one block. Second, the attacker deliberately under-borrowed (130 of 150 capacity) to stay below the liquidation threshold within the same transaction; maxing out would have risked a revert if the oracle read slipped. Third, unlike Poly's USDT freeze or Compound's governance-delayed fix, nothing was freezable — WETH and WBTC are not issuer-freezable tokens — and no on-chain mechanism could reclaim the claim-less borrow once it left the CToken.

Root Cause: When a Donation Became a Price

The cream finance flash loan attack is a textbook oracle surface confusion: Cream consumed a piece of vault accounting as if it were a market price, and vault accounting can be moved by giving money away.

How yUSD Is Priced (and Why That Is Not a Market Price)

The Yearn yUSD vault (0x5dbc...) is a yield-bearing vault: users deposit yCRV (Curve yPool LP) and receive yUSD shares; the vault earns CRV fees and grows totalAssets. The share price reported by pricePerShare() is:

` pricePerShare = totalAssets / totalSupply `

totalAssets is not a Chainlink price — it is the vault's internal accounting: essentially balanceOf(vault) in yCRV terms plus accrued yield. totalSupply is the number of yUSD shares outstanding. In Yearn v1 (the version Cream listed), totalAssets was computed from the vault's raw token balance, so any address could increase totalAssets by calling transfer(vault, amount) with yCRV — a donation that mints zero shares but raises pricePerShare by donation / totalSupply, instantly.

What Cream's Oracle Did With That Number

Cream's PriceOracle.getUnderlyingPrice(crYUSD) was supposed to answer "what is 1 yUSD worth in dollars?" It did:

` price(yUSD) = yUSD.pricePerShare() yCRV_virtualPrice Chainlink(USDC/USD) `

yCRV_virtualPrice and Chainlink(USDC) were fair that day — Curve's yPool was deep and Chainlink is time-aggregated. But pricePerShare was a single-block spot read of vault accounting. The attacker raised it 3–6× by donating $8M of yCRV to the vault in the same transaction before Cream read it.

How the Attacker Weaponized It

  1. Flash loan 500M DAI — 0-fee, one-tx borrow from MakerDAO DssFlash.
  2. Mint yUSD at fair price: DAI → yCRV → deposit to yUSD vault → receive yUSD shares (~$8M worth). This establishes attacker-controlled shares to later deposit as collateral.
  3. Donate ~$8M of yCRV directly to the yUSD vault (transfer(vault, yCRV)). totalSupply unchanged; totalAssets up $8M; pricePerShare jumps. No shares minted to the attacker for this donation — the value accrues to all existing shares, including the attacker's ~$8M.
  4. Deposit inflated yUSD to Cream: crYUSD.mint(yUSD_amount). Cream's oracle now reads pricePerShare at ~3× and credits collateral as yUSD_balance * price(yUSD). ~$8M of shares now book as ~$24–48M each leg of the donation's multiplier effect; with ~$16M deployed the phantom approached ~$200M.
  5. Borrow against the phantom: borrow(WETH), borrow(WBTC), etc. Cream checks collateralValue * collateralFactor >= borrowValue. collateralFactor(yUSD) was 75%, so the attacker could extract 75 cents on the dollar of every dollar of phantom — about $150M capacity; $130M was taken.
  6. Repay flash loan: the 500M DAI is returned inside the same tx; the inflated yUSD stays locked in Cream as undercollateralized bad debt.

Why This Is Cheaper Than Classic AMM Price Manipulation

Most oracle manipulations (e.g., Mango Markets perp funding, MonoX single-pool price pump) move an AMM's spot price by swapping, which pays slippage and needs pool depth. For $130M of phantom from an AMM you might need $30–80M of swap capital and careful pool selection. Cream's pricePerShare donation suffers zero slippage — the formula is a division, not a curve. Donating $8M moves the price exactly $8M / supply, linearly. That capital efficiency is why the entire pump fit inside a 500M DAI flash loan with $0 fee and still left $130M of headroom.

Diagram - Root cause: flash loan -> mint yUSD -> donate to inflate pricePerShare -> Cream oracle reads spot vault accounting -> inflated crYUSD collateral -> borrow $130M, leaving bad debt

Why the Oracle and Collateral Factor Both Failed

The flash loan price manipulation succeeded at the intersection of two under-appreciated risks:

1. Vault accounting is not market price — but Cream treated it as one. Chainlink secures token price (what is yCRV or USDC worth). Cream's oracle also consumed vault share price (pricePerShare), which is a contract's internal division, not a market. That division has no Chainlink feed, no TWAP, no slippage guard. Teams auditing a Compound fork naturally verify Chainlink feeds; they rarely notice that any X.getPricePerShare() is a second, unaudited price feed hiding in plain sight. The fix applied across forks after October 2021 was to ban spot pricePerShare reads entirely — either exclude vault tokens as collateral or use a TWAP of share price (e.g., 30-minute time-weighted) so that a single-block spike cannot be borrowed against before it reverts.

2. yUSD vault v1 was donation-sensitive by design. Yearn v1 vaults counted raw token balance as totalAssets for simplicity; the invariant "donations should not inflate pricePerShare" was not enforced at the vault layer because donation was considered altruistic, not adversarial. After Cream, Yearn v2 vaults added donation resistance — totalAssets is cached and donations through non-deposit paths do not immediately inflate the share price — but Cream had already listed v1. This is the correct layering lesson: vaults and oracles share responsibility; hardening one is not enough if the other still trusts raw balances.

3. Collateral factor as leverage multiplier. An oracle error of 3× is bad; multiplied by a 75% collateral factor it becomes a 2.25× real-theft multiplier. Had Cream listed yUSD at collateralFactor = 0% — a no-borrow collateral, like some protocols do for volatile vault receipts — the inflated price would have been inert: visible but unborrowable. The choice of 75% was equivalent to underwriting 75% loan-to-value on a manipulable price. Post-Cream, the standard is 0% or low-single-digit factors for any yield-bearing vault token until its oracle is TWAP-hardened and donation-proofed together.

4. The flash loan 0-fee regime lowered the marginal cost to gas. At 500M DAI, Aave's 0.09% fee would be ~$450k — still trivial versus $130M, but a cost that must be profit-calculated and handled. MakerDAO's DssFlash 0-fee flash loan launched in mid-2021 removed even that friction; the whole attack became gas + 1 wei. Flash loans do not create the vulnerability — the donations do — but they remove the capital constraint that would otherwise limit who can execute it.

The Attacker and the Money After

Unlike Poly Network's "return it all" negotiation or Compound's "users claimed it" diffusion, the cream 130 million hack was a classic exploit-and-exit with 0% return and no issuer choke point.

The attacker EOA and the attacker contract were funded for gas via Tornado Cash (about 9 ETH), a privacy rail that made the deployer opaque before the transaction. Unlike bridge hacks where a team multisig is the target, this attacker needed no insider — just knowledge of the pricePerShare donation surface and a flash loan. PeckShield linked the flash loan source to MakerDAO's DssFlash on the on-chain trace; SlowMist's earlier thread cited Aave v2 as an alternative path — both offered 500M-scale DAI, but the 0-fee MakerDAO route matches the transaction's economics and the post-attack community consensus.

Once the $130M left Cream, it was swapped within blocks: WBTC/WETH/CRV/YFI → ETH via Uniswap v2/v3 and Curve 3Pool / Sushi; stables were swapped to ETH as well. Within 24 hours, analytics firms tracked ~$20M+ moving toward Tornado Cash deposits — the standard laundering branch for ETH-heavy thefts in late 2021, when Tornado handled larger volume than in 2022–2023 before sanctions. Crucially, Tether could not freeze the loot — the stack was WETH/WBTC, not USDT — and Curve/Uni swaps did not revert (unlike Poly's Curve failures under blacklist pressure). There was no issuer to call, no chain to halt, and no multisig negotiation lever.

Recovery therefore depended entirely on Cream. The team paused borrowing that afternoon, published the borrower list and bad-debt accounting, and committed to repaying affected suppliers (those whose deposited WETH/WBTC was lent out and never returned) from treasury and protocol fees. Over 2022, fee revenue filled the hole gradually — it did not require a bailout token mint. No law enforcement recovery was announced. The cream finance exploit analysis closes this loop bluntly: when collateral is phantom, there is no collateral to liquidate — the protocol socializes the loss until its revenue model covers it.

Diagram - Fund flow: Tornado Cash gas seed + 500M DAI flash loan into yUSD donation and phantom collateral, $130M borrowed across 9 markets, swapped to ETH, partially to Tornado, with bad debt remaining and fee-funded repayment

Fund Flow: From Phantom Collateral to Real ETH

The full passthrough, condensed:

  1. In — 9 ETH (Tornado) for gas + 500M DAI flash loan (MakerDAO) for capital, one tx.
  2. Pump — DAI → yCRV → yUSD mint (~$8M) + yCRV donation (~$8M) → pricePerShare 3–6×.
  3. Phantom credit — yUSD → crYUSD.mint → Cream books ~$200M collateral at inflated oracle.
  4. Real extraction — borrow across 9 cToken markets → ~$130M real assets leave the lending pools.
  5. Out — 500M DAI repaid to MakerDAO within same tx; borrowed assets swapped to ETH; partial Tornado deposit; inflated yUSD stays in Cream worth ~$35M real.
  6. Hole — ~$130M undercollateralized; slowly patched by Cream's fee treasury through 2022.

What did not happen is diagnostic: no failed DEX adds (unlike Poly's three Curve reverts), no issuer freeze (unlike Poly's $33M USDT freeze), no timelock-delayed patch drama (unlike Compound's 7-day governance clock). The Cream path was frictionless because the borrowed assets were the most vanilla — ETH and WBTC — and the oracle surface was the vault share, not an AMM pool.

Lessons: 5 Defenses That Would Have Stopped the Cream Finance Hack

Every lending fork that listed a yield-bearing vault token after October 2021 carries some variant of Cream's risk unless it adopted these five — all now standard checks that auditors run before whitelisting a new collateral type.

Checklist - 5 defense layers: TWAP or exclude vault tokens, cap collateralFactor at 0, per-block borrow caps, donation-resistant vaults, and oracle-review as part of listing governance

1. Never use spot pricePerShare as a price — use TWAP-sharePrice or exclude vault tokens as collateral. A spot read of totalAssets / totalSupply is a single-block manipulation surface. If a vault token must be collateral, sample pricePerShare over a 30-minute window (Uniswap-style oracle or vault-internal TWAP) and update the PriceOracle only on the rolling median. If you cannot add TWAP, do not list the token — exclude it.

2. Cap collateralFactor at 0 for yield-bearing vault receipts until the oracle is hardened. Treat the vault token's collateral factor as a risk dial for the oracle, not just for volatility. A vault receipt with a manipulable share price at 75% LTV turns every $3 of oracle error into $2.25 of real theft. The safe default is 0% (supply-only, no borrowing), elevated only after the other four defenses are proven.

3. Per-block borrow caps and per-market debt ceilings (circuit breaker). Any single transaction borrowing >$5M, or aggregate borrows >$15M in one block, should auto-pause that market and queue a governance review. Cream's $130M left in one block — a volume anomaly that no healthy user creates. Lending protocols that added borrow caps after October 2021 (Aave v3 isolation mode, Compound v3 borrow caps, Euler per-market caps) treat this as the single highest-leverage control.

4. Donation-resistant vaults at the vault layer (Yearn v2 pattern). totalAssets should not inflate on raw transfer to the vault; only deposits through the vault's deposit() entrypoint should affect share price, with totalAssets cached or checkpointed. Yearn v2 and later vault standards do this; retrofitting it at the fork level means never using raw balance as totalAssets.

5. Listing governance: every new collateral type is a new oracle integration. Forking Compound gives you the lending engine, but each new market adds a new price primitive — getPricePerShare(), convertToAssets(), AMM LP pricing, etc. The protocol's whitelist process must therefore be an oracle review, not just a risk-parameter vote: require a diff-scoped audit of the getUnderlyingPrice path for the new token, with explicit sign-off that no internal division or spot balance read is being consumed as price. Cream's October loss traces directly to skipping this step for yUSD.

These five together close the specific failure: (1)+(2) remove the oracle leverage, (4) removes the donation surface, (3) limits the blast radius if the surface is missed, and (5) ensures (1)–(4) are not missed when the new market looks small. The cream finance hack is now the reference case that any lending-protocol book applies when its team proposes adding the next shiny vault receipt as collateral.

Frequently Asked Questions

How much was stolen in the Cream Finance hack? Approximately $130 million (aggregators cite $130–131.5M depending on the ETH/BTC price snapshot on October 27, 2021). The attacker borrowed ~$130M across nine markets — WETH 3,924, WBTC 476, plus CRV, YFI, LINK, DPI, and stables — against phantom yUSD collateral that was inflated by a share-price donation. Real collateral behind the position was about $35M, leaving ~$130M of bad debt. None was returned.

How did the Cream Finance hack work? It was a flash loan price manipulation. The attacker flash-loaned 500M DAI, used ~$16M of it to mint Yearn yUSD shares and to donate yCRV directly to the yUSD vault — which inflated the vault's pricePerShare (totalAssets / totalSupply) 3–6× in one block. Cream's PriceOracle read that spot pricePerShare as the yUSD price, credited ~$200M of phantom collateral for the deposited yUSD, and — with a 75% collateral factor — allowed ~$150M of borrowing capacity. The attacker borrowed ~$130M (WETH, WBTC, stables, etc.), repaid the flash loan in the same transaction, and left the inflated yUSD as bad debt.

Was the Cream hack a flash loan attack or an oracle attack? Both — and the ordering matters. The flash loan was the funding rail (capital-free execution), not the vulnerability. The vulnerability was the price oracle: Cream's PriceOracle for the Yearn yUSD vault token consumed a spot vault-accounting number (pricePerShare) as price — a single-block manipulation surface. The flash loan made the manipulation cheap and atomic; the oracle flaw made it profitable. Most coverage shorthand "flash loan attack" collapses the two — precisely, it was flash loan → share-price donation → oracle manipulation → over-collateralized borrow.

Did the Cream Finance hacker get caught or did funds get returned? The attacker EOA was funded for gas via Tornado Cash, making pre-attack attribution opaque; no identity has been publicly confirmed. Borrowed assets were swapped to ETH via Uniswap/Curve within blocks and a portion moved to Tornado Cash within 24 hours. No funds were returned (0% recovery). Because the loot was WETH/WBTC — not issuer-freezable USDT — no Tether/Circle freeze was possible. Cream repaid affected lenders from protocol fees and treasury over 2022, not from recovered funds.

What happened to Cream Finance after the $130M hack? Cream paused borrowing the same afternoon, published the bad-debt accounting, and committed to making affected suppliers whole from fees. Over 2022, fee revenue covered the hole without a bailout mint. However, having now been exploited three times in eight months (Feb ~$37.5M, Aug ~$18.8M, Oct ~$130M — cumulative >$185M in 2021), Cream's TVL never recovered to its October peak. The incident ended its path as an independent scaled lender and it was eventually discussed as a Yearn-ecosystem merge candidate.

How does the Cream hack compare to other flash loan attacks? Cream is the cleanest archetype of flash loan → yield-vault share price → collateral → borrow. Most sibling attacks use AMM price manipulation: MonoX (single AMM pool), Vee Finance (Pangolin single-oracle), Indexed Finance (deflationary LP token), BeanStalk (flash-loan governance). Cream's donation vector is cheaper than AMM swapping (zero slippage — price is a division, not a curve) and required only ~$8M to create ~$120M of phantom. That capital efficiency is why Cream's hole was nine figures from a single transaction while AMM-based pumps often need deeper pools and careful token selection.

Conclusion

The cream finance hack endures not for its size — it sits between 2021's $18.8M AMP reentrancy and Poly's $611M cross-chain failure — but because its surface is so portable. Yearn did not build a broken vault; MakerDAO did not build a broken flash loan; Cream correctly forked Compound's lending engine. The failure was compositional: listing a yield-bearing vault token as 75% collateral without noticing that getPricePerShare() is itself a price feed. A raw token transfer — an act that in any other context is a gift — became, inside one atomic transaction funded by half a billion of borrowed DAI, a donation as an attack that inflated accounting, fooled an oracle, and withdrew nine markets. The fix is equally compositional and now standard: TWAP or exclude vault tokens, cap collateral factors at zero until hardened, enforce borrow circuit breakers, harden vaults against donations, and review every new listing as a new oracle. In DeFi lending, every new collateral type is a new price primitive — and every price is only as honest as the denominator you forgot to protect.

Tags:

cream finance hackcream finance flash loancream 130 million hackflash loan price manipulationcream finance exploit analysis