Reference
Reference pages for macros, constraints, account wrappers, feature flags, CLI, and alpha limits.
Lookup pages for macros, constraints, account wrappers, feature flags, CLI commands, and known alpha caveats.
Macros and attributes
#[program], #[derive(Accounts)], #[account], #[event], #[error_code], and related attributes.
Account constraints
Built-in constraints and namespaced constraint lifecycle.
Account types
A quick wrapper lookup table.
Feature flags
Default and opt-in anchor-lang-v2 features.
CLI
CLI commands and flags.
Alpha limitations
Known alpha caveats before depending on this version.
Macros and derives
Reference for #[program], #[derive(Accounts)], #[account], #[event], and related attributes.
Anchor programs use attributes and derives to declare instruction handlers, account validation, account layouts, events, errors, and constants. This page collects the core macros and the code they generate.
#[program]#
The #[program] attribute marks a module as the program entry point. The macro generates a discriminator-keyed dispatcher that matches incoming instruction data against the handler’s auto-derived 8-byte discriminator:
#[program]pub mod counter { use super::*;
pub fn init(ctx: &mut Context<Init>) -> Result<()> { /* ... */ } pub fn increment(ctx: &mut Context<Increment>) -> Result<()> { /* ... */ }}Each handler takes &mut Context<Accs> and any extra arguments declared after ctx. The dispatcher decodes the extras through wincode using a borsh-compatible wire format.
The default instruction discriminator is sha256("global:" + name)[..8], where name is the handler’s identifier. CU-sensitive programs can override per handler with #[discrim = N] for a compact 1-byte discriminator. The override is all-or-nothing. If any handler in the module uses #[discrim = N], every other handler must also declare a unique byte value:
#[program]pub mod vault { use super::*;
#[discrim = 0] pub fn deposit(ctx: &mut Context<Deposit>, amount: u64) -> Result<()> { /* ... */ }
#[discrim = 1] pub fn withdraw(ctx: &mut Context<Withdraw>, amount: u64) -> Result<()> { /* ... */ }}#[derive(Accounts)]#
#[derive(Accounts)] declares an instruction’s account list and generates the validation code that runs before the handler. For each struct it expands to:
- A
TryAccountsimpl that loads each declared account from the pre-walked view slice, applies constraints, and returns the validated struct - A
Bumpsimpl whose associated type carries oneu8per PDA seed group (orOption<u8>for optional accounts) - A
Resolvedcompanion struct: a subset of fields the caller must supply when building an instruction from a client or test. The rest auto-derive (PDAs) or auto-fill (system / token programs) - A
to_account_metas()method for client-side instruction building
#[derive(Accounts)]pub struct Create { #[account(mut)] pub creator: Signer, #[account(init, payer = creator, seeds = [b"multisig", creator.address().as_ref()])] pub config: Account<MultisigConfig>, pub system_program: Program<System>,}Constraints#
The derive handles built-in clauses directly. Namespaced extension clauses such as token::mint and third-party my_ns::key route through AccountConstraint, which exposes the four lifecycle hooks init, check, update, and exit. The most common built-in clauses are:
Macro-time PDA bump precomputation#
When all seed values in seeds = [...] are byte literals, the derive runs find_program_address at compile time and bakes the canonical bump in as a const. The runtime PDA search is skipped. See Performance and optimizations.
Topological seed resolution#
When seeds reference other fields in the same struct, the macro sorts derivations topologically. The example below derives vault from config.address(), which is itself a PDA derived from creator:
#[derive(Accounts)]pub struct ExecuteTransfer { pub creator: UncheckedAccount, #[account(seeds = [b"multisig", creator.address().as_ref()], bump = config.bump)] pub config: Account<MultisigConfig>, #[account(mut, seeds = [b"vault", config.address().as_ref()], bump)] pub vault: UncheckedAccount,}#[account]#
The #[account] attribute marks a struct as an account type. It has two modes. The default form is zero-copy, and the opt-in form uses borsh.
Default zero-copy form#
In the default form, the macro generates:
- A compile-time assertion that every field is
bytemuck::Pod - A
#[repr(C)]layout with#[derive(Clone, Copy)] - A
Discriminatorimpl withsha256("account:" + name)[..8] - An
Ownerimpl returningprogram_id(program-owned) - A no-padding assertion (struct size must equal the sum of field sizes)
- An IDL type entry under
idl-build
The struct must be #[repr(C)] (added by the macro) and every field must itself be Pod. Pairing it with Account<T> then yields a fully zero-copy load.
Borsh form#
For accounts whose fields are variable-length (Vec, String, enums with payload variants), use #[account(borsh)] and pair the struct with BorshAccount<T>. The macro auto-derives BorshSerialize, BorshDeserialize, and Default so the struct can round-trip through borsh:
#[account(borsh)]pub struct Profile { pub owner: Address, pub name: String, pub friends: Vec<Address>,}Borsh accounts deserialize on load and serialize on exit, so reach for the borsh form only when variable-length fields are genuinely needed. Fixed-size structs should stay in the default zero-copy form.
#[event] and #[event(bytemuck)]#
Program events have two encoding modes. The right choice depends on whether the event has any variable-length fields.
The default mode encodes through wincode with a borsh-wire-compatible config: u8 enum tags, u32 little-endian length prefixes, and no alignment checks. It still supports Vec, String, Option, and enums, while running roughly 3 to 10 times cheaper than borsh on SBF:
#[event]pub struct OrderFilled { pub maker: Address, pub taker: Address, pub price: u64, pub size: u64, pub note: String,}
emit!(OrderFilled { maker: *ctx.accounts.maker.address(), taker: *ctx.accounts.taker.address(), price: 1_000, size: 1, note: String::from("partial fill"),});#[event(bytemuck)] swaps the encoder for a zero-copy repr(C) memcpy and is the right choice when every event field is fixed-size. With this mode, emission collapses to one discriminator write plus a memcpy of the body, with no per-field encoding step:
#[event(bytemuck)]#[repr(C)]pub struct PriceUpdate { pub price: PodU64, pub timestamp: PodU64,}#[derive(InitSpace)]#
#[derive(InitSpace)] adds a Space implementation whose INIT_SPACE constant equals the struct’s borsh-serialized size. Variable-length fields are not derivable on their own, so annotate them with #[max_len(N)] (or #[max_len(N, M)] for nested vectors) to give the macro an explicit upper bound:
#[account(borsh)]#[derive(InitSpace)]pub struct UserProfile { pub owner: Address, #[max_len(50)] pub name: String, #[max_len(10)] pub friends: Vec<Address>,}UserProfile::INIT_SPACE then drops into the space constraint:
#[account(init, payer = signer, space = 8 + UserProfile::INIT_SPACE)]pub profile: BorshAccount<UserProfile>,#[constant]#
The #[constant] attribute exposes a const value to the IDL and generated clients. It is useful for documenting program-level constants, like seed strings or maximum sizes, that off-chain code has to reproduce verbatim:
#[constant]pub const MAX_SIGNERS: usize = 10;#[error_code]#
The #[error_code] attribute declares a custom error enum. Each variant maps to an Error::Custom(discriminant + offset) where offset defaults to 6000. The variant names and #[msg(...)] strings land in the generated IDL so clients can decode the codes back into named variants:
#[error_code]pub enum MultisigError { #[msg("Threshold must be between 1 and the number of signers")] InvalidThreshold, #[msg("Too many signers; max is MAX_SIGNERS")] TooManySigners, #[msg("A required signer did not sign")] MissingRequiredSignature,}Override the starting offset with #[error_code(offset = N)] when a program needs a non-default error-code base.
Remark (No runtime AnchorError)
The runtime does not allocate an AnchorError struct. The #[msg(...)] text is IDL-only metadata, so emitting an error costs only the Custom(u32) write.
#[access_control]#
The #[access_control] attribute wraps a handler with a guard call that runs before the handler body. The guard returns Result<()>, and a failure short-circuits the instruction without ever entering the handler. The pattern is most useful for invariants that depend on instruction arguments, since #[derive(Accounts)] constraints fire before args are unpacked:
#[program]pub mod admin_only { use super::*;
#[access_control(Reset::is_admin(&ctx))] pub fn reset(ctx: &mut Context<Reset>) -> Result<()> { ctx.accounts.counter.value = 0; Ok(()) }}
impl Reset { pub fn is_admin(ctx: &Context<Reset>) -> Result<()> { require_keys_eq!(ctx.accounts.signer.address(), &ADMIN_PUBKEY); Ok(()) }}#[pod_wrapper]#
Generates a transparent u8-backed wrapper that validates discriminants on equality and conversion. See Pod types.
Account constraints
Built-in and namespaced account constraints.
Account constraints are declared on fields inside a #[derive(Accounts)] struct. They run before the handler and cover account-metadata checks, PDA derivation, account creation and resizing, account closing, and extension-specific checks.
Prefer address = parent.field when a sibling account must match an address stored in another account.
Namespaced constraints#
Namespaced constraints are implemented by marker types that satisfy AccountConstraint<A>:
#[account(token::mint = mint, token::authority = owner)]pub vault: Account<TokenAccount>,The derive routes each marker through lifecycle hooks:
The anchor-spl-v2 crate uses this extension point for token::* and mint::* constraints.
Custom constraints#
constraint runs an arbitrary boolean expression and accepts either the equals form or a parenthesized form. Both spellings can appear multiple times in one #[account(...)], and the checks fire in source order:
#[account( mut, constraint = pool.is_open, constraint(amount > 0 @ MyError::AmountTooSmall), constraint(pool.cap >= pool.total + amount),)]pub pool: Account<Pool>,A bare failure raises ErrorCode::ConstraintRaw. Append @ err to use a custom error. constraint(a, b) is a parse error rather than two checks: chain checks by writing separate entries.
address expressions#
The right-hand side of address = expr is converted through Into<Address>, so wrappers, byte arrays, and references all flow through the same constraint:
#[account(address = pool.authority)]pub authority: UncheckedAccount,
#[account(address = MY_AUTHORITY_BYTES)]pub admin: UncheckedAccount,Use this when an account address comes from a sibling account’s field, a constant, or a domain-specific newtype that implements Into<Address>.
Account types
Quick reference for Anchor account wrappers.
Use Program account types for examples and guidance.
Feature flags
Reference for the alloc, guardrails, account-resize, const-rent, compat, and event-cpi feature flags on anchor-lang-v2.
anchor-lang-v2 ships seven feature flags, three of which are on by default and the other four opt-in. Most programs never need to touch them, but tuning the set is useful for trimming binary size and CU cost in production builds.
alloc (default)#
The alloc feature enables extern crate alloc and re-exports the alloc crate under anchor_lang_v2::__alloc. Generated macro code reaches Vec, String, and other heap-allocated types through that re-export, so user crates do not have to import alloc themselves.
Note (When to disable)
Only embedded targets without a heap have a reason to turn alloc off. The ergonomic cost is significant. Most realistic user code that touches Vec or String will not compile without it.
guardrails (default)#
The guardrails feature enables a small set of runtime checks that catch caller bugs the borrow checker cannot see statically:
check_program_idon dispatch rejects misrouted instructionsis_writableenforcement in data-accountload_mutpaths returns an error when the account is not markedmut- executable checks on
Program<T>reject non-program accounts before CPI
Disabling the feature drops those guardrail-only emits, which saves roughly 300 bytes of binary and 1 to 2 CU per account:
[dependencies]anchor-lang-v2 = { git = "...", branch = "anchor-next", default-features = false, features = ["alloc", "account-resize"] }Warning (Don't disable lightly)
The runtime safety net is what catches accidental misuse during development. Only turn guardrails off in production after the program has been thoroughly tested.
account-resize (default)#
The account-resize feature enables realloc_account and adds a pinocchio entrypoint hook. The hook writes data_len into RuntimeAccount.padding for every non-duplicate account, which is what lets AccountView::resize() enforce the MAX_PERMITTED_DATA_INCREASE cap.
Disabling the feature saves roughly 300 bytes of binary and 2 CU per account, but only when the program never calls realloc_account in the first place.
Danger (Don't disable while call sites exist)
If realloc_account could run without the resize hook, it would corrupt the stored original_data_len. The function is feature-gated so this fails at compile time instead. Disable account-resize only after removing every realloc path.
const-rent (off)#
The const-rent feature folds rent_exempt_lamports to a compile-time constant by combining pinocchio’s DEFAULT_LAMPORTS_PER_BYTE and ACCOUNT_STORAGE_OVERHEAD. With the constant in place, create_account skips the Rent::get() sysvar call entirely. The savings come out to roughly 90 CU per create_account CPI.
Warning (Rent-formula drift)
If Solana changes the rent formula in the future (for example, through a SIMD like SIMD-0194), programs built with const-rent will compute stale values until they are rebuilt. The feature is off by default so programs pick up runtime formula changes transparently.
Tip (When to enable)
Reach for const-rent when 90 CU per create_account matters more than rent-formula drift safety, and when the program is rebuilt and redeployed often enough that refreshing the constant is part of the normal release cycle.
compat (off)#
The compat feature exposes a small set of v1-shaped helpers for programs in the middle of porting. The most useful is debug!(msg), a logging macro shaped like msg! that accepts any Rust format string by routing through alloc::format!. That covers {:?}, {:x}, and dynamic width specifiers, none of which native msg! supports.
The remaining helpers re-expose v1 names so existing call sites compile without rewrites:
Warning (debug! cost)
debug! is meaningfully more expensive than the native msg! because of the heap allocation and fmt-trait dispatch. The feature is off by default to keep production binaries from accidentally shipping the heavier path.
event-cpi (off)#
The event-cpi feature wires the self-CPI event emit pattern through Anchor’s derives. When enabled, it adds the #[event_cpi] accounts attribute and the emit_cpi! macro.
The #[program] dispatcher then reserves an 8-byte tag at the top of instruction data. Indexers that cannot read transaction logs can recover emitted events from instruction history through that tag.
Note (Why off by default)
The dispatcher would pay the 8-byte tag check on every instruction. Programs that do not use event CPIs avoid that cost by keeping event-cpi off.
See Events for the call shape.
testing (off)#
The testing feature exposes the anchor_lang_v2::testing module, which provides stack-backed mock pinocchio types (such as AccountView and the SBF input buffer) that the lang-v2/tests/ integration tests and Miri witnesses depend on. Enabling testing also turns on const-rent so host-side tests can exercise rent-dependent helpers without needing SVM sysvar support:
cargo test -p anchor-lang-v2 --features testingNote (Why off by default)
Production BPF binaries should never ship the test scaffold, so testing stays opt-in.
Recipe: minimal binary, prod profile#
[dependencies]anchor-lang-v2 = { git = "...", branch = "anchor-next", default-features = false, features = ["alloc"] }This profile drops both guardrails and account-resize for the smallest possible binary.
Danger (Verify before shipping)
Only adopt this profile once the program has been thoroughly tested and you have verified that no instruction calls realloc_account. The guardrails net catches caller bugs the borrow checker cannot see statically. Ship without it only after fuzzing and review have closed those holes.
Recipe: maximum CU savings#
[dependencies]anchor-lang-v2 = { git = "...", branch = "anchor-next", default-features = false, features = ["alloc", "const-rent"] }This profile adds rent-formula constant folding on top of the minimal recipe. Rebuild and redeploy whenever Solana changes the rent formula so the constants stay in sync.
Anchor.toml
Reference for the fields available in an Anchor workspace's Anchor.toml file.
Anchor.toml is the workspace configuration file used by the Anchor CLI. The CLI discovers it by walking up from the current directory, and reads it for program paths, the active provider, scripts, test infrastructure, and toolchain settings.
The scaffold from anchor init is the best starting point:
[toolchain]package_manager = "yarn"
[features]resolution = trueskip-lint = false
[programs.localnet]counter = "..."
[provider]cluster = "localnet"wallet = "~/.config/solana/id.json"
[scripts]test = "cargo test"
skip_local_validator = true[provider]#
The provider sets the default cluster and wallet for CLI commands:
[provider]cluster = "localnet"wallet = "~/.config/solana/id.json"cluster can be a named cluster such as localnet, devnet, testnet, or mainnet, or a custom URL. wallet is the keypair used for commands that need a signer.
You can override either value on a command with global flags:
anchor build --provider.cluster devnet --provider.wallet ~/.config/solana/devnet.json[programs.<cluster>]#
Program sections map program names to declared program IDs for a cluster:
[programs.localnet]counter = "Counter111111111111111111111111111111111111"
[programs.devnet]counter = "DevCounter11111111111111111111111111111111"The program name should match the Rust crate name after Anchor normalizes it to snake case. Commands such as anchor keys list, anchor keys sync, anchor build, anchor program deploy, and anchor test use this map to connect source crates, keypair files, IDLs, and deployed program addresses.
The CLI also accepts object-form program entries when a command needs a custom deployment path or IDL path:
[programs.localnet]counter = { address = "Counter111111111111111111111111111111111111", path = "target/deploy/counter.so", idl = "target/idl/counter.json" }[scripts]#
Scripts are named shell commands. Run a script with anchor run <script-name>:
[scripts]test = "cargo test"lint = "cargo clippy --workspace --all-targets"anchor test runs the test script after the build and deployment setup. The generated LiteSVM workspace uses cargo test because the tests run in process rather than through an RPC validator.
skip_local_validator#
Set skip_local_validator = true when the test harness does not need a local RPC validator:
skip_local_validator = trueThe LiteSVM scaffold sets this because the test VM runs inside the Rust process. Use the same setting for other in-process Rust harnesses that do not need an RPC validator. If the flag is absent and the provider cluster is localnet, anchor test can start a local validator before running the test script.
[features]#
Workspace features configure CLI behavior:
[features]resolution = trueskip-lint = false[workspace]#
The workspace section controls which Rust program crates the CLI builds and where generated TypeScript IDL types are copied:
[workspace]members = ["programs/*"]exclude = ["programs/experimental"]types = "app/src/idl"[toolchain]#
Toolchain settings choose versions and JavaScript package tooling:
[toolchain]anchor_version = "1.0.1"solana_version = "3.1.10"package_manager = "pnpm"package_manager accepts npm, yarn, pnpm, or bun. When the field is omitted, Anchor resolves an available package manager from the local environment.
anchor_version is used by workflows that delegate version management to AVM. Git-installed alpha builds are usually run directly from the checkout or from the installed anchor binary on PATH.
[hooks]#
Hooks run shell commands before or after major CLI stages:
[hooks]pre-build = "cargo fmt --check"post-build = "cargo clippy --workspace --all-targets"pre-test = ["cargo test --workspace --no-run", "cargo nextest list"]post-test = "echo done"Supported hook names are pre-build, post-build, pre-test, post-test, pre-deploy, and post-deploy. Snake-case aliases such as pre_build also parse. A hook can be a single command string or a list of command strings. Nonzero exit codes stop the CLI command.
[test]#
The test section configures validator-backed test runs:
[test]startup_wait = 10000shutdown_wait = 2000upgradeable = true[[test.genesis]] preloads programs at genesis:
[[test.genesis]]address = "Dex111111111111111111111111111111111111111"program = "fixtures/dex.so"upgradeable = true[test.validator]#
[test.validator] forwards settings to solana-test-validator for validator-backed tests:
[test.validator]url = "https://api.mainnet-beta.solana.com"warp_slot = 100slots_per_epoch = "32"rpc_port = 8899ledger = "test-ledger"limit_ledger_size = "10000"Common nested entries include cloned accounts, account JSON files, account directories, and deactivated features:
[[test.validator.clone]]address = "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"
[[test.validator.account]]address = "Ev8WSPQsGb4wfjybqff5eZNcS3n6HaMsBkMk9suAiuM"filename = "fixtures/account.json"
[[test.validator.account_dir]]directory = "fixtures/accounts"
[test.validator]deactivate_feature = [ "Feat111111111111111111111111111111111111111",]Use solana-test-validator --help for the full set of validator flags. The fields in Anchor.toml intentionally mirror those options where practical.
[surfpool]#
Surfpool is the default local network backend for supported CLI flows. Configure it with [surfpool]:
[surfpool]startup_wait = 5000shutdown_wait = 2000rpc_port = 8899ws_port = 8900host = "127.0.0.1"online = truedatasource_rpc_url = "https://api.mainnet-beta.solana.com"Additional Surfpool fields include airdrop_addresses, manifest_file_path, runbooks, slot_time, log_level, and block_production_mode.
Test.toml#
Large workspaces can put suite-specific test config in Test.toml files. The CLI discovers them under the workspace and merges them with optional base files:
extends = ["../Test.base.toml"]
[scripts]test = "cargo test -p token-tests"
[test]startup_wait = 15000Test.toml supports the same test and scripts shapes used by Anchor.toml. Relative paths inside test validator config are resolved from the Test.toml file that declares them.
Anchor CLI
CLI reference for project scaffolding, builds, tests, profiling, debugger, coverage, IDL, and workspace commands.
The Anchor CLI is the workspace toolchain. It scaffolds projects, builds programs, runs tests, generates IDLs, deploys, and drives the trace tooling.
This alpha is installed from git:
cargo install --git https://github.com/otter-sec/anchor.git --branch anchor-next anchor-cli --locked --force Warning (Overwrites anchor on PATH)
cargo install overwrites the anchor binary on your PATH, including one
managed by AVM. To keep another Anchor install available, run this binary from a source checkout.
Run anchor --help or anchor <command> --help for the exact flags supported by your checkout.
Common commands#
anchor init#
anchor init counter --no-installInitializes a new workspace. The default program template is the modular Rust template, and the default test template is LiteSVM.
The generated workspace includes:
- programs/Solana program crates
- migrations/Deploy scripts
- tests/Workspace-level tests when selected
- Anchor.toml Anchor workspace config
- Cargo.toml Rust workspace config
- package.json JavaScript dependencies when installed
Useful flags:
The LiteSVM template sets skip_local_validator = true in Anchor.toml and adds this feature to the program crate:
[features]profile = ["anchor-v2-testing/profile"]That feature is what powers anchor test --profile, anchor debugger, and anchor coverage.
anchor new#
anchor new escrowCreates a new program under programs/ in an existing workspace. It uses the modular template by default:
anchor new escrow --template multipleanchor new prototype --template singleanchor new does not create the workspace-level test harness. Use anchor init when starting a new project from scratch.
anchor build#
anchor buildBuilds the workspace programs through cargo build-sbf, writes deploy artifacts under target/deploy/, and emits IDLs under target/idl/.
Useful flags:
Forward arguments to cargo build-sbf after --:
anchor build -- --features my-featureanchor test#
anchor testBuilds the workspace, deploys programs when needed, and runs the configured test script.
In LiteSVM workspaces, the scaffolded skip_local_validator = true setting means anchor test runs Rust tests without starting a validator. If the setting is absent and the configured cluster is localnet, the CLI can still start a local validator unless --skip-local-validator is passed.
Useful flags:
Profiling#
anchor test --profileRecords per-test SBF register traces and renders flamegraph SVGs under target/anchor-v2-profile/. This requires Rust tests that call anchor_v2_testing::svm() and a crate feature that forwards to anchor-v2-testing/profile.
anchor debugger#
anchor debuggeranchor debugger initializeanchor debugger --skip-runRuns the same trace-producing path as anchor test --profile, then opens a TUI over captured instructions. The optional positional argument filters captured traces to tests whose name contains that substring.
Useful flags:
The debugger only works with trace-producing Rust tests. If no traces are found, check that the tests call anchor_v2_testing::svm() and that the program crate has profile = ["anchor-v2-testing/profile"].
anchor coverage#
anchor coverageBuilds programs with DWARF info, runs LiteSVM tests with register tracing, maps executed program counters back to Rust source lines, and writes LCOV.
Useful flags:
anchor coverage --output target/coverage/sbf.lcovanchor coverage --skip-run --trace-dir target/coverage/tracesanchor idl#
The idl subcommand builds local IDL files and manages on-chain IDL metadata accounts.
anchor idl buildBuilds the IDL for the current program. Use -o to write JSON to a file and -t to write the generated TypeScript IDL type:
anchor idl build --program-name counter -o target/idl/counter.json -t target/types/counter.tsanchor idl init -f target/idl/counter.json <program-id>Creates an on-chain IDL account for the given program and writes the IDL file into it.
anchor idl fetch -o counter.json <program-id>Fetches an on-chain IDL.
anchor idl upgrade -f target/idl/counter.json <program-id>Upgrades an existing on-chain IDL metadata account.
Other useful IDL commands:
Use on-chain IDLs carefully while this version is alpha because IDL shapes may change between commits.
anchor codama#
The codama subcommand converts an Anchor IDL to a Codama IDL tree and renders client libraries through the @codama/cli toolchain. It is the path for generating cross-language clients from a single program description.
Convert#
anchor codama convert target/idl/counter.json -o target/codama-counter.jsonconvert translates an Anchor IDL JSON file into a Codama IDL JSON tree rooted at a rootNode. The conversion runs in-process and mirrors the reference TypeScript implementation in @codama/nodes-from-anchor, so the output is stable against the JavaScript toolchain.
Generate#
anchor codama generate -l js,rust -p clients target/idl/counter.jsongenerate runs the same conversion in-process, then hands the result to @codama/cli (run via npx --yes codama by default) to render clients in one or more languages. Each language is written under <path>/<language>/ and uses the matching @codama/renderers-* package:
Flags#
Note (Operational notes)
generate shells out to npx, so the host needs a Node.js toolchain on PATH. The Codama IDL is versioned independently from the Anchor IDL, so rendered clients depend on the @codama/nodes version stamped into rootNode.version.
anchor account#
anchor account <program-name>.<AccountTypeName> <account-pubkey>anchor account <program-name>.<AccountTypeName> <account-pubkey> --idl <path/to/idl.json>Fetches an account and deserializes it to JSON using an IDL type. Not every account wrapper is a borsh account with an Anchor discriminator. SPL accounts and Pod-backed zero-copy accounts have different layouts.
Workspace utilities#
anchor keys#
anchor keys listanchor keys syncLists program keypairs and syncs each program’s declare_id!() value with the keypair file.
anchor cluster#
anchor cluster listLists configured cluster endpoints.
anchor run#
anchor run <script-name>Runs a script declared in the workspace’s Anchor.toml.
anchor shell#
anchor shellStarts a Node.js shell with an Anchor client configured from the local workspace. This workflow is most useful for IDL inspection and client experiments while TypeScript package support is alpha.
anchor clean#
anchor cleanRemoves generated artifacts except program keypairs.
anchor verify#
anchor verify <program-id>Verifies that on-chain bytecode matches a locally compiled artifact. Use anchor build --verifiable for deterministic builds.
Examples and benchmarks
Checked-in example programs, benchmark families, and current size and compute numbers.
Examples are checked into the repository rather than published as a separate examples package. The most important worked programs are under bench/programs/.
These examples do three things:
- They show complete Anchor programs in realistic shapes.
- They provide repeatable benchmark fixtures for binary size and compute units.
- They act as source material for docs, since they compile with the current branch.
If you are learning the framework, read helloworld first, then vault, then multisig. That path moves from one instruction, to PDA-owned state, to a larger program with multiple handlers and shared validation.
Benchmark programs#
The benchmark harness is the anchor-bench crate under bench/. It builds each program, loads the resulting .so into LiteSVM, runs the relevant instruction cases, records binary sizes, and measures compute units from the executed transaction metadata.
Current headline numbers#
The current overview numbers are directional, not a stability guarantee. This version is alpha and exact values can move as codegen, pinocchio, and tooling change.
Binary and CU range are from the current Anchor implementation in each family. Bin down and CU down compare against the older paired Anchor implementation used by the benchmark harness.
What each family compares#
The harness groups programs into families so the same instruction shape can be compared across implementations.
The non-Anchor variants help calibrate the benchmark against lower-level frameworks. The Anchor rows are the rows used for the headline documentation table.
Running benchmarks#
From the repository root, run a specific benchmark integration test:
cargo test -p anchor-bench helloworld_end_to_end -- --nocaptureOther benchmark tests are:
cargo test -p anchor-bench vault_end_to_end -- --nocapturecargo test -p anchor-bench nested_end_to_end -- --nocapturecargo test -p anchor-bench multisig_end_to_end -- --nocapturecargo test -p anchor-bench prop_amm_end_to_end -- --nocaptureSet BENCH_SKIP_BUILD=1 in your shell to reuse existing .so artifacts:
BENCH_SKIP_BUILD=1 cargo test -p anchor-bench helloworld_end_to_end -- --nocaptureRun the full benchmark test suite when updating all headline numbers:
cargo test -p anchor-bench --tests -- --nocaptureReading benchmark output#
Each test asserts that every expected suite produced a nonzero binary size and a sane compute-unit measurement. With --nocapture, the tests also print comparison tables sorted by compute units.
The output table includes:
How comparisons stay fair#
The paired programs are written to keep the business logic and addresses comparable:
- Anchor variants share program IDs within each benchmark family.
- PDA seeds are kept equivalent so bump differences do not skew results.
- The harness measures the same instruction cases for each variant.
- LiteSVM executes the transactions and reports compute units.
- Binary size comes from the built deploy artifact.
- The benchmark payer keypair is derived deterministically from a fixed label.
The suite table in bench/src/lib.rs is the source of truth for program IDs, manifest paths, instruction names, and case builders.
Reading the examples#
Use the examples as source material for real Anchor code:
For SPL token examples, also read tests-v2/programs/spl/src/lib.rs. That test program exercises anchor-spl-v2 init constraints, CPI helpers, interface accounts, and Token-2022 extension readers.
Updating the docs#
When updating benchmark numbers, rerun the relevant anchor-bench tests and update both Anchor overview and this page together. The overview table is the summary. This page is the reference that explains where to find the examples, how the measurements are produced, and how to reproduce them.
Keep benchmark prose modest. The numbers are strong, but this version is still alpha, not audited, and not published on crates.io.
Alpha limitations
Known caveats and incomplete surfaces while v2 is alpha.
This version is alpha. It is not audited, not on crates.io, and APIs may break between commits.
Keep these limitations in mind while depending on the anchor-next branch:
- Rust crates are consumed from git on
anchor-next. - no stable v2 TypeScript package has been published yet. Current scaffolds remain pinned to
@anchor-lang/core^1.0.0. anchor-spl-v2covers core token, mint, interface account, and several Token-2022 extension paths. Less-common SPL surfaces, especially metadata-oriented constraints, may not be covered yet.- generated
program::cpi::*wrappers are available for supported account structs, but account structs containingOption<_>orNested<_>are currently skipped by CPI wrapper generation. - runtime PDA validation supports both array-form seeds and opaque seed expressions. Client-side derivation and rich IDL seed metadata work best for array-form seeds that the macro can inspect.
Do not treat the safety defaults as an audit. Review, fuzz, and test before depending on this version for production funds.