Skip to content

Instantly share code, notes, and snippets.

@halaprix
Last active July 8, 2026 17:15
Show Gist options
  • Select an option

  • Save halaprix/52bd5e32b35be100dc40ba30539e4169 to your computer and use it in GitHub Desktop.

Select an option

Save halaprix/52bd5e32b35be100dc40ba30539e4169 to your computer and use it in GitHub Desktop.
Lazy Summer exploit - 07.06.2026

Summer.fi USDC Vault Exploit — Post-Mortem

A technical breakdown from the smart-contract side. For the coordinated incident response, see Summer.fi's official channels.

Two Summer.fi FleetCommander vaults (LowerRisk + HigherRisk USDC) drained for ~$6.02M in a single atomic, flash-loan-funded transaction via a donation / NAV-inflation attack on an ark that was being offboarded but was still counted in the vault's share price.

Tx 0x0db528c44f23fc7fa4544684a2fab81096450a14aae8bc89f42cd0592d43da12
Block 25,471,348
Timestamp ~2026-07-06 05:17:59 UTC
Status ✅ Success (gas used 14,989,254)
Net profit ~6,016,755 DAI + ~$108K leftover fleet shares
Funding Morpho flash loans — 1.0M USDT + 65.4192M USDC (zero fee), repaid in-tx

Warning

Root cause — an offboarding that was started but never finished, leaving an impaired ark in NAV. The load-bearing failure was operational. The Varlamore Silo ark was mid-decommission — depositCap set to 0 but never removeArk'd or isolated — so it stayed in the vault's active set. An active ark's balance is summed into totalAssets(), and totalAssets() is what the share price (previewRedeem / convertToAssets) is computed from. So when the attacker donated cheap, over-valued vgUSDC straight into that ark, totalAssets() — and the share price — jumped with no real backing. The play is then: mint shares cheap → donate to inflate the price → redeem dear, with the difference paid out in real liquid assets that belonged to other LPs.

This was not the "withdrawable-vs-total accounting bug" some writeups described. Redemptions pull from arks via _forceDisembarkFromSortedArks, which sorts arks by TVL ascending and stops as soon as the requested amount is covered — so the largest ark (here, the donated Silo ark) is emptied last, and in this attack it was never touched at all: the buffer plus the smaller arks covered the entire redemption (confirmed in the trace — the Silo ark is absent from the disembark set). Its withdrawableTotalAssets() being 0 is therefore irrelevant to the exploit; if anything that withdrawable cap was protective, stopping the attacker from also claiming the illiquid donated position. The whole exploit lived in totalAssets() feeding the share price — which reduces to the ark simply never having been removed. This accounting is by design and correct: an active ark is trusted to hold real value, and excluding a capped ark from the share price would instead deflate price-per-share for honest holders and force withdrawal freezes — not a fix. Likewise withdrawableTotalAssets() == 0 is a normal state for an ark still unwinding (e.g. Origin/Syrup arks need time to exit their positions). The real safeguard is procedural: decommission a market by sweeping + socializing the loss (or emptying it) and then removeArk via governance — which is exactly the path that closes the donation window.

Contents

Actors & contracts

Role Address
Attacker 1 (funder / beneficiary) 0x7BF716167B48CF527725722C6d79494b45B3BDCa
Attacker 2 (executor contract) 0x0514F827C129C16418a0933E03C99A6AF982FC61
Flash-loan source (Morpho) 0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb
LowerRisk USDC FleetCommander 0x98C49e13bf99D7CAd8069faa2A370933EC9EcF17
↳ LowerRisk buffer ark 0x106CBB1F445F0bFFa7894F4199EE940BF7f6dD2B
↳ Silo "Varlamore USDC Growth" ark (manipulated) 0x61d7063041d83C8ca3E42c39181dFd14B3Bc76c2
HigherRisk USDC FleetCommander 0xE9cDA459bED6dcfb8AC61CD8cE08E2D52370cB06
↳ HigherRisk buffer ark 0xEB60A8e747d73c58cCc320bcdAbB166F8A0C0D9D
↳ Term ERC4626 ark (donation target) 0xfD899321B1FD8d75e255119766D9097C98568519
Term "Summer USDC" — Yearn v3 TokenizedStrategy 0xA9ca4909700505585B1aD2a1579dA3b670FFA9c4
↳ TokenizedStrategy implementation (v3.0.2) 0xbb51273d6c746910c7c06fe718f30c936170fed0

Vault topology

The Term "Summer USDC" contract is not a Summer contract — it's a Yearn v3 TokenizedStrategy whose reserve vault is the LowerRisk FleetCommander. HigherRisk holds Term shares via an ERC4626 ark. This reciprocal wiring is what lets a LowerRisk drain reach into HigherRisk.

