新着:HeliusがLight Protocolを買収
Solanaでトークン保有者を取得する方法
ブログ/開発

Solanaでトークン保有者を取得する方法

開発者体験エンジニアXのOwen VenterLinkedInのOwen Venter
読了時間:5分

このガイドでは、USDCのような代替可能トークンの全保有者を取得する方法を紹介します。トークン保有者を追跡したい場合や、保有者にエアドロップで報酬を配布したい場合に役立ちます。

概要

まず、Solana上でトークン、特に非代替性トークンがどのように機能するかを見てみましょう。開発者がトークンを作成する際は、トークンプログラムを使用してミントアカウントを作成します。このミントアカウントには、名前、トークンアドレス、画像など、特定のトークンに関する情報が保持されます。ミントアカウントを作成すると、トークンをミントしてトークンアカウントに保存できます。トークンアカウントは、特定のアドレスが所有する特定のトークンについての情報を保持するアカウントです。ミントアドレス、所有者のアドレス、アカウント内にある特定のトークンの数量などが含まれます。たとえば、USDC(SPLトークン)を保有するアドレスには、USDC用のトークンアカウントがあります。

トークンとトークンアカウントの仕組みを理解したところで、特定のトークンの全保有者を取得する方法を見てみましょう。特定のトークンを保有する各ウォレットには、そのトークン用のトークンアカウントがあります。つまり、このトークンは、それを保有するすべてのウォレットのトークンアカウントに関連付けられています。この仕組みを利用して、すべての保有者を特定します。トークンに関連付けられたすべてのトークンアカウントを取得し、それらのアカウントの所有者を取得できれば、全保有者のリストを作成できます。 

getTokenAccountsメソッド

幸い、HeliusのgetTokenAccounts APIメソッドを使えば、まさにこれを実現できます。API呼び出しのパラメータに任意のトークンのミントアドレスを指定すると、そのトークン用に作成されたすべてのトークンアカウントのリストが返されます。さらに、このAPIは各トークンアカウントの所有者も返します。この所有者が、一般にトークン保有者と呼ばれるものです。注意点として、1つのアカウントが同じトークンのトークンアカウントを複数持つ場合があります。これは大きな問題ではありません。同じ所有者を持つトークンアカウントを処理するロジックを用意するだけです。 

実装

それでは、実際にどのように実装するか、コードを詳しく見ていきましょう。この手順を進めるには、Helius APIキーが必要です。Heliusダッシュボードでアカウントを登録すると、無料で取得できます。まず、getTokenHolders.jsというJavaScriptファイルを作成します。Helius URLを追加し、結果をJSONファイルに保存するためのfsライブラリをインポートします。

コード
const url = `https://mainnet.helius-rpc.com/?api-key=`;
const fs = require("fs");

次に、特定のトークンに関連付けられたすべてのトークンアカウントを取得するメソッドを作成します。まず、findHoldersというメソッドを作成し、getTokenAccountsメソッドを使って必要なデータを取得します。getTokenAccountsメソッドの詳細については、こちらをご覧ください。

重要な点として、APIの各呼び出しで返せるトークンアカウントは最大1,000件です。Solana上の大規模なトークンの多くには、100,000件を超えるトークンアカウントがあります。この制限に対応するため、ページネーションを使ってすべてのトークンアカウントを順に処理し、既存の全トークンアカウントに関するデータを取得するまでAPI呼び出しを続けます。

このメソッドでは、getTokenAccounts呼び出しのパラメータにトークンミントを指定します。すべてのトークンアカウントをループ処理しながら、重複しない各トークンアカウント所有者をリストに追加します。メソッドの実行が完了したら、すべてのトークン保有者を含むJSONファイルにこのリストを保存します。

