Yearn yETH Near-Zero-Supply Bootstrap Underflow / Infinite-Mint Exploit (2025-11-30)

  • Name: Yearn yETH near-zero-supply bootstrap underflow / infinite-mint exploit
  • Date: 2025-11-30
  • Protocol: Yearn yETH
  • Chain: Ethereum
  • Category: near-zero-supply bootstrap replay / initialization-path re-entry / invariant-solver failure / infinite mint
  • Loss: about 8M drained from the yETH weighted stableswap pool and about $0.9M from the yETH/WETH Curve exit pool
  • Summary: On Ethereum block 23,914,086, the attacker exploited Yearn’s legacy yETH system by first forcing the weighted stableswap pool 0xCcd04073f4BdC4510927ea9Ba350875C3c65BF81 into a numerically broken state where the internal product term Π collapsed to 0 and LP supply D was grossly over-minted. They then used repeated withdrawal and update_rates() cycles to offload that inconsistency onto protocol-owned liquidity in st-yETH, draining the pool until internal supply reached 0. Once prev_supply == 0 became reachable again, the attacker re-entered the pool’s bootstrap branch with only 16 wei of dust deposits; an unsafe_sub(A * Σ, D * Π) underflow wrapped the solver into minting roughly 2.3544 × 10^56 yETH LP tokens. Those counterfeit LP claims were then used to drain the yETH/ETH Curve pool 0x69ACcb968B19a53790f43e57558F5E443A91aF22 and route proceeds toward Tornado Cash.
  • Trigger / entry point:
    • The yETH pool used a custom weighted-stableswap invariant with iterative fixed-point solving over virtual balances Σ, Π, and internal supply D.
    • Extreme imbalance in add_liquidity() inputs could push the Newton-style solver outside its safe convergence domain without reverting.
    • remove_liquidity(0) recomputed Π from balances, while update_rates() reconciled inflated D against protocol-owned st-yETH balances, making it possible to zero out internal supply without clearing every lifecycle assumption.
    • Once the pool was driven back to prev_supply == 0, initialization-only mint logic became reachable again in production.
    • In that bootstrap path, unchecked math around A * Σ - D * Π could underflow and wrap into an absurd supply value instead of reverting.
  • Root cause:
    • Yearn’s yETH pool combined a numerically fragile invariant solver with missing domain checks, so hostile imbalance could silently corrupt Π and over-mint LP tokens.
    • The protocol’s dual-supply design (D versus ERC-20 totalSupply) plus protocol-owned liquidity let the attacker externalize the cost of that over-mint onto st-yETH during reconciliation.
    • A bootstrap path meant only for first launch was not permanently disabled after deployment, so it could be re-entered once the attacker manufactured a zero-supply state.
    • Invariant-critical code used unchecked arithmetic (unsafe_sub) where a violated domain should have reverted.
    • More broadly, the system treated prev_supply == 0 as synonymous with pristine first deposit, even though production state could be driven back there through adversarial sequencing.
  • Exploit path:
    1. In exploit transaction 0x53fe7ef190c34d810c50fb66f0fc65a1ceedc10309cf4b4013d64042a0331156, the attacker used flash-loaned LST inventory to execute extremely imbalanced add_liquidity() operations against the yETH weighted stableswap pool.
    2. Those deposits pushed the solver into a divergent regime; rounding in the internal update step drove the product accumulator to 0, so the pool accepted a wildly inflated D and over-minted yETH LP to the attacker.
    3. The attacker then used remove_liquidity(0) and permissionless update_rates() calls to partially repair Π while keeping inflated D, causing reconciliation to burn yETH from st-yETH and absorb losses into protocol-owned liquidity rather than the attacker’s position.
    4. By repeating withdrawals, the attacker drained most underlying LST balances and forced the pool into a live but zero-internal-supply state where prev_supply == 0 again held true.
    5. With the bootstrap branch reopened, they deposited a dust configuration totaling only 16 wei; the initialization solver evaluated an invalid A * Σ - D * Π relationship through unchecked math, underflowed, and minted approximately 2.3544 × 10^56 yETH LP tokens.
    6. The attacker used that counterfeit yETH supply to drain the yETH/WETH Curve pool, converted proceeds into ETH and other harder assets, sent about 1,000 ETH to Tornado Cash in the exploit aftermath, and left Yearn and integrators with about $9M of losses before partial recovery.
  • What the attacker needed to be true:
    • The solver had to accept extreme imbalance without enforcing its mathematical domain or convergence assumptions.
    • The pool needed asymmetric state repair: a path that could restore Π or reconcile supply without truly resetting corrupted economic state.
    • Protocol-owned liquidity in st-yETH had to be available as a loss absorber when update_rates() reconciled inflated supply.
    • A runtime path had to exist that made prev_supply == 0 reachable again even after deployment.
    • Bootstrap mint logic had to trust that prev_supply == 0 implied a clean first-deposit state and use unchecked arithmetic in that branch.
  • Technical anatomy worth remembering:
    • This was not a simple one-shot 16 wei in, infinite mint out bug. The dust deposit only paid because the attacker first spent an earlier phase corrupting the invariant, socializing losses into protocol-owned liquidity, and reopening an initialization-only state transition.
    • The exploit is a strong reminder that first deposit risk is not just deployment-day risk. Any protocol that can be driven back to supply == 0 or prev_supply == 0 may have effectively resurrected its bootstrap assumptions.
    • Yearn’s own disclosure is unusually explicit that the key lifecycle error was making an initialization branch re-enterable after the pool had already been live.
    • The most reusable lesson is not merely Newton solvers are hard; it is that lifecycle state, cached accounting, POL reconciliation, and invariant math together formed one exploit surface.

