新着:HeliusがLight Protocolを買収
Solanaでアカウントデータをデシリアライズする方法
ブログ/開発

Solana Dev 101 - Solanaでのアカウントデータのデシリアライズ

Developer RelationsリードLinkedInのHunter Davis
読了時間:5分

はじめに

Solana上のデータを扱うのは簡単ではありません。アカウントやトランザクションのデータは、多くの場合エンコードされています。これは効率面では優れていますが、開発者にとっては扱いにくいものです。

Solanaはデータのシリアライズに、シリアライズとデシリアライズの両方の処理を含むBorsh(Binary Object Representation Serializer for Hashing)を使用します。Borshの主な利点の1つは決定性です。同じ入力に対して一貫したシリアライズ結果を保証します。

このチュートリアルでは、トークンアカウントのデータをデシリアライズし、利用可能な読みやすいデータとして返す方法を解説します。Token Programライブラリを使用し、NFTのmint addressから生のアカウント情報を分解する簡単な例を扱います。

以下は、生のアカウントデータをより読みやすい形式に変換する例です。

このチュートリアルの完全なコードは、deserialize-accountリポジトリをこちらからクローンして確認できます。

前提条件

このチュートリアルの前提条件は次のとおりです。

環境のセットアップ

サンプルリポジトリをクローンします。

コード
git clone https://github.com/helius-labs/deserialize-base.git

プロジェクトディレクトリに移動します。

コード
cd deserialize-base

npmをインストールします。

コード
npm install

これでプロジェクトのセットアップは完了です。

ここから、指定したmint addressのアカウントデータをデシリアライズする処理を実装していきます。

実装手順

以下の手順に従ってください。

1. アカウントデータを取得する

/src/deserialize.tsファイルで、必要なモジュールをインポートします。

コード
import { Connection, PublicKey } from "@solana/web3.js";

これは後ほどSolanaへの接続と、データをデシリアライズするmintのPublicKeyを定義するために使用します。

この下にメイン関数を設定します。

Helius RPC URLを使用してSolanaへの接続を設定し、このチュートリアルでデシリアライズするmintを指定します。

コード
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キーに置き換えてください。また、このチュートリアルで使用するmintを任意の例に置き換えることもできます。

次に、try/catchを設定し、このmintの生のアカウントデータを取得します。

コード
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 mint 6MWfAt3S9Xu4ybxxgPm6e4LSwuXfyAwGXd5yfUqpox9KのAccountInfoを取得します。

そのためには、Solana Program Libraryに記載されている生のmintデータの型とバッファレイアウトを分解する必要があります。データの構造を理解することは、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のアカウントデータをデシリアライズするために必要な、生のmintの形式とレイアウトが定義されます。

次に、上記と同様にインターフェースとレイアウトを設定します。

コード
// 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. 返されたデータをデシリアライズする

想定されるデータの構造を理解できたので、わずか1行のコードでdecode関数を設定できます。src/deserialize.tsファイルに戻って設定します。

まず、メインのdeserialize.tsファイルで、types.tsファイルからMintLayoutをインポートします。

コード
import { MintLayout } from "./types";

次に、アカウントデータを取得している箇所のすぐ下に、デシリアライズ関数のコードを1行追加します。

コード
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())

上記のコードでは、返されたPublicKeysをtoString形式に変換するだけです。それ以外のデータは、受け取った形式のまま返せます。データ形式はあらかじめ分かっているため、事前に設定することもできます。その場合、次の結果が返されます。

コード
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開発の最新情報や新しい記事の公開通知を受け取れます

拡大画像