
Solana Anchor
- 4 installs
- 4 repo stars
- Updated February 25, 2026
- hairyf/blockchain-master
Build Solana programs with the Anchor framework - program structure, account validation, CPI, PDAs, IDL generation, and TypeScript/Rust clients.
About
Anchor is a Solana program framework using Rust macros for structure, account validation, and IDL generation, with clients and a build/test/deploy CLI. A developer uses it to write, review, and integrate Anchor programs.
- declare_id, #[program], #[derive(Accounts)], Context, discriminators
- CPI, PDAs, realloc, close accounts, and IDL-driven clients
Solana Anchor by the numbers
- 4 all-time installs (skills.sh)
- Ranked #347 of 479 Web3 & Blockchain skills by installs in the Skillselion catalog
- Data as of Jul 13, 2026 (Skillselion catalog sync)
npx skills add https://github.com/hairyf/blockchain-master --skill solana-anchorAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 4 |
|---|---|
| repo stars | ★ 4 |
| Last updated | February 25, 2026 |
| Repository | hairyf/blockchain-master ↗ |
What it does
Build Solana programs with the Anchor framework - program structure, account validation, CPI, PDAs, IDL generation, and TypeScript/Rust clients.
Files
Skill based on Anchor (Solana program framework), generated from sources/solana-anchor/docs/ at 2026-02-25.Anchor is a Solana program framework: Rust eDSL with macros (declare_id, #[program], #[derive(Accounts)], #[account]), IDL generation, TypeScript/Rust clients, and CLI for build, test, and deploy. Use this skill when implementing or reviewing Anchor programs, CPIs, account validation, and client integration.
Core References
| Topic | Description | Reference |
|---|---|---|
| Program Structure | declare_id, #[program], #[derive(Accounts)], #[account], Context, discriminators | core-program-structure |
| CPI | Cross-program invocation, CpiContext, PDA signers, invoke/invoke_signed | core-cpi |
| IDL | Interface Description Language, instructions/accounts/discriminators, client use | core-idl |
| PDA | Program Derived Addresses, seeds, bump, seeds::program, init, IDL resolution | core-pda |
| Workspace | init, new, program layout, build/test/deploy flow | core-workspace |
| Realloc | Resize accounts, realloc::payer, realloc::zero | core-realloc |
| Close Account | close = target, rent reclamation | core-close-account |
| Remaining Accounts | ctx.remaining_accounts, variadic instructions, CPI | core-remaining-accounts |
References (Program & Config)
| Topic | Description | Reference |
|---|---|---|
| Account Types | Account, Signer, Program, AccountLoader, UncheckedAccount, etc. | references-account-types |
| Account Constraints | init, mut, seeds/bump, has_one, close, realloc, SPL, #[instruction] | references-account-constraints |
| Anchor.toml | provider, scripts, workspace, programs, test, toolchain, hooks | references-anchor-toml |
| CLI | build, deploy, test, idl, keys, migrate, upgrade, verify | references-cli |
| Space | Account size calculation, InitSpace, type sizes | references-space |
| Type Conversion | Rust ↔ TypeScript type mapping for IDL/client | references-type-conversion |
Features
| Topic | Description | Reference |
|---|---|---|
| Events | emit!, emit_cpi!, addEventListener, decoding | features-events |
| Errors | #[error_code], err!, require! and variants | features-errors |
| Zero-Copy | AccountLoader, load_init/load_mut/load, init vs zero | features-zero-copy |
| declare_program! | IDL-based CPI and Rust client generation | features-declare-program |
| Tokens (SPL) | anchor-spl, mints, token accounts, ATAs, Token 2022, InterfaceAccount | features-tokens |
| Token 2022 Extensions | ExtensionType, tlv_data, extension lifecycle, anchor-spl token_2022_extensions | features-token-extensions |
| Example Programs | Curated program-examples repo—Basics, Tokens, Token 2022—when to use each | features-examples |
| Testing | Mollusk (Rust instruction harness), LiteSVM (Rust/TS/Python VM) | features-testing |
| Upgrade and Migrate | anchor upgrade, migrate script, upgrade authority | features-upgrade-migrate |
Clients
| Topic | Description | Reference |
|---|---|---|
| TypeScript | Program, methods, accounts, signers, rpc/transaction/instruction, fetch | clients-typescript |
| Rust | anchor-client, declare_program!, request/instructions/send, account fetch | clients-rust |
Best Practices
| Topic | Description | Reference |
|---|---|---|
| Security | Sealevel attacks, constraints, UncheckedAccount usage | best-practices-security |
| Constraints and Validation | When to use which constraints, avoid UncheckedAccount pitfalls | best-practices-constraints |
Advanced
| Topic | Description | Reference |
|---|---|---|
| Verifiable Builds | anchor build --verifiable, verify, Docker | advanced-verifiable-builds |
| AVM | Anchor Version Manager, install, use, list | advanced-avm |
Generation Info
- Source:
sources/solana-anchor - Git SHA:
894eb8d6b42e6ca1e3e82b6d2d6308f372739bcc - Generated: 2026-02-25
Anchor Version Manager (AVM)
Use AVM to install and switch between multiple anchor CLI versions (e.g. for verifiable builds or different project requirements).
Commands
- `avm install <VERSION_OR_COMMIT>` – Install a version. Use semver (e.g.
0.32.1),latest, or a commit hash (full or short). - `avm list` – List installed versions.
- `avm use <version>` – Set active version (e.g.
avm use 0.32.1oravm use latest). - `avm uninstall <version>` – Remove an installed version.
Examples
avm install 0.32.1
avm install latest
avm install 0.30.1-cfe82aa682138f7c6c58bf7a78f48f7d63e9e466
avm use 0.32.1Anchor.toml can pin a version via [toolchain] anchor_version when using AVM in CI or team workflows.
<!-- Source references:
- https://github.com/solana-foundation/anchor (docs/content/docs/references/avm.mdx)
-->
Verifiable Builds
Local Solana builds can differ between machines. For verifiable builds, build inside a pinned Docker image so the binary is reproducible.
Build
From the program directory (e.g. programs/my_program/):
anchor build --verifiableUses a Docker image with pinned dependencies (and Cargo.lock). Produces a deterministic .so for the current program.
Verify
After deploying, verify that on-chain bytecode matches your local build:
anchor verify -p <lib-name> <program-id><lib-name> is the library name in the program’s Cargo.toml. If the program has an on-chain IDL, the command also checks that it matches the local IDL.
Docker image
Images are published as solanafoundation/anchor:<version>, e.g.:
docker pull solanafoundation/anchor:v0.32.1If a verifiable build is interrupted, a container may be left running; remove it with:
docker rm -f anchor-program<!-- Source references:
- https://github.com/solana-foundation/anchor (docs/content/docs/references/verifiable-builds.mdx)
-->
Constraints and Validation
Prefer account types (Account, Signer, Program) and constraints over manual checks so Anchor enforces invariants before the handler runs.
Practices
- Use init for new accounts; init_if_needed only when needed (feature-gated) and with the same care as init.
- Use seeds and bump for PDAs so derivation is consistent and bump is validated.
- Use has_one and address to tie accounts to expected keys; avoid accepting arbitrary account keys without constraint.
- Use UncheckedAccount only when necessary; add a CHECK comment and validate owner, key, and data in code or via constraint.
- Close only to an intended target (e.g. signer); do not close to user-supplied accounts without validation.
Key points
- Constraints run before the handler; fail fast with clear errors. Custom errors: use constraint = expr @ MyError.
- Order constraints so dependencies (e.g. payer, space) are available where needed.
Security
Anchor’s account types and constraints reduce common Solana/Sealevel mistakes, but mainnet code should explicitly understand each constraint and the risks of bypassing them.
Reference: Sealevel attacks
The coral-xyz/sealevel-attacks repo documents common attack patterns with three variants each:
- insecure – Flawed code that may be exploitable.
- secure – Fixed version.
- recommended – Idiomatic Anchor fix.
Use these to validate that your program does not rely on insecure patterns (e.g. missing ownership checks, signer checks, or PDA validation).
Practices
- Prefer account types (
Account<T>,Signer,Program) and constraints (init,mut,seeds/bump,has_one,address,owner) over manual checks so the framework enforces invariants. - Use `UncheckedAccount` only when necessary; add a
// CHECK:comment and validate owner, key, and data in code or withconstraint. - Ensure PDA derivation (seeds + program) is strict and that no user input can change the program ID or seed set used for sensitive PDAs.
- For CPI: validate all accounts and data passed to other programs; use
with_signercorrectly for PDA signers. - Run and extend tests and consider fuzzing or audit for high-value programs.
<!-- Source references:
- https://github.com/solana-foundation/anchor (docs/content/docs/references/security-exploits.mdx)
- https://github.com/coral-xyz/sealevel-attacks
-->
Rust Client
The anchor-client crate is the Rust client for Anchor programs. Use declare_program! with the program's IDL to generate typed modules for instructions and accounts; then use Client and the generated program to build and send transactions and fetch accounts.
Setup
- Place the program IDL in an
idls/folder (e.g.idls/example.json).declare_program!(program_name)looks up the IDL there. - Dependencies:
anchor-client(withasyncfeature for async APIs),anchor-lang(fordeclare_program!).
[dependencies]
anchor-client = { version = "0.32.1", features = ["async"] }
anchor-lang = "0.32.1"Creating the client
use anchor_client::{
solana_client::rpc_client::RpcClient,
solana_sdk::commitment_config::CommitmentConfig,
Client, Cluster,
};
use std::rc::Rc;
let connection = RpcClient::new_with_commitment(
"http://127.0.0.1:8899",
CommitmentConfig::confirmed(),
);
let payer = Keypair::new();
let provider = Client::new_with_options(
Cluster::Localnet,
Rc::new(payer),
CommitmentConfig::confirmed(),
);
declare_program!(example);
let program = provider.program(example::ID)?;Cluster can be Localnet, Devnet, Mainnet, or a custom URL.
Building and sending instructions
Use program.request() to build a request, then set accounts and args from the generated accounts::* and args::* types. Call .instructions()? to get instruction(s), or chain .instruction(ix) and then .signer(&keypair).send().await? to send.
use example::{accounts, args};
let init_ix = program
.request()
.accounts(accounts::Initialize {
counter: counter.pubkey(),
payer: program.payer(),
system_program: system_program::ID,
})
.args(args::Initialize)
.instructions()?
.remove(0);
let inc_ix = program
.request()
.accounts(accounts::Increment { counter: counter.pubkey() })
.args(args::Increment)
.instructions()?
.remove(0);
let sig = program
.request()
.instruction(init_ix)
.instruction(inc_ix)
.signer(&counter)
.send()
.await?;Add multiple signers with .signer(&kp) per signer. The provider's payer is used for fee and as signer unless overridden.
Fetching accounts
Use program.account::<AccountType>(address).await? to deserialize an account by type (discriminator and layout must match the IDL).
let counter_account: Counter = program.account::<Counter>(counter.pubkey()).await?;Account types (e.g. Counter) come from the declare_program!-generated module.
Key points
- IDL must live under
idls/<name>.jsonfordeclare_program!(name). program.request()is the builder;.accounts(),.args(), then.instructions()or.instruction(ix)and.signer()/.send().- Use generated
accounts::*andargs::*for type-safe account and argument wiring. - For async: use
Client::new_with_optionsand.send().await; ensuretokioruntime (e.g.#[tokio::main]).
<!-- Source references:
- https://github.com/solana-foundation/anchor/tree/master/docs/content/docs/clients/rust.mdx
-->
TypeScript Client
Use @anchor-lang/core with the program’s IDL to build and send transactions and fetch accounts. Compatible with legacy @solana/web3.js v1, not v2.
Setup
Create a Program from the IDL and a provider (connection + optional wallet):
import { Program, AnchorProvider, setProvider } from "@anchor-lang/core";
import idl from "./idl.json";
import type { MyProgram } from "./idlType";
const provider = new AnchorProvider(connection, wallet, {});
setProvider(provider);
const program = new Program(idl as MyProgram, { connection });In Anchor tests: anchor.setProvider(anchor.AnchorProvider.env()); then const program = anchor.workspace.MyProgram as Program<MyProgram>;.
Invoking instructions
Use the methods builder: instruction name (camelCase), then accounts, then signers, then send/build:
- `.rpc()` – Build, sign (with provider wallet + any
.signers([])), and send. Returns transaction signature. - `.transaction()` – Build
Transaction; you sign and send it yourself. - `.instruction()` – Build
TransactionInstruction; you add to a transaction and send.
await program.methods
.initialize(new BN(42))
.accounts({ newAccount: keypair.publicKey, signer: wallet.publicKey })
.signers([keypair])
.rpc();Accounts that are PDAs or have fixed addresses (e.g. system program) can be omitted if the IDL allows resolution. Pass only additional signers in .signers() when using .rpc().
Fetching accounts
- `program.account.<accountName>.fetch(address)` – Single account.
- `program.account.<accountName>.fetchMultiple([addresses])` – Multiple.
- `program.account.<accountName>.all([filters])` – All accounts; optional
memcmpfilter (offset + bytes; first 8 bytes are discriminator).
Events
Use program.addEventListener("eventName", callback) and program.removeEventListener(listenerId) for events emitted with emit!. For emit_cpi!, fetch the transaction and decode the event from the inner instruction data.
<!-- Source references:
- https://github.com/solana-foundation/anchor (docs/content/docs/clients/typescript.mdx)
-->
Closing Accounts
Use #[account(close = target)] to close an account: lamports go to target, account is removed.
Syntax
close = target - Account that receives the closed account lamports (often signer/payer).
Key points
- Only close to an account you intend (e.g. signer). Avoid arbitrary user-passed targets.
- Combine with has_one = authority so only authorized users can close.
Cross-Program Invocation (CPI)
CPI = one program calling another. In Anchor you typically build a CpiContext and call a helper (e.g. transfer) or use invoke / invoke_signed with raw instructions.
Basic CPI (e.g. System Program transfer)
1. Include the target program in your Accounts struct (e.g. system_program: Program<'info, System>). 2. Build accounts for the callee (e.g. from, to). 3. Create CpiContext::new(program_id, accounts) and call the helper:
use anchor_lang::system_program::{transfer, Transfer};
pub fn sol_transfer(ctx: Context<SolTransfer>, amount: u64) -> Result<()> {
let cpi_ctx = CpiContext::new(
ctx.accounts.system_program.to_account_info(),
Transfer {
from: ctx.accounts.sender.to_account_info(),
to: ctx.accounts.recipient.to_account_info(),
},
);
transfer(cpi_ctx, amount)?;
Ok(())
}CPI with PDA signer
When the “signer” is a PDA, use the same seeds/bump as in the accounts struct and attach them to the CPI context:
let bump = ctx.bumps.pda_account;
let signer_seeds: &[&[&[u8]]] = &[&[b"pda", recipient.key().as_ref(), &[bump]]];
let cpi_ctx = CpiContext::new(program_id, Transfer { from, to })
.with_signer(signer_seeds);
transfer(cpi_ctx, amount)?;Get the bump from ctx.bumps.<account_name>. The accounts struct must use seeds and bump so the PDA is validated and the bump is available.
Lower-level: invoke and invoke_signed
- `invoke(&instruction, &[account_infos...])` – when no program signer is needed.
- `invoke_signed(&instruction, &[account_infos...], signer_seeds)` – when a PDA must sign.
Build the instruction (e.g. via system_instruction::transfer or manual Instruction { program_id, accounts, data }), then pass the right AccountInfo slice and, for PDAs, the same seeds used to derive the PDA.
Key points
- Always pass the correct account metas (writable/signer) and order expected by the callee.
- For PDA signers,
signer_seedsmust match the PDA derivation (seeds + bump). - Use
ctx.bumpsso you don’t recalculate the bump in the handler.
<!-- Source references:
- https://github.com/solana-foundation/anchor (docs/content/docs/basics/cpi.mdx)
-->
Program IDL
The IDL is a JSON description of the program (instructions, accounts, types). Generated at target/idl/<program-name>.json by anchor build. Used to generate clients and to resolve accounts (e.g. PDAs) on the client.
Structure
- address – Program ID.
- metadata – name, version, spec, description.
- instructions – For each instruction:
name,discriminator(8 bytes),accounts(name, writable, signer, optional address),args(name, type). - accounts – Account type names and their
discriminator. - types – Struct/enum definitions for account and instruction data.
Discriminators
- Instruction: first 8 bytes of
sha256("global:<instruction_name>"). Sent as first 8 bytes of instruction data; the client adds this automatically. - Account: first 8 bytes of
sha256("account:<AccountName>"). Stored as first 8 bytes of account data; used when (de)serializing and validating.
Same account name in different programs yields the same discriminator; ownership is still checked by the program.
Client usage
- Instructions: client builds instruction data as discriminator + serialized args; account list and order come from the IDL.
- Accounts:
program.account.<accountName>.fetch(address)and similar use the IDL types and discriminators to deserialize. - PDA resolution: If the IDL has
pda.seeds(const + account refs), the client can resolve the PDA address without manually deriving it.
<!-- Source references:
- https://github.com/solana-foundation/anchor (docs/content/docs/basics/idl.mdx)
-->
Program Derived Addresses (PDA)
PDAs are deterministic addresses derived from seeds and a program ID (off the Ed25519 curve). In Anchor you declare them with account constraints; the runtime validates the address and stores the bump on the accounts struct.
Constraints
- `seeds = [...]` – Array of byte slices: literals (e.g.
b"vault") and/or references (e.g.user.key().as_ref()). Use[]for no extra seeds. - `bump` – Valid bump for the PDA. Use plain
bumpto have Anchor find it, orbump = account.bump_seedwhen the bump is stored on an account to save CUs. - `seeds::program = other_program.key()` – Derive the PDA from another program’s ID (for cross-program PDAs).
seeds and bump are used together.
Init with PDA
Create an account whose address is a PDA with init, seeds, bump, plus payer and space:
#[account(
init,
payer = signer,
space = 8 + 8,
seeds = [b"counter", signer.key().as_ref()],
bump,
)]
pub counter: Account<'info, Counter>,Remember 8 bytes for the account discriminator in space.
Bump in handler
Use ctx.bumps.<account_name> for CPIs that require this PDA as signer:
let bump = ctx.bumps.pda_account;
let signer_seeds: &[&[&[u8]]] = &[&[b"pda", recipient.key().as_ref(), &[bump]]];PDA in the IDL
PDA seeds are reflected in the IDL (pda.seeds with kind: "const" or kind: "account"). The TypeScript client can then resolve the PDA from the IDL and provider (e.g. wallet) without manually calling findProgramAddressSync.
<!-- Source references:
- https://github.com/solana-foundation/anchor (docs/content/docs/basics/pda.mdx)
-->
Program Structure
Anchor uses Rust macros to reduce boilerplate and enforce common security checks. Key building blocks:
- `declare_id!` – Program’s on-chain address (program ID). Default comes from
target/deploy/<program>.json; runanchor keys syncto sync after cloning. - `#[program]` – Module containing instruction handlers. Each public function = one instruction.
- `#[derive(Accounts)]` – Struct listing and validating accounts for an instruction.
- `#[account]` – Custom account data struct (owner set to program, 8-byte discriminator, (de)serialization).
Instruction context
Handlers take Context<T> as first argument; T is the accounts struct.
pub fn initialize(ctx: Context<Initialize>, data: u64) -> Result<()> {
ctx.accounts.new_account.data = data;
Ok(())
}Context fields:
ctx.accounts– Validated accounts from theAccountsstructctx.program_id– Current program’s pubkeyctx.remaining_accounts– Extra accounts not in the structctx.bumps– PDA bump seeds from validation
Additional handler parameters are instruction arguments.
Account validation
Two mechanisms:
1. Account constraints – #[account(...)] on each field (e.g. init, mut, seeds, bump). 2. Account types – Account<T>, Signer, Program, SystemAccount, etc., enforce type and checks.
Validation runs before the handler; then use ctx.accounts safely.
Account discriminator
- First 8 bytes of account data = discriminator (first 8 bytes of
sha256("account:<AccountName>")). - Allocate 8 + data size in
space(e.g.space = 8 + 8for au64). - Used for init and for deserialization/validation.
<!-- Source references:
- https://github.com/solana-foundation/anchor (docs/content/docs/basics/program-structure.mdx)
-->
Account Reallocation
Use #[account(realloc = <size>, ...)] to resize an existing account at the start of an instruction.
Syntax
realloc = size - New total account size in bytes. realloc::payer - Account that pays for extra rent when extending or receives refund when shrinking. realloc::zero = true - Zero new bytes when extending; false leaves them uninitialized.
Key points
- Realloc runs at instruction start. Compute new size like space for init (e.g. 8 + data).
- Use realloc::zero = true for safety when extending.
Remaining Accounts
ctx.remaining_accounts is a slice of AccountInfo for accounts not listed in the Accounts struct. Use for variadic instructions (e.g. multiple mints or token accounts) or when passing extra accounts into a CPI.
Usage
- Iterate and validate: Check owner, key, or data for each account before use.
- Pass to CPI: CpiContext can include remaining_accounts (ToAccountMetas); build the account metas and invoke.
- Security: Do not trust remaining_accounts without validation; treat like user input.
Key points
- Validation runs only on the Accounts struct; remaining_accounts are unchecked.
- Use for optional or variable-length account sets; keep fixed accounts in the struct.
Workspace and Project Layout
Anchor workspaces are created with anchor init and extended with anchor new. The default layout supports modular programs and a standard build/test/deploy flow.
Creating a workspace
- `anchor init <name>` – Create a new workspace with default (modular) or single-file program. Uses
--template multiple(default) or--template single. - `anchor new <program-name>` – Add another program under
programs/in an existing workspace.
Program ID is derived from target/deploy/<program>-keypair.json; declare_id! in source is set from this keypair. Use anchor keys sync after cloning to refresh declare_id! from keypairs.
Default program structure (modular)
For anchor init <name> with default template, each program under programs/<name>/ typically has:
| Path | Purpose |
|---|---|
src/lib.rs | Entry: declare_id!, #[program] module, re-exports |
src/instructions/ | Instruction handlers and #[derive(Accounts)] structs |
src/state/ | #[account] state structs |
src/error.rs | #[error_code] custom errors |
src/constants.rs | Program constants |
#[program] methods usually delegate to handlers in instructions/. Single-file template (--template single) puts everything in lib.rs.
Tests
- TypeScript (default):
tests/*.ts;Anchor.toml[scripts] testruns them (e.g. ts-mocha). Useanchor.setProvider(anchor.AnchorProvider.env())andanchor.workspace.<ProgramName>. - Rust:
anchor init --test-template rust→ tests intests/src/usinganchor-client. - Mollusk:
anchor init --test-template mollusk→ Rust tests using Mollusk harness.
Build, test, deploy
- `anchor build` – Compile programs, emit IDL to
target/idl/. Binaries attarget/deploy/<program>.so. - `anchor test` – Build, start local validator (unless
--skip-local-validator), deploy, run[scripts] test. Logs under.anchor/program-logs/. - `anchor deploy` – Deploy all workspace programs to the cluster in
Anchor.toml[provider] cluster. - `anchor migrate` – Run
migrations/deploy.jswith provider from config.
Switch cluster (e.g. Devnet) by setting [provider] cluster in Anchor.toml; program IDs per cluster are in [programs.<cluster>].
Key points
- Modular layout:
lib.rs+instructions/+state/+error.rs; single-file template available. - Program ID comes from keypair; sync with
anchor keys syncwhen keypairs change or after clone. anchor testruns the script in[scripts] test(default: TypeScript); use--test-template rustormolluskfor alternative test setup.
<!-- Source references:
- https://github.com/solana-foundation/anchor/tree/master/docs/content/docs/quickstart/local.mdx
-->
declare_program!
The declare_program!(name) macro generates Rust modules from an IDL file so you can call another Anchor program without depending on its crate. The IDL must live at idls/<name>.json (e.g. idls/example.json for declare_program!(example)).
Generated modules
- program – Program ID and program type (e.g.
Example). - cpi – Helper functions to perform CPIs (e.g.
cpi::initialize(cpi_ctx),cpi::increment(cpi_ctx)). - accounts – Account structs for CPI (e.g.
Initialize,Increment) and state types (e.g.Counter). - client – For off-chain:
accounts::Initialize,args::Initialize, etc., to build instructions. - account – Account data types.
- constants, events, types, errors – As in the IDL.
On-chain CPI
Place the target program’s IDL in idls/<name>.json. Then:
declare_program!(example);
use example::{
accounts::Counter,
cpi::{ self, accounts::{Increment, Initialize} },
program::Example,
};
// In instruction: build CpiContext with accounts from the generated structs, then:
cpi::initialize(cpi_ctx)?;
cpi::increment(cpi_ctx)?;Use Account<'info, Counter> and Program<'info, Example> in your Accounts structs so types match the callee.
Off-chain client (Rust)
Use the same idls/<name>.json and:
declare_program!(example);
use example::{ accounts::Counter, client::accounts, client::args };
// program = provider.program(example::ID)?;
let init_ix = program.request()
.accounts(accounts::Initialize { counter, payer, system_program })
.args(args::Initialize)
.instructions()?.remove(0);
// Add to transaction and send; then fetch with program.account::<Counter>(pubkey).await?;Build instructions with program.request().accounts(...).args(...).instructions()?, then send with your client/signer.
<!-- Source references:
- https://github.com/solana-foundation/anchor (docs/content/docs/features/declare-program.mdx)
-->
Custom Errors
Instruction handlers return Result<T>; errors use Anchor’s Error type (wraps AnchorError and ProgramError). Custom variants are defined with #[error_code] and returned via err! or require!.
Defining errors
#[error_code]
pub enum MyError {
#[msg("Amount must be >= 10")]
AmountTooSmall,
#[msg("Amount must be <= 100")]
AmountTooLarge,
}Anchor assigns codes starting at 6000 and generates the wiring. Use #[msg("...")] for the message returned to the client.
Returning errors
- `err!(MyError::AmountTooLarge)` – Return this error.
- `require!(condition, MyError::Variant)` – If
conditionis false, return the error.
Other helpers: require_eq!, require_neq!, require_gt!, require_gte!, require_keys_eq!, require_keys_neq! (use require_keys_* for Pubkey comparison).
Client
On failure, the TS client receives an error object with e.g. errorCode.code, errorCode.number, errorMessage, origin, logs. Use these for debugging and user-facing messages.
<!-- Source references:
- https://github.com/solana-foundation/anchor (docs/content/docs/features/errors.mdx)
-->
Events
Anchor provides two ways to emit events:
1. `emit!(Event { ... })` – Writes to program logs (base64-encoded). Simple; logs may be truncated by some RPC providers. 2. `emit_cpi!(Event { ... })` – Emits via a self-CPI; event data is in the inner instruction. Avoids log truncation but costs more CUs. Requires event-cpi feature and #[event_cpi] on the instruction’s Accounts struct.
emit! (program logs)
#[event]
pub struct CustomEvent {
pub message: String,
}
pub fn emit_event(_ctx: Context<EmitEvent>, input: String) -> Result<()> {
emit!(CustomEvent { message: input });
Ok(())
}Client: subscribe with program.addEventListener("customEvent", (event) => { ... }), then program.removeEventListener(listenerId). Ensure the RPC does not truncate logs.
emit_cpi!
- In
Cargo.toml:anchor-lang = { version = "0.32", features = ["event-cpi"] }. - Add
#[event_cpi]to the Accounts struct for the instruction that callsemit_cpi!. - Client: fetch the transaction and decode the event from the inner CPI instruction data (first 8 bytes = discriminator, then event data). No direct subscription.
<!-- Source references:
- https://github.com/solana-foundation/anchor (docs/content/docs/features/events.mdx)
-->
Example Programs
Use the solana-developers/program-examples repository to find reference implementations for common Anchor patterns. Each example includes runnable code; prefer the branch/tag that matches your Anchor version.
When to use this reference
- Implementing a feature (CPI, PDA, realloc, close, tokens) and need a minimal working example.
- Validating account constraints or instruction flow against a known-good program.
- Onboarding: mapping docs/concepts to concrete code (e.g. "PDA rent payer" →
pda-rent-payer).
Basics
| Example | Use case |
|---|---|
| checking-accounts | Account validation with Anchor |
| close-account | Closing accounts and sending lamports to a target |
| counter | Simple stateful counter program |
| create-account | Creating accounts with init |
| cross-program-invocation | CPI with Anchor (CpiContext, invoke) |
| favorites | Storing per-user data (e.g. favorites) |
| hello-solana | Minimal "Hello, Solana!" instruction |
| pda-rent-payer | Using a PDA to pay for account creation |
| processing-instructions | Instruction dispatch and parsing |
| program-derived-addresses | PDA derivation, seeds, bump |
| realloc | Resizing account data with realloc |
| rent | Rent calculation for accounts |
| transfer-sol | Native SOL transfer |
Tokens (SPL)
| Example | Use case |
|---|---|
| create-token | Creating an SPL token (mint + config) |
| escrow | Escrow flow with tokens |
| nft-minter | Minting NFTs |
| nft-operations | NFT operations |
| pda-mint-authority | PDA as mint authority |
| spl-token-minter | SPL token minting |
| token-fundraiser | Token fundraiser flow |
| token-swap | Token swap |
| transfer-tokens | Transferring SPL tokens |
Token 2022 / Extensions
| Example | Use case |
|---|---|
| basics | Token 2022 basics with Anchor |
| cpi-guard | CPI guard extension |
| default-account-state | Default account state |
| group | Token groups |
| immutable-owner | Immutable owner |
| interest-bearing | Interest-bearing tokens |
| memo-transfer | Memo transfer |
| metadata | Token metadata |
| mint-close-authority | Mint close authority |
| multiple-extensions | Multiple extensions on one mint/account |
| nft-meta-data-pointer | NFT metadata pointer |
| non-transferable | Non-transferable tokens |
| permanent-delegate | Permanent delegate |
| transfer-fee | Transfer fees |
| transfer-hook | Transfer hook |
Key points
- Clone or link to the repo and open the specific example folder; each is self-contained.
- Align Anchor and Solana CLI versions with the example's dependencies where possible.
- For security-sensitive logic, cross-check with best-practices-security and the sealevel-attacks repo.
<!-- Source references:
- https://github.com/solana-foundation/anchor (docs/content/docs/references/examples.mdx)
- https://github.com/solana-developers/program-examples
-->
Testing
Anchor docs describe two testing approaches for Solana programs: Mollusk (Rust-only, lightweight instruction-level harness) and LiteSVM (in-process VM, multi-language). Use them to test instructions or full transactions without a full validator.
Mollusk
Mollusk is a minimal test harness that runs program instructions in a minified SVM. It does not use AccountsDB or a full validator: you supply the instruction and account set. Good for fast, deterministic instruction tests.
API:
process_instruction(&instruction, &accounts)– Execute one instruction, return result.process_and_validate_instruction(&instruction, &accounts, &checks)– Execute and run checks; panic on failure.process_instruction_chain/process_and_validate_instruction_chain– Run a sequence of instructions.
Example:
use mollusk_svm::Mollusk;
use solana_account::Account;
use solana_sdk::{instruction::{AccountMeta, Instruction}, pubkey::Pubkey};
let program_id = Pubkey::new_unique();
let instruction = Instruction::new_with_bytes(program_id, &[], vec![
AccountMeta::new(key1, false),
AccountMeta::new_readonly(key2, false),
]);
let accounts = vec![(key1, Account::default()), (key2, Account::default())];
let mollusk = Mollusk::new(&program_id, "path/to/program.so");
let result = mollusk.process_instruction(&instruction, &accounts);
// or: mollusk.process_and_validate_instruction(&instruction, &accounts, &checks);You can configure compute budget, feature set, and sysvars on the harness. No accounts are loaded from a chain; all account data is provided explicitly.
LiteSVM
LiteSVM is an in-process Solana VM for tests. Faster than solana-test-validator. Available in Rust, TypeScript/JavaScript, and Python (via solders).
Rust: cargo add litesvm --dev. Create LiteSVM::new(), airdrop with svm.airdrop(&pubkey, lamports), build and send transactions with svm.send_transaction(tx), inspect state with svm.get_account(&pubkey).
TypeScript: npm i litesvm -D. new LiteSVM(), svm.airdrop(publicKey, lamports), then send transactions and query accounts similarly.
Python: uv add solders; use litesvm from the solders package.
Use LiteSVM when you need validator-like behavior (multiple instructions, persistence between transactions, or tests in TS/Python). Use Mollusk when you only need to run a single instruction with explicit accounts and want minimal setup.
Key points
- Mollusk: instruction + account list only; no chain state; Rust; good for unit-style instruction tests.
- LiteSVM: in-process VM; multi-language; airdrop, send_transaction, get_account; good for integration-style tests.
- For Anchor tests, TypeScript tests with
anchor testtypically use the Node validator or a configurable test validator; Mollusk/LiteSVM are alternatives for Rust or lighter-weight runs.
<!-- Source references:
- https://github.com/solana-foundation/anchor/tree/master/docs/content/docs/testing/index.mdx
- https://github.com/solana-foundation/anchor/tree/master/docs/content/docs/testing/mollusk.mdx
- https://github.com/solana-foundation/anchor/tree/master/docs/content/docs/testing/litesvm.mdx
-->
Token 2022 Extensions
The Token Extensions Program (Token 2022) adds optional extensions to mints and token accounts. Extensions are enabled at creation time; most cannot be added later. Use anchor-spl's token_2022 and token_2022_extensions for instructions and types.
Extension lifecycle
- Mint / account creation: Most extensions are set when the mint or token account is created. Plan which extensions you need up front.
- Add after creation (exceptions):
cpi-guard,memo-transfer,token-group,token-member,token-metadatacan be added to an existing account. - Incompatibilities: Some extensions cannot be combined (e.g. NonTransferable and TransferFeeConfig). Check the Token 2022 program and docs when combining extensions.
Extension types (overview)
Extensions are represented by the ExtensionType enum in the Token 2022 program. Common ones:
| Category | Examples |
|---|---|
| Fees / transfer | TransferFeeConfig, TransferFeeAmount, ConfidentialTransferFeeConfig/Amount |
| Authority | MintCloseAuthority, PermanentDelegate, CpiGuard |
| State / behavior | DefaultAccountState, ImmutableOwner, NonTransferable, MemoTransfer |
| Confidential | ConfidentialTransferMint, ConfidentialTransferAccount, ConfidentialMintBurn |
| Metadata / groups | MetadataPointer, TokenMetadata, GroupPointer, TokenGroup, GroupMemberPointer, TokenGroupMember |
| Other | InterestBearingConfig, TransferHook / TransferHookAccount, Pausable / PausableAccount, ScaledUiAmount |
Mint extensions apply to mints; account extensions apply to token-holding accounts. Use the correct variant for the account type.
State layout: base + TLV
Extension state lives in tlv_data after the base mint or account data. The layout is PodStateWithExtensions<'data, S>: base (e.g. Mint or Account) plus a slice of TLV bytes that are deserialized per extension type. When reading/writing extension data, use the Token 2022 / anchor-spl helpers that understand this layout.
Using in Anchor
1. Dependencies: anchor-spl with token_2022 and token_2022_extensions (see features-tokens). 2. Instructions: Use anchor_spl::token_2022 for base Token 2022 instructions and anchor_spl::token_2022_extensions for extension-specific instructions (when implemented). 3. Gap: Not every extension instruction is fully implemented in anchor-spl; for some extensions you may need to build CPI calls to the Token 2022 program manually using the program's instruction formats. 4. Examples: See solana-developers/program-examples for Token 2022 + Anchor. Also features-examples.
Key points
- Enable extensions at mint/account creation; only a few can be added later.
- Check compatibility between extensions (e.g. non-transferable vs transfer fee).
- Prefer
token_interfaceand InterfaceAccount when supporting both Token and Token 2022. - For missing anchor-spl coverage, implement CPI to the Token 2022 program using its native instruction data.
<!-- Source references:
- https://github.com/solana-foundation/anchor (docs/content/docs/tokens/extensions.mdx)
- https://github.com/solana-program/token-2022
- https://github.com/coral-xyz/anchor (spl/src/token_2022_extensions)
-->
Token Integration (SPL)
Use the anchor-spl crate to interact with Solana's Token Program and Token Extension Program (Token 2022) from Anchor programs. Add anchor-spl and enable anchor-spl/idl-build in your program's Cargo.toml.
Setup
[features]
idl-build = ["anchor-lang/idl-build", "anchor-spl/idl-build"]
[dependencies]
anchor-lang = "0.32.1"
anchor-spl = "0.32.1"Key modules
| Module | Use |
|---|---|
token | Legacy Token Program instructions and account types |
token_2022 | Token 2022 base instructions |
token_2022_extensions | Token 2022 extension instructions |
token_interface | Types that work with both Token and Token 2022 (Mint, TokenAccount, TokenInterface) |
associated_token | Associated Token Account instruction |
Prefer token_interface when you want one code path for both programs: InterfaceAccount<'info, Mint>, Interface<'info, TokenInterface>.
Create a mint
Use InterfaceAccount<'info, Mint> and Interface<'info, TokenInterface>. Constraint init plus mint constraints create the mint.
Keypair-based mint:
use anchor_spl::token_interface::{Mint, TokenInterface};
#[account(
init,
payer = signer,
mint::decimals = 6,
mint::authority = signer.key(),
mint::freeze_authority = signer.key(),
)]
pub mint: InterfaceAccount<'info, Mint>,
pub token_program: Interface<'info, TokenInterface>,PDA mint (deterministic address; same PDA can be mint::authority for CPI minting):
#[account(
init,
payer = signer,
mint::decimals = 6,
mint::authority = mint.key(),
mint::freeze_authority = mint.key(),
seeds = [b"mint"],
bump
)]
pub mint: InterfaceAccount<'info, Mint>,Constraints: payer, mint::decimals, mint::authority (required), mint::freeze_authority (optional). Use seeds and bump for PDA mints.
Create a token account
Token accounts hold a balance of one mint for one owner. Use InterfaceAccount<'info, TokenAccount> (from token_interface) for accounts that may be either Token or Token 2022.
Associated Token Account (ATA): Use anchor_spl::associated_token::Create or the Associated Token Program; address is PDA(owner, token_program, mint). Constraint init with associated_token::mint = ..., associated_token::authority = ... when creating ATAs from Anchor.
PDA token account: Use init, payer, token::mint, token::authority, and seeds/bump for the token account PDA.
Token 2022 extensions
Token 2022 adds extensions (e.g. TransferFeeConfig, NonTransferable, MemoTransfer). Most extensions are set at mint or account creation and cannot be added later. Enable extensions via the appropriate token_2022/token_2022_extensions instructions and account types when building mints or token accounts. Some extensions are mutually exclusive.
Key points
- Use
token_interfacefor programs that support both Token and Token 2022. - Mint authority and freeze authority are set at init; mint address is fixed.
- ATA address = PDA(owner, token_program, mint) via Associated Token Program.
- Pass the correct program id (
spl_token::IDorspl_token_2022::ID) in client and in account structs.
<!-- Source references:
- https://github.com/solana-foundation/anchor/tree/master/docs/content/docs/tokens
- https://github.com/solana-foundation/anchor/tree/master/docs/content/docs/tokens/basics/create-mint.mdx
- https://github.com/solana-foundation/anchor/tree/master/docs/content/docs/tokens/basics/create-token-account.mdx
- https://github.com/solana-foundation/anchor/tree/master/docs/content/docs/tokens/extensions.mdx
-->
Program Upgrade and Migrate
Upgrade
Use anchor upgrade <path/to/program.so> --program-id <program-id> to replace on-chain program code. The configured wallet must be the upgrade authority for the program.
- Build first: anchor build produces target/deploy/<program>.so.
- Same program ID: Upgrading keeps the program address; only the executable code changes.
Migrate
anchor migrate runs the script at migrations/deploy.js, with a provider built from Anchor.toml. Use it for one-off deployment or migration logic.
Key points
- Upgrade replaces code only; account data layout must remain compatible.
- Use anchor keys sync after deploy/upgrade if program IDs change.
Zero-Copy
Zero-copy lets programs use account data in place (no copy/deserialize into a heap struct). Use for large accounts (> ~1KB), order books, event queues, and compute-sensitive paths.
Setup
Add to Cargo.toml:
bytemuck = { version = "1.20", features = ["min_const_generics"] }
anchor-lang = "0.32"Define zero-copy account
#[account(zero_copy)]
pub struct Data {
pub data: [u8; 10232],
}Only fixed-size, Copy types (no Vec, String). Nested structs use #[zero_copy] (no account).
Use AccountLoader
- `AccountLoader<'info, T>` in the Accounts struct.
- `load_init()?` – First-time init; sets discriminator. Use with
initorzeroconstraint. - `load_mut()?` – Mutable access for updates (account must be
mut). - `load()?` – Read-only access.
Init constraints
- `init` – Create account via CPI (max 10240 bytes total, including 8-byte discriminator). Use
space = 8 + data_size,payer, and optionallyseeds/bumpfor PDA. - `zero` – Account must be uninitialized (discriminator zero). You create the account with SystemProgram elsewhere (e.g. client or another instruction), then call an instruction that uses
load_init()to set discriminator and data. Allows up to 10MB.
Pitfalls
- Always reserve 8 bytes for discriminator in
spaceor when creating the account. - Don’t use
Vec/Stringin zero-copy structs; use fixed arrays. - Validate array indices to avoid panics.
- For byte arrays that are logically Pubkeys, consider
#[accessor(Pubkey)]for safe get/set.
<!-- Source references:
- https://github.com/solana-foundation/anchor (docs/content/docs/features/zero-copy.mdx)
-->
Account Constraints
Use #[account(...)] on fields of a #[derive(Accounts)] struct to validate accounts.
Common constraints
| Constraint | Description |
|---|---|
| `signer` | Account must have signed the transaction. |
| `mut` | Account is writable; Anchor will persist changes. |
| `init` | Create account via system program. Requires payer and space. |
| `init_if_needed` | Like init but only if account doesn’t exist (feature-gated). |
| `seeds = [...], bump` | Account must be the PDA for those seeds; bump stores or validates bump. |
| `seeds::program = expr` | Use another program’s ID for PDA derivation. |
| `has_one = target` | Field on the account must equal target’s key (e.g. has_one = authority). |
| `address = expr` | Account key must equal expr. |
| `owner = expr` | Account owner must equal expr. |
| `executable` | Account is executable (program). |
| `constraint = expr` | Custom boolean; use @ CustomError for custom error. |
| `close = target` | Close account and send lamports to target. |
| `realloc = size` | Resize account; use realloc::payer and realloc::zero. |
| `dup` | Allow same account as another mutable account (use with care). |
| `zero` | Discriminator must be zero (uninitialized); used for large zero-copy init. |
SPL / Token
- `token::mint`, `token::authority` – Validate token account mint and authority.
- `mint::authority`, `mint::decimals` – Validate mint account.
- `associated_token::mint`, `associated_token::authority` – Create or validate ATA.
- *`::token_program = expr`** – Override token program (e.g. Token-2022).
Instruction args in constraints
Use #[instruction(...)] on the Accounts struct to use instruction arguments in constraints (same order as handler; can omit trailing args):
#[instruction(input: String)]
pub struct Initialize<'info> {
#[account(init, payer = signer, space = 8 + 4 + input.len())]
pub new_account: Account<'info, DataAccount>,
// ...
}Skipping an argument in the middle is not allowed.
<!-- Source references:
- https://github.com/solana-foundation/anchor (docs/content/docs/references/account-constraints.mdx)
- https://docs.rs/anchor-lang/latest/anchor_lang/derive.Accounts.html
-->
Account Types
Use these types as fields in #[derive(Accounts)] structs to validate and deserialize accounts.
| Type | Purpose |
|---|---|
| `Account<'info, T>` | Owned by program, deserializes as T. Use for #[account] data. |
| `Signer<'info>` | Validates account signed the transaction. |
| `Program<'info, T>` | Validates account is the program T (e.g. System, Token). |
| `SystemAccount<'info>` | Owned by system program (generic data account). |
| `AccountLoader<'info, T>` | Zero-copy load for #[account(zero_copy)] types. Use load(), load_mut(), load_init(). |
| `UncheckedAccount<'info>` | No checks; use with // CHECK when you validate manually. |
| `AccountInfo<'info>` | Prefer UncheckedAccount to make “no checks” explicit. |
| `Option<Account<'info, T>>` | Optional account. |
| `Box<Account<'info, T>>` | Same as Account but boxed to reduce stack size. |
| `Interface<'info, T>` | Account must be one of a set of program IDs (e.g. Token or Token-2022). |
| `InterfaceAccount<'info, T>` | Token-style account (e.g. Mint, TokenAccount) for either program. |
| `Sysvar<'info, T>` | Sysvar account (e.g. Rent, Clock). |
| `Migration<'info, From, To>` | Migrate account from one schema to another (e.g. with realloc). |
Snippets
// Typical program-owned account
pub my_data: Account<'info, MyData>,
// Signer
pub authority: Signer<'info>,
// System program for CPI
pub system_program: Program<'info, System>,
// Zero-copy large account
pub order_book: AccountLoader<'info, OrderBook>,
// Optional
pub optional_config: Option<Account<'info, Config>>,
// Unchecked (add // CHECK and validate in code)
/// CHECK: validated by has_one and constraint
pub other: UncheckedAccount<'info>,<!-- Source references:
- https://github.com/solana-foundation/anchor (docs/content/docs/references/account-types.mdx)
- https://docs.rs/anchor-lang/latest/anchor_lang/accounts/index.html
-->
Anchor.toml
Main workspace config at the repo root.
provider (required)
[provider]
cluster = "localnet" # or devnet, mainnet
wallet = "~/.config/solana/id.json"Used by deploy, test, and other CLI commands.
scripts (required for testing)
[scripts]
test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts"anchor test runs the test script.
features
[features]
resolution = true # IDL account resolution (default true)workspace
- types – Directory to copy generated IDL TypeScript types (e.g. for frontend).
- members – Paths to program crates (default
programs/*). - exclude – Paths to exclude from the workspace.
programs
Per-cluster program IDs:
[programs.localnet]
my_program = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"Use programs.devnet and programs.mainnet for other clusters. programs.localnet is used with solana-test-validator and --bpf-program.
test
- startup_wait – Ms to wait for the test validator (e.g. when cloning many accounts).
- genesis – Pre-load programs at validator start (
address,program,upgradeable). - upgradeable – Deploy test program with upgradeable loader; upgrade authority = provider wallet.
test.validator
Options passed to solana-test-validator: url, warp_slot, rpc_port, ledger, faucet_sol, etc.
- test.validator.clone – Clone accounts from
url(e.g. mainnet) into the test validator.addressper account; program accounts are cloned automatically when address is upgradeable loader. - test.validator.account – Load account from a JSON file (
address,filename).
toolchain
Override toolchain (e.g. for CI/verifiable builds):
[toolchain]
anchor_version = "0.32.1" # requires avm
solana_version = "2.3.0"
package_manager = "yarn" # npm, yarn, pnpm, bunhooks
Run commands at pipeline stages (pre/post build, test, deploy). Non-zero exit aborts.
[hooks]
pre-build = "echo foo"
post_build = "echo bar"
pre-test = ["echo 1", "echo 2"]<!-- Source references:
- https://github.com/solana-foundation/anchor (docs/content/docs/references/anchor-toml.mdx)
-->
Anchor CLI
Run anchor -h and anchor <subcommand> -h for full options.
Build and deploy
- `anchor build` – Build workspace programs and emit IDLs to
target/idl/. Use-- <cargo-args>to pass flags (e.g.--features my-feature). - `anchor build --verifiable` – Build in Docker for reproducible builds (run from program directory, e.g.
programs/my_program/). - `anchor deploy` – Deploy all workspace programs to the configured cluster (generates new program IDs each time unless already deployed).
- `anchor upgrade <path/to/program.so> --program-id <id>` – Upgrade a single program (wallet must be upgrade authority).
Keys
- `anchor keys list` – List program keypairs.
- `anchor keys sync` – Update
declare_id!in source fromtarget/deploy/<program>.json. Run after cloning or when IDs change.
IDL
- `anchor idl build` – Generate IDL from build.
- `anchor idl init -f target/idl/program.json <program-id>` – Create on-chain IDL account.
- `anchor idl fetch -o out.json <program-id>` – Fetch IDL from chain.
- `anchor idl upgrade <program-id> -f target/idl/program.json` – Update on-chain IDL (wallet = authority).
- `anchor idl set-authority -n <new-authority> -p <program-id>` – Change IDL authority.
- `anchor idl erase-authority -p <program-id>` – Make IDL immutable (wallet = current authority).
Test and migrate
- `anchor test` – Build, deploy to localnet (starts validator if needed), run
scripts.test. Use--skip-local-validatorto use an already-running validator. Logs stream to.anchor/program-logs/. - `anchor migrate` – Run
migrations/deploy.jswith provider from Anchor.toml.
Workspace and programs
- `anchor init <name>` – Create new workspace. Use
--template multiple(default) or--template single. - `anchor new <program-name>` – Add a new program under
programs/(same template options).
Utilities
- `anchor expand` – Expand macros (run in program dir for one program, or workspace root for all).
- `anchor shell` – Start Node REPL with Anchor client wired from config.
- `anchor account <program>.<AccountType> <pubkey>` – Fetch and deserialize account with IDL (use
--idlif outside workspace). - `anchor cluster list` – Print cluster endpoints.
- `anchor verify <program-id>` – Verify on-chain bytecode matches local build (run in program directory). Also checks IDL if present.
<!-- Source references:
- https://github.com/solana-foundation/anchor (docs/content/docs/references/cli.mdx)
-->
Account Space
For non–zero-copy accounts, the space constraint must include 8 bytes for the discriminator plus the size of the account data. Zero-copy uses C layout; this table applies to serialized (non–zero-copy) accounts.
Type sizes (bytes)
| Type | Size |
|---|---|
| bool | 1 |
| u8, i8 | 1 |
| u16, i16 | 2 |
| u32, i32 | 4 |
| u64, i64 | 8 |
| u128, i128 | 16 |
| Pubkey | 32 |
| [T; N] | size(T) * N |
| Vec<T> | 4 + size(T) * len |
| String | 4 + byte length |
| Option<T> | 1 + size(T) |
| Enum | 1 + size of largest variant |
| f32, f64 | 4, 8 (NaN fails serialize) |
Example
#[account]
pub struct MyData {
pub val: u16,
pub state: GameState,
pub players: Vec<Pubkey>, // e.g. max 10
}
impl MyData {
pub const MAX_SIZE: usize = 2 + (1 + 32) + (4 + 10 * 32);
}
#[derive(Accounts)]
pub struct InitializeMyData<'info> {
#[account(init, payer = signer, space = 8 + MyData::MAX_SIZE)]
pub acc: Account<'info, MyData>,
// ...
}InitSpace macro
Use #[derive(InitSpace)] to get a constant for the data part (still add 8 for discriminator in space):
#[account]
#[derive(InitSpace)]
pub struct ExampleAccount {
pub data: u64,
#[max_len(50)]
pub string_one: String,
#[max_len(10, 5)]
pub nested: Vec<Vec<u8>>,
}
// In Accounts:
#[account(init, payer = payer, space = 8 + ExampleAccount::INIT_SPACE)]
pub data: Account<'info, ExampleAccount>,max_len on Vec is element count, not bytes (e.g. Vec<u32> with #[max_len(10)] → 4 + 10*4 = 44 bytes).
<!-- Source references:
- https://github.com/solana-foundation/anchor (docs/content/docs/references/space.mdx)
-->
Type Conversion (Rust ↔ TypeScript)
When building or parsing instruction data and account data, the client uses these mappings.
Primitives
| Rust | TypeScript | Example |
|---|---|---|
| bool | boolean | true |
| u8..i32 | number | 99 |
| u64, u128, i64, i128 | BN (e.g. anchor.BN) | new anchor.BN(99) |
| f32, f64 | number | 1.0 |
| String | string | "hello" |
Collections
| Rust | TypeScript |
|---|---|
| [T; N] | T[] |
| Vec<T> | T[] |
| Option<T> | T \ |
Structs and enums
- Structs – Map to TS object types; field names and types match.
- Enums – Unit variant →
{ variant: {} }; named →{ variant: { field: value } }; tuple →{ variant: [a, b] }.
Use the generated types in target/types/<program>.ts for type-safe clients.
<!-- Source references:
- https://github.com/solana-foundation/anchor (docs/content/docs/references/type-conversion.mdx)
-->