Rust Anchor框架CPI接口生成库anchor-generate-cpi-interface的使用,轻松实现跨程序调用的智能合约开发

Rust Anchor框架CPI接口生成库anchor-generate-cpi-interface的使用,轻松实现跨程序调用的智能合约开发

简介

anchor-gen是一个从JSON IDL生成Anchor程序跨程序调用(CPI)客户端的库。它可以帮助开发者轻松实现智能合约之间的交互。

使用方法

在一个新的crate中,只需写入以下代码:

// 从IDL文件生成CPI客户端
anchor_gen::generate_cpi_crate!("../../examples/govern-cpi/idl.json");

// 声明程序ID
declare_id!("GjphYQcbP1m3FuDyCTUJf2mUMxKPE3j6feWU1rxvC7Ps");

这将为你的IDL生成一个功能完整的Rust CPI客户端。

完整示例

以下是一个完整的CPI客户端使用示例:

// 在Cargo.toml中添加依赖
// anchor-generate-cpi-interface = "0.4.0"

use anchor_lang::prelude::*;

// 1. 生成CPI客户端
anchor_gen::generate_cpi_crate!("path/to/your/idl.json");

// 2. 声明程序ID (必须与IDL中的程序ID匹配)
declare_id!("YourProgramIdHere");

// 3. 使用生成的客户端进行跨程序调用
pub fn make_cpi_call(ctx: Context<YourContext>) -> Result<()> {
    // 获取生成的CPI模块
    let cpi_program = your_program::cpi::program(ctx.accounts.your_program.clone());
    
    // 调用IDL中定义的方法
    your_program::cpi::your_instruction(
        cpi_program,
        your_program::accounts::YourInstructionAccounts {
            account1: ctx.accounts.account1.clone(),
            account2: ctx.accounts.account2.clone(),
            // ...
        },
        your_program::YourInstructionArgs {
            arg1: value1,
            arg2: value2,
            // ...
        },
    )?;
    
    Ok(())
}

完整示例demo

以下是一个更完整的示例,展示如何使用anchor-gen进行跨程序调用:

// Cargo.toml 依赖
// [dependencies]
// anchor-lang = "0.24.2"
// anchor-generate-cpi-interface = "0.4.0"

use anchor_lang::prelude::*;

// 1. 生成CPI客户端 - 从IDL文件生成
anchor_gen::generate_cpi_crate!("governance_program_idl.json");

// 2. 声明程序ID (必须与IDL中的程序ID匹配)
declare_id!("GjphYQcbP1m3FuDyCTUJf2mUMxKPE3j6feWU1rxvC7Ps");

// 定义自己的账户结构
#[derive(Accounts)]
pub struct VoteContext<'info> {
    #[account(mut)]
    pub governance_account: AccountInfo<'info>,
    #[account(mut)]
    pub voter_account: AccountInfo<'info>,
    pub system_program: Program<'info, System>,
    pub governance_program: Program<'info, governance_program::program::Governance>,
}

// 实现投票逻辑
pub fn vote(ctx: Context<VoteContext>, vote_amount: u64, side: bool) -> Result<()> {
    // 获取生成的CPI模块
    let cpi_program = governance_program::cpi::program(ctx.accounts.governance_program.clone());
    
    // 调用IDL中定义的vote方法
    governance_program::cpi::vote(
        cpi_program,
        governance_program::accounts::VoteAccounts {
            governance: ctx.accounts.governance_account.clone(),
            voter: ctx.accounts.voter_account.clone(),
            system_program: ctx.accounts.system_program.clone(),
        },
        governance_program::VoteArgs {
            amount: vote_amount,
            side,
        },
    )?;
    
    Ok(())
}

// 主模块
#[program]
pub mod my_program {
    use super::*;
    
    pub fn execute_vote(ctx: Context<VoteContext>, amount: u64, side: bool) -> Result<()> {
        vote(ctx, amount, side)
    }
}

注意事项

  1. 该库不支持旧版IDL。要迁移旧版IDL,请使用anchor idl convert idl.json命令。

  2. 更多示例可以在项目的examples目录中找到。

  3. 确保生成的程序ID与IDL中的程序ID匹配,否则CPI调用将失败。

  4. 该库采用Apache-2.0许可证。

通过使用anchor-gen,开发者可以轻松地实现Anchor程序之间的交互,大大简化了跨程序调用的开发工作。生成的CPI客户端提供了类型安全的接口,有助于减少错误并提高开发效率。


1 回复

Rust Anchor框架CPI接口生成库anchor-generate-cpi-interface使用指南

完整示例demo

基于提供的内容,下面是一个完整的示例demo,展示如何使用anchor-generate-cpi-interface实现CPI调用:

1. 目标程序(target_program)

use anchor_lang::prelude::*;

// 定义目标程序ID
declare_id!("TARGET11111111111111111111111111111111111111");

