新消息:Helius 收购 Light Protocol
如何反序列化 Solana 账户数据
博客/开发

Solana 开发入门 101:反序列化 Solana 账户数据

开发者关系负责人LinkedIn 上的 Hunter Davis
阅读需 5 分钟

简介

与 Solana 上的数据交互可能颇具挑战。账户和交易数据通常经过编码,这有利于提升效率,却会让开发者难以理解。

Solana 使用 Borsh(Binary Object Representation Serializer for Hashing)进行数据序列化,其中包括序列化和反序列化过程。Borsh 的主要优势之一是确定性,可确保相同输入始终产生一致的序列化输出。

本教程将介绍如何反序列化代币账户中的账户数据,并返回可供使用的可读数据。你将通过一个简单示例,使用 Token Program 库,根据 NFT 的铸造地址解析其原始账户信息。

下面展示了如何将原始账户数据转换为更易读的形式:

你可以克隆我们的 deserialize-account 仓库,在这里查看并跟随本教程的完整代码。

前提条件

本教程需要满足以下前提条件:

环境设置

克隆示例仓库:

代码
git clone https://github.com/helius-labs/deserialize-base.git

进入项目目录:

代码
cd deserialize-base

安装 npm:

代码
npm install

现在项目已经设置完成!

接下来,你可以开始构建针对给定铸造地址的账户数据反序列化逻辑。

构建步骤

请按照以下步骤操作:

1. 获取账户数据

在我们的 /src/deserialize.ts 文件中,导入所需模块:

代码
import { Connection, PublicKey } from "@solana/web3.js";

稍后,你将使用这些模块定义与 Solana 的连接,以及要反序列化其数据的铸造地址 PublicKey。

在这段代码下方设置主函数。

你还将使用 Helius RPC URL 设置 Solana 连接,并指定本教程中要反序列化的铸造地址。

代码
async function deserializeMint() {
		// CONNECTION TO SOLANA USING HELIUS
    const rpc = 'https://rpc.helius.xyz/?api-key=';
    const connection = new Connection(rpc);
		// MINT THAT WE ARE DESERIALIZING
    const mint = new PublicKey('6MWfAt3S9Xu4ybxxgPm6e4LSwuXfyAwGXd5yfUqpox9K');

}
deserializeMint()

请务必将上面的 api-key 替换为你自己的 Helius API 密钥。你也可以将其中的铸造地址替换为自己的示例。

接下来,设置 try/catch 来获取该铸造地址的原始账户数据:

代码
try {
    let { data } = (await connection.getAccountInfo(mint)) || {};
    if (!data) {
      return;
    }
    console.log(data);
  } catch {
    return null;
  }

在上面的步骤中,你通过已建立的连接在 Solana 上为 getAccountInfo 发起 RPC 调用。这将返回需要进一步解析的初始数据。

如果未找到数据,它将返回 null。否则,搜索结果将输出到终端。

现在可以运行 ts-node deserialize 查看结果。

你应该会看到类似以下内容的结果:

这里进行反序列化的目的是将数据转换为可读格式。

为此,开发者需要查看相关源代码,找到创建该数据的程序所规定的数据布局。

2. 设置账户类型

在本例中,你需要获取 SPL 铸造地址 6MWfAt3S9Xu4ybxxgPm6e4LSwuXfyAwGXd5yfUqpox9K 的 AccountInfo。

为此,你需要解析 Solana Program Library 中提供的原始铸造数据类型和缓冲区布局。理解给定数据的结构,是反序列化 Solana 上任何数据的关键一步。

上图展示了程序定义的 RawMint 结构体和 MintLayout。你可以直接将它们复制到类型文件中使用。

现在,进入 ./src/types 目录。

在 /src/types.ts 文件中,为类型设置导入:

代码
import { PublicKey } from "@solana/web3.js";
import { u32, u8, struct } from "@solana/buffer-layout";
import { publicKey, u64, bool } from "@solana/buffer-layout-utils";

这将定义反序列化本例中 NFT 账户数据所需的原始铸造数据格式和布局。

现在,你可以参照上面的内容设置接口和布局。

代码
// Defining RawMint from https://github.com/solana-labs/solana-program-library/blob/48fbb5b7c49ea35848442bba470b89331dea2b2b/token/js/src/state/mint.ts#L31 //
export interface RawMint {
    mintAuthorityOption: 1 | 0;
    mintAuthority: PublicKey;
    supply: bigint;
    decimals: number;
    isInitialized: boolean;
    freezeAuthorityOption: 1 | 0;
    freezeAuthority: PublicKey;
}