Three lifecycle-and-solver promotions this incident collapsed

  1. The live pool’s internal supply reached a launch-like zero state again

    • Through invariant corruption, remove_liquidity(0), and update_rates()-mediated reconciliation, the attacker forced Yearn’s internal D / prev_supply state back to 0 without rewinding the rest of production history.
    • That only proved the pool had become bootstrap-shaped again.
  2. Yearn treated that zero-like runtime state as canonical proof that initialization-only mint semantics were valid again

    • Once prev_supply == 0 held, the pool reopened its bootstrap branch as if the system were at honest first launch rather than in a damaged post-socialization state.
    • This was the decisive false promotion: a reversible runtime condition regained one-time lifecycle authority.
  3. The shares printed under that reopened bootstrap story still deserved live exit authority against healthier liquidity

    • The underflow-minted yETH LP supply was then honored by the sibling yETH/WETH Curve exit pool and could be converted into ETH and harder assets.
    • This is where a lifecycle replay and solver-domain failure became a realized reserve loss instead of only a bizarre local accounting state.
  • Representative transactions and addresses:
    • Main exploit tx: 0x53fe7ef190c34d810c50fb66f0fc65a1ceedc10309cf4b4013d64042a0331156
    • Exploited yETH weighted stableswap pool: 0xCcd04073f4BdC4510927ea9Ba350875C3c65BF81
    • Drained yETH/WETH Curve pool: 0x69ACcb968B19a53790f43e57558F5E443A91aF22
    • Recovery tx (857.49 pxETH): 0x0e83bb95bb9d05fb81213b2fad11c01ea671796752e8770b09935f7052691c35
    • Attacker EOA cited in public analysis: 0xa80D3F2022F6Bfd0B260bF16D72CaD025440C822
    • Primary helper contract cited in public analysis: 0xB8e0A4758Df2954063Ca4ba3d094f2d6EdA9B993
  • Response / mitigation:
    • Yearn disclosed that the exploit was isolated to yETH and direct integrators, with v2/v3 vaults unaffected.
    • Yearn, ChainSecurity, and SEAL 911 coordinated incident response and recovered 857.49 pxETH with assistance from Plume and Dinero.
    • Public remediation guidance focused on enforcing solver domain checks, rejecting degenerate Π = 0 states, removing unchecked arithmetic from invariant-critical code, and permanently gating bootstrap logic after first initialization.
    • The incident also forced a harder governance lesson into the open: legacy products with bespoke math cannot be treated as harmless just because the flagship system has moved on.

Why this incident matters

  • Yearn yETH is a valuable preserved exemplar for the near-zero-supply replay branch of first-deposit-style risk: a protocol can resurrect initialization-only assumptions long after launch if adversaries can drive state back to zero.
  • It also shows how dangerous the combination of custom invariant math and lifecycle re-entry can be. The infinite mint was the last step, not the first one.
  • For the exploit corpus, it adds a concrete incident where bootstrap branch reachable again is not a theoretical audit warning but a live mainnet drain with tx-level evidence.

Pattern tags

  • Bootstrap-state replay: the pool treated prev_supply == 0 as permission to reuse initialization-only mint semantics even though the system had already been live and economically dirty.
  • Invariant corruption before payout: the decisive 16 wei infinite-mint leg only worked because the attacker first forced the weighted-stableswap solver into a broken Π = 0 regime and over-minted D.
  • Protocol-owned-liquidity loss socialization: update_rates() and the st-yETH reconciliation path let attacker-created accounting damage be absorbed by protocol-owned liquidity on the way to the reset state.
  • Unchecked bootstrap arithmetic: once the bootstrap branch reopened, unsafe_sub(A * Σ, D * Π) turned a violated domain assumption into wrapped infinite-mint behavior instead of a revert.
  • Design companions: bootstrap-state-replay-reversible-initialization-semantics, bootstrap-state-irreversibility-and-supply-floor-design, and low-supply-recovery-and-recapitalization-discipline.

