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.
- Actors & contracts
- Vault topology
- Pre-positioning
- Attack flow
- Step-by-step
- Profit accounting
- Why route through Term
- Response & fund tracing
- Lessons & remediation
- Methodology
| 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 |
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…"]
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.
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"]
Line numbers reference a local cast run replay of the exploit tx (used for our own verification).
Borrowed 1,000,000 USDT + 65,419,171.88 USDC from Morpho (zero flash fee); all balances repaid at the end.
- 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).
- Deposit 490,636.886986 USDC into Term
0xA9ca…(line 4314) → attacker receives 439,778.128542 Term shares. - Term's
deployFundsdeposits that USDC into LowerRisk (line 4604) → Term gets 460,050.830821 LR shares; boarded to the LowerRisk buffer (line 4610).
- 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 ark0x61d7…(line 6385). Ark's Silo balance 0 → 1.955e16, inflatingtotalAssets()→ 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.
_forceDisembarkFromSortedArksempties 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).
- 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 atTerm.convertToAssets = 490,636.886985— fair value, no appreciation, because Term's stored YearntotalAssetswas never refreshed by areport()(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.
- 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).
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.
The Term deposit does double duty, which is why a generic vault would not have worked:
- It mints the Term shares donated into HigherRisk's Term ark (the only asset that ark values).
- 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.
- 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-
removeArkflow (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.
- Decommission capped Arks promptly via the governor flow. A
cap == 0Ark 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), thenremoveArk. 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
totalAssetsonly refreshed onreport()) 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.
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.