Early rate$2,400 of senior audit time for $500. Early members keep the rate as it climbs.$2,400 of senior audit time for $500See how →
F-2026-0004·first-depositor

Adapter donation recovery lets an unseeded bootstrapper steal depositor principal

Mitigatedvaultcrosschainepoch-accountinggithub.com/contractlevel/yield-v2
TL;DR

Deposit claims are computed against shrinking pools with the remainder going to whoever exhausts them, and claim order is caller-chosen. Combined with adapter TVL that counts donated aTokens, an unseeded bootstrapper can recover its donation and redirect honest depositors' principal to itself.

Severity
MEDIUM
Impact
MEDIUM
Likelihood
MEDIUM
Method
MManual review
CAT.
Complexity
MEDIUM
Exploitability
MEDIUM
02Section · Description

Description

Two properties of settlement combine to let an unprivileged account take honest depositors' principal.

ParentVaultUserEpochLib._claimShares calculates each deposit claim against shrinking remaining-input and remaining-output pools. Every non-final claimant is rounded down, while the claimant that exhausts the pool receives the entire remainder. ParentVault.claimSharesFor is permissionless, so claim order is chosen by whoever calls it rather than by the claimants — an attacker can force many minimum-sized deposits to claim first and make its own deposit the exhausting claim.

Separately, AaveV3Adapter.getTVL() counts aTokens held by the adapter as strategy backing even when the value never passed through a ParentVault deposit, and anyone can supply underlying to Aave on behalf of the adapter. Combining a withdrawal of existing shares with new deposits so that the next epoch settles at exactly zero net flow leaves the unsolicited position in Aave while fresh ParentVault deposits pay the withdrawal, returning the out-of-band capital in full.

Both are bounded by how coarse the share unit is, and the share unit is coarse exactly once: at bootstrap. The parent deployment leaves both authoritative and ERC-20 share supply at zero, and neither DeployParent nor the deployment runbook performs the permanent seed deposit on which KI-010's bootstrap analysis relies. The first unprivileged depositor can therefore own the complete bootstrap supply, withdraw almost all of it, and leave only a few share wei outstanding — at which point a single floored claim represents material underlying value rather than dust.

This exceeds the accepted behavior in KI-008 and KI-010. KI-008 concludes that unsolicited adapter value cannot be fully recovered permissionlessly; the zero-net settlement pays it back in full. KI-010 relies on a permanent operational seed and states that other users are not put at risk, but the audited deployment path neither creates nor requires that seed and this sequence transfers honest principal.

Where this sits relative to the existing protections

The guards around settlement work, and one of them shapes the attack rather than stopping it. Settlement rejects an aggregate allocation under which a minimum-sized deposit would mint zero shares, which caps how coarse the share unit can be made: with a one-USDC minimum deposit, one share wei can never be made to carry more than roughly one USDC of value. The attacker tunes the supply to sit just inside that limit rather than defeating it, so the per-claim rounding loss is bounded by that same figure. That guard's edge was clearly anticipated: an operator role exists specifically to force-cancel a deposit that would otherwise leave an epoch unclosable. The sequence below never trips it — the supply is tuned to sit inside the admissible range, so settlement proceeds normally and no operator intervention is prompted.

The shrinking-pool claim calculation is likewise deliberate and, in ordinary conditions, correct. Assigning the exact remaining balance to the claimant that exhausts a pool is what guarantees the deposit-side counters reach zero together and that no share is stranded. It is only harmful once the share unit is coarse enough that a single floored claim represents real value, and once claim order is chosen by someone other than the claimants.

The seed deposit that bounds this class is described as the operational mitigation for the accepted bootstrap-allocation issue. It is stated only in that acceptance rationale. The deployment runbook does not instruct it and the parent deployment script does not perform it, so an operator following the documented deployment procedure produces a vault with zero authoritative shares and zero token supply. Nothing in the contracts requires the seed to exist.

The consequence exceeds what the accepted issues describe. The bootstrap-allocation entry states that no other user's balance is diluted or put at risk and that the effect is a one-time transfer of dust value; here honest depositors lose a third of their principal. The third-party-supply entry concludes that unsolicited adapter value cannot be recovered permissionlessly; an exactly zero-net epoch returns it in full, because fresh deposits fund the old withdrawal while the donated balance stays in the strategy as backing.

Vulnerable scenario:

  1. The attacker is the first depositor after deployment, deposits 1 USDC, and receives 1e18 bootstrap share wei.
  2. The attacker withdraws all but 300 share wei. Rounding leaves one USDC base unit in Aave.
  3. The attacker supplies 199.999999 USDC directly to Aave on behalf of the adapter, making its TVL exactly 200 USDC.
  4. In one epoch, the attacker deposits 100 USDC, withdraws its 300 old shares, and 100 users each deposit 1 USDC. Deposits and the old withdrawal are both valued at 200 USDC, so the close has zero net flow.
  5. The attacker claims the 200-USDC old withdrawal from the new ParentVault deposits. The Aave position remains as backing, so all 199.999999 USDC supplied out of band has been recovered.
  6. The attacker calls claimSharesFor for every victim before claiming its own deposit. Victims receive 100 share wei in aggregate and the attacker receives 200; with the large claim first, both cohorts would receive 150.
  7. After an ordinary full exit, the attacker holds 334.333332 USDC from 300.999999 USDC of starting capital, while victims hold 66.666667 USDC from 100 USDC deposited.

