Skip to main content
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:
anchor build
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:
anchor idl build

Fetch IDL

Download IDL from a deployed program:
anchor idl fetch <PROGRAM_ID>

Initialize IDL account

Upload IDL to the blockchain:
anchor idl init -f target/idl/my_program.json <PROGRAM_ID>

Upgrade IDL

Update an existing on-chain IDL:
anchor idl upgrade <PROGRAM_ID> -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

Build docs developers (and LLMs) love