Why the exploit worked

  • Yearn’s yETH system did not keep lifecycle state, invariant health, and supply reconciliation inside one hard security boundary. The attacker could first corrupt the invariant, then use repair-style paths to socialize losses into protocol-owned liquidity, and only then cash out through a reopened bootstrap branch.
  • The code treated prev_supply == 0 as if it still meant pristine first deposit, even though reachable production sequencing could manufacture that state after the pool had already been damaged and partially drained.
  • Invariant-critical math relied on unchecked arithmetic at exactly the point where hostile state should have forced an immediate revert, so a violated domain became a mint primitive.
  • The counterfeit yETH LP supply was accepted by a healthier sibling exit venue, which turned a vault-local lifecycle failure into immediately spendable value through the yETH/WETH Curve pool.

Detection and audit pivots

  • Test full lifecycle adversarial sequences, not just one-call bootstrap edges: corrupt invariant -> reconcile against POL -> drive internal supply to zero -> re-enter initialization branch should be an explicit security test.
  • Treat prev_supply == 0, totalSupply == 0, or similar zero-state predicates as dangerous if any live runtime path can recreate them after launch.
  • Audit repair, sync, and rate-update helpers as loss-socialization surfaces. If they can absorb attacker-created accounting gaps into protocol-owned liquidity without also sealing bootstrap assumptions, they may be funding the reset state.
  • Ban unchecked arithmetic in bootstrap or invariant-critical paths unless a stronger proof makes the domain violation impossible; here the missing revert boundary was the exploit boundary.
  • Ask which downstream pools, adapters, or collateral venues will trust freshly minted claims if a local share system reopens launch-only semantics under stress.

Failure-prevention checklist

  • Make bootstrap one-way: initialization-only mint logic must be governed by an irreversible control fact, not by reversible runtime supply predicates like prev_supply == 0.
  • Add explicit degraded-mode and recovery handling for low- or zero-supply states so reconciliation, recapitalization, or reserve repair cannot silently reuse first-launch semantics.
  • Enforce solver-domain checks before every invariant update and revert on degenerate states such as Π = 0 or impossible A * Σ - D * Π relationships rather than trying to continue through repair logic.
  • Keep protocol-owned liquidity and treasury buffers out of automatic loss socialization paths that can buy an attacker access to a cleaner reset state.
  • Quarantine freshly repaired or freshly minted shares from downstream exit pools, collateral adapters, and other solvency consumers until the system proves capitalization and invariant state are coherent again.

Audit prompts

  • What exact irreversible fact proves the bootstrap path has already been consumed, and can any runtime sequence involving remove_liquidity, update_rates, reconciliation, or POL maintenance ever make production state look bootstrap-clean again?
  • Which invariant-domain assumptions are merely believed by the solver today? Can hostile imbalance, rounding, rebasing inputs, or iterative divergence drive Π to 0, violate A * Σ >= D * Π, or otherwise enter a state the code still tries to solve through?
  • Do any repair, sync, or rate update helpers recompute one side of state from balances while leaving sibling supply, liabilities, or capitalization assumptions stale enough that an attacker can externalize losses and keep a favorable claim?
  • If the protocol maintains multiple supply notions such as internal equilibrium D versus ERC-20 totalSupply, what invariant prevents one from reaching 0 while economically meaningful claims still exist elsewhere in the system?
  • Can protocol-owned liquidity, staking buffers, treasury balances, or insurer inventory absorb attacker-created accounting gaps automatically, effectively subsidizing the path into a cleaner replay state?
  • What exact low- or zero-supply runtime mode exists today, and which mints, withdrawals, rate publications, collateral uses, or downstream exits are supposed to be disabled until the capitalization table is re-established explicitly?
  • If freshly minted or freshly repaired shares hit a sibling AMM, exit pool, collateral adapter, or oracle consumer, what independent brake stops a vault-local lifecycle failure from becoming immediate external cash-out?
  • Which tests prove the full exploit ladder rather than one isolated edge: solver drift -> partial product repair -> POL loss socialization -> internal supply zero -> bootstrap branch re-entry -> downstream exit drain?

Lessons that should already be standard

  • Bootstrap gating belongs to one-way control state, not to reversible accounting predicates. prev_supply == 0 is an economic observation, not proof that the system is pristine again.
  • Numerically fragile invariant code and lifecycle logic are one exploit surface. It is not enough to review the Newton solver separately from recovery, supply reconciliation, or bootstrap branches if those paths can be chained together.
  • Partial repair can be worse than outright failure. Recomputing Π while leaving inflated D or related liability state alive created a bridge from invariant corruption into subsidized extraction.
  • Protocol-owned liquidity is part of the loss surface. If reconciliation burns POL to absorb attacker-created accounting damage, POL is not just a buffer; it is a potential exploit-funded subsidy.
  • Near-zero or zero supply in production should trigger recovery semantics, not launch semantics. A live system that can become new again under adversarial sequencing still owns a bootstrap exploit.
  • Healthier downstream venues will gladly monetize local accounting fiction unless the protocol quarantines repaired or freshly printed claims before they can exit.
  • Legacy bespoke math needs active review or retirement. Mostly forgotten is not a safety property when old invariant code still controls millions in liquid assets.

Internal linkages

Control surface