
Solanaでトークン保有者を取得する方法
このガイドでは、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開発の最新情報や新しい記事の公開通知を受け取れます


