Skip to main content

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[];
};
address
string
required
The on-chain program address.
console.log(program.idl.address);
metadata
IdlMetadata
required
Program metadata including name, version, and spec version.
docs
string[]
Documentation comments from the Rust program.
instructions
IdlInstruction[]
required
Array of program instructions.
accounts
IdlAccount[]
Array of account type definitions.
events
IdlEvent[]
Array of event definitions.
errors
IdlErrorCode[]
Array of custom error codes.
types
IdlTypeDef[]
Array of custom type definitions (structs, enums).
constants
IdlConst[]
Array of program constants.

IdlMetadata

Program metadata and version information.
type IdlMetadata = {
  name: string;
  version: string;
  spec: string;
  description?: string;
  repository?: string;
  dependencies?: IdlDependency[];
  contact?: string;
  deployments?: IdlDeployments;
};
name
string
required
The program name.
version
string
required
The program version (e.g., “0.1.0”).
spec
string
required
The IDL spec version (e.g., “0.1.0”).
description
string
Program description.
repository
string
Source code repository URL.
dependencies
IdlDependency[]
External program dependencies.

IdlInstruction

Defines a single program instruction.
type IdlInstruction = {
  name: string;
  docs?: string[];
  discriminator: IdlDiscriminator;
  accounts: IdlInstructionAccountItem[];
  args: IdlField[];
  returns?: IdlType;
};
name
string
required
The instruction name in snake_case.
program.idl.instructions.forEach(ix => {
  console.log(ix.name); // e.g., "initialize", "transfer"
});
docs
string[]
Documentation from Rust doc comments.
discriminator
number[]
required
8-byte instruction discriminator for identifying the instruction.
accounts
IdlInstructionAccountItem[]
required
Required accounts for the instruction.
args
IdlField[]
required
Instruction arguments.
returns
IdlType
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[];
};
name
string
required
Account name.
writable
boolean
Whether the account is writable. Defaults to false.
signer
boolean
Whether the account must be a signer. Defaults to false.
optional
boolean
Whether the account is optional. Defaults to false.
address
string
Hardcoded account address if known at compile time.
pda
IdlPda
PDA derivation information if this account is a PDA.
relations
string[]
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;
};
seeds
IdlSeed[]
required
Array of seeds used to derive the PDA.
program
IdlSeed
Optional program ID seed. Defaults to the current program.

IdlAccount

Defines an account type stored by the program.
type IdlAccount = {
  name: string;
  discriminator: IdlDiscriminator;
};
name
string
required
The account type name.
discriminator
number[]
required
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;
};
name
string
required
Event name in PascalCase.
discriminator
number[]
required
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;
};
name
string
required
Type name.
type
IdlTypeDefTy
required
The type definition (struct, enum, or type alias).
generics
IdlTypeDefGeneric[]
Generic type parameters.

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

bool
boolean
Boolean value.
u8, i8, u16, i16, u32, i32
number
Integer types that fit in JavaScript number.
u64, i64, u128, i128, u256, i256
BN
Large integer types requiring BN (Big Number) in JavaScript.
f32, f64
number
Floating point types.
bytes
Uint8Array
Raw bytes.
string
string
UTF-8 string.
pubkey
PublicKey
Solana public key.

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[];
  };
};
option
{ option: IdlType }
Rust Option<T> type.
coption
{ coption: IdlType }
Compact option for primitives.
vec
{ vec: IdlType }
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;
};
name
string
required
Field name.
type
IdlType
required
Field type.
docs
string[]
Field documentation.

IdlErrorCode

Defines a custom error.
type IdlErrorCode = {
  name: string;
  code: number;
  msg?: string;
};
name
string
required
Error name.
code
number
required
Error code (starting at 6000 for custom errors).
msg
string
Error message.

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);

Build docs developers (and LLMs) love