anchor_parser/lib.rs
1#![cfg_attr(docsrs, feature(doc_cfg))]
2
3//! # anchor-parser
4//!
5//! [](https://crates.io/crates/anchor-parser)
6//! [](https://docs.rs/anchor-parser)
7//! [](https://github.com/goni098/anchor-parser/blob/main/LICENSE)
8//!
9//! Generate Rust types and helpers from [Anchor](https://www.anchor-lang.com/) IDL
10//! JSON files using [`solana-sdk`] types directly — no `anchor-lang` dependency required.
11//!
12//! # Quick start
13//!
14//! ```toml
15//! [dependencies]
16//! anchor-parser = "0.1.3"
17//! ```
18//!
19//! Place an Anchor IDL JSON file at `<crate-root>/idls/<name>.json`, then:
20//!
21//! ```ignore
22//! anchor_parser::declare_program!(my_program);
23//! ```
24//!
25//! # Generated modules
26//!
27//! [`declare_program!`] generates a module with the following sub-modules:
28//!
29//! | Module | Contents |
30//! |--------|----------|
31//! | `accounts` | Account structs with [`AccountDeserialize`] and `from_account_data` |
32//! | `events` | Event structs with `from_logs` and `from_cpi_logs` |
33//! | `instructions` | Builder functions returning [`solana_sdk::instruction::Instruction`] |
34//! | `types` | Shared structs, enums, and type aliases from the IDL |
35//! | `constants` | Program constants with doc comments |
36//! | `utils` | `Event` / `Account` wrapper enums for generic parsing |
37//!
38//! # Examples
39//!
40//! ## Deserializing accounts
41//!
42//! ```ignore
43//! use my_program::accounts::MyAccount;
44//!
45//! // Deserialize from raw account data (discriminator + payload)
46//! let account = MyAccount::from_account_data(&raw_bytes)?;
47//!
48//! // Check discriminator
49//! assert_eq!(MyAccount::DISCRIMINATOR, [/* 8 bytes */]);
50//! ```
51//!
52//! ## Fetching accounts via RPC
53//!
54//! *Requires the [`client`] feature.*
55//!
56//! Every account type gets `fetch` and `fetch_multiple` methods via the
57//! [`AccountDeserialize`] trait:
58//!
59//! ```ignore
60//! use anchor_parser::AccountDeserialize;
61//! use my_program::accounts::MyAccount;
62//!
63//! // Fetch a single account
64//! let account = MyAccount::fetch(&rpc, &address).await?;
65//!
66//! // Fetch multiple accounts at once
67//! let accounts = MyAccount::fetch_multiple(&rpc, &[addr1, addr2]).await?;
68//! ```
69//!
70//! Or use the standalone functions from the [`client`] module:
71//!
72//! ```ignore
73//! use anchor_parser::client;
74//!
75//! let account = client::fetch_account::<MyAccount>(&rpc, &address).await?;
76//! ```
77//!
78//! ## Parsing events
79//!
80//! ```ignore
81//! use my_program::events::SwapEvent;
82//! use my_program::utils::Event;
83//!
84//! // Parse events from emit! log lines
85//! let events = SwapEvent::from_logs(&log_messages);
86//! let all_events = Event::from_logs(&log_messages);
87//!
88//! // Parse events from emit_cpi! inner instruction data (bs58-encoded)
89//! let events = SwapEvent::from_cpi_logs(&inner_ix_data_strings);
90//! let all_events = Event::from_cpi_logs(&inner_ix_data_strings);
91//! ```
92//!
93//! ## Building instructions
94//!
95//! ```ignore
96//! use my_program::instructions;
97//!
98//! let ix = instructions::swap(
99//! &my_program::ID,
100//! &instructions::SwapAccounts { /* ... */ },
101//! amount,
102//! min_out,
103//! );
104//! ```
105//!
106//! # Feature flags
107//!
108//! | Feature | Description |
109//! |---------|-------------|
110//! | `client` | Enables [`AccountDeserialize::fetch`] / [`AccountDeserialize::fetch_multiple`] and the [`client`] module via `solana-client` |
111
112/// Generates a module from an Anchor IDL JSON file.
113///
114/// Looks for `idls/{name}.json` by walking up from `CARGO_MANIFEST_DIR`.
115///
116/// # Generated items
117///
118/// - `ID` — program [`Pubkey`](solana_sdk::pubkey::Pubkey)
119/// - `accounts` — account structs implementing [`AccountDeserialize`]
120/// - `events` — event structs with `from_logs` / `from_cpi_logs`
121/// - `instructions` — builder functions returning [`Instruction`](solana_sdk::instruction::Instruction)
122/// - `types` — shared structs, enums, and type aliases
123/// - `constants` — program constants
124/// - `utils` — `Event` / `Account` wrapper enums
125///
126/// # Example
127///
128/// ```ignore
129/// anchor_parser::declare_program!(my_program);
130///
131/// // Now use my_program::accounts, my_program::events, etc.
132/// ```
133pub use anchor_parser_macros::declare_program;
134
135/// Async RPC helpers for fetching and deserializing on-chain accounts.
136///
137/// Enable with the `client` feature:
138///
139/// ```toml
140/// [dependencies]
141/// anchor-parser = { version = "0.1.3", features = ["client"] }
142/// ```
143///
144/// # Example
145///
146/// ```ignore
147/// use anchor_parser::client;
148/// use my_program::accounts::MyAccount;
149///
150/// let account = client::fetch_account::<MyAccount>(&rpc, &address).await?;
151/// let accounts = client::fetch_accounts::<MyAccount>(&rpc, &[a1, a2]).await?;
152/// ```
153#[cfg(feature = "client")]
154#[cfg_attr(docsrs, doc(cfg(feature = "client")))]
155pub mod client;
156
157/// Trait implemented by all generated account types.
158///
159/// Provides a discriminator constant and a method to deserialize from raw
160/// on-chain account data (discriminator prefix + serialized payload).
161///
162/// You generally won't need to use this trait directly — generated account
163/// types also expose a convenient `from_account_data` method.
164///
165/// # Example
166///
167/// ```ignore
168/// use anchor_parser::AccountDeserialize;
169///
170/// fn parse<T: AccountDeserialize>(data: &[u8]) -> std::io::Result<T> {
171/// T::deserialize(data)
172/// }
173/// ```
174pub trait AccountDeserialize: Sized {
175 /// The discriminator bytes that prefix this account's on-chain data.
176 const DISCRIMINATOR: &'static [u8];
177
178 /// Deserialize from raw account data (including the discriminator prefix).
179 ///
180 /// Returns an error if the data is too short, the discriminator doesn't
181 /// match, or deserialization fails.
182 fn deserialize(data: &[u8]) -> Result<Self, std::io::Error>;
183
184 /// Fetch and deserialize a single account from an async RPC client.
185 ///
186 /// Requires the `client` feature.
187 ///
188 /// # Example
189 ///
190 /// ```ignore
191 /// use my_program::accounts::MyAccount;
192 ///
193 /// let account = MyAccount::fetch(&rpc, &address).await?;
194 /// ```
195 #[cfg(feature = "client")]
196 #[cfg_attr(docsrs, doc(cfg(feature = "client")))]
197 fn fetch(
198 client: &solana_client::nonblocking::rpc_client::RpcClient,
199 address: &solana_sdk::pubkey::Pubkey,
200 ) -> impl std::future::Future<Output = Result<Self, solana_client::client_error::ClientError>>
201 {
202 crate::client::fetch_account::<Self>(client, address)
203 }
204
205 /// Fetch and deserialize multiple accounts in a single RPC call.
206 ///
207 /// Returns `None` for accounts that don't exist or fail deserialization.
208 ///
209 /// Requires the `client` feature.
210 ///
211 /// # Example
212 ///
213 /// ```ignore
214 /// use my_program::accounts::MyAccount;
215 ///
216 /// let accounts = MyAccount::fetch_multiple(&rpc, &[a1, a2]).await?;
217 /// ```
218 #[cfg(feature = "client")]
219 #[cfg_attr(docsrs, doc(cfg(feature = "client")))]
220 fn fetch_multiple(
221 client: &solana_client::nonblocking::rpc_client::RpcClient,
222 addresses: &[solana_sdk::pubkey::Pubkey],
223 ) -> impl std::future::Future<
224 Output = Result<Vec<Option<Self>>, solana_client::client_error::ClientError>,
225 > {
226 crate::client::fetch_accounts::<Self>(client, addresses)
227 }
228}
229
230#[doc(hidden)]
231pub mod __private {
232 pub use solana_sdk::instruction::{AccountMeta, Instruction};
233 pub use solana_sdk::pubkey::Pubkey;
234
235 pub use borsh::{BorshDeserialize, BorshSerialize};
236 pub use bytemuck::{Pod, Zeroable};
237
238 #[inline]
239 pub fn base64_decode(input: &str) -> Option<Vec<u8>> {
240 use base64::Engine;
241 base64::engine::general_purpose::STANDARD.decode(input).ok()
242 }
243
244 #[inline]
245 pub fn bs58_decode(input: &str) -> Option<Vec<u8>> {
246 bs58::decode(input).into_vec().ok()
247 }
248
249 #[inline]
250 pub fn bytemuck_read<T: bytemuck::Pod>(data: &[u8]) -> T {
251 bytemuck::pod_read_unaligned(data)
252 }
253}