Repository navigation
Name ignoring donations as the vault strategy's first-depositor defense - #96
Merged
Merged
Conversation
The empty-pool section stated the mechanism in the abstract: a minuscule first deposit, a donation, a division that floors to zero. Nothing in it could be checked by a reader with a pencil. It now runs the attack against a pool priced the way the vault strategy prices its shares: one minor unit deposited through the handler, 1,000 USDC sent straight to the vault, and Bob's 1,000 USDC deposit landing on the skewed rate to mint zero shares. A figure draws the three states, with the vault balance as a bar and the share supply stuck at one beneath it. The same numbers then run through each defense, so the reader sees why a locked minimum makes the donation cost a hundred times what it takes, why virtual shares hand half the donation to nobody, and why a pool size kept in program state leaves the donation outside the accounting. The vault's own minimum_shares floor is named as the caller-side bound it is: it protects the one transaction that sets it, not the pool. "Going through deposit" is now "through the deposit handler", the book's term for the function rather than its input. Claude-Session: https://claude.ai/code/session_0154xpTf5MDvrp4m2qSVAtsn
Bob's step computed 1,000 × 1 ÷ 1,000.000001 with nothing telling the reader where the three numbers came from. The formula now sits on its own line before the steps, as the betting market's payout formula does, with its terms named: deposit, shares outstanding, pool value. A sentence before it says what the formula means (a deposit that doubles the pool's value doubles the share supply), and Bob's step maps each of his numbers onto a term before substituting. The 1,500 USDC variant and the figure's annotation use the same form, and the opening paragraph's "supply" and "value" now match the formula's names. Claude-Session: https://claude.ai/code/session_0154xpTf5MDvrp4m2qSVAtsn
The figure's headline said Bob's deposit bought no shares, without saying who did it or what it earned them. It now reads "An attacker makes Bob's deposit buy no shares", and step 3 draws the payoff: a bracket across both deposits marks the attacker's one share as redeeming for all 2,000.000001 USDC, and a bold line under it states the result, 1,000.000001 USDC in and 2,000.000001 out. The prose had the same gap in the same place. Bob's step ended on "the attacker holds the share" and left the profit to the paragraph after the figure; the profit now closes Bob's step, so the reader has it before the figure, and the following paragraph no longer repeats it. Claude-Session: https://claude.ai/code/session_0154xpTf5MDvrp4m2qSVAtsn
Audited against the companion AI plugin's writing rules. The chapter leaned
on metaphor where a mechanism was available ("every mistake is a bounty",
"attacks are industrialized", "the boundary values are where the money
is", "fantasy prices", "renting one block"), closed paragraphs on quotable
restatements, used "X is not Y; it is Z" where nobody believed Y, and ran
lists together inside paragraphs. Each is replaced by the literal claim:
a flaw worth a cent per call is worth $10,000 to a script that calls it a
million times; the price-manipulation defenses are a list.
The empty-pool section loses its invented labels ("degenerate zone",
"named defense", "relies on circumstance") and its closing sentence about
the vault strategy. The locked-shares bullet now states the design its
numbers assume, locked shares counted in every divisor, instead of
attributing them to the AMM, whose deposit formula leaves the floor out.
The virtual-shares numbers are recomputed: Bob buys 1,999,999 shares
redeeming for 999.99975 USDC.
The Wormhole story now says what the deprecated function failed to check,
the Hundred and Sonne story is sentences instead of a semicolon list, and
two headings say what they mean: "Every Mistake Is Money Anyone Can Take"
and "A Rounding Error Repeated Enough Times Drains the Pool".
Claude-Session: https://claude.ai/code/session_0154xpTf5MDvrp4m2qSVAtsn
The Adversary chapter conflicted with four edits main made while this branch was open. The branch's plain-language rewrite already covers most of them; three are carried into it: the stored-address check and its per-framework footnote replace the has_one pattern, the order book is described as checking every vault against its market's recorded addresses, and the Cashio footnote takes main's wording. Main's new lint rules also caught two things on the branch: the inflation-attack figure used curly-apostrophe entities where figure text now takes straight ones, and two sentences in the Hundred and Sonne story used "drained" without naming the attacker. Claude-Session: https://claude.ai/code/session_0154xpTf5MDvrp4m2qSVAtsn
mikemaccana
force-pushed
the
claude/laughing-mayer-kkl3q2
branch
from
September 22, 2026 23:09
e6bf5a4 to
9f77941
Compare
The companion repository's vault strategy now records what it holds, usdc_holdings and asset_holdings, and prices shares and pays withdrawals from those records rather than its vaults' token balances, so a donation cannot move the share price. It also refuses a deposit whose swap returns none of its asset (DepositTooSmall). The Vault Strategy chapter describes the records in the account model, the deposit, the withdrawal, the math and the invariants, replaces the "no dead-shares guard" failure mode with the donation defense and the dust deposit guard, and names the new tests. The Adversary chapter's empty-pool section names the vault strategy alongside the lending market under "Ignore donations" and says which defense each of the book's programs chose; the Perpetual Futures cross-reference and the War Story Register follow. Claude-Session: https://claude.ai/code/session_012jbHknFnC6NKkeWvgvziB1
mikemaccana
force-pushed
the
claude/laughing-mayer-kkl3q2
branch
from
September 22, 2026 23:45
9f77941 to
145176e
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Merge order: this PR must merge after the companion PR quicknode/solana-program-examples#144. That PR makes the vault strategy record what it holds and price shares from those records, ignoring donations. Every identifier and number below describes what it ships, so merging this first would have the book describing a defense the companion had not shipped.
What changed
usdc_holdingsandasset_holdings.DepositTooSmall).rebalancecannot sell or spend donated tokens (InsufficientHoldings).minimum_sharesremains for a share price that moved between signing and landing. The pointer to the Adversary chapter's empty-pool section stays.minimum_sharesparagraph says the vault strategy keeps the argument without relying on it.available_liquidity, and points attest_donation_does_not_inflate_share_price.I grepped for
one-to-one,dead-shares,virtualandmissing guard. The remaining hits are the bare pool in the Adversary walkthrough, the vault's still-one-to-one first deposit, the AMM's and lending's own mechanisms, and the virtual-shares bullet describing that general defense. No line says the vault's first deposit is unguarded.Relation to other PRs
main, plus one commit. If Walk the inflation attack through with numbers, and rewrite the Adversary chapter in plain language #94 merges first, this diff shrinks to that one commit.Verification
npm run lintclean.node scripts/check-figure-geometry.mjsclean.npm run build-pdfwrote the book, 260 pages.usdc_holdings,asset_holdings,DepositTooSmall,InsufficientHoldings,minimum_shares, and the four test names.🤖 Generated with Claude Code
https://claude.ai/code/session_012jbHknFnC6NKkeWvgvziB1