flowchart TD
    U[Depositors] --> HR["HigherRisk USDC<br/>FleetCommander<br/>0xE9cD…"]
    U --> LRx["LowerRisk USDC<br/>FleetCommander<br/>0x98C4…"]
    HR -->|"ERC4626 ark 0xfD89…"| TERM["Term Yearn v3 Strategy<br/>0xA9ca…"]
    TERM -->|"reserve deposit"| LRx
    LRx -->|"active ark (offboarding, cap=0)"| SILO["Silo Varlamore ark<br/>0x61d7…"]
Loading

Pre-positioning

This was set up well in advance. Roughly three months before the attack (late Mar – early Apr 2026), five setup wallets pre-acquired the Silo "Varlamore USDC Growth" vault shares later used in the donation — buying Stream xUSD for ~$40K and swapping it for those shares through a Balancer pool. The shares were cheap because their on-chain valuation had never been marked down after the November 2025 Stream Finance collapse, yet the offboarded ark still credits them near-par. Three blocks before the cash-out, a separate permit-aggregation transaction consolidated the five wallets' balances into the executor. This near-free, over-valued asset is what makes the LowerRisk leg profitable.

Attack flow

flowchart TD
    FL["Flash-loan 1.0M USDT + 65.4192M USDC from Morpho"] --> S1
    S1["1 · Pre-stage HR buffer<br/>deposit + withdraw 398,172 USDC (net ~0)"] --> S2
    S2["2 · Deposit 490,637 USDC into Term<br/>→ mints 439,778 Term shares + seeds LR buffer"] --> S3
    S3["3 · LowerRisk: deposit 64.83M → donate Silo shares<br/>→ redeem 70.96M @ inflated price"] --> S4
    S4["4 · HigherRisk: deposit 29.52M → donate Term shares<br/>→ redeem 29.92M @ inflated price"] --> S5
    S5["5 · Repay Morpho, sweep ~6.02M DAI to Attacker 1"]
Loading

Step-by-step

Line numbers reference a local cast run replay of the exploit tx (used for our own verification).

0 · Flash loans

Borrowed 1,000,000 USDT + 65,419,171.88 USDC from Morpho (zero flash fee); all balances repaid at the end.

1 · Pre-stage the HigherRisk buffer (net-zero round trip)

  • Deposit 398,172.237752 USDC into HR (line 2287) → boarded to HR buffer: 1,000.000000 → 399,172.237752.
  • Immediately withdraw 398,172.236752 (line 4043), sourced from HR's liquid arks — Sky sUSDC (114,808.834350) + a MorphoV2 ark (283,363.402402) — not the buffer.
  • Net: buffer stays at 399,172.237752 (up from 1,000); attacker cost = 1 wei.
  • Why: relocate ~398K from HR's liquid arks into the buffer so the later inflated HR redeem is fully covered by liquid buffer, never touching HR's illiquid arks (esp. the Term ark).

2 · Mint Term shares (HR donation ammo + LR liquidity)

  • Deposit 490,636.886986 USDC into Term 0xA9ca… (line 4314) → attacker receives 439,778.128542 Term shares.
  • Term's deployFunds deposits that USDC into LowerRisk (line 4604) → Term gets 460,050.830821 LR shares; boarded to the LowerRisk buffer (line 4610).

3 · LowerRisk exploit (~$5.6M)

  • Deposit 64,828,534.992005 USDC → 60,787,156.805949 shares @ 1.0665; LR buffer now 65,320,172.
  • Donate the pre-positioned Silo shares: SiloVault::transfer(19,551,517,226,711,127) to ark 0x61d7… (line 6385). Ark's Silo balance 0 → 1.955e16, inflating totalAssets()price 1.0665 → 1.1677.
  • Redeem 60,766,209.130494 shares (line 7726) → 70,959,584.459769 USDC @ 1.1677 — paid entirely from the buffer + smaller liquid arks. _forceDisembarkFromSortedArks empties arks in ascending-TVL order, so the donated Silo ark (the largest) is sorted last and is never disembarked; the payout came from the smaller arks, to the cent:
Withdrawable pool breakdown (= redeem payout exactly)
  buffer                       65,320,171.878978
+ SkyUsds ark                       25,835.626746
+ MorphoV2 Avantgarde (0x857)       34,465.252249
+ MorphoVault Gauntlet (0xB10c)    284,184.162322
+ MorphoV2 API3 (0x81f)            969,759.251134
+ Spark (0x8948)                 1,294,220.796797
+ MorphoV2 KPK (0xd0a)           3,030,947.491543
──────────────────────────────────────────────────
= 70,959,584.459769   ==  redeem payout (line 9288)

