Skip to content

Escrow: refuse offers with zero tokens or the same token on both sides - #169

Open
mikemaccana wants to merge 2 commits into
mainfrom
claude/clever-brahmagupta-khml89
Open

mikemaccana wants to merge 2 commits into
mainfrom
claude/clever-brahmagupta-khml89

Conversation

@mikemaccana

@mikemaccana mikemaccana commented Sep 29, 2026 •

Copy link
Copy Markdown
Collaborator

The book's Escrow chapter says any escrow should reject offers with zero amounts and offers where both tokens are the same, and the example did not. This adds both refusals to make_offer in all four variants, with a test for each.

What each variant does

  • anchor, anchor-v1: make_offer fails with ZeroAmount when either token_a_offered_amount or token_b_wanted_amount is zero. A same-token offer was already refused before any handler runs: the maker's token-A and token-B associated token accounts would be the same account, and Anchor refuses the same mutable account twice (ConstraintDuplicateMutableAccount, 2040, in both Anchor 1.2 and 2.0). A SameMint check in the handler could never run, so there isn't one. A test pins the framework refusal instead.
  • quasar: make_offer fails with ZeroAmount (a new EscrowError starting at 6000, like the other finance Quasar programs). A same-token offer was already refused before the handler: both mint slots hold the same account, and loading it twice fails with AccountBorrowFailed. The first CI run showed this, and the handler's SameMint check was removed as unreachable. A test pins the refusal instead.
  • native: make_offer fails with ZeroAmount or SameMint (new EscrowError variants, appended so existing codes don't move). Nothing in the native program refuses a same-token offer on its own, so here the explicit check is what does it.

Tests

Each variant gets three tests: zero offered amount, zero wanted amount, and same token. Each asserts the exact error. The Anchor and native tests also assert that the maker's token-A balance didn't change (and, for native, that no offer account was created).

Verification

The Solana toolchain can't be installed in the environment this was written in (the network policy blocks release.anza.xyz). Every variant passes cargo check --tests and cargo fmt, and the tests themselves were first run by this PR's CI.

🤖 Generated with Claude Code

https://claude.ai/code/session_01JGEoAUjMm7Evv69k46eNcn

Mike MacCana added 2 commits September 29, 2026 21:11
make_offer now refuses an offer with zero tokens on either side, and an
offer of a token for a different amount of itself, in all four variants:

- anchor and anchor-v1: ZeroAmount is checked in the handler. A same-token
  offer never reaches the handler, because the maker's token-A and token-B
  accounts would be the same account and Anchor refuses the same mutable
  account twice (ConstraintDuplicateMutableAccount); a test pins that.
- native and quasar: ZeroAmount and SameMint are both checked in the
  handler, since neither refuses the duplicate on its own (quasar's token
  accounts are not associated token accounts, so they need not coincide).

Each variant gets a test per refusal that asserts the error code and that
the maker's tokens did not move.

Claude-Session: https://claude.ai/code/session_01JGEoAUjMm7Evv69k46eNcn
CI showed a same-token offer never reaches make_offer's handler: both mint
slots hold the same account, and loading it twice fails with
AccountBorrowFailed. Drop the SameMint check, which could never run, and
have the test assert the refusal that actually happens, as the Anchor
variants do.

Claude-Session: https://claude.ai/code/session_01JGEoAUjMm7Evv69k46eNcn
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant