- Anchor is the standard framework for building Solana programs. It handles account validation, serialization, and security checks through Rust macros.
anchor initscaffolds a full workspace: program source, tests, deployment config, and TypeScript types.- The
#[program]macro defines instructions,#[derive(Accounts)]defines account validation, and#[account]defines on-chain state. - Anchor 1.0.0 (April 2026) ships breaking changes:
@anchor-lang/corereplaces@coral-xyz/anchor, Surfpool replaces solana-test-validator, and LiteSVM is the default test template.
Prerequisites
Install the following before starting. The guide targets Anchor 1.0.0.
Verify your setup:
Anchor 1.0.0 no longer requires the Solana CLI to be on your PATH for most commands. The
anchor CLI ships native implementations of balance, airdrop, address, and deploy. You still need the Solana CLI for operations like solana-keygen and solana program close.Creating a project
Scaffold a new Anchor workspace:lib.rs, use --template single:
Test templates
Anchor 1.0.0 generates a LiteSVM Rust test by default. You can pick a different template:Project structure
Afteranchor init, the workspace looks like this:
programs/— your on-chain programs. A workspace can contain multiple programs.target/deploy/— the compiled program binary (.sofile) and program keypair.target/idl/— the generated IDL JSON file that clients use to interact with your program.target/types/— TypeScript type definitions generated from the IDL.
Anchor.toml
The workspace configuration file controls which cluster you deploy to, which wallet signs transactions, and what scripts run during testing:[toolchain] section:
The
[registry] section has been removed in Anchor 1.0.0. If you see it in older examples, delete it.Program anatomy
An Anchor program is built around four macros. Here is a complete counter program that demonstrates all of them:declare_id!
Sets the program’s on-chain address. By default, this matches the public key from/target/deploy/my_counter-keypair.json.
#[program]
Defines the module containing your instruction handlers. Each public function in this module becomes a callable instruction:Context<T> as its first parameter, where T is the accounts struct. Additional parameters become the instruction arguments that clients pass in.
The Context struct provides:
ctx.accounts— the validated accountsctx.program_id— the program’s public keyctx.remaining_accounts— extra accounts not in the structctx.bumps— PDA bump seeds found during validation
#[derive(Accounts)]
Defines which accounts an instruction requires and how to validate them. Each field specifies an account with its type and constraints:#[account]
Defines the data layout for accounts your program creates. Anchor adds an 8-byte discriminator as a prefix to distinguish account types:SHA256("account:Counter"). It is automatically set during init and verified during deserialization.
Account constraints
Constraints are the core of Anchor’s security model. They go inside#[account(...)] attributes and tell Anchor what to validate before executing your instruction.
Initialization constraints
Create a new account and set its discriminator:init constraint requires:
payer— the account paying the rentspace— total bytes (8-byte discriminator + your data)
init_if_needed to create only if the account does not already exist. This requires the init-if-needed feature in your Cargo.toml:
PDA constraints
Validate that an account is a PDA derived from specific seeds:init, this creates the PDA account:
ctx.bumps.counter.
Mutability and ownership
Reallocation
Resize an existing account:SPL token constraints
Create or validate token accounts:Accessing instruction arguments in constraints
Use#[instruction(...)] to reference instruction arguments in your constraints:
Space calculation
Every account needs enough space for the 8-byte discriminator plus its data fields.Type sizes
Manual calculation
Using InitSpace
TheInitSpace derive macro calculates space automatically. Use #[max_len(...)] for dynamic types:
#[max_len(5, 50)] on Vec<String> means: up to 5 strings, each up to 50 bytes.
Building and the IDL
Build your program:- Program binary —
target/deploy/my_counter.so— the compiled BPF bytecode. - IDL file —
target/idl/my_counter.json— a JSON description of your program’s instructions, accounts, and types. - TypeScript types —
target/types/my_counter.ts— type definitions generated from the IDL.
IDL structure
The IDL maps your Rust program to a format that clients can consume:On-chain IDL via Program Metadata Program
Anchor 1.0.0 replaces the legacy IDL storage mechanism with the Program Metadata Program (PMP). When you runanchor program deploy, the IDL is uploaded automatically. To skip the upload:
Testing
Anchor 1.0.0 defaults to LiteSVM for Rust-based testing. LiteSVM runs an in-process Solana VM—faster startup and teardown thansolana-test-validator.
TypeScript tests
For TypeScript tests (generated with--test-template mocha), the test file uses @anchor-lang/core:
Running tests
Anchor 1.0.0 uses Surfpool as the default local validator foranchor test:
Deploying
Local deployment
Start a local validator and deploy:Deploying to devnet
- Set your cluster to devnet in
Anchor.toml:
- Fund your wallet:
- Build and deploy:
anchor program deploy in 1.0.0 automatically uploads the IDL to the on-chain Program Metadata Program. The IDL becomes fetchable by any client using Program.fetchIdl.Deploying to mainnet with Chainstack
- Deploy a Solana node on Chainstack or use the Chainstack Solana endpoint.
-
Set your cluster to your Chainstack endpoint in
Anchor.toml:
- Build and deploy:
Client interaction
The IDL enables typed client interactions. Anchor’s TypeScript client (in the@anchor-lang/core package) deserializes accounts and instruction arguments automatically.
Connecting to a program
The
anchor.workspace object is only available inside anchor test. For standalone scripts and frontend code, load the IDL JSON file directly and instantiate the program with new Program(idl, provider).Sending transactions
The wallet must be funded before sending transactions. On devnet, request an airdrop first:Fetching accounts
Listening to events
If your program emits events with theemit! macro:
Common gotchas
CpiContext takes Pubkey, not AccountInfo
Anchor 1.0.0 removed the redundantAccountInfo copy from CpiContext. If you are adapting pre-1.0 code:
Duplicate mutable accounts are now rejected
Passing the same mutable account twice in an instruction errors by default. If you intentionally need this, opt in withdup:
AccountInfo is deprecated in Accounts structs
UsingAccountInfo directly inside #[derive(Accounts)] now produces a compile warning. Use UncheckedAccount instead:
Space must include the 8-byte discriminator
Everyinit constraint needs 8 extra bytes for the discriminator:
Error codes are limited to one block
Anchor 1.0.0 restricts#[error_code] to a single definition per program. If you have multiple error blocks, consolidate them:
Surfpool vs solana-test-validator
anchor test now uses Surfpool by default. If you encounter compatibility issues:
solana-test-validator. Use the legacy validator when you need full RPC method support or when cloning accounts from mainnet.
Reference repos
These are the source repositories we worked against while writing this guide. They stay closer to reality than docs — check them first when something here looks off.- solana-foundation/anchor — Anchor 1.0 source; macros,
anchor build,anchor keys sync, andanchor deploybehavior verified against master - solana-foundation/solana-bootcamp-2026 — bootcamp’s Anchor projects used as a correctness reference
- txtx/surfpool — local validator used in the Anchor mainnet-fork workflow