Anchor v2 alpha is here! Up to 95% smaller binaries, 3.0 to 50.4× fewer CU
Anchor Docs
A lone boy hauls an anchor on the shore, evoking the weight of maritime destiny and Homer's mastery of atmosphere and technique.
Boy with Anchor, Winslow Homer, 1873
Overview

Clients and IDL

Build instructions from generated Rust account structs and the alpha IDL/TypeScript surface.

A client is the code that builds instructions for your program. It picks an instruction, serializes its arguments, orders the accounts, then signs and sends the transaction to a cluster or test VM.

Anchor clients are built around the IDL and the generated helper structs that the program crate emits. The Rust path is the most complete in alpha because it has typed instruction structs, account-meta builders, resolved account helpers, and LiteSVM test integration.

TypeScript support exists through the current IDL and the @anchor-lang/core scaffold. The stable v2 npm surface is not published yet, so use TypeScript for integration and inspection while the package settles, and rely on Rust tests for exact instruction construction.

For a new v2 program, start with Rust tests. Once the transaction shape is proven there, use the generated IDL and TypeScript type output for app integration.

What to use today#

TaskRecommended path
Integration testsRust tests with anchor-v2-testing and anchor_v2_testing::svm().
Instruction constructionGenerated program::instruction::* and program::accounts::* modules.
PDA derivationGenerated find_<account>_address() helpers or Resolved account structs.
Profiling and debuggerRust tests that call anchor_v2_testing::svm().
TypeScript integrationGenerated IDL and TypeScript IDL type output, with alpha package caveats.

Rust

Build instructions in Rust with generated instruction, account, and resolved account structs.

Rust is the primary client and test surface while the TypeScript package is still alpha. An Anchor program crate generates typed modules that Rust tests and other Rust clients can use directly, without reading an IDL at runtime.

The generated Rust surface is useful even when you do not plan to ship a Rust client. It gives tests a strongly typed way to build the exact instruction bytes and account metas the program expects.

In the default LiteSVM scaffold, the Rust client is an integration test next to the program crate:

  • programs/
    • counter/
      • tests/
        • test_initialize.rs Builds and sends instructions
      • src/
        • lib.rs Program crate that emits generated modules

The generated modules mirror the shape Anchor users expect:

  • program::instruction::* structs encode instruction discriminators and arguments.
  • program::accounts::* structs build full account metas.
  • program::accounts::*Resolved structs fill in derivable PDAs and well-known programs when possible.
  • PDA helper methods such as find_counter_address() derive addresses from the same seed metadata used by validation.

Generated instruction data#

Every handler gets an instruction struct under program::instruction. The struct implements InstructionData, so calling data() returns the discriminator plus serialized arguments:

use anchor_lang_v2::InstructionData;
let data = vault::instruction::Deposit { amount: 1_000_000 }.data();

By default, instruction discriminators are the first 8 bytes of sha256("global:" + handler_name). Programs that opt into compact byte discriminators with #[discrim = N] still use the same InstructionData trait.

Account builders#

Full account structs include every account the instruction expects. This is the most explicit form and works for every account shape:

use {
anchor_lang_v2::{solana_program::instruction::Instruction, InstructionData, ToAccountMetas},
anchor_lang_v2::programs::System,
};
let ix = Instruction::new_with_bytes(
counter::id(),
&counter::instruction::Initialize {}.data(),
counter::accounts::Initialize {
payer: payer.pubkey(),
counter: counter.pubkey(),
system_program: System::id(),
}
.to_account_metas(None),
);

Use this form when every address is already known, when an account cannot be derived from IDL seed metadata, or when a test should be explicit about the exact transaction shape.

When the derive can infer account addresses, Anchor also emits a Resolved struct. The resolved form asks only for accounts the caller must supply and fills in the rest:

use anchor_lang_v2::ToAccountMetas;
let metas = multisig_v2::accounts::CreateResolved {
creator: creator.pubkey(),
}
.to_account_metas(None);

Resolved structs derive PDAs whose seeds are visible to the macro, and insert well-known program accounts such as System or Token when the account wrapper determines the address.

The instruction struct also has a convenience to_instruction() method:

let ix = multisig_v2::instruction::Create {
threshold: 2,
}
.to_instruction(multisig_v2::accounts::CreateResolved {
creator: creator.pubkey(),
});

Several bench tests use this shape because it keeps the test focused on the accounts the caller actually controls.

PDA helpers#

For PDA fields, the generated accounts module includes helper methods named from the account field:

let (counter, bump) = counter::accounts::Initialize::find_counter_address(
payer.pubkey().as_ref(),
);

When seeds have no runtime inputs, the helper has no arguments:

let (config, bump) = nested_v2::accounts::Initialize::find_config_address();

Prefer these helpers in Rust tests when available. They keep client-side derivation locked to the program’s account declaration.

LiteSVM tests#

The default scaffold uses Rust integration tests with LiteSVM through anchor-v2-testing:

