anchor-parser 0.1.0

Generate Rust types and helpers from Anchor IDL JSON files using solana-sdk directly — no anchor-lang dependency
Documentation

anchor-parser

Generate Rust types and helpers from Anchor IDL JSON files using solana-sdk types directly — no anchor-lang dependency required.

Features

  • Accounts — Structs with discriminator constants and deserialization (Borsh & bytemuck/zero-copy).
  • Events — Structs with from_logs (emit!) and from_cpi_logs (emit_cpi!) parsers.
  • Instructions — Builder functions that return solana_sdk::instruction::Instruction.
  • Types — Shared structs, enums, and type aliases from the IDL.
  • Constants — Program constants with doc comments.
  • UtilsEvent and Account wrapper enums for generic parsing across all program types.
  • Client (optional) — Async fetch_account / fetch_accounts via solana-client.

Installation

[dependencies]
anchor-parser = { path = "." }

To enable the async RPC client (solana-client dependency):

[dependencies]
anchor-parser = { path = ".", features = ["client"] }

Quick start

  1. Place an Anchor IDL JSON file in an idls/ directory at your crate root:
my-crate/
├── Cargo.toml
├── idls/
│   └── my_program.json
└── src/
    └── main.rs
  1. Declare the program:
use anchor_parser::declare_program;

declare_program!(my_program);

This generates a my_program module with the following sub-modules:

Module Contents
my_program::accounts Account structs with DISCRIMINATOR and from_account_data
my_program::events Event structs with from_logs and from_cpi_logs
my_program::instructions Builder functions → Instruction
my_program::types Shared structs, enums, type aliases
my_program::constants Program constants
my_program::utils Event / Account wrapper enums

The program ID is available as my_program::ID.

Usage

Accounts

use my_program::accounts::MyAccount;

// Deserialize from raw account data (discriminator + payload)
let account = MyAccount::from_account_data(&raw_bytes)?;

// Check discriminator
assert_eq!(MyAccount::DISCRIMINATOR, [/* 8 bytes */]);

// Async fetch (requires "client" feature)
use anchor_parser::client;
let account = client::fetch_account::<MyAccount>(&rpc_client, &address).await?;
let accounts = client::fetch_accounts::<MyAccount>(&rpc_client, &[addr1, addr2]).await?;

Events

use my_program::events::SwapEvent;

// Parse events from emit! logs (base64-encoded "Program data:" log lines)
let events = SwapEvent::from_logs(&log_messages);

// Parse events from emit_cpi! inner instruction data (bs58-encoded)
let cpi_events = SwapEvent::from_cpi_logs(&inner_instruction_data);

Both methods accept any IntoIterator<Item = I> where I: AsRef<str> — vec, slice, array, iterator, &[String], &[&str], etc.

Event & Account enums

The utils module provides wrapper enums that try all known discriminators:

use my_program::utils::{Event, Account};

// Parse any program event from logs
let events: Vec<Event> = Event::from_logs(&log_messages);
let events: Vec<Event> = Event::from_cpi_logs(&log_messages);

match &events[0] {
    Event::SwapEvent(e) => println!("swap amount: {}", e.amount_0),
    Event::PoolCreatedEvent(e) => println!("new pool: {}", e.pool_state),
    _ => {}
}

// Parse any program account from raw data
let account: Account = Account::parse(&raw_bytes)?;

Instructions

use my_program::instructions;

let ix = instructions::swap(
    &my_program::ID,
    &instructions::SwapAccounts {
        payer: wallet.pubkey(),
        pool_state: pool_address,
        // ...
    },
    amount,
    min_out,
);
// ix: solana_sdk::instruction::Instruction

Constants

use my_program::constants;

let fee = constants::FEE_DENOMINATOR; // u64
let prefix = constants::POOL_PREFIX;  // &[u8]

from_logs vs from_cpi_logs

Method Source Input Decoding
from_logs emit! Transaction log messages Scans for "Program data: <base64>" lines, base64-decodes, skips 8-byte discriminator, borsh-deserializes
from_cpi_logs emit_cpi! Inner instruction data strings bs58-decodes each string, skips 16 bytes (8-byte CPI event tag + 8-byte discriminator), borsh-deserializes

Both methods share the same generic signature:

pub fn from_logs<T, I>(logs: T) -> Vec<Self>
where
    T: IntoIterator<Item = I>,
    I: AsRef<str>,

How emit_cpi! events work

Anchor's emit_cpi! emits events as self-CPI inner instructions. The instruction data is laid out as:

[8 bytes: Anchor CPI event tag] [8 bytes: event discriminator] [borsh payload]

To use from_cpi_logs, pass the bs58-encoded inner instruction data strings (not log messages) from instructions targeting your program.

Supported serialization formats

Format Accounts Events
Borsh
Bytemuck (zero-copy)
Bytemuck-unsafe (packed)

IDL compatibility

Supports the Anchor IDL spec 0.1.0 JSON format. The IDL file name (without .json) becomes the Rust module name.

Tests

cargo test

The test suite covers three real-world programs:

Program IDL Tests
Pumpfun idls/pumpfun.json 23
Meteora DAMM v2 idls/meteora_damm_v2.json 52
Raydium CLMM idls/raydium_clmm.json 44

License

MIT