Solana Anchor 程序接口定义语言(IDL)

  • 0xE
  • 发布于 2025-03-21 15:06
  • 阅读 635

本文将带你探索 Anchor 框架中的 IDL(接口定义语言),这是一个自动生成的 JSON 文件,用于描述 Solana 程序的接口。我们将通过示例展示 IDL 的作用,解释 TypeScript 测试如何调用程序函数。

本文将带你探索 Anchor 框架中的 IDL(接口定义语言),这是一个自动生成的 JSON 文件,用于描述 Solana 程序的接口。我们将通过示例展示 IDL 的作用,解释 TypeScript 测试如何调用程序函数。


什么是 IDL?

IDL(Interface Definition Language)是 Anchor 生成的程序接口定义,存储在 target/idl/<program_name>.json 中,类似于 Solidity 的 ABI。它列出了程序的公共函数(instructions)、参数(args)和账户要求(accounts),为客户端(如 TypeScript)提供与链上程序交互的蓝图。


示例 1:函数调用与 IDL 映射

初始化项目

创建一个新项目:

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&lt;Initialize>) -> Result&lt;()> {
        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"
  }
}
  • 解析
    • "instructions":列出公共函数,类似 Solidity 的外部函数。
    • TypeScript 的 program.methods 根据 IDL 映射函数名,生成调用逻辑。

Anchor 0.30.x 后的命名规则

如果在 Anchor 0.30.0 及以上版本(如 0.30.1):

  • 函数名:保留 Rust 的蛇形命名(如 boaty_mc_boatface),不再转换为驼峰式(如 boatyMcBoatface)。
    • 原因:提升 Rust 代码与 IDL 的一致性,响应社区反馈。
  • 参数和账户名:仍使用驼峰式(如 firstArg、anotherSigner),适配 JavaScript/TypeScript 惯例。

测试调用注意

await program.methods.boatyMcBoatface();   // 正确,客户端仍需驼峰式
await program.methods.boaty_mc_boatface(); // 可行但不推荐
  • 经验建议:尽管 IDL 使用蛇形命名,建议在前端保持驼峰式调用,以符合生态习惯。

示例 2:带参数的算术函数

添加加减法函数

更新 lib.rs,实现加法和减法(Solana 不支持直接返回值,需用 msg! 输出):

use anchor_lang::prelude::*;

declare_id!("3ytdGXdSqfQ5Z9NB9c5bGbkNqkzVPkpijs4hj4BeoAa7");

#[program]
pub mod anchor_function_tutorial {
    use super::*;

    pub fn add(ctx: Context&lt;Empty>, a: u64, b: u64) -> Result&lt;()> {
        let sum = a + b;
        msg!("Sum is {}", sum);
        Ok(())
    }

    pub fn sub(ctx: Context&lt;Empty>, a: u64, b: u64) -> Result&lt;()> {
        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&lt;NonEmptyAccountExample>) -> Result&lt;()> {
        msg!("Signers present");
        Ok(())
    }
}

#[derive(Accounts)]
pub struct NonEmptyAccountExample&lt;'info> {
    signer: Signer&lt;'info>,
    another_signer: Signer&lt;'info>,
}

构建:

anchor build

生成的 IDL 中的 instructions 部分为:

  "instructions": [
    {
      "name": "nonEmptyAccountExample",
      "accounts": [
        {
          "name": "signer",
          "isMut": false,
          "isSigner": true
        },
        {
          "name": "anotherSigner",
          "isMut": false,
          "isSigner": true
        }
      ],
      "args": []
    }
  ]
  • 变化
    • 函数名:nonEmptyAccountExample(驼峰式)。
    • 账户名:signer 和 anotherSigner(another_signer 转为驼峰式)。
  • Signer:表示交易签名者,类似 Ethereum 的 tx.origin。

示例 3:综合应用

完整代码

use anchor_lang::prelude::*;

declare_id!("3ytdGXdSqfQ5Z9NB9c5bGbkNqkzVPkpijs4hj4BeoAa7");

#[program]
pub mod anchor_function_tutorial {
    use super::*;

    pub fn function_a(ctx: Context&lt;NonEmptyAccountExample>) -> Result&lt;()> {
        msg!("Function A called");
        Ok(())
    }

    pub fn function_b(ctx: Context&lt;Empty>, first_arg: u64) -> Result&lt;()> {
        msg!("Function B with arg {}", first_arg);
        Ok(())
    }
}

#[derive(Accounts)]
pub struct NonEmptyAccountExample&lt;'info> {
    signer: Signer&lt;'info>,
    another_signer: Signer&lt;'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"
        }
      ]
    }
  ]
}
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
0xE
0xE
0x59f6...a17e
17年进入币圈,Web3 开发者。刨根问底探链上真相,品味坎坷悟 Web3 人生。