
Как получить список держателей токена в Solana
В этом руководстве мы рассмотрим, как получить список всех держателей взаимозаменяемого токена, например 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 и получайте уведомления о наших публикациях