Attacker's full entitlement was ~70,984,032 (all shares × 1.1677) — ~24,448 more than the pool could pay, so they kept 20,947.68 shares: the sliver liquidity couldn't honor. Textbook liquidity cap.

  • LowerRisk left with ~$4.04M of illiquid assets (Syrup + the donated Silo position).

4 · HigherRisk exploit (~$0.4M)

  • Deposit 29,517,258.144045 USDC → 27,891,852.787610 shares @ 1.0583; HR buffer → 29,916,430.381797 (= 1,000 pre-existing + 398,172 pre-stage + deposit).
  • Donate the 439,778.128542 Term shares to the (empty) Term ark 0xfD89… (line 9865). Credited at Term.convertToAssets = 490,636.886985fair value, no appreciation, because Term's stored Yearn totalAssets was never refreshed by a report() (none appears in the trace). Price 1.0583 → 1.0756.
  • Redeem 27,814,155.738915 shares (line 10433) → 29,916,430.381787 USDC, drained entirely from the HR buffer (→ 10 wei). Kept 77,697.05 shares — sized so the redeem exactly equals the buffer, keeping the whole payout in liquid buffer and avoiding HR's illiquid Term/Silo arks.

5 · Settlement

  • Repay Morpho flash loans (lines 10823, 10829).
  • Sweep profit to Attacker 1: 6,016,754.998120906520734632 DAI (line 10886) + leftover fleet shares (LR 20,947.675455 + HR 77,697.049639).

Profit accounting

Single-entry over the attacker's wallet:

OUT  398,172.24 (HR pre-stage) + 490,636.89 (Term) + 64,828,534.99 (LR) + 29,517,258.14 (HR)
   =  95,234,602.26
IN   398,172.24 (HR pre-stage) + 70,959,584.46 (LR) + 29,916,430.38 (HR)
   = 101,274,187.08
NET  = +6,039,584.82 USDC   (+ ~$108K leftover fleet shares, swept as part of the DAI)
Leg Net Source
LowerRisk ≈ +$5.64M LowerRisk LPs' liquid assets, extracted at the inflated price
HigherRisk ≈ +$399K (+ ~$84K kept shares) HigherRisk LPs' liquid assets

Note

Counterfactual check: skipping the Term + HigherRisk detour nets +5,640,412; the actual net is +6,039,584. The difference is exactly the +399,172 HigherRisk extraction — confirming the detour was net-positive, not incidental.

Why route through Term

The Term deposit does double duty, which is why a generic vault would not have worked:

  1. It mints the Term shares donated into HigherRisk's Term ark (the only asset that ark values).
  2. It routes the 490K into LowerRisk's withdrawable buffer. Because the LowerRisk redeem is liquidity-capped (it drains the entire withdrawable pool), that 490K is fully extracted in the drain — i.e. recovered.

So the 490K cost of the Term shares is clawed back on the LowerRisk side, making the HigherRisk donation effectively free → the HR leg is net-positive. A generic Morpho-vault deposit would sit outside LowerRisk, be stranded when donated, and that route would net ~−$8K. The Term choice was deliberate and profitable.

Response & fund tracing

  • Contained: all Lazy Summer vaults were paused; the affected arks had already been capped to 0 (the HigherRisk Term ark pre-incident), and impacted markets are being decommissioned via the governor sweep-and-removeArk flow (see Lessons).
  • Traced: the attacker's gas was FixedFloat-funded and bridged in via LayerZero; the ~6M in proceeds is being converted to ETH and moved into Tornado Cash. The flow is traced end-to-end and the wallets are being monitored, with recovery coordination underway.
  • Thanks to Blockaid, CertiK, and PeckShield for fast detection, and to the community analysts who dug in alongside us.

Lessons & remediation

  • Decommission capped Arks promptly via the governor flow. A cap == 0 Ark is still active and still in NAV by design — it may need time to unwind (Origin/Syrup, etc.), so it can't just be dropped from pricing without deflating PPS and freezing withdrawals. It must be removed deliberately: sweep + socialize loss (or empty), then removeArk. The exposure is letting a capped, donatable Ark linger in the active set — expedite the sweep-and-remove.
  • Mind reciprocal integrations. A stored-accounting wrapper (Yearn v3 totalAssets only refreshed on report()) can hide impairment of a coupled vault; avoid circular vault-in-vault exposure or value it live.
  • Stale external valuations (here, Silo shares un-marked-down after Stream Finance) are prime donation ammo — monitor and cap arks holding assets with known impairment risk.

Methodology

Reconstructed by replaying the transaction on a mainnet fork (cast run) and cross-referencing the Summer deployment config (deployed_addresses.json, config/index.json). Addresses, amounts, trace line references, and the profit accounting are verified from the on-chain execution. Attacker labels and flash-loan token legs are as surfaced by block explorers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment