# Yearn yETH exploit (Nov 2025, Ethereum)

## 1. What happened

- **30 November 2025**, **Ethereum mainnet** block **23,914,086**, tx at **21:11 UTC**. The legacy, self-governed **yETH weighted stableswap pool** was exploited; Yearn's V2/V3 vaults were unaffected. (https://github.com/yearn/yearn-security/blob/master/disclosures/2025-12-01.md, https://rekt.news/yearn-rekt3)
- **Lost ~$9M:** **~$8M** from the yETH stableswap pool + **~$0.9M** from the **yETH/WETH Curve pool**. (https://github.com/yearn/yearn-security/blob/master/disclosures/2025-12-01.md, https://news.bitcoin.com/yearn-finance-hit-by-9m-defi-exploit-recovers-2-39m-pxeth/)
- **Taken:** the pool's LSTs (apxETH, sfrxETH, wstETH, cbETH, rETH, ETHx, mETH, wOETH) plus **298.35 WETH** from the Curve pool. (https://github.com/yearn/yearn-security/blob/master/disclosures/2025-12-01.md)
- Attacker **minted ~2.3544e56 yETH LP** from dust and drained both pools, then sent **1,000 ETH (~$3M)** to Tornado Cash in the same block. **857.49 pxETH (~$2.33-2.39M)** was later recovered with Plume and Dinero. (https://github.com/yearn/yearn-security/blob/master/disclosures/2025-12-01.md, https://rekt.news/yearn-rekt3, https://www.theblock.co/post/380993/yearn-recovers-2-4-million-stolen-assets-unchecked-arithmetic-bug)
- **Sources disagree on amount:** early coverage reported a **"$3M"** drain (https://www.msn.com/en-us/money/markets/yearn-finance-s-yeth-pool-drained-in-3m-liquid-staking-token-exploit/ar-AA1Rsfeb), matching the 1,000 ETH laundered then; Yearn's settled figure is **~$9M**.

## 2. Root cause

**Not** a stolen key, a tricked signer, or an oracle attack - a **numeric flaw in the pool's own code**. The custom weighted-stableswap pool solves its invariant variable `D` (the LP supply) with an iterative fixed-point `_calc_supply()` using **unchecked ("unsafe") arithmetic**. Under an adversarially imbalanced pool, the iteration's product term `r = r * sp / s` suffered **integer round-down to exactly 0** (`unsafe_div` floors), permanently collapsing `Π` to zero and degenerating the invariant into near-constant-sum behaviour, so `_calc_supply` returned a **grossly inflated supply** minted as real yETH. A second unchecked **underflow** (`unsafe_sub` on `l - s*r`, wrapping to ~2^256) fired in the degenerate zero-supply state and minted ~2.35e56 LP. Two design choices made it reachable: `add_liquidity` updated `Π` incrementally (corruptible) while `remove_liquidity(0)` recomputed it from scratch, and the `prev_supply == 0` **bootstrap branch was never permanently disabled** after deployment. (https://github.com/yearn/yearn-security/blob/master/disclosures/2025-12-01.md, https://blocksec.com/blog/yearn-finance-incident-unsafe-arithmetic-in-the-invariant-solver-earns-its-name, https://github.com/banteg/yeth-exploit/blob/main/report.pdf)

## 3. Pattern

**Rounding / precision bug in an unchecked fixed-point solver, plus a reachable initialization path** - a close cousin of the Balancer rounding exploit; a16z groups both as numerical edge cases exploited under adversarially engineered conditions. (https://a16zcrypto.com/posts/article/runtime-enforcement-defense-numerical-exploits/)

```
# invariant solver, unchecked uint256
sp = unsafe_div( unsafe_sub(l, unsafe_mul(s, r)), d )   # can underflow -> huge sp
r  = unsafe_div( unsafe_mul(r, sp), s )                 # can floor to 0
# once r == 0 it stays 0 -> inflated sp returned -> over-mint

# add_liquidity():  if (prev_supply == 0) { bootstrap path }  # never gated
```

## 4. The check that would have caught it

- **Fuzz/invariant test on the solver.** Property-test `_calc_supply` (with `add_liquidity`/`remove_liquidity(0)`/`update_rates`) over extreme, imbalanced inputs and assert as an **invariant that `Π` is never 0 and stays in a valid band during iteration**, and that LP minted per call is bounded by deposited value (e.g. `minted <= k * deposit_value`). Fuzzing skewed deposit vectors like the final dust deposit `[1,1,1,1,1,1,1,9]` surfaces the divergence. (https://github.com/yearn/yearn-security/blob/master/disclosures/2025-12-01.md)
- **Review/ops rules:** replace `unsafe_*` with checked math in invariant-critical paths so a domain violation **reverts**; enforce the solver domain `A*Σ >= D*Π` explicitly; and **permanently disable the bootstrap branch after the first deposit** so `prev_supply == 0` cannot be re-entered. (https://blocksec.com/blog/yearn-finance-incident-unsafe-arithmetic-in-the-invariant-solver-earns-its-name)

## 5. Sources

- Yearn post-mortem: https://github.com/yearn/yearn-security/blob/master/disclosures/2025-12-01.md
- Yearn governance discussion thread: https://gov.yearn.fi/t/yeth-exploit-discussion
- banteg report: https://github.com/banteg/yeth-exploit/blob/main/report.pdf
- 0xkorin summary: https://github.com/0xkorin/yETH-exploit-summary/blob/master/summary.pdf
- BlockSec in-depth analysis: https://blocksec.com/blog/yearn-finance-incident-unsafe-arithmetic-in-the-invariant-solver-earns-its-name
- QuillAudits hack analysis: https://www.quillaudits.com/blog/hack-analysis/yearn-9m-exploit
- rekt.news: https://rekt.news/yearn-rekt3
- The Block: https://www.theblock.co/post/380993/yearn-recovers-2-4-million-stolen-assets-unchecked-arithmetic-bug
- Bitcoin.com: https://news.bitcoin.com/yearn-finance-hit-by-9m-defi-exploit-recovers-2-39m-pxeth/
- a16z crypto (numerical exploits): https://a16zcrypto.com/posts/article/runtime-enforcement-defense-numerical-exploits/
- Attack tx: https://app.blocksec.com/explorer/tx/eth/0x53fe7ef190c34d810c50fb66f0fc65a1ceedc10309cf4b4013d64042a0331156
- Pool: https://etherscan.io/address/0xCcd04073f4BdC4510927ea9Ba350875C3c65BF81
- Early "$3M" report: https://www.msn.com/en-us/money/markets/yearn-finance-s-yeth-pool-drained-in-3m-liquid-staking-token-exploit/ar-AA1Rsfeb
- SlowMist (URL found; page returned a bot-check, unreadable): https://slowmist.medium.com/9-million-stolen-analysis-of-the-yearn-yeth-pool-vulnerability-557237092054
