幸运的是,Helius getTokenAccounts API 方法允许我们精确地做到这一点。我们能够在 API 调用参数中包含任何代币的铸币地址,我们将获得为该代币创建的所有代币帐户的列表。除此之外,API 还返回每个代币帐户的所有者;这个所有者就是我们通常所说的代币持有者。需要注意的一件小事是,一个帐户可以有多个针对同一代币的代币帐户。这不是什么大问题;我们只需要设置一些逻辑来处理共享所有者的代币帐户。
接下来,我们将创建一种方法来获取与特定代币相关联的所有代币帐户。我们可以首先创建一个名为 findHolders 的方法,该方法将使用 getTokenAccounts 方法获取所需的数据。您可以在此处阅读有关getTokenAccounts方法的更多信息。需要注意的一件重要事情是,每次调用 API 最多只能返回 1000 个代币帐户。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)
);
};