本文将带你探索 Anchor 框架中的 IDL(接口定义语言),这是一个自动生成的 JSON 文件,用于描述 Solana 程序的接口。我们将通过示例展示 IDL 的作用,解释 TypeScript 测试如何调用程序函数。
本文将带你探索 Anchor 框架中的 IDL(接口定义语言),这是一个自动生成的 JSON 文件,用于描述 Solana 程序的接口。我们将通过示例展示 IDL 的作用,解释 TypeScript 测试如何调用程序函数。
IDL(Interface Definition Language)是 Anchor 生成的程序接口定义,存储在 target/idl/<program_name>.json 中,类似于 Solidity 的 ABI。它列出了程序的公共函数(instructions)、参数(args)和账户要求(accounts),为客户端(如 TypeScript)提供与链上程序交互的蓝图。
创建一个新项目:
anchor init anchor-function-tutorial
cd anchor-function-tutorial
启动本地验证器:
solana-test-validator
将默认的 initialize 函数改为 boaty_mc_boatface。编辑 programs/anchor-function-tutorial/src/lib.rs:
use anchor_lang::prelude::*;
declare_id!("3ytdGXdSqfQ5Z9NB9c5bGbkNqkzVPkpijs4hj4BeoAa7");
#[program]
pub mod anchor_function_tutorial {
use super::*;
pub fn boaty_mc_boatface(ctx: Context<Initialize>) -> Result<()> {
msg!("Boaty says hi!");
Ok(())
}
}
#[derive(Accounts)]
pub struct Initialize {}
编辑 tests/anchor-function-tutorial.ts:
it("Call boaty mcboatface", async () => {
const tx = await program.methods.boatyMcBoatface().rpc();
console.log("Your transaction signature", tx);
});
运行测试:
anchor test --skip-local-validator
Anchor 在构建时生成 IDL(target/idl/anchor_function_tutorial.json):
{
"version": "0.1.0",
"name": "anchor_function_tutorial",
"instructions": [
{
"name": "boatyMcBoatface",
"accounts": [],
"args": []
}
],
"metadata": {
"address": "3ytdGXdSqfQ5Z9NB9c5bGbkNqkzVPkpijs4hj4BeoAa7"
}
}
如果在 Anchor 0.30.0 及以上版本(如 0.30.1):
测试调用注意:
await program.methods.boatyMcBoatface(); // 正确,客户端仍需驼峰式
await program.methods.boaty_mc_boatface(); // 可行但不推荐
更新 lib.rs,实现加法和减法(Solana 不支持直接返回值,需用 msg! 输出):
use anchor_lang::prelude::*;
declare_id!("3ytdGXdSqfQ5Z9NB9c5bGbkNqkzVPkpijs4hj4BeoAa7");
#[program]
pub mod anchor_function_tutorial {
use super::*;
pub fn add(ctx: Context<Empty>, a: u64, b: u64) -> Result<()> {
let sum = a + b;
msg!("Sum is {}", sum);
Ok(())
}
pub fn sub(ctx: Context<Empty>, a: u64, b: u64) -> Result<()> {
let difference = a - b;
msg!("Difference is {}", difference);
Ok(())
}
}
#[derive(Accounts)]
pub struct Empty {}
编辑 tests/anchor-function-tutorial.ts:
it("Should add", async () => {
const tx = await program.methods.add(new anchor.BN(1), new anchor.BN(2)).rpc();
console.log("Your transaction signature", tx);
});
it("Should subtract", async () => {
const tx = await program.methods.sub(new anchor.BN(10), new anchor.BN(3)).rpc();
console.log("Your transaction signature", tx);
});
运行测试:
anchor test --skip-local-validator
生成的 IDL
"instructions": [
{
"name": "add",
"accounts": [],
"args": [
{
"name": "a",
"type": "u64"
},
{
"name": "b",
"type": "u64"
}
]
},
{
"name": "sub",
"accounts": [],
"args": [
{
"name": "a",
"type": "u64"
},
{
"name": "b",
"type": "u64"
}
]
}
],
Context<T> 与结构体命名
函数中的 ctx: Context<T> 指定账户上下文,T 是自定义结构体,名称(如 Initialize 或 Empty)任意,只要与函数签名一致。
#[derive(Accounts)] 的作用
这是 Anchor 的 Rust 属性宏,解析结构体字段并映射到 IDL 的 accounts。空结构体(如 Empty)生成空的 accounts 数组。
非空账户示例
更新 lib.rs:
use anchor_lang::prelude::*;
declare_id!("3ytdGXdSqfQ5Z9NB9c5bGbkNqkzVPkpijs4hj4BeoAa7");
#[program]
pub mod anchor_function_tutorial {
use super::*;
pub fn non_empty_account_example(ctx: Context<NonEmptyAccountExample>) -> Result<()> {
msg!("Signers present");
Ok(())
}
}
#[derive(Accounts)]
pub struct NonEmptyAccountExample<'info> {
signer: Signer<'info>,
another_signer: Signer<'info>,
}
构建:
anchor build
生成的 IDL 中的 instructions 部分为:
"instructions": [
{
"name": "nonEmptyAccountExample",
"accounts": [
{
"name": "signer",
"isMut": false,
"isSigner": true
},
{
"name": "anotherSigner",
"isMut": false,
"isSigner": true
}
],
"args": []
}
]
完整代码
use anchor_lang::prelude::*;
declare_id!("3ytdGXdSqfQ5Z9NB9c5bGbkNqkzVPkpijs4hj4BeoAa7");
#[program]
pub mod anchor_function_tutorial {
use super::*;
pub fn function_a(ctx: Context<NonEmptyAccountExample>) -> Result<()> {
msg!("Function A called");
Ok(())
}
pub fn function_b(ctx: Context<Empty>, first_arg: u64) -> Result<()> {
msg!("Function B with arg {}", first_arg);
Ok(())
}
}
#[derive(Accounts)]
pub struct NonEmptyAccountExample<'info> {
signer: Signer<'info>,
another_signer: Signer<'info>,
}
#[derive(Accounts)]
pub struct Empty {}
生成的 IDL
{
"version": "0.1.0",
"name": "anchor_function_tutorial",
"instructions": [
{
"name": "functionA",
"accounts": [
{
"name": "signer",
"isMut": false,
"isSigner": true
},
{
"name": "anotherSigner",
"isMut": false,
"isSigner": true
}
],
"args": []
},
{
"name": "functionB",
"accounts": [],
"args": [
{
"name": "firstArg",
"type": "u64"
}
]
}
]
}
【笔记配套代码】\ <https://github.com/0xE1337/rareskills_evm_to_solana>\ 【参考资料】\ <https://learnblockchain.cn/column/119>\ <https://www.rareskills.io/solana-tutorial>
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!