文章/教程 Anchor 发布了新版本 0.30

shooter · 2024年05月21日 · 211 次阅读
本帖已被管理员设置为精华贴

作者:https://twitter.com/liushooter

经过 6 个月的开发,Solana Anchor 推出了 0.30 版本:https://github.com/coral-xyz/anchor/releases/tag/v0.30.0

内容有点多,重点有几个:

  • 新的 IDL 标准
  • 支持了更多 flag anchor init --force anchor deploy --verifiable anchor build --no-idl
  • 支持 event
  • 支持 token-extensions
  • 添加了宏 declare_program

IDL 标准

本篇文章先介绍新的 IDL 标准。

IDL 规范大更新,idl 文件前后对比请看 https://www.diffchecker.com/JqI33i4w/

Program Address 属性

之前只有项目在部署后,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);

Metadata 属性

元数据字段曾经是无类型对象。现在,它具有以下字段与属性:

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"
  }
}

Accounts and events as type

帐户和事件类型信息过去分别存储在它们自己的属性、帐户和事件中。这产生了一个问题,在某些情况下找不到类型,因为类型字段中不存在类型定义。 现在,所有定义的类型都存储在 types 字段中,包括帐户和事件类型。帐户和事件字段仍然存在,它们充当指向实际类型定义的指针。

{
  "accounts": [{ "name": "MyAccount" }],
  "events": [{ "name": "MyEvent" }],
  "types": [
    {
      "name": "MyAccount"
      // Definition...
    },
    {
      "name": "MyEvent"
      // Definition...
    }
  ]
}

Discriminator

在新版本中,所有 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]
  }]
}

Unit and tuple struct

#[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"]
  }
}

Generic types 泛型

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" }]
        }
      }
    ]
  }
}

Serialization 序列化

Anchor 早期决定使用 borsh 作为默认序列化方法。虽然它仍然是 Solana 生态系统中最常用的序列化方法, 但还有其他正在使用的序列化方法,例如用于 zero_copy 操作的 bytemuck。

当前支持的序列化方法有:

  • borsh (default)
  • bytemuck
  • bytemuckunsafe
  • Custom
#[zero_copy]
pub struct ZcStruct {
    pub bytes: [u8; 32],
}
{
  "name": "ZcStruct",
  "serialization": "bytemuck"
}

Representation

Rust 允许使用 repr 属性修改用户定义类型的内存表示,但该数据未存储在 IDL 中。因此,使用默认表示以外的任何内容都很难使用。 在新的 IDL 中,内存表示信息存储在 repr 字段中。 它有 3 个属性:

  • kind
  • align
  • packed
#[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 }
}

Expression evaluation

#[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"
}

Type alias resolution

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"
          }
        }
      }
    ]
  }
}

External types

#[derive(AnchorDeserialize, AnchorSerialize, Clone)]
pub struct MyStruct {
    timestamp: anchor_lang::solana_program::clock::UnixTimestamp,
}
{
  "name": "NamedStruct",
  "type": {
    "kind": "struct",
    "fields": [{
      "name": "timestamp",
      "type": "i64"
    }]
  }
}

Customization

pub struct U256(external_math_library::u256);

impl AnchorDeserialize for U256 {
}

impl AnchorSerialize for U256 {
}

#[cfg(feature = "idl-build")]
impl IdlBuild for U256 {
}

Account resolution

#[derive(Accounts)]
pub struct AddressField<'info> {
    pub metadata_program: Program<'info, Metadata>,
}
"accounts": [
  {
    "name": "metadata_program",
    "address": "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"
  }
]

Case conversion

缺乏一致的大小写给 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

Fifi 如何使用 Kinobi 创建 Anchor 程序客户端 提及了此话题。 07月02日 14:38
shooter 将本帖设为了精华贴。 07月06日 15:15
需要 登录 后方可回复, 如果你还没有账号请 注册新账号