All Articles

Anchor IDL and managing older versions

anchor idl

What if a Solana Anchor program is created with an older version of Anchor and you want to interact with it using a newer version of the Anchor framework? Or what if the on-chain program does not provide source code, but only the IDL? This article may help with all of this.

Anchor IDL

Anchor IDL (Interface Definition Language) is a JSON file that describes the interface of an Anchor program. It includes information about the program’s instructions, accounts, types, and events. The IDL is used by the Anchor framework to generate client code that can interact with the program.

The IDL is generated automatically when you build an Anchor program using the Anchor CLI. The IDL file is typically located in the target/idl directory of your Anchor project. Any good Anchor program should have the IDL file available on-chain, so you can fetch it using the program’s public key. One way is to check the Solana Explorer. See for example the IDL of Marinade Liquid Staking program.

Another way to fetch the IDL is to use the Anchor CLI:

anchor idl fetch <PROGRAM_ID> -o <OUTPUT_FILE>

E.g., for Marinade Liquid Staking program:

anchor idl fetch MarBmsSgKXdrN1egZf5sqe1TMai9K1rChYNDJgjq7aD | head -n 10
{
  "version": "0.1.0",
  "name": "marinade_finance",
  "instructions": [
    {
      "name": "initialize",
      "accounts": [
        {
          "name": "state",
          "isMut": true,

Besides the IDL file, the Anchor program provides a binding for TypeScript client code. When an Anchor program is built, the TypeScript bindings are generated in the target/types directory. It is not available directly on-chain.

Issues of installing older Solana client

Let’s take a look at Marinade Liquid Staking program. It is a program that was written quite some time ago, and it uses an older version of Anchor in version 0.27.0. That’s a version released in March 2023 and it is dependent on Solana client v1.14.x. The source code of the Liquid Staking program is available on GitHub, but just building it is a bit problematic as the older versions of Solana client cannot be easily installed with the curl toolchain command.

This one just does not work:

sh -c "$(curl -sSfL https://release.anza.xyz/v1.14.29/install)"

> curl: (22) The requested URL returned error: 404

The solution requires a few manual steps as the older versions of Solana client are still available on GitHub.

  1. Download the Solana v1.14.29 tarball from the Solana releases page. In my case, I downloaded the solana-release-x86_64-unknown-linux-gnu.tar.bz2 file.

  2. Extract the tarball to $HOME/.local/share/solana/install/releases/<version> directory.

    VERSION=1.14.29
    mkdir -p $HOME/.local/share/solana/install/releases/$VERSION
    # creating the releases directory: $HOME/.local/share/solana/install/releases/$VERSION/solana-release/
    tar xvjf solana-release-x86_64-unknown-linux-gnu.tar.bz2 -C $HOME/.local/share/solana/install/releases/$VERSION
  3. Symlink the newly extracted directory

    rm -f $HOME/.local/share/solana/install/active_release
    ln -s $HOME/.local/share/solana/install/releases/$VERSION/solana-release $HOME/.local/share/solana/install/active_release
  4. Install the Anchor version

    # expected that the avm tool is already installed
    avm install 0.27.0
    avm use 0.27.0
  5. Now you can build the Anchor program

    git clone https://github.com/marinade-finance/liquid-staking-program.git
    cd liquid-staking-program/
    anchor build
    
    ls target/idl/
    > marinade_finance.json
    ls target/types/
    > marinade_finance.ts

Different version of Anchor and IDL

The Anchor library is (hopefully we can say was) deeply coupled with particular versions of the Solana toolchain. So working with a program coded in Anchor version 0.27.0 where Solana version 1.17.x was required was problematic when integrating CPI calls or client linking with newer versions (e.g. Anchor 0.29.0).

Anchor developers strive to address this issue by making Anchor more independent of Solana tooling but these difficulties still occur especially when working with programs coded in older Anchor versions.

Additionally, from the past to nowadays there was one substantial change of IDL format that was introduced in version 0.30.0 and made required in Anchor 0.30.1. This is an issue especially for Anchor TypeScript SDK client that uses the IDL to understand the on-chain program structure and to serialize and deserialize the instruction calls to the program.

Converting IDL to the newer format

Anchor CLI from version 0.30.1 provides a utility that is capable of converting the IDL from the older format to the newer one. The command is anchor idl convert.

Running it simply with an input file does not work in case the IDL format does not contain the metadata.address field. This field is required in the newer IDL format and it has to be provided manually when converting the IDL.

# downloading the IDL from the chain of older version
anchor --provider.cluster mainnet idl fetch \
  MarBmsSgKXdrN1egZf5sqe1TMai9K1rChYNDJgjq7aD > /tmp/marinade_finance.json

# converting the IDL to the newer format
anchor idl convert /tmp/marinade_finance.json \
  --program-id MarBmsSgKXdrN1egZf5sqe1TMai9K1rChYNDJgjq7aD \
  -o /tmp/marinade_finance_v0.30.0.json

Program Rust dependencies

The way to overcome the issue with Anchor version incompatibilities, there are two options in Rust. Either using the third party crate anchor-gen that makes it possible to work with programs of the older Anchor. From Anchor 0.30.0 there is an option to use macro declare_program! doing similar.

WARNING be aware that anchor-gen code has not been audited

declare_program! macro

When using the Anchor 0.30.1+ then declare_program is probably the way to go for CPI calls within the program. The example usage of the macro can be found in the Anchor sources.

The idea is to have the idls alongside the program using the macro. The declare_program! then generates the required Rust structures that can be used for interacting with the program defined in the IDL.

Similarly for client calls you download the idl into a specific idls directory and then use the macro declare_program! with the name of the idl.json file. The use directive then declares what instructions and accounts are imported to the client code. Here is an example of the usage [in Anchor client calls.

Anchor documents this approach at the chapter Dependency free composability.

anchor-gen crate

When using the prior version of Anchor then you have to either upgrade (possibly recommended) or use the anchor-gen library. That works in a similar manner where a macro generates for you Rust code based on IDL and that can be used within your project. As a difference there is created a separate "CPI crate" for the program you want to interact with. You can see the example of declaration of Marinade Liquid Staking crate inside of the anchor-gen/examples.

You can see that you create the anchor-gen project and place the IDL definition into idl.json and from here the anchor_gen::generate_cpi_crate macro builds the Rust structures and functions that can be called from your project.

This crate is linked to your project and used. Here is an example of the usage in Anchor client calls.

It could be good to align the versions of anchor-gen that work with particular Anchor versions to make it work with your project.

TypeScript client

When you have the IDL file you can use it in your TypeScript client code. As mentioned before it is important to have the IDL in a format aligned with the Anchor client version you are using.

On building the Anchor program the TypeScript bindings are generated in the target/types directory while the IDL is in the target/idl directory. The on-chain program provides the IDL only. Having types for the TypeScript client is nice to have as it provides type safety. When not having access to Anchor program source code you can generate the TypeScript types from the IDL using my utility anchor-idl-to-ts.

anchor --provider.cluster mainnet idl fetch \
  MarBmsSgKXdrN1egZf5sqe1TMai9K1rChYNDJgjq7aD > /tmp/marinade_finance.json

cargo install anchor-idl-to-ts
anchor-idl-to-ts /tmp/marinade_finance.json -o /tmp/marinade_finance.ts

With IDL and types you can use both in your TypeScript client code. The code that creates the Anchor program client looks similar. The most notable difference is that for older versions you need to manually provide the program address as it is not present in the IDL.

import { PublicKey } from '@solana/web3.js'
import { IDL as MarinadeIDL, MarinadeFinance } from './types/marinade'
import { AnchorProvider, IdlAccounts, Program, Wallet } from '@coral-xyz/anchor'

const MARINADE_LIQUID_STAKE_PROGRAM_ID = new PublicKey("MarBmsSgKXdrN1egZf5sqe1TMai9K1rChYNDJgjq7aD")
const MARINADE_LIQUID_STAKE_STATE = new PublicKey("8szGkuLTAux9XMgZ2vtY39jVSowEcpBfFfD8hXSEqdGC")

export type MarinadeProgram = Program<MarinadeFinance>
export type MarinadeState = IdlAccounts<MarinadeFinance>['state']

...

const wallet = new Wallet(keypair);
const provider = new AnchorProvider(
   connection,
   wallet,
   AnchorProvider.defaultOptions(),
)
const program = new Program<MarinadeFinance>(MarinadeIDL, MARINADE_LIQUID_STAKE_PROGRAM_ID, provider)
const marinadeState = await program.account.state.fetch(MARINADE_LIQUID_STAKE_STATE)

You can see the example code for:

Troubleshooting

Missing idl.metadata.address field

The declare_program! macro is developed to be capable of working with IDLs generated in the older versions of Anchor. But there are some pitfalls that you may encounter.

One is when the IDL does not contain the metadata.address field. When Anchor was generating the IDL in the older versions, the metadata address field was not added until the IDL was uploaded to the chain. For manual IDL deployment it meant that the address field was not present in the IDL. You may see an error like this:

error: Program id missing in `idl.metadata.address` field
  --> src/main.rs:10:18
   |
10 | declare_program!(marinade);

In older versions of Anchor the IDL was not created with the address field by default. The address was added to the IDL for example only at the time the IDL was uploaded to the chain.

Name collision

When working with an older IDL it may happen that the names of accounts or instructions collide. They were not separated as strictly as nowadays. For example, in the Marinade Liquid Staking program there is a named set of accounts used for an instruction in the same way as common that generates the name collision.

error[E0428^]: the name `__cpi_client_accounts_common` is defined multiple times
  --> src/main.rs:10:1
   |
10 | declare_program!(marinade);
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^ `__cpi_client_accounts_common` redefined here
   |
   = note: `__cpi_client_accounts_common` must be defined only once in the type namespace of this module
   = note: this error originates in the macro `declare_program` (in Nightly builds, run with -Z macro-backtrace for more info)

My resolution was to manually edit the IDL and rename the common instruction account set to common_<suffix>.

Summary

This article addresses the common challenge of working with Solana Anchor programs built with older framework versions when you need to interact with them using newer versions. It covers how to fetch and convert IDL files between different formats, particularly the breaking change introduced in Anchor 0.30.0. You’ll learn two main approaches for Rust integration (declare_program! macro for newer versions and the anchor-gen crate for older ones) and how to set up TypeScript clients that work across versions.

Published Oct 16, 2025

Developer notes.