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)
}
}
注意事项
-
该库不支持旧版IDL。要迁移旧版IDL,请使用
anchor idl convert idl.json
命令。 -
更多示例可以在项目的examples目录中找到。
-
确保生成的程序ID与IDL中的程序ID匹配,否则CPI调用将失败。
-
该库采用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"
这个完整示例展示了:
- 目标程序定义了两个操作(transfer和approve)
- 调用程序使用
anchor-generate-cpi-interface
生成了CPI接口 - 两种CPI调用方式:直接调用和使用CpiContext
- 完整的账户结构和类型安全验证
- 模块化的项目结构
开发者可以根据实际需求调整账户结构和业务逻辑,但核心的CPI接口生成和使用模式保持不变。