123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778 |
- ---
- title: Dependency Free Composability
- description:
- Learn how to use Anchor's declare_program macro to interact with programs
- without additional dependencies.
- ---
- The
- [`declare_program!()`](https://github.com/coral-xyz/anchor/tree/0e5285aecdf410fa0779b7cd09a47f235882c156/lang/attribute/program/src/declare_program)
- macro simplifies the process of interacting with Anchor programs by generating
- Rust modules (from a program's IDL) that can be used in both on-chain and
- off-chain code. You can find an example program
- [here](https://github.com/coral-xyz/anchor/tree/master/tests/declare-program).
- The following modules are generated by the `declare_program!()` macro:
- | Module | Description |
- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
- | [`cpi`](https://github.com/coral-xyz/anchor/blob/0e5285aecdf410fa0779b7cd09a47f235882c156/lang/attribute/program/src/declare_program/mods/cpi.rs) | Helper functions for making cross-program invocations (CPIs) to the program from other on-chain programs |
- | [`client`](https://github.com/coral-xyz/anchor/blob/0e5285aecdf410fa0779b7cd09a47f235882c156/lang/attribute/program/src/declare_program/mods/client.rs) | Accounts and arguments required to build program instructions to add to client-side transactions |
- | [`account`](https://github.com/coral-xyz/anchor/blob/0e5285aecdf410fa0779b7cd09a47f235882c156/lang/attribute/program/src/declare_program/mods/accounts.rs) | Account data types (program state) defined in the program |
- | [`program`](https://github.com/coral-xyz/anchor/blob/0e5285aecdf410fa0779b7cd09a47f235882c156/lang/attribute/program/src/declare_program/mods/program.rs) | Program ID constant used to identify the program |
- | [`constants`](https://github.com/coral-xyz/anchor/blob/0e5285aecdf410fa0779b7cd09a47f235882c156/lang/attribute/program/src/declare_program/mods/constants.rs) | Program constants defined in the program |
- | [`events`](https://github.com/coral-xyz/anchor/blob/0e5285aecdf410fa0779b7cd09a47f235882c156/lang/attribute/program/src/declare_program/mods/events.rs) | Program events defined in the program |
- | [`types`](https://github.com/coral-xyz/anchor/blob/0e5285aecdf410fa0779b7cd09a47f235882c156/lang/attribute/program/src/declare_program/mods/types.rs) | Program types defined in the program |
- ## Examples
- The following examples demonstrate how to use the `declare_program!()` macro in
- two scenarios:
- 1. Making Cross Program Invocations (CPIs) from one program to another program
- 2. Building client-side transactions to invoke a program's instructions
- Both examples show how the modules generated by the `declare_program!()` macro
- simplify program interactions, whether you're writing on-chain or off-chain
- code.
- ### On-chain CPI
- To use the `declare_program!()` macro, you need the IDL file for the target
- program. The IDL file must be placed in a directory named `/idls` in your
- project. The `/idls` directory can be located at any level in your project
- structure. For example, your project could have this layout:
- <Files>
- <Folder name="idls" defaultOpen={true}>
- <File name="example.json" />
- </Folder>
- <Folder name="programs" defaultOpen={true}>
- <Folder name="example-cpi" defaultOpen={true}>
- <Folder name="src" defaultOpen={true}>
- <File name="lib.rs" />
- </Folder>
- <File name="Cargo.toml" />
- </Folder>
- </Folder>
- </Files>
- Below is the source code (`lib.rs`) for the target (callee) program that
- generates the `example.json` IDL file shown above.
- Using the program's IDL file, another program can use the `declare_program!()`
- macro to generate a CPI module, enabling it to make CPIs to this program's
- instructions.
- <Tabs items={["Callee Program", "IDL"]}>
- ```rust tab="Callee Program"
- use anchor_lang::prelude::*;
- declare_id!("8HupNBr7SBhBLcBsLhbtes3tCarBm6Bvpqp5AfVjHuj8");
- #[program]
- pub mod example {
- use super::*;
- pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
- let counter = &ctx.accounts.counter;
- msg!("Counter account created! Current count: {}", counter.count);
- Ok(())
- }
- pub fn increment(ctx: Context<Increment>) -> Result<()> {
- let counter = &mut ctx.accounts.counter;
- msg!("Previous counter: {}", counter.count);
- counter.count += 1;
- msg!("Counter incremented! Current count: {}", counter.count);
- Ok(())
- }
- }
- #[derive(Accounts)]
- pub struct Initialize<'info> {
- #[account(mut)]
- pub payer: Signer<'info>,
- #[account(
- init,
- payer = payer,
- space = 8 + 8
- )]
- pub counter: Account<'info, Counter>,
- pub system_program: Program<'info, System>,
- }
- #[derive(Accounts)]
- pub struct Increment<'info> {
- #[account(mut)]
- pub counter: Account<'info, Counter>,
- }
- #[account]
- pub struct Counter {
- pub count: u64,
- }
- ```
- ```ts tab="IDL"
- {
- "address": "8HupNBr7SBhBLcBsLhbtes3tCarBm6Bvpqp5AfVjHuj8",
- "metadata": {
- "name": "example",
- "version": "0.1.0",
- "spec": "0.1.0",
- "description": "Created with Anchor"
- },
- "instructions": [
- {
- "name": "increment",
- "discriminator": [
- 11,
- 18,
- 104,
- 9,
- 104,
- 174,
- 59,
- 33
- ],
- "accounts": [
- {
- "name": "counter",
- "writable": true
- }
- ],
- "args": []
- },
- {
- "name": "initialize",
- "discriminator": [
- 175,
- 175,
- 109,
- 31,
- 13,
- 152,
- 155,
- 237
- ],
- "accounts": [
- {
- "name": "payer",
- "writable": true,
- "signer": true
- },
- {
- "name": "counter",
- "writable": true,
- "signer": true
- },
- {
- "name": "system_program",
- "address": "11111111111111111111111111111111"
- }
- ],
- "args": []
- }
- ],
- "accounts": [
- {
- "name": "Counter",
- "discriminator": [
- 255,
- 176,
- 4,
- 245,
- 188,
- 253,
- 124,
- 25
- ]
- }
- ],
- "types": [
- {
- "name": "Counter",
- "type": {
- "kind": "struct",
- "fields": [
- {
- "name": "count",
- "type": "u64"
- }
- ]
- }
- }
- ]
- }
- ```
- </Tabs>
- Below is the source code (`lib.rs`) for the caller program (example-cpi) that
- uses the `declare_program!()` macro to generate a CPI module to invoke the
- instructions defined in the callee program above.
- <Tabs items={["Caller Program", "Test"]}>
- ```rust tab="Caller Program"
- use anchor_lang::prelude::*;
- declare_id!("GENmb1D59wqCKRwujq4PJ8461EccQ5srLHrXyXp4HMTH");
- // [!code word:declare_program]
- // [!code highlight:9]
- declare_program!(example);
- use example::{
- accounts::Counter,
- cpi::{
- self,
- accounts::{Increment, Initialize},
- },
- program::Example,
- };
- #[program]
- pub mod example_cpi {
- use super::*;
- pub fn initialize_cpi(ctx: Context<InitializeCpi>) -> Result<()> {
- // Create CPI context for initialize
- let cpi_ctx = CpiContext::new(
- ctx.accounts.example_program.to_account_info(),
- Initialize {
- payer: ctx.accounts.payer.to_account_info(),
- counter: ctx.accounts.counter.to_account_info(),
- system_program: ctx.accounts.system_program.to_account_info(),
- },
- );
- // Invoke the initialize instruction
- cpi::initialize(cpi_ctx)?;
- Ok(())
- }
- pub fn increment_cpi(ctx: Context<IncrementCpi>) -> Result<()> {
- // Create CPI context for increment
- let cpi_ctx = CpiContext::new(
- ctx.accounts.example_program.to_account_info(),
- Increment {
- counter: ctx.accounts.counter.to_account_info(),
- },
- );
- // Invoke the increment instruction
- cpi::increment(cpi_ctx)?;
- Ok(())
- }
- }
- #[derive(Accounts)]
- pub struct InitializeCpi<'info> {
- #[account(mut)]
- pub payer: Signer<'info>,
- #[account(mut)]
- pub counter: Signer<'info>,
- pub system_program: Program<'info, System>,
- pub example_program: Program<'info, Example>,
- }
- #[derive(Accounts)]
- pub struct IncrementCpi<'info> {
- #[account(mut)]
- pub counter: Account<'info, Counter>,
- pub example_program: Program<'info, Example>,
- }
- ```
- ```ts tab="Test"
- import * as anchor from "@coral-xyz/anchor";
- import { Program } from "@coral-xyz/anchor";
- import { Example } from "../target/types/example";
- import { ExampleCpi } from "../target/types/example_cpi";
- import { Keypair } from "@solana/web3.js";
- describe("example", () => {
- anchor.setProvider(anchor.AnchorProvider.env());
- const program = anchor.workspace.Example as Program<Example>;
- const cpiProgram = anchor.workspace.ExampleCpi as Program<ExampleCpi>;
- const counterAccount = Keypair.generate();
- it("Is initialized!", async () => {
- const transactionSignature = await cpiProgram.methods
- .initializeCpi()
- .accounts({
- counter: counterAccount.publicKey,
- })
- .signers([counterAccount])
- .rpc({ skipPreflight: true });
- const accountData = await program.account.counter.fetch(
- counterAccount.publicKey,
- );
- console.log(`Transaction Signature: ${transactionSignature}`);
- console.log(`Count: ${accountData.count}`);
- });
- it("Increment", async () => {
- const transactionSignature = await cpiProgram.methods
- .incrementCpi()
- .accounts({
- counter: counterAccount.publicKey,
- })
- .rpc();
- const accountData = await program.account.counter.fetch(
- counterAccount.publicKey,
- );
- console.log(`Transaction Signature: ${transactionSignature}`);
- console.log(`Count: ${accountData.count}`);
- });
- });
- ```
- </Tabs>
- #### Explanation
- <Steps>
- <Step>
- The `declare_program!()` macro takes a single argument - the name of the
- program's IDL file (e.g. `example.json`):
- ```rust
- declare_program!(example); // Looks for /idls/example.json
- ```
- </Step>
- <Step>
- Bring into scope the generated modules:
- ```rust
- use example::{
- accounts::Counter, // Account types
- cpi::{ // Cross program invocation helpers
- self,
- accounts::{Increment, Initialize},
- },
- program::Example, // Program type
- };
- ```
- </Step>
- <Step>
- Use the imported types in the account validation structs:
- ```rust
- #[derive(Accounts)]
- pub struct IncrementCpi<'info> {
- // Counter type from accounts module
- #[account(mut)]
- // [!code word:Counter]
- // [!code highlight]
- pub counter: Account<'info, Counter>,
- // Example type from program module
- // [!code word:Example]
- // [!code highlight]
- pub example_program: Program<'info, Example>,
- }
- ```
- </Step>
- <Step>
- Use the CPI module to invoke the program's instructions:
- ```rust
- pub fn initialize_cpi(ctx: Context<InitializeCpi>) -> Result<()> {
- // Create CPI context for initialize
- let cpi_ctx = CpiContext::new(
- ctx.accounts.example_program.to_account_info(),
- Initialize {
- payer: ctx.accounts.payer.to_account_info(),
- counter: ctx.accounts.counter.to_account_info(),
- system_program: ctx.accounts.system_program.to_account_info(),
- },
- );
- // Invoke the initialize instruction
- // [!code highlight]
- cpi::initialize(cpi_ctx)?;
- Ok(())
- }
- ```
- ```rust
- pub fn increment_cpi(ctx: Context<IncrementCpi>) -> Result<()> {
- // Create CPI context for increment
- let cpi_ctx = CpiContext::new(
- ctx.accounts.example_program.to_account_info(),
- Increment {
- counter: ctx.accounts.counter.to_account_info(),
- },
- );
- // Invoke the increment instruction
- // [!code highlight]
- cpi::increment(cpi_ctx)?;
- Ok(())
- }
- ```
- </Step>
- </Steps>
- ### Off-chain Client
- To use the `declare_program!()` macro, you need the IDL file for the target
- program. The IDL file must be placed in a directory named `/idls` in your
- project. The `/idls` directory can be located at any level in your project
- structure. For example, your project could have this layout:
- <Files>
- <Folder name="idls" defaultOpen={true}>
- <File name="example.json" />
- </Folder>
- <Folder name="src" defaultOpen={true}>
- <File name="main.rs" />
- </Folder>
- <File name="Cargo.toml" />
- </Files>
- Below is the source code (`lib.rs`) for the target program that generates the
- `example.json` IDL file shown above. The program's IDL can then be used in a
- client script along with the `declare_program!()` macro to generate a Client
- module to build the program's instructions.
- <Tabs items={["Callee Program", "IDL"]}>
- ```rust tab="Callee Program"
- use anchor_lang::prelude::*;
- declare_id!("6khKp4BeJpCjBY1Eh39ybiqbfRnrn2UzWeUARjQLXYRC");
- #[program]
- pub mod example {
- use super::*;
- pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
- let counter = &ctx.accounts.counter;
- msg!("Counter account created! Current count: {}", counter.count);
- Ok(())
- }
- pub fn increment(ctx: Context<Increment>) -> Result<()> {
- let counter = &mut ctx.accounts.counter;
- msg!("Previous counter: {}", counter.count);
- counter.count += 1;
- msg!("Counter incremented! Current count: {}", counter.count);
- Ok(())
- }
- }
- #[derive(Accounts)]
- pub struct Initialize<'info> {
- #[account(mut)]
- pub payer: Signer<'info>,
- #[account(
- init,
- payer = payer,
- space = 8 + 8
- )]
- pub counter: Account<'info, Counter>,
- pub system_program: Program<'info, System>,
- }
- #[derive(Accounts)]
- pub struct Increment<'info> {
- #[account(mut)]
- pub counter: Account<'info, Counter>,
- }
- #[account]
- pub struct Counter {
- pub count: u64,
- }
- ```
- ```ts tab="IDL"
- {
- "address": "6khKp4BeJpCjBY1Eh39ybiqbfRnrn2UzWeUARjQLXYRC",
- "metadata": {
- "name": "example",
- "version": "0.1.0",
- "spec": "0.1.0",
- "description": "Created with Anchor"
- },
- "instructions": [
- {
- "name": "increment",
- "discriminator": [11, 18, 104, 9, 104, 174, 59, 33],
- "accounts": [
- {
- "name": "counter",
- "writable": true
- }
- ],
- "args": []
- },
- {
- "name": "initialize",
- "discriminator": [175, 175, 109, 31, 13, 152, 155, 237],
- "accounts": [
- {
- "name": "payer",
- "writable": true,
- "signer": true
- },
- {
- "name": "counter",
- "writable": true,
- "signer": true
- },
- {
- "name": "system_program",
- "address": "11111111111111111111111111111111"
- }
- ],
- "args": []
- }
- ],
- "accounts": [
- {
- "name": "Counter",
- "discriminator": [255, 176, 4, 245, 188, 253, 124, 25]
- }
- ],
- "types": [
- {
- "name": "Counter",
- "type": {
- "kind": "struct",
- "fields": [
- {
- "name": "count",
- "type": "u64"
- }
- ]
- }
- }
- ]
- }
- ```
- </Tabs>
- Below is the client script (main.rs) that uses the `declare_program!()` macro to
- generate a Client module to build the program's instructions.
- <Tabs items={["Client Script", "Dependencies"]}>
- ```rust tab="Client Script"
- use anchor_client::{
- solana_client::rpc_client::RpcClient,
- solana_sdk::{
- commitment_config::CommitmentConfig, native_token::LAMPORTS_PER_SOL, signature::Keypair,
- signer::Signer, system_program,
- },
- Client, Cluster,
- };
- use anchor_lang::prelude::*;
- use std::rc::Rc;
- declare_program!(example);
- use example::{accounts::Counter, client::accounts, client::args};
- #[tokio::main]
- async fn main() -> anyhow::Result<()> {
- let connection = RpcClient::new_with_commitment(
- "http://127.0.0.1:8899", // Local validator URL
- CommitmentConfig::confirmed(),
- );
- // Generate Keypairs and request airdrop
- let payer = Keypair::new();
- let counter = Keypair::new();
- println!("Generated Keypairs:");
- println!(" Payer: {}", payer.pubkey());
- println!(" Counter: {}", counter.pubkey());
- println!("\nRequesting 1 SOL airdrop to payer");
- let airdrop_signature = connection.request_airdrop(&payer.pubkey(), LAMPORTS_PER_SOL)?;
- // Wait for airdrop confirmation
- while !connection.confirm_transaction(&airdrop_signature)? {
- std::thread::sleep(std::time::Duration::from_millis(100));
- }
- println!(" Airdrop confirmed!");
- // Create program client
- let provider = Client::new_with_options(
- Cluster::Localnet,
- Rc::new(payer),
- CommitmentConfig::confirmed(),
- );
- let program = provider.program(example::ID)?;
- // Build and send instructions
- println!("\nSend transaction with initialize and increment instructions");
- let initialize_ix = program
- .request()
- .accounts(accounts::Initialize {
- counter: counter.pubkey(),
- payer: program.payer(),
- system_program: system_program::ID,
- })
- .args(args::Initialize)
- .instructions()?
- .remove(0);
- let increment_ix = program
- .request()
- .accounts(accounts::Increment {
- counter: counter.pubkey(),
- })
- .args(args::Increment)
- .instructions()?
- .remove(0);
- let signature = program
- .request()
- .instruction(initialize_ix)
- .instruction(increment_ix)
- .signer(&counter)
- .send()
- .await?;
- println!(" Transaction confirmed: {}", signature);
- println!("\nFetch counter account data");
- let counter_account: Counter = program.account::<Counter>(counter.pubkey()).await?;
- println!(" Counter value: {}", counter_account.count);
- Ok(())
- }
- ```
- ```toml tab="Dependencies"
- [package]
- name = "rs"
- version = "0.1.0"
- edition = "2021"
- [dependencies]
- anchor-client = { version = "0.31.1", features = ["async"] }
- anchor-lang = "0.31.1"
- anyhow = "1.0.93"
- tokio = { version = "1.0", features = ["full"] }
- ```
- </Tabs>
- <Steps>
- <Step>
- The `declare_program!()` macro takes a single argument - the name of the
- program's IDL file (e.g. `example.json`):
- ```rust
- declare_program!(example); // Looks for /idls/example.json
- ```
- </Step>
- <Step>
- Bring into scope the generated modules:
- ```rust
- use example::{
- accounts::Counter, // Program Account types
- client::accounts, // Accounts for program instructions
- client::args, // Arguments for program instructions
- };
- ```
- </Step>
- <Step>
- Use the Client module to build the program's instructions:
- ```rust
- // Build initialize instruction
- let initialize_ix = program
- .request()
- // Accounts required for initialize instruction
- .accounts(accounts::Initialize {
- counter: counter.pubkey(),
- payer: program.payer(),
- system_program: system_program::ID,
- })
- // Arguments for initialize instruction (discriminator)
- .args(args::Initialize)
- .instructions()?
- .remove(0);
- ```
- ```rust
- // Build increment instruction
- let increment_ix = program
- .request()
- // Accounts required for increment instruction
- .accounts(accounts::Increment {
- counter: counter.pubkey(),
- })
- // Arguments for increment instruction (discriminator)
- .args(args::Increment)
- .instructions()?
- .remove(0);
- ```
- </Step>
- <Step>
- Add the program's instructions to a transaction and send the transaction:
- ```rust
- let signature = program
- .request()
- .instruction(initialize_ix)
- .instruction(increment_ix)
- .signer(&counter)
- .send()
- .await?;
- ```
- </Step>
- <Step>
- Use the Account module to fetch and deserialize the program's account types:
- ```rust
- // Counter type from accounts module
- let counter_account: Counter = program.account::<Counter>(counter.pubkey()).await?;
- ```
- </Step>
- </Steps>
|