作者:https://twitter.com/liushooter
经过 6 个月的开发,Solana Anchor 推出了 0.30 版本:https://github.com/coral-xyz/anchor/releases/tag/v0.30.0
内容有点多,重点有几个:
anchor init --force
anchor deploy --verifiable
anchor build --no-idl
declare_program
本篇文章先介绍新的 IDL 标准。
IDL 规范大更新,idl 文件前后对比请看 https://www.diffchecker.com/JqI33i4w/
之前只有项目在部署后,Program Address 才会在 idl.json 出现,现在不需要部署也会出现在 idl.json 文件中
{
"address": "id11111111111111111111111111111111111111111"
}
还涉及到另一个更新,js sdk 中的 Program 构造函数不需要传递 address 了。
- const program = new Program(idl, programId, provider);
+ const program = new Program(idl, provider);
元数据字段曾经是无类型对象。现在,它具有以下字段与属性:
type IdlMetadata = {
name: string;
version: string;
spec: string;
description?: string;
repository?: string;
dependencies?: IdlDependency[];
contact?: string;
}
{
"metadata": {
"name": "idl",
"version": "0.1.0",
"description": "Created with Anchor"
}
}
帐户和事件类型信息过去分别存储在它们自己的属性、帐户和事件中。这产生了一个问题,在某些情况下找不到类型,因为类型字段中不存在类型定义。 现在,所有定义的类型都存储在 types 字段中,包括帐户和事件类型。帐户和事件字段仍然存在,它们充当指向实际类型定义的指针。
{
"accounts": [{ "name": "MyAccount" }],
"events": [{ "name": "MyEvent" }],
"types": [
{
"name": "MyAccount"
// Definition...
},
{
"name": "MyEvent"
// Definition...
}
]
}
在新版本中,所有 instructions、account 和 event 都有一个新的字段 discriminator
,使用字节表示:
{
"events": [{
"name": "SomeEvent",
"discriminator": [39,221,150,148,91,206,29,93]
}]
}
{
"instructions": [{
"name": "initialize",
"discriminator": [...],
"accounts": [],
"args": []
}]
}
{
"accounts": [{
"name": "SomeZcAccount",
"discriminator": [56, 72, 82, 194, 210, 35, 17, 191]
}]
}
#[derive(AnchorSerialize, AnchorDeserialize, Clone)]
struct UnitStruct;
#[derive(AnchorSerialize, AnchorDeserialize, Clone)]
struct TupleStruct(u64, String);
{
"name": "UnitStruct",
"type": { "kind": "struct" }
}
{
"name": "TupleStruct",
"type": {
"kind": "struct",
"fields": ["u64", "string"]
}
}
IDL 和 TS 包都支持通用类型和通用数组长度。这是使用两者的示例:
#[derive(AnchorSerialize, AnchorDeserialize, Clone)]
pub struct GenericStruct<T, const N: usize> {
field: [T; N],
}
{
"name": "GenericStruct",
"generics": [
{
"kind": "type",
"name": "T"
},
{
"kind": "const",
"name": "N",
"type": "usize"
}
],
"type": {
"kind": "struct",
"fields": [
{
"name": "field",
"type": {
"array": [{ "generic": "T" }, { "generic": "N" }]
}
}
]
}
}
Anchor 早期决定使用 borsh 作为默认序列化方法。虽然它仍然是 Solana 生态系统中最常用的序列化方法, 但还有其他正在使用的序列化方法,例如用于 zero_copy 操作的 bytemuck。
当前支持的序列化方法有:
#[zero_copy]
pub struct ZcStruct {
pub bytes: [u8; 32],
}
{
"name": "ZcStruct",
"serialization": "bytemuck"
}
Rust 允许使用 repr 属性修改用户定义类型的内存表示,但该数据未存储在 IDL 中。因此,使用默认表示以外的任何内容都很难使用。 在新的 IDL 中,内存表示信息存储在 repr 字段中。 它有 3 个属性:
#[repr(transparent)]
pub struct ReprStruct {
}
#[repr(align(4))]
pub struct ReprStruct {
}
#[repr(C, packed)]
pub struct ReprStruct {
}
{
"name": "ReprStruct",
"repr": { "kind": "transparent" }
}
{
"name": "ReprStruct",
"repr": { "kind": "rust", "align": 4 }
}
{
"name": "ReprStruct",
"repr": { "kind": "c", "packed": true }
}
#[constant]
pub const MY_ACCOUNT_ALIGNMENT: u8 = std::mem::align_of::<MyAccount>() as u8;
{
"name": "MY_ACCOUNT_ALIGNMENT",
"type": "u8",
"value": "std :: mem :: align_of :: < MyAccount > () as u8"
}
{
"name": "MY_ACCOUNT_ALIGNMENT",
"type": "u8",
"value": "1"
}
pub type Pubkeys = Vec<Pubkey>; // Worked
pub type OptionalElements<T> = Vec<Option<T>> // Did not work
#[derive(AnchorDeserialize, AnchorSerialize, Clone)]
pub struct MyStruct {
pubkeys: OptionalElements<Pubkey>,
}
{
"name": "MyStruct",
"type": {
"kind": "struct",
"fields": [
{
"name": "pubkeys",
"type": {
"vec": {
"option": "pubkey"
}
}
}
]
}
}
#[derive(AnchorDeserialize, AnchorSerialize, Clone)]
pub struct MyStruct {
timestamp: anchor_lang::solana_program::clock::UnixTimestamp,
}
{
"name": "NamedStruct",
"type": {
"kind": "struct",
"fields": [{
"name": "timestamp",
"type": "i64"
}]
}
}
pub struct U256(external_math_library::u256);
impl AnchorDeserialize for U256 {
}
impl AnchorSerialize for U256 {
}
#[cfg(feature = "idl-build")]
impl IdlBuild for U256 {
}
#[derive(Accounts)]
pub struct AddressField<'info> {
pub metadata_program: Program<'info, Metadata>,
}
"accounts": [
{
"name": "metadata_program",
"address": "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"
}
]
缺乏一致的大小写给 Anchor 开发人员带来了很多问题,IDL 也不例外。在 TypeScript 库中, 有些东西使用 camelCase 命名法,而另一些则需要使用 PascalCase 或 snake_case。
新规范:
用户定义的任何值都会未经编辑地放入 IDL,例如 my_instruction 存储为 my_instruction 而不是 myInstruction。
所有非用户定义的字符串值均为小写,例如 pubkey(从 publicKey 重命名)、struct、borsh。
目前,所有 IDL 字段都是一个单词,这样可以更轻松地使用其他语言的 IDL。
TypeScript 库完全采用驼峰命名法。
IDL 常量不再从目标/类型导出(类型导出仍然存在)。
IDL 的更新导致 anchor build
时间增加,可以配合使用 anchor build --no-idl
。