use anchor_v2_testing::{Keypair, LiteSVM, Message, Signer, VersionedMessage, VersionedTransaction};
use anchor_lang_v2::{
solana_program::instruction::Instruction,
InstructionData,
ToAccountMetas,
};
let mut svm: LiteSVM = anchor_v2_testing::svm();
let payer = Keypair::new();
let counter_account = Keypair::new();
svm.airdrop(&payer.pubkey(), 1_000_000_000).unwrap();
let bytes = include_bytes!("../../../target/deploy/counter.so");
svm.add_program(counter::id(), bytes).unwrap();
let ix = Instruction::new_with_bytes(
counter::id(),
&counter::instruction::Initialize {}.data(),
counter::accounts::Initialize {
payer: payer.pubkey(),
counter: counter_account.pubkey(),
system_program: anchor_lang_v2::programs::System::id(),
}
.to_account_metas(None),
);
let blockhash = svm.latest_blockhash();
let msg = Message::new_with_blockhash(&[ix], Some(&payer.pubkey()), &blockhash);
let tx = VersionedTransaction::try_new(
VersionedMessage::Legacy(msg),
&[&payer, &counter_account],
)
.unwrap();
let result = svm.send_transaction(tx);
assert!(result.is_ok());

Use anchor_v2_testing::svm() instead of calling LiteSVM::new() directly. The wrapper turns into a trace-recording VM when the crate is built with the generated profile = ["anchor-v2-testing/profile"] feature, which powers anchor test --profile, anchor debugger, and anchor coverage.

When to use Rust first#

Use the Rust generated surface for:

  • instruction tests that need to run against LiteSVM
  • profiler, debugger, and coverage workflows
  • client code that wants compile-time checking against the program crate
  • account resolution features that TypeScript clients do not fully mirror yet

The IDL still matters for cross-language clients, but Rust is the most complete client surface during alpha.

Standalone Rust clients#

The in-crate generated modules above are the common path because integration tests share the program crate. For a Rust client that ships in a separate crate, anchor codama generate -l rust renders a standalone Rust crate from the IDL using the @codama/renderers-rust package:

Terminal window
anchor codama generate -l rust -p clients target/idl/counter.json

This is useful for off-chain services, SDKs, or downstream Rust apps that consume an Anchor program without depending on its program crate. See CLI reference: anchor codama for renderer details.

TypeScript

Use generated IDLs and TypeScript type output from Anchor programs.

The TypeScript surface is an alpha integration path. The CLI builds IDL JSON and emits TypeScript type files. Current scaffolds use @anchor-lang/core ^1.0.0 while the stable v2 npm surface is still settling.

This page covers connecting an app, script, or frontend to a v2 program. The examples show the familiar Anchor client flow, but the alpha package is less authoritative than the generated Rust helpers.

Warning (Alpha package surface)

Current scaffolds are pinned to @anchor-lang/core ^1.0.0 until a stable v2 package is published. Treat TypeScript codegen as active development, and use Rust tests as the source of truth for exact instruction construction.

During alpha, use TypeScript for application integration and IDL inspection. Prove exact instruction data, account ordering, PDA resolution, and optional-account behavior with generated Rust helpers and LiteSVM tests.

Generated artifacts#

anchor build writes the client-facing artifacts under target/:

ArtifactPathStatus
IDL JSONtarget/idl/<program>.jsonThe canonical cross-language description of the program.
TypeScript IDL typetarget/types/<program>.tsUseful for typed client experiments and app code.
Rust generated helpersProgram crate modulesThe source of truth for instruction builders today.
JavaScript client package@anchor-lang/core ^1.0.0Usable for IDL-backed experiments while the stable v2 package is settling.

Regenerate the IDL artifacts with:

Terminal window
anchor build

The generated IDL includes instruction names, discriminators, args, accounts, account types, events, constants, errors, optional account markers, and PDA seed metadata when the macro can inspect the seed list.

Codama clients#

Anchor also ships a Codama integration for projects that prefer the Codama toolchain over @anchor-lang/core. anchor codama generate converts the IDL in-process, then renders client libraries through @codama/cli:

Terminal window
anchor codama generate -l js -p clients target/idl/counter.json

The JavaScript renderer pairs naturally with @solana/kit, and the same command supports js-umi, rust, and go outputs. See CLI reference: anchor codama for the full flag set.

A typical app or script imports the generated files from the Anchor workspace:

  • target/
    • idl/
      • counter.json IDL JSON
    • types/
      • counter.ts TypeScript IDL type
  • app/
    • src/
      • program.ts Constructs the Program client

Client program#

A Program is built from an IDL and a provider. Read-only flows can use any provider-shaped object with a connection. Calls that send transactions need an AnchorProvider or another provider that implements sendAndConfirm.

In a frontend, wire the connection and wallet adapter into an Anchor provider before constructing the program:

