Skip to content

refactor: use free functions instead of impl methods in Anchor examples#561

Open
mikemaccana-edwardbot wants to merge 46 commits intosolana-developers:mainfrom
mikemaccana:anchor-free-functions
Open

refactor: use free functions instead of impl methods in Anchor examples#561
mikemaccana-edwardbot wants to merge 46 commits intosolana-developers:mainfrom
mikemaccana:anchor-free-functions

Conversation

@mikemaccana-edwardbot
Copy link
Copy Markdown
Contributor

Based on #558 (Anchor 1.0 stable).

Refactors all Anchor examples to use free handler functions instead of impl blocks on account constraint structs.

Why

The impl pattern is fake OOP — the methods don't modify instance state, they just need access to accounts. Free functions make this explicit and are more consistent with Rust conventions.

Changes

  • ~50 Anchor programs refactored
  • Account constraint structs renamed to end with AccountConstraints
  • ctxcontext throughout
  • self.fieldaccounts.field in handler functions
  • context.accounts.method()instructions::handle_method(&mut context.accounts) in lib.rs

mikemaccana and others added 30 commits April 10, 2026 15:24
- Update anchor-lang and anchor-spl to 1.0.0 in all Cargo.toml files
- Update @anchor-lang/core to 1.0.0 in all package.json files
- Remove stale RC pin comments from Cargo.toml files
- Update .reference/ANCHOR-1.0-MIGRATION.md to reflect stable release
- Keep all existing tests unchanged (no LiteSVM additions)
Adds Rust integration tests using LiteSVM and solana-kite for all 46 Anchor
programs. These run via anchor test → cargo test.

Existing TypeScript tests preserved — this PR only adds the Rust test files,
updates Cargo.toml dev-dependencies, and changes the Anchor.toml test script.