No administrator deviation, false CRE report, CCIP failure, or Aave exploit is required. The attacker uses only public vault calls and Aave's ordinary supply-on-behalf functionality. Honest epoch closes use the live adapter TVL.

03Section · Impact

Impact

An unprivileged bootstrapper can recover all capital supplied outside ParentVault accounting and then redirect honest depositors' principal through permissionless claim ordering. In the deterministic reproduction, the attacker earns 33.333333 USDC net and the 100 depositors lose the same amount. The final vault, adapter, and share balances are all zero, so the result is realized cash rather than an unrealized valuation artifact.

Exploitability. The sequence is available in a narrow window and needs several conditions to coincide. The attacker must be the first depositor after deployment — a single, publicly observable opportunity rather than a repeatable one — and the deployment must have proceeded without a seed deposit. Reducing authoritative supply into the coarse range requires holding essentially all of it, which follows from being first but is not otherwise obtainable once other depositors are present.

The capital supplied outside vault accounting is recovered through the zero-net-flow epoch rather than forfeited, so the attacker's cost is gas and opportunity cost. The extraction is bounded in the other direction as well: settlement rejects any allocation under which a minimum-sized deposit would mint zero shares, which caps one share wei at roughly one minimum deposit of value. The transfer therefore scales with the number of separate claims rather than with the amount deposited, and it is zero where victim deposits divide the supply evenly and leave no remainder to sweep.

What outlasts the window is the share unit itself. New shares mint in proportion to existing supply, so the coarse unit established at bootstrap persists as the vault grows rather than diluting away, and every later depositor cohort remains exposed to the same bounded per-claim rounding loss for as long as the vault operates.

04Section · Recommendation

Recommendation

Reserve minimum-liquidity shares at first settlement. At the bootstrap allocation, carve a fixed share amount out of the newly allocated shares and mint it immediately to an address nobody controls, leaving the remainder as the claimable pool. The lock is never withdrawable, so authoritative supply can never return to the coarse range the attack depends on, and because new shares mint in proportion to existing supply the share unit stays fine-grained permanently.

This approach adds no new failure mode to settlement. The only guard it needs — rejecting a bootstrap whose allocation is smaller than the reserved amount — is unreachable while a minimum deposit is enforced, since the smallest admissible bootstrap allocates several orders of magnitude more shares than the reserve. It did not fire anywhere in the test suite.

Two integration costs should be planned for rather than discovered:

  • The share-accounting property that states authoritative shares change at close by newly allocated deposit shares minus submitted withdraw shares needs to account for the bootstrap lock, which is allocated but never claimable. Its stateful harness is the only invariant affected; solvency, deposit- and withdraw-pool reconciliation, recovery, nonce and CCIP properties all continue to hold unchanged.
  • Roughly eighteen existing tests assert the exact bootstrap share amount or withdraw exactly that amount, and need their expectations adjusted by the reserved quantity. All are fixture expectations; none is a behavioural failure.

A minimum-supply guard on settlement is not a safe alternative. Rejecting any close that would leave authoritative supply between zero and a floor blocks this attack, but it introduces a settlement revert that withdraw intents can trigger. There is no operator function to cancel a withdraw intent — the existing force-cancel covers deposits only — so a withdrawer whose intent lands the supply inside the forbidden band and who declines to cancel it voluntarily leaves the epoch permanently unclosable, freezing every other deposit and intent in that epoch and preventing the next epoch from opening. That guard also rejects at least one settlement the current suite treats as correct.

Independently, remove caller-selected remainder assignment. Calculate each deposit claim from the epoch's immutable totals rather than from shrinking pools, and route the aggregate rounding remainder to a deterministic destination rather than to whichever claim exhausts the pool. This removes the value of ordering claims regardless of how coarse the share unit is, and it also closes the lower-value ordering advantage that exists on the withdraw side.

Finally, make the seed deposit real or unnecessary. If the operational seed is the intended control, the deployment runbook and deployment script should perform it rather than it existing only as a stated assumption. The minimum-liquidity reserve above makes it unnecessary, which is the more robust outcome.

05Section · Resolution

Resolution

Mitigated at deployment in 259528f: no contract change was required, with the permanent seed lock, the mandatory bootstrap procedure in DEPLOYMENT.md and KI-024 together addressing it, leaving the residual that nothing on-chain enforces the seed.

06Section · Affected files

Affected files

  • evm/src/libraries/vaults/ParentVaultEpochLib.sol#L209-L268
  • evm/src/libraries/vaults/ParentVaultUserEpochLib.sol#L232-L263
  • evm/src/vaults/ParentVault.sol#L200-L224
  • evm/src/modules/adapters/AaveV3Adapter.sol#L109-L127
  • Deployment context (outside the reviewed contract scope, referenced only to establish the starting
  • state): evm/script/deploy/DeployParent.s.sol, docs/operator/DEPLOYMENT.md.
Status
Mitigated
F-2026-0004