Overview
The IDL (Interface Definition Language) is a JSON structure that describes an Anchor program’s interface. It defines instructions, accounts, types, events, and errors in a machine-readable format.
Idl
The root IDL type that describes an entire Anchor program.
type Idl = {
address: string;
metadata: IdlMetadata;
docs?: string[];
instructions: IdlInstruction[];
accounts?: IdlAccount[];
events?: IdlEvent[];
errors?: IdlErrorCode[];
types?: IdlTypeDef[];
constants?: IdlConst[];
};
The on-chain program address.console.log(program.idl.address);
Program metadata including name, version, and spec version.
Documentation comments from the Rust program.
Array of program instructions.
Array of account type definitions.
Array of event definitions.
Array of custom error codes.
Array of custom type definitions (structs, enums).
Array of program constants.
Program metadata and version information.
type IdlMetadata = {
name: string;
version: string;
spec: string;
description?: string;
repository?: string;
dependencies?: IdlDependency[];
contact?: string;
deployments?: IdlDeployments;
};
The program version (e.g., “0.1.0”).
The IDL spec version (e.g., “0.1.0”).
Source code repository URL.
External program dependencies.
IdlInstruction
Defines a single program instruction.
type IdlInstruction = {
name: string;
docs?: string[];
discriminator: IdlDiscriminator;
accounts: IdlInstructionAccountItem[];
args: IdlField[];
returns?: IdlType;
};
The instruction name in snake_case.program.idl.instructions.forEach(ix => {
console.log(ix.name); // e.g., "initialize", "transfer"
});
Documentation from Rust doc comments.
8-byte instruction discriminator for identifying the instruction.
accounts
IdlInstructionAccountItem[]
required
Required accounts for the instruction.
Return type if the instruction returns a value.
IdlInstructionAccount
Defines a single account required by an instruction.
type IdlInstructionAccount = {
name: string;
docs?: string[];
writable?: boolean;
signer?: boolean;
optional?: boolean;
address?: string;
pda?: IdlPda;
relations?: string[];
};
Whether the account is writable. Defaults to false.
Whether the account must be a signer. Defaults to false.
Whether the account is optional. Defaults to false.
Hardcoded account address if known at compile time.
PDA derivation information if this account is a PDA.
Related accounts for validation.
IdlPda
Defines how to derive a Program Derived Address.
type IdlPda = {
seeds: IdlSeed[];
program?: IdlSeed;
};
type IdlSeed = IdlSeedConst | IdlSeedArg | IdlSeedAccount;
type IdlSeedConst = {
kind: "const";
value: number[];
};
type IdlSeedArg = {
kind: "arg";
path: string;
};
type IdlSeedAccount = {
kind: "account";
path: string;
account?: string;
};
Array of seeds used to derive the PDA.
Optional program ID seed. Defaults to the current program.
IdlAccount
Defines an account type stored by the program.
type IdlAccount = {
name: string;
discriminator: IdlDiscriminator;
};
8-byte account discriminator for identifying the account type.
IdlEvent
Defines an event that can be emitted by the program.
type IdlEvent = {
name: string;
discriminator: IdlDiscriminator;
};
Event name in PascalCase.
8-byte event discriminator.
IdlTypeDef
Defines a custom type (struct or enum).
type IdlTypeDef = {
name: string;
docs?: string[];
serialization?: IdlSerialization;
repr?: IdlRepr;
generics?: IdlTypeDefGeneric[];
type: IdlTypeDefTy;
};
type IdlTypeDefTy =
| IdlTypeDefTyEnum
| IdlTypeDefTyStruct
| IdlTypeDefTyType;
type IdlTypeDefTyStruct = {
kind: "struct";
fields?: IdlDefinedFields;
};
type IdlTypeDefTyEnum = {
kind: "enum";
variants: IdlEnumVariant[];
};
type IdlTypeDefTyType = {
kind: "type";
alias: IdlType;
};
The type definition (struct, enum, or type alias).
IdlType
Represents any Anchor type.
type IdlType =
| "bool"
| "u8" | "i8"
| "u16" | "i16"
| "u32" | "i32"
| "f32"
| "u64" | "i64"
| "f64"
| "u128" | "i128"
| "u256" | "i256"
| "bytes"
| "string"
| "pubkey"
| IdlTypeOption
| IdlTypeCOption
| IdlTypeVec
| IdlTypeArray
| IdlTypeDefined
| IdlTypeGeneric;
Primitive Types
u8, i8, u16, i16, u32, i32
Integer types that fit in JavaScript number.
u64, i64, u128, i128, u256, i256
Large integer types requiring BN (Big Number) in JavaScript.
Complex Types
type IdlTypeOption = {
option: IdlType;
};
type IdlTypeCOption = {
coption: IdlType;
};
type IdlTypeVec = {
vec: IdlType;
};
type IdlTypeArray = {
array: [idlType: IdlType, size: IdlArrayLen];
};
type IdlTypeDefined = {
defined: {
name: string;
generics?: IdlGenericArg[];
};
};
Compact option for primitives.
Dynamic array (Rust Vec<T>).
array
{ array: [IdlType, number] }
Fixed-size array.
defined
{ defined: { name: string } }
Custom defined type (references a type in idl.types).
IdlField
A named field with a type.
type IdlField = {
name: string;
docs?: string[];
type: IdlType;
};
IdlErrorCode
Defines a custom error.
type IdlErrorCode = {
name: string;
code: number;
msg?: string;
};
Error code (starting at 6000 for custom errors).
Helper Functions
convertIdlToCamelCase
<I extends Idl>(idl: I) => I
Converts IDL from snake_case to camelCase for TypeScript.import { convertIdlToCamelCase } from "@anchor-lang/anchor";
const camelIdl = convertIdlToCamelCase(rawIdl);
The Program class automatically applies this conversion.
idlAddress
async (programId: PublicKey) => Promise<PublicKey>
Derives the deterministic IDL account address for a program.import { idlAddress } from "@anchor-lang/anchor";
const idlPubkey = await idlAddress(programId);
decodeIdlAccount
(data: Buffer) => IdlProgramAccount
Decodes an on-chain IDL account.import { decodeIdlAccount } from "@anchor-lang/anchor";
const accountInfo = await connection.getAccountInfo(idlAddress);
const idlAccount = decodeIdlAccount(accountInfo.data.slice(8));
isCompositeAccounts
(item: IdlInstructionAccountItem) => boolean
Type guard to check if an account item is a nested accounts group.import { isCompositeAccounts } from "@anchor-lang/anchor";
if (isCompositeAccounts(accountItem)) {
// Handle nested accounts
accountItem.accounts.forEach(...);
}
Usage Examples
Reading IDL Structure
import { Program } from "@anchor-lang/anchor";
const program = anchor.workspace.MyProgram;
const idl = program.idl;
console.log("Program:", idl.metadata.name);
console.log("Version:", idl.metadata.version);
console.log("Address:", idl.address);
// List all instructions
idl.instructions.forEach(ix => {
console.log(`Instruction: ${ix.name}`);
console.log(` Args: ${ix.args.map(a => `${a.name}: ${JSON.stringify(a.type)}`).join(", ")}`);
console.log(` Accounts: ${ix.accounts.map(a => a.name).join(", ")}`);
});
// List all account types
idl.accounts?.forEach(acc => {
console.log(`Account: ${acc.name}`);
});
// List all custom types
idl.types?.forEach(type => {
console.log(`Type: ${type.name}`);
});
Working with Types
const idl = program.idl;
// Find a specific type
const myStruct = idl.types?.find(t => t.name === "MyStruct");
if (myStruct && myStruct.type.kind === "struct") {
console.log("Struct fields:");
myStruct.type.fields?.forEach((field: any) => {
console.log(` ${field.name}: ${JSON.stringify(field.type)}`);
});
}
// Find an enum
const myEnum = idl.types?.find(t => t.name === "MyEnum");
if (myEnum && myEnum.type.kind === "enum") {
console.log("Enum variants:");
myEnum.type.variants.forEach(variant => {
console.log(` ${variant.name}`);
});
}
Fetching IDL from Chain
import { Program, AnchorProvider } from "@anchor-lang/anchor";
import { Connection, PublicKey } from "@solana/web3.js";
const connection = new Connection("https://api.devnet.solana.com");
const programId = new PublicKey("YourProgramId");
// Fetch IDL from blockchain
const idl = await Program.fetchIdl(programId, provider);
if (idl) {
console.log("Fetched IDL:", idl.metadata.name);
// Create program with fetched IDL
const program = new Program(idl, provider);
} else {
console.log("IDL not found on-chain");
}
Type Checking
import { IdlType } from "@anchor-lang/anchor";
function describeType(type: IdlType): string {
if (typeof type === "string") {
return type; // Primitive type
}
if ("vec" in type) {
return `Vec<${describeType(type.vec)}>`;
}
if ("option" in type) {
return `Option<${describeType(type.option)}>`;
}
if ("array" in type) {
return `[${describeType(type.array[0])}; ${type.array[1]}]`;
}
if ("defined" in type) {
return type.defined.name;
}
return "unknown";
}
// Use with IDL
idl.instructions.forEach(ix => {
ix.args.forEach(arg => {
console.log(`${arg.name}: ${describeType(arg.type)}`);
});
});
Generating Documentation
const idl = program.idl;
// Generate markdown documentation
let markdown = `# ${idl.metadata.name}\n\n`;
markdown += `Version: ${idl.metadata.version}\n\n`;
if (idl.metadata.description) {
markdown += `${idl.metadata.description}\n\n`;
}
markdown += `## Instructions\n\n`;
idl.instructions.forEach(ix => {
markdown += `### ${ix.name}\n\n`;
if (ix.docs) {
markdown += `${ix.docs.join("\n")}\n\n`;
}
markdown += `**Arguments:**\n`;
ix.args.forEach(arg => {
markdown += `- \`${arg.name}\`: ${describeType(arg.type)}\n`;
});
markdown += `\n`;
});
console.log(markdown);