I almost submitted a wrong finding. Not a borderline one — a confidently written, textbook-shaped, "Low severity, high confidence" report whose core math was wrong in a way I could only see when I finally pushed concrete wei-level numbers through it. This is the story, because the failure mode is more instructive than the techniques that worked.
The finding
An automation contract deposits a position's whole idle balance into an ERC4626 vault and reverts if it receives zero shares. No minShares parameter anywhere. The draft wrote itself: classic ERC4626 inflation attack. Attacker takes a first position of one share, donates 999× the expected deposit to the pool, victim deposits and receives a positive but negligible share count, attacker redeems and captures ~99.9% of the victim's principal. "The zero-share check blocks only total loss; every partial-loss shape passes." I had a four-step attack script, a severity justification, and a recommendation mirroring the amountOutMin pattern used elsewhere in the same codebase. It looked airtight.
The refutation
At final review I made myself do what I now consider mandatory: walk the exploit with exact numbers, end to end, in wei.
Setup: attacker holds 1 share. Attacker donates D = 999·A where A is the victim's deposit. Pool before victim: totalShares = 1, totalAssets = 1 + D.
Victim deposits A: shares_minted = floor(A · 1 / (1 + D + A)) = floor(A / (1000A)) = floor(1/1000) = 0.
Zero. The zero-share check reverts the transaction. Try to pass the check — push shares_minted to at least 1 — and you need D ≈ A, and then the victim's share of the pool is A/(A + A + A-ish) ≈ one third, and they redeem back roughly everything they put in, minus dust.
That's when the algebra fell apart. Pro-rata redemption is self-consistent: a large depositor's fraction of the pool after donating-and-depositing is A/(D + 2A), and they redeem almost all of A back no matter how big D gets. The classic inflation attack only harms deposits small enough to round down to zero shares — and this contract only ever deposits whole-position liquidity, which is large, and it already reverts on zero shares. The only provably harmful form was blocked; the form I claimed was impossible.
Why the intuition was wrong
The "donation dilutes the victim to nothing" intuition comes from fixed-share schemes. ERC4626 mints shares at deposit time, priced by the post-donation ratio — so the donation is already baked into the exchange rate the victim receives. The victim is never holding pre-donation shares. Every "they lost 99.9%" claim quietly assumed the victim's shares were priced before the attack. Analogies to famous exploits skip exactly the term that matters.
The rules I took from it
- Wei-level numbers, end to end, before believing any finding. If you can't walk the exploit with concrete amounts, you don't have an exploit; you have a story.
- Push the boundary values both ways. Make the attack fail the guard on purpose, then relax it one unit at a time and watch what the victim actually recovers. The loss curve is not what you think it is.
- Analogies are how findings die in triage. The judge runs the same arithmetic you skipped. A wrong submission costs credibility with every triager who remembers your name; a self-refuted one costs an evening.
I submitted nothing that round. That was the correct outcome.
Top comments (0)