Skip to content

fix(quasar): use transfer_checked in finance programs#101

Merged
mikemaccana merged 2 commits into
mainfrom
claude/quasar-transfer-checked
Jul 7, 2026
Merged

fix(quasar): use transfer_checked in finance programs#101
mikemaccana merged 2 commits into
mainfrom
claude/quasar-transfer-checked

Conversation

@mikemaccana

@mikemaccana mikemaccana commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

What

The Quasar finance programs moved tokens with raw .transfer(), which omits the mint and decimals from the CPI. transfer_checked carries both, so a wrong-mint or wrong-decimals token account fails the CPI instead of being caught only by downstream balance checks (or not at all). This matches the Anchor twins and the lending/quasar program, which already used transfer_checked.

Converted all raw token transfers in:

  • escrow — make_offer / take_offer (×2) / cancel_offer
  • token-swap — deposit_liquidity, withdraw_liquidity, swap_tokens (×4), claim_admin_fees (×2)
  • perpetual-futures — add/remove liquidity, open/close position, liquidate (×2), collect_fees (all in the single collateral_mint)
  • token-fundraiser — contribute / refund / check_contributions

Each call now passes the mint whose token accounts are being moved and its .decimals(). .burn / .mint_to / .close_account CPIs are untouched.

token-fundraiser needed an account added

Its contribute/refund/check_contributions instructions had no mint in scope, so each gained a mint_to_raise: Account<Mint> bound to Fundraiser.mint_to_raise via has_one (validated against stored state, not caller-supplied). The generated client picks up the new account automatically; the LiteSVM tests pass it via fixture.mint.

Verification

cargo check passes on all four crates (token-fundraiser via --lib, since its tests depend on the quasar build-generated client). The Quasar CI (quasar build + cargo test under QuasarSVM) exercises the full behavior — token conservation and vault invariants are unchanged.

🤖 Generated with Claude Code

claude added 2 commits July 7, 2026 17:00
…tures

Raw .transfer() omits the mint and decimals, so a wrong-mint or wrong-decimals
token account would be caught only by downstream balance checks (or not at all).
transfer_checked carries the mint and decimals through the CPI, matching the
Anchor twins and the lending/quasar program. Each call now passes the mint whose
token accounts are being moved and its .decimals(); token conservation and vault
invariants are unchanged.

Verified with cargo check on all three crates (QuasarSVM tests run in CI).
token-fundraiser needs its mint threaded into three instruction structs and is
handled separately.
Unlike the other finance programs, token-fundraiser's contribute/refund/
check_contributions instructions had no mint account in scope, so the transfer
CPIs could not be checked. Thread the fundraiser's mint through each instruction:
add `mint_to_raise: Account<Mint>` bound to `Fundraiser.mint_to_raise` via
has_one, then transfer_checked with its decimals. The generated client picks up
the new account automatically; the LiteSVM tests pass it via fixture.mint.

Program verified with cargo check (lib); QuasarSVM tests run in CI, which
regenerates the client with the added account.
@mikemaccana mikemaccana merged commit 3b14186 into main Jul 7, 2026
26 checks passed
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.

2 participants