#[program]
pub mod target_program {
    use super::*;
    
    // 转账操作
    pub fn transfer(ctx: Context<Transfer>, amount: u64) -> Result<()> {
        // 实现转账逻辑
        let from = &mut ctx.accounts.from;
        let to = &mut ctx.accounts.to;
        from.sub(amount)?;
        to.add(amount)?;
        Ok(())
    }

    // 授权操作
    pub fn approve(ctx: Context<Approve>, amount: u64) -> Result<()> {
        // 实现授权逻辑
        ctx.accounts.delegate.approved_amount = amount;
        Ok(())
    }
}

// 转账所需账户
#[derive(Accounts)]
pub struct Transfer<'info> {
    #[account(mut)]
    pub from: Account<'info, TokenAccount>,
    #[account(mut)]
    pub to: Account<'info, TokenAccount>,
    pub authority: Signer<'info>,
}

// 授权所需账户
#[derive(Accounts)]
pub struct Approve<'info> {
    #[account(mut)]
    pub delegate: Account<'info, DelegateAccount>,
    pub owner: Signer<'info>,
}

// 账户结构定义
#[account]
pub struct TokenAccount {
    pub balance: u64,
    // 其他字段
}

#[account]
pub struct DelegateAccount {
    pub approved_amount: u64,
    // 其他字段
}

2. 调用程序(caller_program)

use anchor_lang::prelude::*;
use anchor_generate_cpi_interface::generate_cpi_interface;

// 生成目标程序的CPI接口
generate_cpi_interface!(
    TargetProgramInterface,
    target_program::ID,
    {
        Transfer: target_program::cpi::accounts::Transfer,
        Approve: target_program::cpi::accounts::Approve
    },
    {
        Transfer: target_program::instruction::Transfer { amount: u64 },
        Approve: target_program::instruction::Approve { amount: u64 }
    }
);

// 定义调用程序ID
declare_id!("CALLER1111111111111111111111111111111111111");

#[program]
pub mod caller_program {
    use super::*;
    
    // 调用目标程序的转账方法
    pub fn call_transfer(ctx: Context<CallTransfer>, amount: u64) -> Result<()> {
        // 使用生成的接口进行CPI调用
        TargetProgramInterface::transfer(
            ctx.accounts.target_program.clone(),
            ctx.accounts.transfer_accounts.clone(),
            amount
        )?;
        
        Ok(())
    }

    // 调用目标程序的授权方法
    pub fn call_approve(ctx: Context<CallApprove>, amount: u64) -> Result<()> {
        // 使用CpiContext进行更灵活的调用
        let cpi_ctx = CpiContext::new(
            ctx.accounts.target_program.clone(),
            TargetProgramInterface::approve_ctx(
                ctx.accounts.approve_accounts.clone(),
                amount
            )
        );
        TargetProgramInterface::approve(cpi_ctx, amount)?;
        
        Ok(())
    }
}

// 调用转账所需的账户
#[derive(Accounts)]
pub struct CallTransfer<'info> {
    #[account(mut)]
    pub target_program: AccountInfo<'info>,
    #[account(signer)]
    pub authority: AccountInfo<'info>,
    pub transfer_accounts: TransferAccounts<'info>,
}

// 调用授权所需的账户
#[derive(Accounts)]
pub struct CallApprove<'info> {
    #[account(mut)]
    pub target_program: AccountInfo<'info>,
    #[account(signer)]
    pub owner: AccountInfo<'info>,
    pub approve_accounts: ApproveAccounts<'info>,
}

// 生成的CPI接口账户类型
#[derive(Accounts)]
pub struct TransferAccounts<'info> {
    #[account(mut)]
    pub from: Account<'info, TokenAccount>,
    #[account(mut)]
    pub to: Account<'info, TokenAccount>,
}

#[derive(Accounts)]
pub struct ApproveAccounts<'info> {
    #[account(mut)]
    pub delegate: Account<'info, DelegateAccount>,
}

3. 项目结构

/solana_cpi_example
  /programs
    /target_program
      src/
        lib.rs  # 目标程序代码
    /caller_program
      src/
        lib.rs  # 调用程序代码
        cpi_interfaces.rs  # 存放生成的CPI接口(可选)
  Cargo.toml

4. Cargo.toml配置

[workspace]
members = [
    "programs/target_program",
    "programs/caller_program"
]

[dependencies]
anchor-lang = "0.24.2"
anchor-generate-cpi-interface = "0.1.0"

这个完整示例展示了:

  1. 目标程序定义了两个操作(transfer和approve)
  2. 调用程序使用anchor-generate-cpi-interface生成了CPI接口
  3. 两种CPI调用方式:直接调用和使用CpiContext
  4. 完整的账户结构和类型安全验证
  5. 模块化的项目结构

开发者可以根据实际需求调整账户结构和业务逻辑,但核心的CPI接口生成和使用模式保持不变。

回到顶部