У Solana баланс токена зберігається не на гаманці користувача, а на окремому токен-акаунті, який прив'язаний до цієї гаманці та до конкретного mint-акаунта токена. Тому перевірка балансу — це завжди запит до токен-акаунта, а не до адреси гаманця. Нижче наведено чотири перевірені способи зробити це на Devnet.

Середовище: Devnet (https://api.devnet.solana.com)
Передумови: встановлений CLI інструмент spl-token (пакет @solana/spl-token-cli), Node.js 18+, наявний токен-акаунт із тестовими токенами (наприклад, створений у попередньому кроці через Як mint-нути токен через Anchor).
Очікуваний результат: отримання кількості токенів із правильним урахуванням decimals.

Через CLI

spl-token balance — баланс токен-акаунта

Найшвидший спосіб дізнатися баланс конкретного токен-акаунта. Переконайтеся, що CLI налаштований на Devnet:

solana config set --url https://api.devnet.solana.com

Тепер передайте адресу токен-акаунта:

spl-token balance <TOKEN_ACCOUNT_ADDRESS>

CLI поверне число з урахуванням decimals. Наприклад, якщо токен має 9 decimals і на акаунті зберігається 1 000 000 000 одиниць, ви побачите 1.000000000.

Якщо ви не знаєте адресу токен-акаунта, але знаєте адресу гаманця, можна скористатися прапорцем --owner:

spl-token balance --owner <WALLET_PUBKEY>

Ця команда покаже баланси всіх токен-акаунтів, що належать цій гаманці на поточному кластері.

spl-token accounts — список усіх токен-акаунтів

Коли потрібно знайти, які токен-акаунти взагалі існують для гаманця:

spl-token accounts --owner <WALLET_PUBKEY>

Результат — таблиця, де кожен рядок містить:

  • Token — адреса mint-акаунта токена;
  • Account — адреса токен-акаунта (саме її передаєте до spl-token balance);
  • Balance — поточний баланс.

Цей спосіб корисний на етапі налагодження, коли ви не впевнені, чи створився Associated Token Account (ATA) автоматично, чи потрібно створювати його вручну.

Через TypeScript SDK

getTokenAccountBalance — запит до RPC

У програмах на TypeScript баланс токена запитується через RPC-метод getTokenAccountBalance. Нижче — мінімальний робочий приклад із використанням @solana/web3.js (стабільна гілка 1.x):

import { Connection, PublicKey } from "@solana/web3.js";

const connection = new Connection("https://api.devnet.solana.com", "confirmed");
const tokenAccount = new PublicKey("YOUR_TOKEN_ACCOUNT_ADDRESS");

try {
  const response = await connection.getTokenAccountBalance(tokenAccount);
  console.log("Сирий баланс (у найменших одиницях):", response.value.amount);
  console.log("Decimals:", response.value.decimals);
  console.log("Форматований баланс:", response.value.uiAmountString);
} catch (err) {
  console.error("Помилка:", err);
}

Метод повертає об'єкт RpcResponseAndContext, де поле value містить:

  • amount — рядок із кількістю у найменших одиницях (наприклад, "1500000000");
  • decimals — кількість знаків після коми для цього токена (наприклад, 9);
  • uiAmountString — рядок із форматованим значенням (наприклад, "1.5");
  • uiAmount — те саме, але типу number (у новіших версіях може бути null, тому надійніше використовувати uiAmountString).

Десеріалізація та форматування результату

У production-коді не покладайтеся на uiAmount або uiAmountString як на єдине джерело істини. Надійніший підхід — самостійно перетворити amount з урахуванням decimals:

function formatTokenBalance(amount: string, decimals: number): string {
  const whole = amount.slice(0, -decimals) || "0";
  const fraction = amount.slice(-decimals).padStart(decimals, "0");
  return `${whole}.${fraction}`;
}

const raw = response.value.amount;
const dec = response.value.decimals;
console.log(formatTokenBalance(raw, dec));

Це усуває залежність від поведінки конкретної версії RPC-сервера та гарантує однаковий результат у клієнті й на сервері.

Через Explorer

Пошук токен-акаунта за адресою

Solana Explorer на Devnet доступний за адресою https://explorer.solana.com/?cluster=devnet. Щоб перевірити баланс:

  1. Вставте адресу токен-акаунта в рядок пошуку.
  2. Перейдіть на сторінку акаунта.
  3. У блоці Token Account буде вказано: mint-адресу токена, власника (owner) та поточний баланс із урахуванням decimals.

Якщо ви вставили адресу гаманця, а не токен-акаунта, ви побачите лише баланс SOL. Токен-акаунти будуть перелічені в нижчому блоці Token Holdings, але для точного значення краще переходити на кожен окремий токен-акаунт.

Перегляд історії транзакцій токена

На сторінці токен-акаунта в Explorer є розділ Transactions. Він показує всі транзакції, що змінили баланс цього акаунта: mint, transfer, burn. Це корисно, коли баланс не збігається з очікуваним — ви можете візуально трасувати, яка саме транзакція змінила його останньою.

Обмеження: Explorer не підходить для автоматизованої перевірки балансу. Це інструмент для ручного налагодження.

Баланс у контексті програми

Читання балансу під час тестування

Коли ви пишете тести для Anchor-програми (файли .ts у папці tests/), баланс токен-акаунта перевіряється тією самою функцією getTokenAccountBalance, але через provider.connection:

const tokenAccount = await getAssociatedTokenAddress(
  mint, // PublicKey mint-акаунта
  owner // PublicKey власника
);

const balance = await provider.connection.getTokenAccountBalance(tokenAccount);
assert.strictEqual(balance.value.uiAmountString, "100.5");

Функція getAssociatedTokenAddress із @solana/spl-token обчислює адресу ATA детерміновано — вам не потрібно зберігати її окремо, якщо ви знаєте mint і owner.

Перевірка балансу після instruction

Після виконання instruction (наприклад, transfer або mint) переконайтеся, що транзакція підтверджена, перш ніж запитувати баланс. У тестах це робиться через await на відправленій транзакції:

const txSig = await program.methods
  .transfer(new BN(50))
  .accounts({ /* ... */ })
  .rpc();

// Чекаємо підтвердження
await provider.connection.confirmTransaction(txSig, "confirmed");

// Тепер баланс актуальний
const balanceAfter = await provider.connection.getTokenAccountBalance(tokenAccount);

Усередині самої Solana-програми (Rust/Anchor) прямого виклику «отримати баланс» немає. Замість цього програма отримує токен-акаунт як параметр акаунта, а Anchor через Token interface автоматично перевіряє його належність до правильного mint та owner. Якщо вам потрібно порівняти баланс усередині програми, ви читаєте поле amount структури Account із модуля spl_token.

Типові помилки

Невірний token account address

Найчастіша помилка — передати адресу mint-акаунта або адресу гаманця замість адреси токен-акаунта. RPC у цьому випадку поверне помилку, оскільки ці акаунти мають інший тип даних (не відповідають структурі Token Account).

Як перевірити: викличте getAccountInfo і перевірте, що поле owner акаунта дорівнює TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA (адреса програми SPL Token). Якщо owner інший — це не токен-акаунт.

Токен-акаунт не існує

Associated Token Account створюється не автоматично при перевірці балансу. Якщо ви запитуєте баланс ATA, який ще не було створено (наприклад, гаманець ніколи не отримував цей токен), RPC поверне помилку.

Рішення для CLI: спочатку викличте spl-token create-account або переконайтеся, що ATA існує через spl-token accounts.

Рішення для TypeScript: перед викликом getTokenAccountBalance створіть ATA, якщо його немає:

import { getAssociatedTokenAddress, createAssociatedTokenAccountInstruction } from "@solana/spl-token";

const ata = await getAssociatedTokenAddress(mint, owner);
try {
  await connection.getTokenAccountBalance(ata);
} catch {
  // ATA не існує — створюємо
  const tx = new Transaction().add(
    createAssociatedTokenAccountInstruction(payer, ata, owner, mint)
  );
  await sendAndConfirmTransaction(connection, tx, [payerKeypair]);
}

Цей підхід гарантує, що запит балансу не впаде з помилкою відсутнього акаунта. У production-коді обов'язково обробляйте цей випадок окремо, а не через спробу запиту балансу як індикатор існування акаунта — це повільніше і створює зайве навантаження на RPC.

Джерела