コード
const findHolders = async () => {
  // Pagination logic
  let page = 1;
 	// allOwners will store all the addresses that hold the token
  let allOwners = new Set();

  while (true) {
    const response = await fetch(url, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        jsonrpc: "2.0",
        method: "getTokenAccounts",
        id: "helius-test",
        params: {
          page: page,
          limit: 1000,
          displayOptions: {},
					//mint address for the token we are interested in
          mint: "CKfatsPMUf8SkiURsDXs7eK6GWb4Jsd6UDbs7twMCWxo",
        },
      }),
    });

		// Check if any error in the response
      if (!response.ok) {
        console.log(
          `Error: ${response.status}, ${response.statusText}`
        );
        break;
      }

    const data = await response.json();
  	// Pagination logic.
    if (!data.result || data.result.token_accounts.length === 0) {
      console.log(`No more results. Total pages: ${page - 1}`);
      break;
    }
    console.log(`Processing results from page ${page}`);
 		// Adding unique owners to a list of token owners.
    data.result.token_accounts.forEach((account) =>
      allOwners.add(account.owner)
    );
    page++;
  }

  fs.writeFileSync(
    "output.json",
    JSON.stringify(Array.from(allOwners), null, 2)
  );
};

‍上記の例では、すべてのトークンアカウントをページネーションで処理しながら、getTokenAccountsメソッドを複数回呼び出しています。APIレスポンスでは、各トークンアカウントについて次のデータが返されます。 

コード
{
"address": "CVMR1nbxTcQ7Jpa1p137t5TyKFii3Y7Vazt9fFct3tk9",
"mint": "SHDWyBxihqiCj6YekG2GUr7wqKLeLAMK1gHZck9pL6y",
"owner": "CckxW6C1CjsxYcXSiDbk7NYfPLhfqAm3kSB5LEZunnSE",
"amount": 100000000,
"delegated_amount": 0,
"frozen": false
},

これらのトークンアカウントから所有者を抽出し、リストに追加しました。必要であれば、各トークンアカウントが保有するトークンの数量も保存し、最大の保有者を特定できます。 

ここまで完了したら、あとはメソッドを呼び出すだけです。

コード
findHolders();

getTokenHolders.jsファイルの完全なコードは次のようになります。

コード
const url = `https://mainnet.helius-rpc.com/?api-key=`;
const fs = require("fs");

const findHolders = async () => {
  let page = 1;
  let allOwners = new Set();

  while (true) {
    const response = await fetch(url, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        jsonrpc: "2.0",
        method: "getTokenAccounts",
        id: "helius-test",
        params: {
          page: page,
          limit: 1000,
          displayOptions: {},
          mint: "DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263",
        },
      }),
    });

		// Check if any error in the response
      if (!response.ok) {
        console.log(
          `Error: ${response.status}, ${response.statusText}`
        );
        break;
      }

    const data = await response.json();

    if (!data.result || data.result.token_accounts.length === 0) {
      console.log(`No more results. Total pages: ${page - 1}`);

      break;
    }
    console.log(`Processing results from page ${page}`);
    data.result.token_accounts.forEach((account) =>
      allOwners.add(account.owner)
    );
    page++;
  }

  fs.writeFileSync(
    "output.json",
    JSON.stringify(Array.from(allOwners), null, 2)
  );
};

findHolders();

出力

コードを実行すると、次のような全保有者のリストが出力されます。

コード
[
  "111An9SVxuPpgjnuXW9Ub7hcVmZpYNrYZF4edsGwJEW",
  "11Mmng3DoMsq2Roq8LBcqdz6d4kw9oSD8oka9Pwfbj",
  "112uNfcC8iwX9P2TkRdJKyPatg6a4GNcr9NC5mTc2z3",
  "113uswn5HNgEfBUKfK4gVBmd2GpZYbxd1N6h1uUWReg",
  "11CyvpdYTqFmCVWbJJeKFNX8F8RSjNSYW5VVUi8eX4P",
  "11MANeaiHEy9S9pRQNu3nqKa2gpajzX2wrRJqWrf8dQ",
…
]

Replitのサンプルを使って、ご自身で試すこともできます。

まとめ

このガイドでは、HeliusのgetTokenAccounts APIを使用して、Solanaトークンの保有者を特定する方法を説明しました。この手順を活用すれば、エアドロップ、分析、その他の施策を通じて、トークンコミュニティと直接関わるために必要なスキルを身につけられます。ご質問がある場合は、TwitterまたはDiscordからお気軽にお問い合わせください。 ‍

Heliusを購読

Solana開発の最新情報や新しい記事の公開通知を受け取れます

拡大画像