НОВОЕ: Helius приобретает Light Protocol
Как получить список держателей токена в Solana
Блог/Разработка

Как получить список держателей токена в Solana

Инженер по взаимодействию с разработчикамиOwen Venter в XOwen Venter в LinkedIn
Чтение: 5 мин

В этом руководстве мы рассмотрим, как получить список всех держателей взаимозаменяемого токена, например USDC. Это может пригодиться, если вы хотите отслеживать держателей токена или вознаградить их с помощью аирдропа.

Обзор

Сначала разберемся, как в Solana работают токены, особенно невзаимозаменяемые. Когда разработчик создает токен, он использует токен-программу для создания аккаунта минта. В этом аккаунте хранится информация о конкретном токене, например его название, адрес и изображение. После создания аккаунта минта токен можно выпустить и хранить в токен-аккаунте. Токен-аккаунт — это аккаунт с информацией о конкретном токене, принадлежащем определенному адресу. В нем хранятся такие данные, как адрес минта, адрес владельца и количество этого токена на аккаунте. Например, у адреса с некоторым количеством USDC (токена SPL) будет токен-аккаунт для USDC.

Теперь, когда мы понимаем, как работают токены и токен-аккаунты, можно получить список всех держателей заданного токена. У каждого кошелька, в котором хранится конкретный токен, есть токен-аккаунт для этого токена. Таким образом, токен связан с токен-аккаунтами всех кошельков, в которых он хранится. Это позволит определить всех держателей. Если получить все токен-аккаунты, связанные с токеном, а затем найти их владельцев, мы получим список всех держателей! 

Метод getTokenAccounts

К счастью, метод Helius getTokenAccounts API позволяет сделать именно это. В параметры вызова API можно передать адрес минта любого токена и получить список всех токен-аккаунтов, созданных для него. Кроме того, API возвращает владельца каждого токен-аккаунта. Именно его обычно называют держателем токена. Следует учитывать, что у одного аккаунта может быть несколько токен-аккаунтов для одного и того же токена. Это несущественная проблема: достаточно добавить логику для обработки токен-аккаунтов с общим владельцем. 

Реализация

Теперь рассмотрим код и разберемся, как это реализовать. Для выполнения примера вам понадобится ключ Helius API. Его можно бесплатно получить, зарегистрировав аккаунт в панели управления Helius. Сначала создайте файл JavaScript с именем getTokenHolders.js. Затем добавьте в него URL Helius и импортируйте библиотеку 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",
        },
      }),
    });

		// 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.

Заключение

В этом руководстве мы рассмотрели, как определять держателей токена Solana с помощью Helius getTokenAccounts API. Теперь у вас есть необходимые навыки для непосредственного взаимодействия с сообществом вашего токена: проведения аирдропов, анализа данных и других задач. Если у вас остались вопросы, свяжитесь с нами через Twitter или Discord. ‍

Подпишитесь на Helius

Следите за новостями разработки на Solana и получайте уведомления о наших публикациях

Увеличенное изображение