import { AnchorProvider, Program, setProvider } from '@anchor-lang/core'
import { useAnchorWallet, useConnection } from '@solana/wallet-adapter-react'
import type { Counter } from '../target/types/counter'
import idl from '../target/idl/counter.json'
const { connection } = useConnection()
const wallet = useAnchorWallet()
if (!wallet) {
throw new Error('Wallet not connected')
}
const provider = new AnchorProvider(connection, wallet, {
commitment: 'confirmed',
})
setProvider(provider)
export const program = new Program(idl as Counter, provider)

A read-only script can construct a program from a connection without a wallet.

import { clusterApiUrl, Connection } from '@solana/web3.js'
import { Program } from '@anchor-lang/core'
import type { Counter } from '../target/types/counter'
import idl from '../target/idl/counter.json'
const connection = new Connection(clusterApiUrl('devnet'), 'confirmed')
export const program = new Program(idl as Counter, {
connection,
})

This is useful for account queries, IDL-backed name lookup, and early integration tests that do not sign transactions.

Invoke instructions#

The methods builder is the entry point for TypeScript clients. The builder chain has these pieces:

StepPurpose
program.methodsStarts an instruction call from the IDL namespace.
.initialize(...args)Selects the instruction and passes serialized args.
.accounts({...})Supplies accounts the client must provide.
.signers([...])Adds keypairs beyond the provider wallet when using .rpc().
.rpc()Builds, signs, sends, and confirms a transaction.
.transaction()Builds a transaction without sending it.
.instruction()Builds one instruction for manual transaction composition.

The basic chain looks like this:

const signature = await program.methods
.increment()
.accounts({
counter,
authority: wallet.publicKey,
})
.rpc()

Start at program.methods and call the instruction by name with its args. Supply accounts with .accounts(), add extra keypair signers via .signers() when needed, then pick a terminal method.

const signature = await program.methods
.increment()
.accounts({
counter,
authority: wallet.publicKey,
})
.rpc()
const transaction = await program.methods
.increment()
.accounts({
counter,
authority: wallet.publicKey,
})
.transaction()
const ix = await program.methods
.increment()
.accounts({
counter,
authority: wallet.publicKey,
})
.instruction()

During alpha, compare important TypeScript-built transactions against the generated Rust helper for the same instruction. The Rust helper is the most reliable reference for account ordering, PDA metadata, and any instruction shape that depends on code generated inside the program crate.

PDA seed metadata#

Generated clients can only auto-derive PDAs when the IDL contains enough seed metadata. Array-form seeds are the best shape:

#[account(seeds = [b"vault", authority.address().as_ref()], bump)]
pub vault: Account<Vault>,

Opaque expression seeds still validate at runtime, but clients may not know how to reproduce them:

#[account(seeds = Vault::seeds(authority.address()), bump)]
pub vault: Account<Vault>,

For public client-facing instructions, prefer array-form seeds whenever generated TypeScript clients should derive addresses automatically.

Account fetching#

Account clients are exposed under program.account:

Fetch and deserialize one account by address:

const counterAccount = await program.account.counter.fetch(counter)

Fetch multiple accounts by address:

const manyCounters = await program.account.counter.fetchMultiple([counterA, counterB])

Fetch all accounts of a given account type:

const allCounters = await program.account.counter.all()

Filter accounts by matching bytes at an offset:

const counters = await program.account.counter.all([
{
memcmp: {
offset: 16,
bytes: authority.toBase58(),
},
},
])

Use the account wrapper to choose the right decoding and filter offsets. Not every account starts with an Anchor discriminator followed by a borsh payload.

Account wrapperClient decoding note
Account<T>Zero-copy and Pod-backed by default. Fixed offsets come from the #[repr(C)] layout after the Anchor discriminator.
BorshAccount<T>Variable-length borsh account data.
Account<Mint>SPL mint layout owned by the Token Program, with no Anchor discriminator.
Account<TokenAccount>SPL token-account layout owned by the Token Program, with no Anchor discriminator.
InterfaceAccount<T>Accepts Token or Token-2022 ownership. Token-2022 accounts may have TLV extension bytes after the base layout.

For memcmp filters, calculate offsets from the actual wrapper layout. SPL token accounts start at byte offset 0, while Anchor-discriminated program accounts start after the discriminator.

Keep Rust integration tests as the execution harness:

Define the program surface

Define the instruction and accounts in Rust.

Regenerate IDL artifacts

Run anchor build and inspect target/idl/ plus target/types/.

Prove the transaction shape

Write or update a LiteSVM test using the generated Rust helper.

Integrate TypeScript

Build the app integration against the same IDL and keep TypeScript assumptions small until the alpha package lands.

App developers get a usable IDL and type file, and the TypeScript package isn’t forced to carry guarantees the alpha code hasn’t earned yet.

Current limitations#

Some surfaces are still worth testing carefully in downstream clients:

  • account-resolution paths that rely on generated PDA metadata
  • generated CPI metadata
  • opaque seed expressions
  • Pod-backed account decoding conventions
  • SPL and Token-2022 interface-account surfaces

Use the IDL for integration planning and early experiments, but keep Rust tests and generated account builders as the reference until the TypeScript package catches up.

Esc

Start typing to search the docs.