The Interface Description Language (IDL) is a JSON specification that describes an Anchor program’s public interface. The IDL enables automatic client generation and provides a standardized way to interact with Solana programs.
What is an IDL?
The IDL contains:
Program metadata (name, version, address)
Instruction definitions with parameters
Account structures and types
Custom type definitions
Error codes and messages
Events emitted by the program
Generating IDLs
Anchor automatically generates IDLs during the build process:
The IDL is output to:
JSON format: target/idl/<program_name>.json
TypeScript format: target/types/<program_name>.ts
IDL build feature
To generate IDLs, programs must have the idl-build feature enabled in Cargo.toml:
[ features ]
idl-build = [ "anchor-lang/idl-build" ]
This is automatically added to new Anchor projects.
IDL structure
Basic example
{
"address" : "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" ,
"metadata" : {
"name" : "counter" ,
"version" : "0.1.0" ,
"spec" : "0.1.0"
},
"instructions" : [
{
"name" : "initialize" ,
"discriminator" : [ 175 , 175 , 109 , 31 , 13 , 152 , 155 , 237 ],
"accounts" : [
{
"name" : "counter" ,
"writable" : true ,
"signer" : true
},
{
"name" : "user" ,
"writable" : true ,
"signer" : true
},
{
"name" : "system_program" ,
"address" : "11111111111111111111111111111111"
}
],
"args" : []
},
{
"name" : "increment" ,
"discriminator" : [ 11 , 18 , 104 , 9 , 104 , 174 , 59 , 33 ],
"accounts" : [
{
"name" : "counter" ,
"writable" : true
}
],
"args" : []
}
],
"accounts" : [
{
"name" : "Counter" ,
"discriminator" : [ 255 , 176 , 4 , 245 , 188 , 253 , 124 , 25 ],
"type" : {
"kind" : "struct" ,
"fields" : [
{
"name" : "count" ,
"type" : "u64"
}
]
}
}
],
"types" : [],
"errors" : []
}
Instructions
Each instruction includes:
{
"name" : "update_data" ,
"discriminator" : [ 29 , 158 , 252 , 191 , 10 , 83 , 219 , 104 ],
"accounts" : [
{
"name" : "data_account" ,
"writable" : true
},
{
"name" : "authority" ,
"signer" : true
}
],
"args" : [
{
"name" : "new_value" ,
"type" : "u64"
}
]
}
Account types
{
"name" : "GameAccount" ,
"discriminator" : [ 112 , 191 , 145 , 200 , 83 , 154 , 214 , 51 ],
"type" : {
"kind" : "struct" ,
"fields" : [
{
"name" : "player" ,
"type" : "pubkey"
},
{
"name" : "score" ,
"type" : "u64"
},
{
"name" : "level" ,
"type" : "u8"
}
]
}
}
Custom types
{
"name" : "GameState" ,
"type" : {
"kind" : "enum" ,
"variants" : [
{
"name" : "Inactive"
},
{
"name" : "Active"
},
{
"name" : "Finished"
}
]
}
}
Error codes
{
"code" : 6000 ,
"name" : "InvalidAuthority" ,
"msg" : "The provided authority does not match"
}
Using IDLs
TypeScript client
Anchor generates TypeScript types from the IDL:
import { Program , AnchorProvider } from "@anchor-lang/core" ;
import { Counter } from "./target/types/counter" ;
import idl from "./target/idl/counter.json" ;
const program = new Program < Counter >(
idl as Counter ,
provider
);
// Type-safe instruction calls
await program . methods
. initialize ()
. accounts ({
counter: counterPda ,
user: user . publicKey ,
systemProgram: SystemProgram . programId ,
})
. rpc ();
Rust client
Use declare_program! to generate Rust types:
use anchor_lang :: prelude ::* ;
declare_program! ( counter );
let cpi_accounts = counter :: cpi :: accounts :: Initialize {
counter : ctx . accounts . counter . to_account_info (),
user : ctx . accounts . user . to_account_info (),
system_program : ctx . accounts . system_program . to_account_info (),
};
counter :: cpi :: initialize (
CpiContext :: new (
ctx . accounts . counter_program . to_account_info (),
cpi_accounts ,
),
) ? ;
IDL CLI commands
Build IDL
Generate IDL without building the program:
Fetch IDL
Download IDL from a deployed program:
anchor idl fetch < PROGRAM_I D >
Initialize IDL account
Upload IDL to the blockchain:
anchor idl init -f target/idl/my_program.json < PROGRAM_I D >
Upgrade IDL
Update an existing on-chain IDL:
anchor idl upgrade < PROGRAM_I D > -f target/idl/my_program.json
Discriminators
Discriminators are 8-byte identifiers that uniquely identify instructions and accounts:
// Instruction discriminator
let discriminator = anchor_lang :: solana_program :: hash :: hash ( b"global:initialize" );
let discriminator = & discriminator . to_bytes ()[ .. 8 ];
// Account discriminator
#[account]
pub struct MyAccount {
pub data : u64 ,
}
// Discriminator: hash(b"account:MyAccount")[..8]
Custom discriminators can be specified:
#[account(discriminator = [1, 2, 3, 4, 5, 6, 7, 8])]
pub struct MyAccount {
pub data : u64 ,
}
IDL parsing
Read and parse IDL files programmatically:
import fs from "fs" ;
const idl = JSON . parse (
fs . readFileSync ( "./target/idl/my_program.json" , "utf-8" )
);
// Access instructions
idl . instructions . forEach (( ix ) => {
console . log ( `Instruction: ${ ix . name } ` );
console . log ( `Accounts: ${ ix . accounts . length } ` );
console . log ( `Args: ${ ix . args . length } ` );
});
// Access accounts
idl . accounts . forEach (( account ) => {
console . log ( `Account: ${ account . name } ` );
console . log ( `Fields: ${ account . type . fields . length } ` );
});
Best practices
Enable idl-build feature - Ensure all programs have idl-build in their feature flags.
Version your IDLs - Store IDL snapshots when deploying to track changes over time.
Upload IDLs on-chain - Use anchor deploy (which uploads IDL by default) or anchor idl init for client discoverability.
Next steps
TypeScript client Use IDLs with the TypeScript client
IDL CLI reference Complete IDL command reference