IDL & Codegen Blueprint
The Solana IDL and codegen stack is how a program's public surface becomes typed Rust CPI modules, TypeScript instruction builders, and discoverable interfaces for explorers and integrators. Once you treat the Interface Definition Language (IDL) as the shared contract and every client generator as a consumer of that contract, Anchor macros, declare_program!, Codama, npm packages, and on-chain IDL accounts stop looking like separate tool tribes and start looking like one pipeline from macros to machines.
The Anchor IDL is the hands-on entry; declare_program!, Generating TS Clients, Codama from an Anchor IDL, IDL Versioning, and On-Chain IDLs each zoom into one stage. This page sits underneath: how the pieces connect, what you pin, and which path to take from first build to multi-client production.
Summary
- An IDL describes a program's public API (instructions, accounts, types, events, errors, program address metadata), and codegen tools regenerate typed clients and CPI helpers from that JSON so humans do not hand-encode discriminators and layouts.
- Insight: Without a single IDL spine, dApps, bots, CPI callers, and indexers invent incompatible layouts; with it, you ship one artifact from
anchor buildand regenerate every consumer when the program surface changes. - Key Concepts: Anchor IDL JSON, discriminators and Borsh layouts,
declare_program!, Anchor TSProgram, Codama + @solana/kit, IDL versioning / release tags, on-chain IDL accounts, CI drift checks. - When to Use: Integrating or CPIing into Anchor programs, scaffolding typed clients, publishing SDKs, coordinating upgrades across apps, or explaining why "the IDL is stale" breaks wallets and CPI at once.
- Limitations/Trade-offs: IDL quality tracks macros and build config; remaining accounts and some native programs need docs beyond IDL; on-chain IDL is discoverable but not a substitute for trusted release hashes; Codama and Anchor TS are different runtimes so pick one client story per app.
- Related Topics: Anchor IDL, declare_program, generating TS clients, Codama from Anchor IDL, IDL versioning, on-chain IDLs.
Foundations
Solana programs do not expose a built-in "ABI file" the way some EVM toolchains do by default. Anchor fills that gap for framework programs: Rust attributes (#[program], #[account], #[event], #[error_code], and account constraint metadata) expand into validation and serialization code, and anchor build emits a JSON IDL under target/idl/ that mirrors the public handlers and types.
That JSON is the contract, not a nice-to-have dump. Instruction names map to 8-byte discriminators and arg encodings. Account lists name roles, mutability, and signers. Types section mirrors account and custom structs. Errors and events give clients human-readable codes and log shapes. The address field (when present) ties the artifact to a program id from declare_id!.
Think of the stack as layers from macros out to integrators:
+------------------------------------------+
| Integrators (dApps, bots, explorers) | <- typed calls, discovery
+------------------------------------------+
| Codegen consumers |
| declare_program! (Rust CPI) |
| Anchor TS Program / Codama + kit |
+------------------------------------------+
| IDL artifact (JSON, git/npm/on-chain) | <- versioned contract
+------------------------------------------+
| anchor build (macros + sBPF compile) |
+------------------------------------------+
| Program source (Anchor 0.32.1 crates) |
+------------------------------------------+Codegen means "do not invent bytes by hand." Off-chain, that is usually TypeScript: either @coral-xyz/anchor's Program (common with anchor test and many existing dApps) or Codama generating @solana/kit 7.0.0-native instruction codecs and account decoders for greenfield kit stacks. On-chain between programs, declare_program! embeds a callee IDL at compile time and generates CPI account structs and invoke helpers so your program fails to compile when the IDL path or shape is wrong, rather than failing only after a bad CPI on-chain.
Versioning is how you keep those consumers honest across deploys. On-chain IDLs (anchor idl init / upgrade / fetch) put a copy of the interface on a program-owned account for discovery; production systems still pin a trusted IDL hash or release tag so a compromised upgrade authority cannot silently redefine the API for automated clients.
Off-chain kit (this site: @solana/kit 7.0.0) sits at the client layer for RPC and transaction assembly. It does not replace the IDL; Codama is the usual bridge from Anchor IDL into kit types. Platform pins for the program side remain Agave 4.1.1, Solana CLI 3.0.10, Anchor 0.32.1, and Rust 1.91.1.
Mechanics & Interactions
Build produces the contract
anchor build runs the sBPF compile path and regenerates IDL from the current macros. The authoritative file is whatever build just wrote under target/idl/<program>.json (teams often copy a reviewed artifact into idl/ for git). Hand-editing that JSON to "fix" a client almost always causes silent drift: the program still encodes the macro-defined layout, while clients encode fiction.
programs/foo/src/lib.rs --macros--> anchor build
|
+-----------------------+-----------------------+
v v v
target/deploy/foo.so target/idl/foo.json target/types/...
| |
v +--> declare_program! / Codama / TS
solana program deployThe Anchor IDL details field layout. The blueprint rule is: regenerate, review, pin; never invent.
declare_program! is compile-time CPI codegen
When program A must CPI into Anchor program B, A can vendor B's IDL and use declare_program!(b). The macro generates modules for program type checks, instruction builders, and cpi helpers that fill AccountMeta lists consistently with B's IDL.
That path is static: wrong or missing IDL fails the build. Runtime safety still depends on the deployed B matching the IDL you compiled against (same instruction discriminators and account expectations). Pin callee IDL to a git SHA or release that matches the deployed program hash. Details: declare_program!.
Two TypeScript stories, one IDL
Both major TS paths start from the same JSON:
- Anchor TS client loads IDL into
new Program(idl, provider)and exposesprogram.methods.<ix>(...).accounts(...).rpc(). Strong fit for Anchor tests and stacks already on@coral-xyz/anchor+ web3.js-era deps. See Generating TS Clients. - Codama reads the Anchor IDL and emits kit-oriented modules (instruction getters, codecs, error maps). Strong fit when the app standardizes on @solana/kit 7.0.0 transaction message builders. See Codama from an Anchor IDL.
Anchor IDL JSON
|
+-------------+-------------+
v v
@coral-xyz/anchor Codama CLI
Program.methods.* generated kit codecs
| |
v v
legacy / test dApps kit-first dAppsDo not mix kit transaction assembly with web3.js instruction objects without a deliberate bridge layer. Pick one client runtime per app and regenerate when IDL changes.
Versioning is release engineering
Treat each program release as a bundle: .so hash, IDL JSON, optional npm package or git tag, and changelog of IDL-facing changes. CI should fail if anchor build produces an IDL that differs from the committed artifact without an explicit review.
| Change | Typical client impact |
|---|---|
| New instruction | Additive; old clients ignore it |
| New account field / larger layout | Breaking without migration plan |
| Field reorder or type change | Breaking for decoders |
| Error enum reorder | Breaking for code maps; append only |
| New event | Additive if clients do not require it |
Account version bytes and migration instructions live in program design; the IDL still must describe the live layout clients decode. Full practice: IDL Versioning.
On-chain IDL is discovery, not sole trust
anchor idl init creates an on-chain IDL account for a program id; upgrade updates it after API changes; fetch pulls JSON for explorers and opportunistic clients. That is excellent for discoverability. It is not a full supply-chain story by itself: anyone who controls the IDL authority can publish a misleading interface.
Trusted release pipeline Chain discovery
--------------------- ---------------
git tag + IDL hash ----pin----> anchor idl fetch (optional check)
solana-verify / program hash explorers, wallets, demos
npm @org/program-idl@x.y.z still compare to pinned hashUse on-chain IDL for bootstrap and tooling; pin verified artifacts for production bots, CPI, and high-value UIs. Details: On-Chain IDLs.
One day of work across the pipeline
- Edit program instructions and account structs under Anchor 0.32.1; build with pinned Agave/CLI and Rust.
- Review
target/idldiff; commit the artifact if it is the team's source of truth. - Update
declare_program!consumers and rebuild CPI programs. - Regenerate Anchor TS types or Codama kit clients; fail CI on dirty generated trees.
- On deploy, upgrade on-chain IDL if you publish one; tag release with program hash + IDL version.
- Integrators bump their IDL package or git pin and redeploy clients.
Advanced Considerations & Applications
Teams do not need every consumer on day one. Match integration surface and risk to a path, then add channels when third parties or multi-runtime clients appear.
| Path | What you ship | Strengths | Weaknesses | Best fit |
|---|---|---|---|---|
| Repo IDL only | Git-committed JSON + program build | Simple; reviewable diffs | No on-chain discovery | Single-team monorepos |
| IDL + Anchor TS | npm or monorepo types for Program | Fast tests and classic dApps | Kit migration friction later | Existing Anchor frontends |
| IDL + Codama + kit | Generated kit codecs in CI | Modern kit stack; explicit codecs | Extra codegen step to pin | Greenfield kit dApps |
| IDL + declare_program! | Vendored callee JSON in CPI crates | Typed CPI; compile-time checks | Must rebuild on callee upgrades | Multi-program workspaces |
| Full release bundle | Hash, IDL, npm, on-chain upgrade, changelog | Integrator-ready; audit-friendly | Process cost | Mainnet programs with external clients |
Multi-program workspaces: one IDL file per program crate; never merge surfaces into a single JSON. CPI edges declare which IDL version each callee pin uses.
Indexers and analytics also parse with IDL discriminators and layouts; a stale IDL in the indexer is the same class of bug as a stale dApp client, only quieter until metrics go wrong.
Native or Pinocchio callees may lack a complete Anchor IDL. Prefer a hand-maintained compatible IDL, Codama from a custom description, or manual CPI metas; do not assume declare_program! invents a full surface.
For design reviews, answer first: who owns the IDL artifact, how CI detects drift, which TS runtime you standardize on, which callees are pinned for CPI, and whether on-chain IDL is marketing discovery or an authority-controlled production feed.
Common Misconceptions
- "The IDL is optional documentation." For Anchor ecosystems it is the machine contract for clients, CPI macros, and many indexers; treat it as API product.
- "I can hand-edit the IDL to match my frontend." Frontend and on-chain encoding will diverge; regenerate from macros only.
- "declare_program! tracks mainnet automatically." It embeds whatever IDL you compile against; pin to the deployed program version.
- "Anchor TS and Codama are interchangeable drop-ins." Both read IDL, but they target different client stacks; choose one transaction path.
- "Publishing on-chain IDL replaces verifiable builds." Discovery is not bytecode attestation; pair with program hash verification when trust matters.
- "Any IDL change is non-breaking if the program upgrades." Layout and error renumbering break old clients even when the upgrade succeeds.
- "Remaining accounts will show up in the IDL." Dynamic account lists often need separate docs and careful client construction.
- "npm package version alone is enough." Pin program id, cluster, and IDL content hash (or commit SHA) so a republish cannot swap layouts silently.
FAQs
What is the single most important idea in IDL and codegen?
One versioned IDL artifact is the shared contract; every typed client and CPI helper is a regenerated consumer of that contract, not a parallel hand-written ABI.
What does the Anchor IDL contain?
JSON for public instructions (accounts and args), account and custom types, events, errors, and program address metadata derived from macros at build time.
When is the IDL regenerated?
On anchor build (and related Anchor IDL commands). Commit and review diffs; do not treat target/idl as optional noise.
What is declare_program! for?
Compile-time generation of Rust modules and CPI helpers from another program's IDL so your program can call it with typed account metas.
Do I need declare_program! for off-chain clients?
No. Off-chain apps use Anchor TS or Codama (or other language bindings). declare_program! is for on-chain Rust CPI into IDL-described programs.
Should new dApps use Anchor TS or Codama?
If you standardize on @solana/kit 7.0.0, prefer Codama-generated clients. If you live inside classic Anchor test and web stacks, Anchor's Program client remains practical.
How does Codama relate to the Anchor IDL?
Codama ingests Anchor IDL JSON and emits kit-oriented TypeScript codecs and instruction helpers; rebuild when the IDL changes and pin the Codama CLI version in CI.
How should we version IDLs?
Ship IDL with each program release, use semantic or release tags, CI-diff committed JSON, append-only error codes when possible, and document breaking layout changes with migrations.
Is on-chain IDL required?
No. It helps explorers and discovery. Many production clients use git or npm pins and only optionally cross-check on-chain fetches.
What breaks if IDL and program diverge?
Wrong discriminators, account order, or layouts cause simulation failures, cryptic custom errors, corrupt account decodes, or CPI rejection at the callee.
Can non-Anchor programs participate?
Yes if you supply a correct IDL or hand-built codecs. Anchor's automatic generation is the happy path; native programs need extra discipline.
Where does @solana/kit fit?
Kit is the modern TypeScript RPC and transaction stack (this site: 7.0.0). Codama bridges Anchor IDL into that stack; kit does not compile sBPF or own the IDL format.
What should CI enforce?
Pinned Anchor/Agave/Rust, IDL artifact equals fresh build output, regenerated clients clean, and release jobs that publish matching program hash and IDL version.
How do remaining accounts interact with codegen?
They are often outside fixed IDL account lists. Document them, pass them explicitly in clients, and never assume codegen invented the full runtime account graph.
Related
- The Anchor IDL - what the JSON contains and how macros produce it
- declare_program! - compile-time CPI clients from callee IDL
- Generating TS Clients - Anchor TypeScript
Programand typing - Codama from an Anchor IDL - kit-native codegen from IDL
- IDL Versioning - release pins, breaking changes, migrations
- On-Chain IDLs - publish, upgrade, and fetch IDL accounts
Stack versions: This page was written for Agave 4.1.1, Solana CLI 3.0.10, Anchor 0.32.1, Rust 1.91.1, and @solana/kit 7.0.0.