Project Structure
An Anchor workspace separates on-chain programs, client tests, and deployment config. Knowing where each artifact lives prevents IDL drift and broken deploy scripts.
Recipe
my_workspace/
Anchor.toml # cluster, scripts, program IDs
Cargo.toml # workspace root
programs/
my_program/
Cargo.toml # anchor-lang = "0.32.1"
src/lib.rs
tests/
my_program.ts
target/
deploy/ # .so after build
idl/ # JSON IDLWhen to reach for this: You are onboarding to an existing repo or splitting one program into multiple crates.
Working Example
# Anchor.toml (excerpt)
[toolchain]
anchor_version = "0.32.1"
[programs.localnet]
my_program = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"
[scripts]
test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts"# Cargo.toml (workspace root)
[workspace]
members = ["programs/*"]
resolver = "2"What this demonstrates:
Anchor.tomlis the single source for program IDs per cluster- Each program is its own crate under
programs/ target/idl/is generated; do not hand-edit- Tests live outside Rust for faster client iteration
Deep Dive
How It Works
- Workspace members - Cargo builds all programs/* crates with shared lockfile.
- Program crate - One lib.rs with #[program], account structs, and state types.
- IDL output - anchor build writes JSON used by TS clients and declare_program!.
- Test runner - [scripts] section wires anchor test to your TS test framework.
Key Files
| Path | Purpose |
|---|---|
programs/*/src/lib.rs | On-chain logic |
Anchor.toml | Deploy targets and test scripts |
target/idl/*.json | Client codegen input |
target/deploy/*.so | Upgradeable program binary |
Gotchas
- Editing IDL by hand - Clients compile against stale types.. Fix: Regenerate with
anchor buildonly. - Program ID only in lib.rs - Deploy uses a different key than
declare_id!.. Fix: Runanchor keys syncafter deploy. - Tests in wrong folder -
anchor testcannot find them.. Fix: Keep tests undertests/per Anchor convention. - Missing workspace member - New program crate not built.. Fix: Add crate path to root
Cargo.tomlmembers. - Mixed Anchor versions - Macro expansion fails mysteriously.. Fix: Align CLI,
anchor-lang, andanchor-splon 0.32.1.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
| Monorepo with multiple Anchor workspaces | Independent products sharing infra | Single program with simple layout |
| Native Cargo workspace without Anchor.toml | Maximum control, no IDL | You want typed clients and constraints |
| Pinocchio/Steel layout | CU-critical programs | You need Anchor account macros |
FAQs
What Anchor version does this site use?
0.32.1 for anchor-lang, Anchor CLI, and examples in this section.
Do I need Solana CLI alongside Anchor?
Yes. Solana CLI 3.0.10 handles keypairs, airdrops, and solana program inspection.
Where does the IDL live after build?
target/idl/<program>.json in your workspace.
Can I mix UncheckedAccount with Signer?
Yes, but every unchecked field needs explicit constraints or handler checks.
How do I test without devnet?
Use anchor test with Surfpool 0.12.0 or LiteSVM 0.6.x in CI.
What is the 8-byte prefix on account data?
Anchor account discriminator; do not strip it when sizing space.
Should I commit generated IDL?
Yes, or publish on-chain IDL so clients have a canonical source.
How do I debug constraint failures?
Run with logs; Anchor prints constraint name and account index.
Does Anchor work on Agave 4.1.1?
Yes. This stack targets Agave validators with Solana CLI 3.0.10.
What should I read next in this section?
See sibling articles linked in Related for deeper project structure topics.
Related
- Anchor Basics - first program tour
- Building & Deploying - build and deploy commands
- The #[program] Module - handler layout
Stack versions: This page was written for Agave 4.1.1, Solana CLI 3.0.10, Anchor 0.32.1, anchor-lang 0.32.1, Rust 1.91.1, @solana/kit 7.0.0, Surfpool 0.12.0, and LiteSVM 0.6.x.