Adapter donation recovery lets an unseeded bootstrapper steal depositor principal
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.
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:
- The attacker is the first depositor after deployment, deposits
1 USDC, and receives1e18bootstrap share wei. - The attacker withdraws all but
300share wei. Rounding leaves one USDC base unit in Aave. - The attacker supplies
199.999999 USDCdirectly to Aave on behalf of the adapter, making its TVL exactly200 USDC. - In one epoch, the attacker deposits
100 USDC, withdraws its300old shares, and 100 users each deposit1 USDC. Deposits and the old withdrawal are both valued at200 USDC, so the close has zero net flow. - The attacker claims the
200-USDCold withdrawal from the new ParentVault deposits. The Aave position remains as backing, so all199.999999 USDCsupplied out of band has been recovered. - The attacker calls
claimSharesForfor 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. - After an ordinary full exit, the attacker holds
334.333332 USDCfrom300.999999 USDCof starting capital, while victims hold66.666667 USDCfrom100 USDCdeposited.
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.
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.
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.
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.
Affected files
evm/src/libraries/vaults/ParentVaultEpochLib.sol#L209-L268evm/src/libraries/vaults/ParentVaultUserEpochLib.sol#L232-L263evm/src/vaults/ParentVault.sol#L200-L224evm/src/modules/adapters/AaveV3Adapter.sol#L109-L127Deployment context (outside the reviewed contract scope, referenced only to establish the starting- state):
evm/script/deploy/DeployParent.s.sol,docs/operator/DEPLOYMENT.md.