// Defining Buffer Layout from https://github.com/solana-labs/solana-program-library/blob/48fbb5b7c49ea35848442bba470b89331dea2b2b/token/js/src/state/mint.ts#L31 //

/** Buffer layout for de/serializing a mint */
export const MintLayout = struct([
    u32('mintAuthorityOption'),
    publicKey('mintAuthority'),
    u64('supply'),
    u8('decimals'),
    bool('isInitialized'),
    u32('freezeAuthorityOption'),
    publicKey('freezeAuthority'),
]);

现在,你已经为原始账户信息设置好了类型!接下来,可以在主 deserialize.ts 文件中导入这些类型,并用它们反序列化之前返回的数据。

3. 反序列化返回的数据

了解预期数据的结构后,你就可以设置解码函数,它只需要一行代码。返回 src/deserialize.ts 文件进行设置。

首先,在主 deserialize.ts 文件中,从 types.ts 文件导入 MintLayout:

代码
import { MintLayout } from "./types";

现在,只需在获取账户数据的代码下方,向反序列化函数添加一行代码:

代码
const deserialize = MintLayout.decode(data)
console.log(deserialize)

这会使用 MintLayout 解码 deserializeMint 函数返回的数据。

你可以在 ./src 文件夹中运行 ts-node deserialize,并获得类似以下内容的结果:

代码
{
  mintAuthorityOption: 1,
  mintAuthority: PublicKey [PublicKey(5WQAPQ8i8wqHcSWSEkBQ9kqfwRJxxgyZqAtKiwJSW5zT)] {
    _bn:
  },
  supply: 1n,
  decimals: 0,
  isInitialized: true,
  freezeAuthorityOption: 1,
  freezeAuthority: PublicKey [PublicKey(5WQAPQ8i8wqHcSWSEkBQ9kqfwRJxxgyZqAtKiwJSW5zT)] {
    _bn:
  }
}

这显然更易读了!下一步可以根据响应中的类型进一步解析响应,让结果更整洁。

最后,你仍然可以调整响应,将这些数据整理成上面显示的格式。具体操作如下:

代码
// Breaking down the response //
    console.log(deserialize.mintAuthorityOption)
    console.log(deserialize.mintAuthority.toString())
    console.log(deserialize.decimals)
    console.log(deserialize.isInitialized)
    console.log(deserialize.freezeAuthorityOption)
    console.log(deserialize.freezeAuthority.toString())

在上面的代码中,只需转换以 toString 格式返回的 PublicKeys。其他数据可以保持接收时的格式返回。由于数据格式是确定的,我们也可以预先完成此设置,这将返回以下内容:

代码
1
5WQAPQ8i8wqHcSWSEkBQ9kqfwRJxxgyZqAtKiwJSW5zT
0
true
1

你可以按需要调整这些数据。

这只是将数据解析成更方便查看结果的形式。

完整代码:

在这里查看 deserialize.ts 的完整代码:

代码
import { Connection, PublicKey } from "@solana/web3.js";
import { RawMint, MintLayout } from "./types";

async function deserializeMint() {
  const rpc =
    "https://rpc.helius.xyz/?api-key=";
  const connection = new Connection(rpc);
  const mint = new PublicKey("6MWfAt3S9Xu4ybxxgPm6e4LSwuXfyAwGXd5yfUqpox9K");

  try {
    let { data } = (await connection.getAccountInfo(mint)) || {};
    if (!data) {
      return;
    }
		// Data returned.
    console.log(data);
		// Deserialize Data.
    const deserialize = MintLayout.decode(data)

    // Breaking down the response //
    console.log(deserialize.mintAuthorityOption)
    console.log(deserialize.mintAuthority.toString())
    console.log(deserialize.decimals)
    console.log(deserialize.isInitialized)
    console.log(deserialize.freezeAuthorityOption)
    console.log(deserialize.freezeAuthority.toString)

  } catch {
    return null;
  }
}
deserializeMint();

总结

现在,你已经成功反序列化了 Solana 上的 NFT 账户数据!你可以将同样的方法应用到其他用例中,并使用类似的研究方法确定给定数据所属程序的结构。

请务必查看你要反序列化其数据的程序源代码,并尝试使用这些方法自行完成反序列化。

资源

‍

订阅 Helius

及时了解 Solana 开发的最新动态,并在我们发布新内容时收到更新

放大图片