IDL Generation
How Quasar generates program interface descriptions and client code from your Rust source.
The IDL is a JSON file describing your program's interface -- instructions, accounts, events, types, and errors. Generated from your Rust source by the CLI, it drives TypeScript and Rust client generation, and is consumed by declare_program! for typed CPI at compile time.
What the IDL Contains
{
"address": "22222222222222222222222222222222222222222222",
"metadata": { "name": "quasar_escrow", "version": "0.1.0", "spec": "0.1.0" },
"instructions": [ ... ],
"accounts": [ ... ],
"events": [ ... ],
"types": [ ... ],
"errors": [ ... ]
}Instructions
Each instruction includes its name, discriminator bytes, accounts with flags, and typed arguments:
{
"name": "Make",
"discriminator": [0],
"accounts": [
{ "name": "maker", "writable": true, "signer": true },
{ "name": "escrow", "writable": true, "pda": {
"seeds": [
{ "kind": "const", "value": [101, 115, 99, 114, 111, 119] },
{ "kind": "account", "path": "maker" }
]
}},
{ "name": "mintA" },
{ "name": "makerTaA", "writable": true },
{ "name": "vaultTaA", "writable": true },
{ "name": "tokenProgram", "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" },
{ "name": "systemProgram", "address": "11111111111111111111111111111111" }
],
"args": [
{ "name": "deposit", "type": "u64" },
{ "name": "receive", "type": "u64" }
]
}- PDA seeds are encoded as
const(literal bytes) oraccount(reference to another account) writableandsignerflags appear only whentrue- Known program addresses are included as
addressfields - Instructions using
CtxWithRemainingsethasRemaining: true
Accounts and events each list their name and discriminator. Event discriminators are prefixed with 0xFF on the wire to distinguish them from instructions.
Types
Struct types referenced by instructions or accounts. The only non-obvious mapping is pubkey for Address -- everything else (u8, u64, bool, etc.) maps directly to its Rust equivalent:
{
"name": "Escrow",
"type": {
"kind": "struct",
"fields": [
{ "name": "maker", "type": "pubkey" },
{ "name": "mintA", "type": "pubkey" },
{ "name": "receive", "type": "u64" },
{ "name": "bump", "type": "u8" }
]
}
}Dynamic fields have dedicated representations:
{ "name": "label", "type": { "string": { "maxLength": 32 } } }
{ "name": "signers", "type": { "vec": { "items": "pubkey", "maxLength": 10 } } }
{ "name": "data", "type": { "tail": { "element": "u8" } } }| IDL | Rust | Description |
|---|---|---|
string | String<'a, MAX> | Length-prefixed string with max byte count |
vec | Vec<'a, T, MAX> | Length-prefixed array with max element count |
tail | &'a [u8] / &'a str | Trailing data, no length prefix |
Errors
#[error_code] variants are offset by 6000. Framework errors (QuasarError) start at 3000:
{ "code": 6000, "name": "InvalidAmount", "msg": "Invalid amount" }Generating the IDL
quasar idl path/to/programOutput structure
target/
idl/
my_program.idl.json
client/
typescript/
my_program/
web3.ts # @solana/web3.js v2
kit.ts # @solana/kit
package.json
rust/
my-program-client/
Cargo.toml
src/lib.rs # Typed instruction buildersTypeScript clients
Two flavors generated from the same IDL -- one for @solana/web3.js v2, one for @solana/kit. Both include typed instruction builders, account/event type definitions, discriminator constants, and codec functions.
Rust client
A standalone crate with typed Instruction builders. Each instruction struct implements From<T> for Instruction:
let ix: Instruction = DepositInstruction {
user: user_pubkey,
vault: vault_pda,
system_program: system_program::ID,
amount: 1_000_000,
}.into();Next Steps
- Program Structure -- how the
#[program]macro generates the dispatch table - Instructions -- discriminators and argument handling
- CPI --
declare_program!for typed cross-program calls
