跟我一起从0开始学习Solana合约开发,一起实操,一起做项目。这是一个系列文章,系统地记录了我的学习笔记。
IDL(Interface Definition Language)文件是一个标准化的 JSON 文件,用于描述程序的指令(可以理解为函数)和账户。它类似于以太坊的 ABI 文件,帮助我们与智能合约交互。本文将通过一个简单示例,带你了解 IDL 的结构、生成过程以及它在程序开发中的实际作用。
在使用 Anchor 框架开发 Solana 程序时,运行 anchor build
命令会自动生成 IDL 文件,生成路径为 /target/idl/<program-name>.json
。
在 IDL 文件里有一个 instructions
数组对象,里面的每一个元素都对应主程序的一个函数。观看下面的主程序代码:
#[program]
mod hello_anchor {
use super::*;
pub fn initialize(ctx: Context<Initialize>, data: u64) -> Result<()> {
ctx.accounts.new_account.data = data;
msg!("Changed data to: {}!", data);
Ok(())
}
}
#[derive(Accounts)]
pub struct Initialize<'info> {
#[account(init, payer = signer, space = 8 + 8)]
pub new_account: Account<'info, NewAccount>,
#[account(mut)]
pub signer: Signer<'info>,
pub system_program: Program<'info, System>,
}
这段代码包含:
一个 initialize
函数,接收一个 Initialize 账户上下文和一个 data
字段作为参数。
Initialize 账户上下文包含了 3 个字段:new_account
, signer
, 和 system_program
。
下面我们来看它的 IDL 文件内容:
"instructions": [
{
"name": "initialize",
"discriminator": [175, 175, 109, 31, 13, 152, 155, 237],
"accounts": [
{
"name": "new_account",
"writable": true,
"signer": true
},
{
"name": "signer",
"writable": true,
"signer": true
},
{
"name": "system_program",
"address": "11111111111111111111111111111111"
}
],
"args": [
{
"name": "data",
"type": "u64"
}
]
}
],
通过对比,可以看到 instructions
数组的第一个元素对应主程序的 initialize
函数及其所需的账户集合和参数。
主程序代码 | IDL 对应部分(JSON) |
---|---|
pub fn initialize(...) |
"name": "initialize" |
ctx.accounts.new_account |
"accounts": [{ "name": "new_account", "writable": true }] |
data: u64 |
"args": [{ "name": "data", "type": "u64" }] |
在上面的代码里,还有一个 NewAccount 自定义账户,它的定义如下:
#[account]
pub struct NewAccount {
data: u64,
}
这个账户信息也会包含在 IDL 文件里,在与 instructions
同一层级的字段里,有下面的内容:
"instructions": [...],
"accounts": [
{
"name": "NewAccount",
"discriminator": [176, 95, 4, 118, 91, 177, 125, 232]
}
],
"types": [
{
"name": "NewAccount",
"type": {
"kind": "struct",
"fields": [
{
"name": "data",
"type": "u64"
}
]
}
}
]
通过这段代码,可以看到 accounts
数组的第一个元素对应自定义账户结构体 NewAccount
。而这个结构体的类型和字段信息在 types
数组对象里。
主程序代码 | IDL 对应部分(JSON) |
---|---|
#[account] pub struct NewAccount {...} |
"name": "NewAccount" |
Rust 结构体 data: u64 |
"fields": [{ "name": "data", "type": "u64" }] |
无论是 instruction
还是 account
都有一个 discriminator
字段,这个是什么意思呢?
Anchor 为程序中的每个指令和账户类型分配了一个唯一的 8 字节标识符(discriminator)。用于确保程序的安全性。其生成方式是:
对“前缀 + 名称”组合进行 Sha256 哈希。
提取哈希值的前 8 个字节作为结果。
示例
对于指令 initialize,其输入为 global:initialize
。
对于账户 NewAccount,其输入为 account:NewAccount
。
总之,Discriminator 是通过对前缀与指令或账户名称组合后的字符串进行 Sha256 哈希计算,并取其前 8 个字节生成的。从 Anchor v0.30 开始,这些 discriminators 会包含在 IDL 文件中。
需要注意的是,在使用 Anchor 开发时,通常无需直接与这些 discriminators 交互。这里主要是为了说明 discriminator 的生成方式及其用途。
Anchor 框架还会为这个 IDL 文件自动生成 typescript
类型文件,生成路径为 /target/types/<program-name>.ts
。我们在编写测试代码时,实际上就是引用了这个 .ts
文件。
需要提醒的是,在 rust 代码里的 蛇形命名(下划线连接) 字段生成 .ts
文件时,会自动改为 驼峰命名 以遵循 typescript 开发的命名习惯
本文通过示例代码、IDL 文件结构以及相关概念的剖析,帮助你掌握了 IDL 的作用及其在 Solana Anchor 开发中的重要性。在下一篇文章中,我们将深入探讨 SPL 代币标准的具体实现。
如果你对 Web3 前沿探索、Go 后端技术,以及产品与哲学的深度思考感兴趣,可以关注我的公众号:认知那些事
。每一篇文章,都是我精心打磨的干货。扫码关注,一起探索技术与思想的广阔世界。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!