{"content":{"title":"Solana 上如何获取Token 所有持有者","body":"在本指南中，我们将看看如何获取诸如 USDC 之类的同质化Token的所有持有者。这在你想要跟踪Token持有者或奖励持有者进行空投的情况下非常有用。\r\n\r\n\r\n## 概述\r\n\r\n让我们首先看看Token，特别是在 Solana 上如何工作。当开发人员创建一个Token时，他们使用Token程序（Token Program）来创建一个铸造账户。这个铸造账户保存了关于特定 Token 的信息，比如名称、Token地址和图像。一旦铸造账户被创建，Token 就可以被铸造并存储在一个 Token 账户中。Token账户是一个账户，保存了关于特定Token的信息，由特定地址拥有。Token账户将包括铸造地址(对应下图的Token Address)、所有者地址以及账户中特定Token的数量等细节。例如，持有一些 USDC（一个 [SPL Token](https://spl.solana.com/) ）的地址将拥有一个 USDC 的Token账户。\r\n\r\n \r\n![65d59a8c5f6bee1516ab8758_tokenholders-diagram.jpeg](https://img.learnblockchain.cn/attachments/2024/02/XxTVcls665dc9c0a9aa20.jpeg)\r\n\r\n> 账户分解图\r\n\r\n‍\r\n现在我们了解了Token和Token账户的工作原理，我们可以看看如何获取给定Token的所有Token持有者。持有特定Token的每个钱包都将拥有该 Token的Token账户。这意味着 Token 与持有该Token 钱包的 Token账户 相关联。这就是我们将找出所有持有者的方式。如果我们能找到一种方法来获取与一个Token相关联的所有Token账户，然后获取这些账户的所有者，我们将得到一个所有持有者的列表！\r\n\r\n\r\n\r\n> 译者备注：与以太坊不同，在以太坊中用户的Token余额保存在 Token合约中，在 Solana 中，每个钱包账号持有的余额单独保存的 Token 账户。在 Solana 中甚至没有单独的Token合约， 而是使用Token 的铸造地址来区分不同的 Token。\r\n\r\n## getTokenAccounts 方法\r\n\r\n‍\r\n\r\n幸运的是，[Helius **getTokenAccounts** API 方法](https://docs.helius.dev/compression-and-das-api/digital-asset-standard-das-api/get-token-accounts)允许我们精确地做到这一点。我们可以在 API 调用参数中包含任何Token的铸造地址，我们将得到一个包含为该Token创建的所有Token账户的列表。除此之外，API 还返回每个Token账户的所有者；这个所有者通常被我们称为Token持有者。需要注意的一件小事是，一个账户可以拥有同一个Token的多个Token账户。这不是一个大问题；我们只需要设置一些逻辑来处理共享所有者的Token账户。\r\n\r\n‍\r\n\r\n## 实现\r\n\r\n现在让我们深入一些代码，看看我们实际上如何做到这一点。你需要一个 Helius API 密钥才能跟随。你可以通过访问 [https://dev.helius.xyz](https://dev.helius.xyz/) 注册一个免费账户来获取一个。首先，我们必须创建一个名为**getTokenHolders.js**的 Javascript 文件。我们可以通过添加我们的 Helius URL 并导入 fs 库来将我们的结果保存到一个**JSON**文件中。\r\n\r\n```Javascript\r\nconst url = `https://mainnet.helius-rpc.com/?api-key=`;\r\nconst fs = require(\"fs\");\r\n```\r\n\r\n接下来，我们将创建一个方法来获取与特定Token相关联的所有Token账户。我们可以通过创建一个名为 findHolders 的方法来开始，该方法将使用 getTokenAccounts 方法来获取所需的数据。你可以在[这里](https://docs.helius.dev/compression-and-das-api/digital-asset-standard-das-api/get-token-accounts)了解更多关于**getTokenAccounts**方法的信息。需要注意的一件重要的事情是，每次对 API 的调用只能返回最多 1000 个Token账户。Solana 上的大多数大型Token都有超过 100,000 个Token账户。为了解决这个问题，我们将使用分页来浏览所有Token账户，并继续进行 API 调用，直到我们获取了所有现有Token账户的数据。在这个方法中，我们将在**getTokenAccounts**调用的参数中包含Token铸造账号。当我们遍历所有Token账户时，我们将把每个唯一的Token账户所有者添加到一个列表中。一旦方法运行完成，我们将把这个列表保存到一个包含所有Token持有者的**JSON**文件中。\r\n\r\n```Javascript\r\nconst findHolders = async () => {\r\n  // Pagination logic\r\n  let page = 1;\r\n \t// allOwners will store all the addresses that hold the token\r\n  let allOwners = new Set();\r\n\r\n  while (true) {\r\n    const response = await fetch(url, {\r\n      method: \"POST\",\r\n      headers: {\r\n        \"Content-Type\": \"application/json\",\r\n      },\r\n      body: JSON.stringify({\r\n        jsonrpc: \"2.0\",\r\n        method: \"getTokenAccounts\",\r\n        id: \"helius-test\",\r\n        params: {\r\n          page: page,\r\n          limit: 1000,\r\n          displayOptions: {},\r\n\t\t\t\t\t//我们关注的token 铸币地址\r\n          mint: \"CKfatsPMUf8SkiURsDXs7eK6GWb4Jsd6UDbs7twMCWxo\",\r\n        },\r\n      }),\r\n    });\r\n    const data = await response.json();\r\n  \t// Pagination logic. \r\n    if (!data.result || data.result.token_accounts.length === 0) {\r\n      console.log(`No more results. Total pages: ${page - 1}`);\r\n      break;\r\n    }\r\n    \r\n    console.log(`Processing results from page ${page}`);\r\n \t\t// Adding unique owners to a list of token owners. \r\n    data.result.token_accounts.forEach((account) =>\r\n      allOwners.add(account.owner)\r\n    );\r\n    page++;\r\n  }\r\n\r\n  fs.writeFileSync(\r\n    \"output.json\",\r\n    JSON.stringify(Array.from(allOwners), null, 2)\r\n  );\r\n};\r\n\r\nCopy\r\n```\r\n\r\n‍\r\n\r\n在上面的示例中，**getTokenAccounts**方法在分页浏览所有Token账户时被多次调用。API 响应将为每个Token账户提供以下数据：\r\n\r\n```json\r\n{\r\n\"address\": \"CVMR1nbxTcQ7Jpa1p137t5TyKFii3Y7Vazt9fFct3tk9\",\r\n\"mint\": \"SHDWyBxihqiCj6YekG2GUr7wqKLeLAMK1gHZck9pL6y\",\r\n\"owner\": \"CckxW6C1CjsxYcXSiDbk7NYfPLhfqAm3kSB5LEZunnSE\",\r\n\"amount\": 100000000,\r\n\"delegated_amount\": 0,\r\n\"frozen\": false\r\n},\r\n```\r\n\r\n\r\n\r\n\r\n\r\n我们从这些 Token 账户中提取了所有者，并将其添加到我们的列表中。如果我们愿意，我们还可以存储每个Token账户持有的Token数量，以找出最大的持有者。\r\n\r\n现在一旦这个操作完成，我们需要做的就是调用这个方法：\r\n\r\n```Javascript\r\nfindHolders();\r\n```\r\n\r\n我们的**getTokenHolders.js**文件中的完整代码应该如下所示：\r\n\r\n```Javascript\r\nconst url = `https://mainnet.helius-rpc.com/?api-key=`;\r\nconst fs = require(\"fs\");\r\n\r\n\r\nconst findHolders = async () => {\r\n  let page = 1;\r\n  let allOwners = new Set();\r\n\r\n  while (true) {\r\n    const response = await fetch(url, {\r\n      method: \"POST\",\r\n      headers: {\r\n        \"Content-Type\": \"application/json\",\r\n      },\r\n      body: JSON.stringify({\r\n        jsonrpc: \"2.0\",\r\n        method: \"getTokenAccounts\",\r\n        id: \"helius-test\",\r\n        params: {\r\n          page: page,\r\n          limit: 1000,\r\n          displayOptions: {},\r\n          mint: \"DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263\",\r\n        },\r\n      }),\r\n    });\r\n    const data = await response.json();\r\n\r\n    if (!data.result || data.result.token_accounts.length === 0) {\r\n      console.log(`No more results. Total pages: ${page - 1}`);\r\n\r\n      break;\r\n    }\r\n    console.log(`Processing results from page ${page}`);\r\n    data.result.token_accounts.forEach((account) =>\r\n      allOwners.add(account.owner)\r\n    );\r\n    page++;\r\n  }\r\n\r\n  fs.writeFileSync(\r\n    \"output.json\",\r\n    JSON.stringify(Array.from(allOwners), null, 2)\r\n  );\r\n};\r\n\r\nfindHolders();\r\n```\r\n\r\n## 输出\r\n\r\n我们代码的输出将是所有持有者的列表，看起来类似于这样：\r\n\r\n```js\r\n[\r\n \"111An9SVxuPpgjnuXW9Ub7hcVmZpYNrYZF4edsGwJEW\",\r\n \"11Mmng3DoMsq2Roq8LBcqdz6d4kw9oSD8oka9Pwfbj\",\r\n \"112uNfcC8iwX9P2TkRdJKyPatg6a4GNcr9NC5mTc2z3\",\r\n \"113uswn5HNgEfBUKfK4gVBmd2GpZYbxd1N6h1uUWReg\",\r\n \"11CyvpdYTqFmCVWbJJeKFNX8F8RSjNSYW5VVUi8eX4P\",\r\n \"11MANeaiHEy9S9pRQNu3nqKa2gpajzX2wrRJqWrf8dQ\",\r\n…\r\n]\r\n```\r\n\r\n‍\r\n\r\n你可以通过我们的 [replit 示例](https://replit.com/@owen47/getTokenAccountsExample)来测试这一点。\r\n\r\n‍\r\n\r\n## 结论\r\n\r\n总结以下，我们成功地介绍了通过 Helius **getTokenAccounts** API 来识别 Solana Token持有者的过程。这个步骤应该为你提供必要的技能，直接与你的Token社区进行互动。 \r\n\r\n\r\n\r\n原文：https://www.helius.dev/blog/how-to-get-token-holders-on-solana"},"author":{"user":"https://learnblockchain.cn/people/18699","address":null},"history":"bafkreihq5v4gwmp2ds2ld7dpuw4qgxdcrnqomchvrkqaiyyy5esqkopx34","timestamp":1708956703,"version":1}