Skip to content

Replace the Whitelist transfer hook with Allow List, and remove Block List - #208

Merged
mikemaccana merged 3 commits into
mainfrom
claude/jolly-carson-szdntp
Nov 7, 2026
Merged

mikemaccana merged 3 commits into
mainfrom
claude/jolly-carson-szdntp

Conversation

@mikemaccana

Copy link
Copy Markdown
Collaborator

Why

The Whitelist transfer hook had four problems:

  • Size limit. Every allowed address lived in one account, capped at max_len(11).
  • One list for every mint. The list sat at a single global PDA (seeds = [b"white_list"]).
  • Anyone could take it over. initialize_extra_account_meta_list opened the list with init_if_needed and set authority = payer on every call, so setting up the hook for any mint of your own made you the authority of everyone's list.
  • Wrong check. The hook checked the destination token account's address rather than the wallet that owns it, and rejected transfers with panic!.

Block List was a Pinocchio-only example with no catalogue entry. Its design, one account per blocked wallet, is what Allow/Block List already does in Block mode, in Anchor v1, Anchor v2 and Quasar.

Changes

Whitelist becomes Allow List (tokens/token-extensions/transfer-hook/allow-list/). All three variants (Anchor v1, Anchor v2, Quasar) change together, and the program ID is unchanged.

  • Each mint has its own allow list account, at a PDA seeded by "allow_list" and the mint. It holds the list's authority.
  • Each allowed wallet has its own allowed wallet account, at a PDA seeded by "allowed_wallet", the mint and the wallet. If the account exists, the wallet is allowed, so the list has no size limit.
  • add_to_whitelist is replaced by allow_wallet. The new disallow_wallet closes a wallet's account and returns its rent.
  • initialize_extra_account_meta_list requires the mint authority's signature and uses init, so only the issuer can set up a mint's list.
  • The hook's one extra account is derived on each transfer from the mint and the destination token account's owner (account 2, bytes 32..64). If that account doesn't exist, the transfer fails with RecipientNotAllowed (in Quasar, custom error 6000).

Block List removed. tokens/token-extensions/transfer-hook/block-list/ is deleted.

Notices. whitelist/README.md and block-list/README.md remain in the old paths, saying where each example went.

Docs. The root README catalogue, the root Cargo.toml workspace member, Cargo.lock, scripts/generate-quasar-readmes.mjs and CHANGELOG.md are updated. The lending and managed-fund docs now say "allow list" instead of "whitelist".

Tests

Variant Result How the hook is exercised
Anchor v2 7 passed Real Token Extensions transfers through the hook
Anchor v1 7 passed Real Token Extensions transfers through the hook
Quasar 9 passed Calls the hook directly with the accounts Token Extensions would pass, because the harness can't make a hooked transfer

The tests cover:

  • allowing a wallet, and rejecting a wallet that isn't on the list;
  • disallowing a wallet;
  • a wallet allowed for one mint only;
  • fifty allowed wallets;
  • a stranger trying to create a mint's allow list;
  • a non-authority trying to allow a wallet. Quasar also tests disallowing as a non-authority, and passing another wallet's account.

cargo fmt --check and cargo clippy -p transfer-hook-allow-list -- -D warnings are clean.

The Quasar program was compiled with cargo-build-sbf and tested from that build. quasar build, which also generates the IDL and clients, wasn't run locally, so CI is its first run.


Generated by Claude Code

Mike MacCana added 3 commits November 5, 2026 23:13
…limit

The whitelist kept every allowed address in one account, capped at eleven
entries and shared by every mint. Its setup instruction used
`init_if_needed` and made the caller the list's authority each time, so
anyone could take over the list by setting up the hook for a mint of
their own.

Allow List gives each mint its own allow list account (PDA seeds
"allow_list", mint) holding the authority, and each allowed wallet its
own account (PDA seeds "allowed_wallet", mint, wallet). `allow_wallet`
creates a wallet's account and `disallow_wallet` closes it. Setup now
requires the mint authority's signature and uses `init`. The hook's one
extra account is derived per transfer from the mint and the destination
token account's owner, and a transfer to a wallet without an account
fails with `RecipientNotAllowed` instead of panicking.

All three variants (Anchor v1, Anchor v2, Quasar) change together, with
tests for allowing, disallowing, per-mint scoping, fifty allowed wallets,
and the takeover attempts. The lending and managed-fund docs say "allow
list" too.

Claude-Session: https://claude.ai/code/session_01GZHin7uZN95tCycB2vWhog
Block List was a Pinocchio-only example with no catalog entry. Its one
account per blocked wallet is what Allow/Block List does in Block mode,
in Anchor v1, Anchor v2 and Quasar, so it duplicated that example.

`whitelist/` and `block-list/` each keep a README.md saying where the
example went, so old links still land somewhere useful.

Claude-Session: https://claude.ai/code/session_01GZHin7uZN95tCycB2vWhog
Resolve CHANGELOG.md (keep both new entries, this one dated 2026-11-07 on
top) and README.md (keep main's new NFTs section, drop the Whitelist entry
this branch replaces). Use 'anchor program deploy' in the Allow List
READMEs, matching main's change to every other Anchor README.

Claude-Session: https://claude.ai/code/session_01GZHin7uZN95tCycB2vWhog
@mikemaccana
mikemaccana merged commit fcd71cf into main Nov 7, 2026
30 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.

1 participant