如何获得 Solana 的代币持有者

在本指南中,我们将了解如何获取 USDC 等可替代代币的所有持有者。当您想要跟踪代币持有者或通过空投奖励持有者的情况下,这非常有用。

概述

我们首先来看看代币(尤其是不可替代代币)如何在 Solana 上运行。当开发者创建代币时,他们使用代币程序创建一个铸币账户。该铸币账户保存有关特定代币的信息,例如名称、代币地址和图像。创建铸币账户后,就可以铸造代币并将其存储在代币账户中。代币账户是保存特定地址所拥有的特定代币信息的账户。这将包括铸币地址、所有者地址以及帐户中特定代币数量等详细信息。例如,持有一些 USDC(SPL 代币)的地址将拥有一个 USDC 代币账户。

账户明细图

现在,我们掌握了代币和代币账户的工作原理,我们可以考虑获取给定代币的所有代币持有者。每个持有特定代币的钱包都会有一个该代币的代币账户。这意味着该代币将与所有持有该代币的钱包的代币账户相关联。这就是我们如何找出所有持有者是谁的方法。如果我们能找到一种方法来获取与代币关联的所有代币帐户,然后获取这些帐户的所有者,我们将获得所有持有者的列表! 

getTokenAccounts 方法

幸运的是,Helius getTokenAccounts API 方法使我们能够准确地做到这一点。我们可以在 API 调用参数中包含任何代币的铸造地址,并且我们将获得为该代币创建的所有代币帐户的列表。除此之外,API 还返回每个代币账户的所有者;这个所有者就是我们通常所说的代币持有者。需要注意的一件小事是,一个账户可以拥有同一个代币的多个代币账户。这不是什么大问题;我们只需要设置一些逻辑来处理共享所有者的代币帐户。 

执行

现在让我们深入研究一些代码,看看如何实际做到这一点。您将需要 Helius API 密钥才能继续操作。您可以通过前往https://dev.helius.xyz并注册一个帐户来免费获得一个。首先,我们必须创建一个名为getTokenHolders.js的 Javascript 文件。我们可以通过添加 Helius URL 并导入 fs 库来将结果保存到JSON文件来开始。


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

接下来,我们将创建一个方法来获取与特定令牌关联的所有令牌帐户。我们可以首先创建一个名为 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",
        },
      }),
    });
    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",
        },
      }),
    });
    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 示例自行测试。

💡
原文链接:Solana MEV: An Introduction
本文由SlerfTools翻译,转载请注明出处。

SlerfTools专为Solana设计的工具箱,致力于简化区块链操作,提供无编程全可视化界面,使发币管理流动性无代码创建Dapp等复杂过程变得安全简单。