
Anchor
- 4 installs
- 4 repo stars
- Updated February 25, 2026
- hairyf/blockchain-master
Build Solana programs with the Anchor framework: program structure, PDAs, CPI, IDL, custom errors, events, zero-copy, and account constraints.
About
A concise Anchor reference covering macros, accounts, PDAs, cross-program invocation, IDL, and the Anchor CLI. A developer uses it when writing or tooling Solana programs with Anchor v0.32.
- Program structure via declare_id, #[program], #[derive(Accounts)], and Context
- PDA seeds/bump, CPI signer patterns, zero-copy AccountLoader, and account constraints
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 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, PDAs, CPI, IDL, custom errors, events, zero-copy, and account constraints.
Files
Skill is based on Anchor v0.32.1, generated 2026-02-09.
Concise reference for building Solana programs with Anchor: macros, accounts, PDAs, CPI, IDL, custom errors, events, zero-copy, constraints, account types, and CLI.
Core References
| Topic | Description | Reference |
|---|---|---|
| Program Structure | declare_id, #[program], #[derive(Accounts)], #[account], Context | core-program-structure |
| PDA | seeds, bump, init, IDL/client resolution, PDA signer | core-pda |
| CPI | CpiContext, transfer, PDA signer, invoke/invoke_signed | core-cpi |
| IDL | Instructions, accounts, discriminators, client usage | core-idl |
Features
| Topic | Description | Reference |
|---|---|---|
| Custom Errors | error_code, err!, require! macros, client handling | features-errors |
| Events | emit!, emit_cpi!, #[event], addEventListener, event-cpi | features-events |
| Zero-Copy | AccountLoader, #[account(zero_copy)], load/load_mut/load_init | features-zero-copy |
References
| Topic | Description | Reference |
|---|---|---|
| Account Constraints | signer, mut, init, seeds/bump, has_one, address, owner | references-account-constraints |
| Account Types | Account, Signer, Program, AccountLoader, Interface types | references-account-types |
| CLI | build, deploy, test, idl, keys, account, expand, upgrade, verify | references-cli |
Generation Info
- Source:
sources/anchor - Git SHA:
2cb7ababa7dba3ac269fd2e60cfa06793ad2b989 - Generated: 2026-02-09
Anchor CPI (Cross Program Invocation)
CPI = one program calling another. Same mental model as an instruction: program ID, accounts, instruction data.
Anchor pattern (recommended)
1. Include the target program and its accounts in your #[derive(Accounts)] struct. 2. Build a CpiContext with program ID and accounts. 3. Call the Anchor helper (e.g. transfer) or the target program’s cpi module.
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(())
}
#[derive(Accounts)]
pub struct SolTransfer<'info> {
#[account(mut)]
sender: Signer<'info>,
#[account(mut)]
recipient: SystemAccount<'info>,
system_program: Program<'info, System>,
}PDA as signer
When the “from” (or any signer) is a PDA, derive signer seeds and pass 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)?;Low-level: invoke / invoke_signed
Equivalent without Anchor helpers:
- Build instruction (e.g.
system_instruction::transfer(...)). - Call
invoke(instruction, &[from, to, program])orinvoke_signed(instruction, accounts, signer_seeds)for PDA signer.
Manual construction: build Instruction { program_id, accounts: AccountMeta::new/readonly, data } and then invoke/invoke_signed.
<!-- Source references:
- docs/content/docs/basics/cpi.mdx
-->
Anchor IDL (Interface Description Language)
JSON description of the program: instructions, accounts, types. Generated by anchor build at target/idl/<program-name>.json. Used by clients to build and decode instructions and accounts.
IDL layout
- address — Program ID
- metadata — name, version, description
- instructions — name, discriminator (8 bytes), accounts (name, writable, signer, address, pda seeds), args
- accounts — name, discriminator
- types — struct definitions for account/instruction data
Discriminators
- Instruction: first 8 bytes of
sha256("global:<instruction_name>"). - Account: first 8 bytes of
sha256("account:<AccountName>").
Clients send the instruction discriminator as the first 8 bytes of instruction data; Anchor does this automatically. Same account discriminator is written when creating accounts and checked when deserializing.
Client usage (TypeScript)
- Call instruction:
program.methods.<camelCaseInstruction>(...args).accounts({...}).rpc()or.instruction(). - Fetch account:
program.account.<accountName>.fetch(pubkey). - PDA resolution: If the IDL defines
pda.seedsfor an account, the client can resolve the address from seeds (e.g. from other accounts in the same instruction).
<!-- Source references:
- docs/content/docs/basics/idl.mdx
-->
Anchor PDA (Program Derived Addresses)
PDAs are deterministic addresses derived from seeds and a program ID. Anchor validates them via account constraints.
Constraints
- `seeds` — Array of seeds (static bytes or account refs, e.g.
signer.key().as_ref()). Use[]for no optional seeds. - `bump` — Bump seed; use alone for auto-calculation, or
bump = account.bump_seedwhen stored on account (saves CUs). - `seeds::program` — Program ID for derivation; only when deriving a PDA for another program.
seeds and bump must be used together.
Examples
// Static seed only
#[account(seeds = [b"hello_world"], bump)]
pub pda_account: SystemAccount<'info>,
// Multiple seeds, one from signer
#[account(seeds = [b"hello_world", signer.key().as_ref()], bump)]
pub pda_account: SystemAccount<'info>,
// Stored bump (compute optimization)
#[account(seeds = [b"hello_world"], bump = pda_account.bump_seed)]
pub pda_account: Account<'info, CustomAccount>,
// PDA from another program
#[account(seeds = [b"hello_world"], bump, seeds::program = other_program.key())]
pub pda_account: SystemAccount<'info>,Init with PDA
Use init with seeds and bump to create an account at a PDA. Requires payer and space (include 8-byte discriminator).
#[account(
init,
payer = signer,
space = 8 + 1,
seeds = [b"hello_world", signer.key().as_ref()],
bump,
)]
pub pda_account: Account<'info, CustomAccount>,PDA Signer (CPI)
For CPIs where the PDA must sign, build signer seeds and pass to CpiContext::with_signer:
let bump_seed = ctx.bumps.pda_account;
let signer_seeds: &[&[&[u8]]] = &[&[b"pda", seed.as_ref(), &[bump_seed]]];
let cpi_context = CpiContext::new(program_id, accounts).with_signer(signer_seeds);IDL and Client
PDA seeds in #[account(seeds = ...)] are reflected in the IDL. The Anchor TS client can resolve PDA addresses from the IDL (e.g. using provider wallet as signer for account refs), so you often don't need to derive PDAs manually when calling program.methods.instruction().accounts({...}).rpc().
<!-- Source references:
- docs/content/docs/basics/pda.mdx
-->
Anchor Program Structure
Anchor uses Rust macros to reduce boilerplate and enforce common security checks for Solana programs.
Key Macros
- `declare_id!` — Program on-chain address (program ID). Sync with keypair:
anchor keys sync. - `#[program]` — Module containing instruction handlers. Each public function = one instruction.
- `#[derive(Accounts)]` — Struct listing accounts required by an instruction; implements validation and (de)serialization.
- `#[account]` — Custom account data struct: owner set to program, 8-byte discriminator, auto (de)serialization.
Instruction Context
Handlers receive Context<T> as first parameter. T is the Accounts struct.
pub fn initialize(ctx: Context<Initialize>, data: u64) -> Result<()> {
ctx.accounts.new_account.data = data;
Ok(())
}ctx.accounts— Validated accountsctx.program_id— Program pubkeyctx.remaining_accounts— Extra accounts not in the structctx.bumps— PDA bump seeds from the Accounts struct
Account Validation
Two mechanisms used together:
1. Constraints — #[account(...)] on fields (e.g. init, mut, seeds, bump). See account-constraints. 2. Account types — Account<'info, T>, Signer<'info>, Program<'info, System>, etc. See account-types.
Validation runs before instruction logic; then use ctx.accounts safely.
Account Discriminator
8-byte discriminator = first 8 bytes of sha256("account:<AccountName>"). Stored as first 8 bytes of account data. Allocate 8 bytes in space when using init: space = 8 + 8 for an 8-byte field.
Minimal Example
use anchor_lang::prelude::*;
declare_id!("11111111111111111111111111111111");
#[program]
mod hello_anchor {
use super::*;
pub fn initialize(ctx: Context<Initialize>, data: u64) -> Result<()> {
ctx.accounts.new_account.data = data;
Ok(())
}
}
#[derive(Accounts)]
pub struct Initialize<'info> {
#[account(init, payer = signer, space = 8 + 8)]
pub new_account: Account<'info, NewAccount>,
#[account(mut)]
pub signer: Signer<'info>,
pub system_program: Program<'info, System>,
}
#[account]
pub struct NewAccount {
data: u64,
}<!-- Source references:
- https://github.com/coral-xyz/anchor/tree/master/docs
- docs/content/docs/basics/program-structure.mdx
-->
Anchor Custom Errors
Instruction handlers return Result<T>; E is Anchor’s Error (AnchorError or ProgramError). Use custom variants for business logic.
Defining errors
#[error_code]
pub enum MyError {
#[msg("My custom error message")]
MyCustomError,
#[msg("Amount must be between 10 and 100")]
AmountOutOfRange,
}Anchor assigns codes from 6000 and generates IDL/metadata. Use #[msg("...")] for the message returned to clients.
Throwing errors
- `err!(MyError::MyCustomError)` — Return this error from the current function.
- `require!(condition, MyError::Variant)` — If
conditionis false, return the error. - `require_eq!`, `require_neq!` — For non-pubkey equality checks.
- `require_keys_eq!`, `require_keys_neq!` — For pubkey comparison.
- `require_gt!`, `require_gte!` — Numeric comparisons.
Example:
require!(amount >= 10 && amount <= 100, CustomError::AmountOutOfRange);Client (TypeScript)
On failure, the client receives an error object with e.g. errorCode.code, errorCode.number, errorMessage, and optional origin / comparedValues. Match on errorCode.code or number for flow control.
<!-- Source references:
- docs/content/docs/features/errors.mdx
-->
Anchor Events
Two ways to emit structured events: program logs (emit!) or CPI instruction data (emit_cpi!). Logs can be truncated by RPC; for robust indexing consider Geyser (e.g. Triton, Helius).
emit! (program logs)
1. Define an event struct with #[event]. 2. In the instruction, call emit!(MyEvent { field: value }). 3. Client: program.addEventListener("eventName", callback) before sending the tx; parse logs (Anchor client decodes “Program data:” base64).
#[event]
pub struct CustomEvent {
pub message: String,
}
// In handler:
emit!(CustomEvent { message: input });emit_cpi! (CPI-based)
Event data is encoded in a CPI instruction to the same program, so it’s in transaction data rather than logs.
1. Enable feature: anchor-lang = { version = "0.32", features = ["event-cpi"] }. 2. Add #[event_cpi] to the #[derive(Accounts)] struct for the instruction that emits. 3. In handler: emit_cpi!(CustomEvent { message: input }). 4. Client: fetch transaction by signature, read meta.innerInstructions, decode the inner instruction data (skip 8-byte discriminator, then decode event).
No direct subscription; decode from full transaction after confirmation.
<!-- Source references:
- docs/content/docs/features/events.mdx
-->
Anchor Zero-Copy
Zero-copy lets the program use account data in place (no full deserialize/serialize). Use for large accounts (> ~1KB), order books, event queues; saves CUs and supports up to 10MB accounts.
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], // fixed size; no Vec/String
}Nested structs: use #[zero_copy] (no account) and ensure they are Copy + repr(C) (Anchor derives Zeroable, Pod, etc.).
AccountLoader in Accounts struct
pub data_account: AccountLoader<'info, Data>,- Init:
#[account(init, payer = payer, space = 8 + 10232)]— max 10240 bytes withinit(CPI limit). - Larger than 10240: use
#[account(zero)](discriminator not set), create account via SystemProgram in a separate instruction, then in program useload_init()to set discriminator and init data. Max account 10MB (10_485_760 bytes); reserve 8 for discriminator.
Access in instructions
- First init:
let account = &mut ctx.accounts.data_account.load_init()?;then set fields. - Update:
let account = &mut ctx.accounts.data_account.load_mut()?; - Read-only:
let account = ctx.accounts.data_account.load()?;
Pitfalls
- Always
space = 8 + size_of::<T>()(8-byte discriminator). - All fields must be fixed-size/Copy (no
Vec,String). - Use
#[accessor(Type)]for byte arrays that represent other types (e.g.Pubkey) to get safe accessors. - Validate array indices to avoid panics.
<!-- Source references:
- docs/content/docs/features/zero-copy.mdx
-->
Anchor Account Constraints
Constraints are used in #[account(...)] on fields of a struct that #[derive(Accounts)]. They validate accounts before the instruction runs.
Common constraints
| Constraint | Purpose |
|---|---|
signer | Account must have signed the transaction |
mut | Account is writable; Anchor persists changes |
init | Create account via System CPI; requires payer, space (include 8-byte discriminator) |
init_if_needed | Like init but only if account doesn’t exist; needs init_if_needed feature |
seeds = [...], bump | Account must be PDA with these seeds (and optional bump = expr or seeds::program) |
has_one = <target> | Account field must equal the key of <target> in the Accounts struct |
address = <expr> | Account key must equal <expr> |
owner = <expr> | Account owner must equal <expr> |
executable | Account is executable (program) |
zero | Account discriminator is zero (uninitialized); used for large zero-copy init |
dup | Allow duplicate mutable account (otherwise Anchor disallows) |
Custom error: append @ MyError::Variant to a constraint, e.g. #[account(address = expected @ MyError::WrongAddress)].
Constraint combinations
initis often used withpayer,space, and with PDA:seeds,bump.- For zero-copy init over 10240 bytes: use
zero(noinit), create account externally, thenload_init()in the instruction.
Full list and signatures: docs.rs anchor_lang Accounts and Anchor repo lang/syn/src/codegen/accounts/constraints.rs.
<!-- Source references:
- docs/content/docs/references/account-constraints.mdx
- https://github.com/coral-xyz/anchor
-->
Anchor Account Types
Types for fields in #[derive(Accounts)] structs. They enforce ownership and (where applicable) deserialization.
Core types
- `Account<'info, T>` — Owned by program, deserialized as
T. Use for#[account]structs. - `Signer<'info>` — Validates account signed the transaction. Prefer over raw
#[account(signer)]when no extra constraints. - `Program<'info, T>` — Validates account is the program
T(e.g.System, or custom program type for CPI). - `SystemAccount<'info>` — Account owned by System Program; no data parsing.
- `AccountLoader<'info, T>` — Zero-copy; use with
#[account(zero_copy)]andload/load_mut/load_init. - `UncheckedAccount<'info>` / `AccountInfo<'info>` — No validation. Use only when necessary and add
/// CHECK:and manual checks.
Optional and boxed
- `Option<Account<'info, T>>` — Optional account; client can omit.
- `Box<Account<'info, T>>` — Same as Account but boxed to reduce stack size.
SPL / interfaces
- `InterfaceAccount<'info, T>` — Account conforming to an interface (e.g. SPL Mint, TokenAccount).
- `Interface<'info, T>` — Program implementing an interface (e.g. Token program or Token-2022).
Use with anchor_spl::token_interface::{Mint, TokenAccount, TokenInterface} for token-agnostic code.
<!-- Source references:
- docs/content/docs/references/account-types.mdx
- https://docs.rs/anchor-lang/latest/anchor_lang/accounts/
-->
Anchor CLI
CLI for building and managing Anchor workspaces. Run anchor -h and anchor <subcommand> -h for full options.
Core commands
| Command | Purpose |
|---|---|
anchor build | Build workspace programs and emit IDLs to target/idl |
anchor build --verifiable | Deterministic build (Docker); run from program dir |
anchor deploy | Deploy all workspace programs to configured cluster |
anchor test | Run integration tests (local validator) |
anchor keys sync | Update declare_id! from keypair in target/deploy/<program>.json |
IDL and accounts
| Command | Purpose |
|---|---|
anchor idl init ... | Initialize on-chain IDL at deterministic address |
anchor idl upgrade ... | Upgrade on-chain IDL |
anchor account <program>.<AccountType> <pubkey> | Fetch and deserialize account to JSON using workspace IDL |
anchor account ... --idl <path> | Use given IDL file instead of workspace |
Program name = crate/folder name (e.g. kebab-case). AccountType = PascalCase struct name.
Program and workspace
| Command | Purpose |
|---|---|
anchor init | Create new workspace |
anchor new <name> | Add new program to workspace |
anchor expand | Expand macros (in program dir or workspace) |
anchor upgrade <program> | Upgrade single program; wallet must be upgrade authority |
anchor verify <program> | Verify on-chain bytecode matches local build; run inside program dir |
Cluster
anchor cluster list— List mainnet/devnet/testnet RPC URLs.- Cluster and wallet are configured in
Anchor.toml(or env).
Tips
- Pass args to
cargo build-sbfviaanchor build -- --features my-feature. anchor deploycreates a new program id per run; useupgradefor existing programs.
<!-- Source references:
- docs/content/docs/references/cli.mdx
-->