Based on anchor-10-existing-tests (solana-developers#558).
Add Quasar framework implementations alongside existing Anchor, native,
and Pinocchio variants for three basic examples:

- hello-solana: logs a greeting and the program ID
- counter: initialize + increment with PDA-derived state
- transfer-sol: CPI transfer via system program + direct lamport manipulation

Each is a standalone Cargo project (not in root workspace) with:
- Program source using quasar-lang macros
- Auto-generated client crate via quasar build
- Tests using quasar-svm

All examples build with 'quasar build' and pass 'quasar test'.
Follows the same pattern as solana-native.yml and solana-pinocchio.yml:
- Detects changed **/quasar/** directories
- Matrix strategy for parallel builds
- Installs quasar CLI from git
- Runs quasar build + cargo test (pure Rust, no JS/pnpm)
- Skipped beta channel (Quasar CLI pins its own Solana toolchain)
All 5 examples build and pass tests with quasar-lang 0.0.0:

- basics/account-data/quasar — PDA with multiple String<u8, 50> fields
- basics/close-account/quasar — create + close account with String field
- basics/favorites/quasar — fixed (u64) + dynamic (String) field mixing
- basics/processing-instructions/quasar — String instruction args, no state
- basics/realloc/quasar — auto-realloc via set_inner with String field

These demonstrate Quasar's dynamic type system:
- String<P, N> marker types in #[account] structs (becomes &'a str)
- String in #[instruction] args (u32-prefixed wire format, becomes &str)
- set_inner() for writing dynamic fields with auto-realloc
- Manual instruction data encoding in tests (quasar-svm framework)
create-token: mint creation and minting (no Metaplex metadata)
transfer-tokens: mint_to and transfer SPL token operations
escrow: full make/take/refund with PDA vault, has_one checks, close

All three build with quasar build and pass cargo test.
pda-mint-authority: PDA as mint authority with signed minting
token-fundraiser: crowdfunding with initialize, contribute, check, refund

Both build with quasar build and pass cargo test.
Port the token examples that were skipped by the previous subagent:

1. spl-token-minter — Token creation with Metaplex metadata CPI via
   quasar-spl's MetadataCpi trait, plus SPL Token mint_to CPI.

2. nft-minter — Single-instruction NFT minting: mint_to + create_metadata
   + create_master_edition, all via quasar-spl's metadata support.

3. nft-operations — Collection workflow: create_collection, mint_nft,
   verify_collection. Uses PDA authority with invoke_signed for all
   Metaplex CPIs. Uses verify_sized_collection_item.

4. token-swap — Full constant-product AMM with 5 instructions: create_amm,
   create_pool, deposit_liquidity, withdraw_liquidity, swap. Pure integer
   math (no fixed-point crate needed). Integer sqrt via Newton's method.

5. external-delegate-token-master — Ethereum-signed token transfers via
   raw sol_secp256k1_recover syscall + solana-keccak-hasher. Avoids
   solana-secp256k1-recover crate (pulls std via thiserror).

Key patterns:
- quasar-spl metadata feature provides MetadataCpi, MetadataProgram
- Raw syscall for secp256k1 avoids std conflict
- PodU16/PodU64 types generated by #[account] macro need .get()/.into()
- Quasar seeds resolve field names to addresses, not account data fields

All examples build (cargo build-sbf) and tests pass (cargo test).
Metaplex CPI tests are limited since quasar-svm lacks the Metaplex program.
Token Extensions examples ported using quasar-spl's TokenInterface
and InterfaceAccount types for Token-2022 compatibility.

Examples: basics, cpi-guard, default-account-state, group,
immutable-owner, interest-bearing, memo-transfer, metadata,
mint-close-authority, non-transferable, permanent-delegate,
transfer-fee
Transfer hook examples ported: hello-world, counter, account-data-as-seed,
transfer-cost, transfer-switch, whitelist.

allow-block-list-token skipped (most complex, multi-program interaction).
Compression examples (cnft-burn, cnft-vault, cutils) ported using raw
invoke()/invoke_signed() for Bubblegum and SPL Account Compression CPI,
matching the approach used in the Anchor versions.

Oracle example (pyth) ported with manual PriceUpdateV2 account data
parsing in Quasar's no_std environment.
Port the ABL (Allow/Block List) token transfer hook program from Anchor
to Quasar. This program enforces allow/block lists on Token-2022 transfers.

All 7 instructions ported:
- init_mint: Creates Token-2022 mint with transfer hook, permanent delegate,
  metadata pointer, and embedded metadata (AB mode + threshold)
- init_config: Creates config PDA with authority
- attach_to_mint: Attaches transfer hook to an existing mint
- tx_hook: SPL transfer hook execute handler with allow/block/threshold logic
- init_wallet: Creates per-wallet allow/block entries
- remove_wallet: Removes wallet entries (closes PDA)
- change_mode: Changes mode via Token-2022 metadata update

Key Quasar patterns:
- Raw CPI for Token-2022 extension init (TransferHook, PermanentDelegate,
  MetadataPointer, InitializeMint2, TokenMetadata)
- Manual TLV parsing of Token-2022 metadata in tx_hook for mode detection
- ExtraAccountMetaList with AccountData seed (destination owner lookup)
- Direct lamport manipulation for account closing (remove_wallet)
- 8-byte instruction discriminators (required by SPL transfer hook interface)

Builds with quasar build (54.5 KB). State unit tests pass.
Removes per-project TypeScript test files, package.json, tsconfig.json,
and pnpm-lock.yaml from all Anchor example directories. These are
superseded by Rust LiteSVM tests (PR solana-developers#559).

97 test files, 47 package.json, 48 tsconfig.json, 47 pnpm-lock.yaml removed.
239 files total, 96,320 lines deleted.
All 15 basics examples refactored to use free functions instead of
impl methods on Accounts structs. Pattern: handle_{method_name}(accounts, ...)
replaces self.method_name(...) on impl blocks.
All tokens examples (escrow, create-token, transfer-tokens, nft-minter,
nft-operations, spl-token-minter, token-swap, token-fundraiser,
external-delegate-token-master, pda-mint-authority) refactored.

For escrow: disambiguated handle_withdraw_tokens_and_close using
module-qualified paths (take:: and refund::) since both Take and Refund
had methods with the same name.
…unctions

All compression examples (cnft-burn, cnft-vault, cutils) and
oracles (pyth) refactored. Pyth lib.rs call site updated though the
instruction file itself is missing (pre-existing incomplete example).
…amples

Converts all Anchor examples from impl blocks on account constraint structs
to free handler functions. Structs renamed to *AccountConstraints, variable
name ctx → context, self.field → accounts.field throughout.

This removes fake OOP — the methods never modified instance state, they just
needed access to accounts. Free functions make this explicit.
mikemaccana-edwardbot and others added 16 commits April 13, 2026 02:06
# Conflicts:
#	basics/account-data/anchor/package.json
#	basics/checking-accounts/anchor/package.json
#	basics/close-account/anchor/package.json
#	basics/counter/anchor/package.json
#	basics/create-account/anchor/package.json
#	basics/cross-program-invocation/anchor/package.json
#	basics/favorites/anchor/package.json
#	basics/hello-solana/anchor/package.json
#	basics/pda-rent-payer/anchor/package.json
#	basics/processing-instructions/anchor/package.json
#	basics/program-derived-addresses/anchor/package.json
#	basics/realloc/anchor/package.json
#	basics/rent/anchor/package.json
#	basics/repository-layout/anchor/package.json
#	basics/transfer-sol/anchor/package.json
#	compression/cnft-burn/anchor/package.json
#	compression/cnft-vault/anchor/package.json
#	compression/cutils/anchor/package.json
#	tokens/create-token/anchor/package.json
#	tokens/escrow/anchor/package.json
#	tokens/external-delegate-token-master/anchor/package.json
#	tokens/nft-minter/anchor/package.json
#	tokens/nft-operations/anchor/package.json
#	tokens/pda-mint-authority/anchor/package.json
#	tokens/spl-token-minter/anchor/package.json
#	tokens/token-2022/basics/anchor/package.json
#	tokens/token-2022/cpi-guard/anchor/package.json
#	tokens/token-2022/default-account-state/anchor/package.json
#	tokens/token-2022/group/anchor/package.json
#	tokens/token-2022/immutable-owner/anchor/package.json
#	tokens/token-2022/interest-bearing/anchor/package.json
#	tokens/token-2022/memo-transfer/anchor/package.json
#	tokens/token-2022/metadata/anchor/package.json
#	tokens/token-2022/mint-close-authority/anchor/package.json
#	tokens/token-2022/nft-meta-data-pointer/anchor-example/anchor/package.json
#	tokens/token-2022/non-transferable/anchor/package.json
#	tokens/token-2022/permanent-delegate/anchor/package.json
#	tokens/token-2022/transfer-fee/anchor/package.json
#	tokens/token-2022/transfer-hook/account-data-as-seed/anchor/package.json
#	tokens/token-2022/transfer-hook/counter/anchor/package.json
#	tokens/token-2022/transfer-hook/hello-world/anchor/package.json
#	tokens/token-2022/transfer-hook/transfer-cost/anchor/package.json
#	tokens/token-2022/transfer-hook/transfer-switch/anchor/package.json
#	tokens/token-2022/transfer-hook/whitelist/anchor/package.json
#	tokens/token-fundraiser/anchor/package.json
#	tokens/token-swap/anchor/package.json
#	tokens/transfer-tokens/anchor/package.json
The '_mut' before 'context' is a typo from a find-and-replace that renamed
'ctx' to 'context'. Since context is not mutated in this function (body is
just Ok(())), the parameter should simply be 'context' with no prefix.

This fixes Rust Lint / Clippy CI failures.
The crate was renamed from 'cli' to 'quasar-cli' in the quasar repository.
The old 'cargo install ... cli --locked' fails because the crate no longer
exists under that name.

This fixes the Quasar CI workflow.
Add the Quasar workflow badge alongside the existing Anchor, Pinocchio,
and Native badges for visibility into CI status.
package.json specifies @anchor-lang/core 1.0.0 but the lockfile still
referenced 1.0.0-rc.5, causing 'pnpm install --frozen-lockfile' to fail
in CI (Anchor + TypeScript workflows).

Regenerated with 'pnpm install --no-frozen-lockfile' to sync the lockfile.
Quasar is the closest to Anchor in ergonomics (Anchor-compatible
no_std framework), so it makes sense to list it right after Anchor.
Reordered in: subtitle, badges, description list, and all example links.
Fix CI failures: stale lockfile, lever typo, Quasar CLI rename, add Quasar badge
…add Quasar pyth files

- Remove AccountConstraints suffix from all Anchor account struct names so
  tests referencing the shorter names (e.g. CreateToken, InitRentVault) compile
- Fix Clippy warnings in lever/src/lib.rs: prefix unused context with _,
  remove unnecessary mut on switch_power context parameter
- Create missing oracles/pyth/quasar/src/instructions/read_price.rs and
  src/tests.rs so the Quasar pyth build and test steps succeed

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
@mikemaccana mikemaccana force-pushed the anchor-free-functions branch 2 times, most recently from 61314ca to e0e2db2 Compare April 14, 2026 12:38
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