It is recommened to instead use my new tool, idlgen. It is Anchor 0.29.0 and Anchor 0.30.0 IDL compatible and creates a production-ready crate with feature flags enabling you to do CPI, Introspection and RPC without having to include your program as a dependency.
IDLgen generates a code scaffold for calling instructions for custom Solana program in Rust based upon its IDL.
Simply insert a compatible IDL into the idlgen!()
macro and you can generate:
- A program struct
- An
impl
containing callable functions to generate validInstructions
, as well as signed and unsignedTransactions
- All required data
structs
for said instructions with Borsh serialization implemented
Add solana-sdk
to your Cargo.toml then consume the macro like so:
use solana_idlgen::idlgen;
idlgen!({
"version": "0.1.0",
"name": "example_program",
"instructions": [{
"name": "example_instrution",
"accounts": [{
"name": "signer",
"isMut": true,
"isSigner": true
}, {
"name": "exampleAccount",
"isMut": true,
"isSigner": false
}, {
"name": "systemProgram",
"isMut": false,
"isSigner": false
}],
"args": [{
"name": "name",
"type": "bytes"
}]
}],
"accounts": [{
"name": "ExampleAccount",
"type": {
"kind": "struct",
"fields": [{
"name": "name",
"type": "bytes"
}]
}
}],
"metadata": {
"address": "11111111111111111111111111111111"
}
});
This will generate:
use borsh::{BorshSerialize, to_vec};
use solana_sdk::{
hash::Hash,
instruction::{AccountMeta, Instruction},
message::Message,
pubkey::Pubkey,
signature::{Keypair, Signer},
transaction::Transaction,
};
#[derive(Debug, BorshSerialize)]
pub struct ExampleArgs {
pub name: Vec<u8>,
}
#[derive(Debug)]
pub struct ExampleProgram {}
impl ExampleProgram {
pub fn id() -> Pubkey {
Pubkey::new_from_array([
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0,
])
}
pub fn example_ix_from_bytes(accounts: &[&Pubkey; 3usize], bytes: &[u8]) -> Instruction {
let account_meta: Vec<AccountMeta> = vec![
AccountMeta::new(accounts[0usize].clone(), true),
AccountMeta::new(accounts[1usize].clone(), false),
AccountMeta::new_readonly(accounts[2usize].clone(), false),
];
Instruction::new_with_bytes(Self::id(), &bytes, account_meta)
}
pub fn example_ix_from_data(accounts: &[&Pubkey; 3usize], args: &ExampleArgs) -> Instruction {
let mut data_bytes: Vec<u8> = vec![189, 174, 40, 25, 180, 44, 109, 58];
data_bytes.extend_from_slice(&to_vec(&args).expect("Unable to serialize data"));
Self::example_ix_from_bytes(accounts, &data_bytes)
}
pub fn example(
accounts: &[&Pubkey; 3usize],
args: &ExampleArgs,
payer: Option<&Pubkey>,
signers: &[&Keypair; 1usize],
blockhash: Hash,
) -> Transaction {
let ix = Self::example_ix_from_data(accounts, args);
Transaction::new_signed_with_payer(&[ix], payer, signers, blockhash)
}
pub fn example_unsigned(
accounts: &[&Pubkey; 3usize],
args: &ExampleArgs,
payer: Option<&Pubkey>,
) -> Transaction {
let ix = Self::example_ix_from_data(accounts, args);
Transaction::new_unsigned(Message::new(&[ix], payer))
}
pub fn derive_program_address(seeds: &[&[u8]]) -> Pubkey {
Self::derive_program_address_and_bump(seeds).0
}
pub fn derive_program_address_and_bump(seeds: &[&[u8]]) -> (Pubkey, u8) {
Pubkey::find_program_address(seeds, &Self::id())
}
}
You can then generate PDAs and call instructions from your program like so:
// Connect to devnet RPC endpoint
let rpc_client = RpcClient::new("https://api.devnet.solana.com".to_string());
// Get the recent blockhash
let blockhash = rpc_client.get_latest_blockhash().unwrap();
let signer = Keypair::new();
// Derive the example PDA from ExampleProgram
let example = ExampleProgram::derive_program_address(&[&b"example".as_ref(), &signer.pubkey().as_ref()]);
// Example instruction args
let args = ExampleArgs {
name: b"anatoly".to_vec()
};
// Call the "example" instruction of ExampleProgram
ExampleProgram::example(
&[
&signer.pubkey(),
&example,
&system_program::id()
],
&args,
Some(&signer.pubkey()),
&[
&signer
],
blockhash
);
- you must add the
solana-sdk
andborsh
withderive
feature enabled to the Cargo.toml of the package where you consumeidlgen
- you must populate the optional IDL
metadata -> address
field to get the Program ID