Після прочитання цієї сторінки ви зможете: налаштувати робоче середовище з Solana CLI, підключитися до Devnet через TypeScript, читати стан акаунтів, відправляти транзакції, обробляти помилки гаманця та зібрати простий frontend-застосунок з підключенням гаманця. Усі приклади орієнтовані на кластер Devnet і стабільну гілку @solana/web3.js версії 1.x. Передумови: встановлений Node.js 18+ LTS та базове знайомство з TypeScript.
Як встановити та налаштувати Solana CLI
Встановлення Solana CLI
Solana CLI — це інструмент командного рядка для управління ключами, розгортання програм та взаємодії з кластерами. Для macOS та Linux виконайте:
sh -c "$(curl -sSfL https://release.anza.xyz/stable/install)"
Для Windows використовуйте WSL2 і ту саму команду. Перевірте, що CLI доступний у PATH:
solana --version
Очікуваний результат — вивід версії, наприклад solana-cli 1.18.x. Якщо команда не знайдена, додайте шлях ~/.local/share/solana/install/active_release/bin до змінної середовища PATH. Перевірте актуальну URL для встановлення в офіційній документації Solana, оскільки адреса репозиторію релізів може змінюватися.
Конфігурація CLI
Після встановлення вкажіть кластер за замовчуванням. Для початкової розробки використовуйте Devnet:
solana config set --url devnet
Перевірте поточну конфігурацію:
solana config get
Очікуваний результат: поле RPC URL містить https://api.devnet.solana.com, а WebSocket URL — відповідний wss-endpoint.
Генерація ключової пари
Створіть нову ключову пару для розробки:
solana-keygen new --outfile ~/devnet-wallet.json
Система запропонує ввести парольну фразу (bip39 mnemonic). Збережіть її в надійному місці — це єдиний спосіб відновити доступ до коштів. Перевірте публічний ключ:
solana-keygen pubkey ~/devnet-wallet.json
Очікуваний результат: рядок з базовим-58 закодованим публічним ключем, наприклад 7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU.
Перехід між кластерами
Solana має три основні кластери: Devnet, Testnet та Mainnet-beta. Перемикання виконується командою config set:
solana config set --url mainnet-beta
solana config set --url devnet
solana config set --url localhost
Для локальної розробки з solana-test-validator використовуйте localhost. Перевіряйте поточний кластер перед кожною сесією — відправка транзакцій на неправильний кластер є поширеною помилкою.
Типові помилки
- Command not found — CLI не додано до PATH. Перезавантажте термінал або виконайте source ~/.bashrc (або відповідний файл вашої оболонки).
- Потрапили на Mainnet замість Devnet — завжди виконуйте solana config get перед роботою. Транзакції на Mainnet вимагають реальних SOL.
- Застаріла версія CLI — оновіть через agave-install update. Невідповідність версій CLI та кластера може призвести до непередбачуваних помилок сериалізації.
Solana web3.js: перші кроки
Що таке @solana/web3.js
@solana/web3.js — це офіційна TypeScript-бібліотека для взаємодії з кластером Solana. Вона забезпечує створення підключень, формування транзакцій, серіалізацію даних та роботу з криптографічними ключами. Стабільна гілка 1.x є рекомендованою для production-застосунків. Гілка 2.x перебуває в стадії активної розробки і має інший API — не змішуйте їх в одному проєкті.
Підключення до кластера
Ініціалізуйте проєкт і встановіть залежність:
npm init -y
npm install @solana/web3.js@1
Створіть підключення:
import { Connection } from "@solana/web3.js";
const connection = new Connection(
"https://api.devnet.solana.com",
"confirmed"
);
Другий аргумент — рівень комітменту. confirmed підходить для більшості сценаріїв Devnet: транзакція підтверджена блоком, але ще не фіналізована. Для локального validator можна використовувати processed — це найшвидший рівень.
Основні операції
Базові операції, які доступні одразу після створення Connection:
const version = await connection.getVersion();
const slot = await connection.getSlot();
const health = await connection.getHealth();
Очікуваний результат: об'єкт з версією RPC-сервера, поточний слот (число) та рядок "ok" для health. Якщо будь-який з цих викликів видає помилку — підключення не працює.
Робота з ключами
@solana/web3.js надає два основні типи для ключів:
import { Keypair, PublicKey } from "@solana/web3.js";
// Генерація нової пари (тільки для розробки)
const keypair = Keypair.generate();
console.log(keypair.publicKey.toBase58());
// Створення PublicKey з рядка
const address = new PublicKey("7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU");
Keypair містить і закритий, і відкритий ключі. Ніколи не логуйте закритий ключ (secretKey) у production. PublicKey — це лише відкритий ключ, його можна безпечно передавати та відображати.
Типові помилки
- Змішування версій 1.x і 2.x — імпорти з різних гілок мають різні шляхи та сигнатури. Перевіряйте package.json, щоб у залежностях була лише одна гілка.
- Неправильний рівень комітменту — використання finalized на Devnet може значно уповільнити виконання, оскільки фіналізація вимагає додаткових епох.
- Invalid public key — рядок не відповідає формату Base58 або має неправильну довжину. Завжди перевіряйте адреси перед створенням PublicKey.
Solana Kit для початківця
Що таке Solana Kit
У екосистемі Solana існують community-бібліотеки під назвою "Solana Kit", які надають абстракції вищого рівня над @solana/web3.js та @solana/wallet-adapter. Їхня мета — зменшити кількість шаблонного коду для типових операцій: підключення гаманця, відправка SOL, читання стану. Оскільки це не офіційні пакети, їхній API та статус підтримки можуть змінюватися. Перед використанням перевірте актуальну версію та кількість завантажень на npm.
Встановлення та налаштування
Якщо ви обираєте community-Kit, встановіть його через npm. Конкретне ім'я пакета перевірте на npm за запитом "solana-kit". Типова схема підключення виглядає так:
npm install solana-kit
Однак для надійності в цьому посібнику ми використовуємо офіційні пакети @solana/web3.js та @solana/wallet-adapter-* як "kit" для початківця. Цей підхід гарантує сумісність із кластером та довгострокову підтримку.
Основні можливості
Типові можливості, які надають подібні Kit-бібліотеки: обгортки над створенням транзакцій, утиліти для конвертації lamports у SOL, готові хуки для React, спрощене читання даних з акаунтів. У цьому посібнику кожну з цих можливостей ми реалізуємо через офіційний SDK — це дає повний контроль над поведінкою коду.
Порівняння з web3.js
| Критерій | @solana/web3.js (офіційний) | Community Kit |
|---|---|---|
| Підтримка | Офіційна команда Solana | Community-розробники |
| Абстракція | Низький рівень, повний контроль | Високий рівень, менше коду |
| Стабільність API | Стабільна в межах гілки 1.x | Може змінюватися між мінорними версіями |
| Документація | Офіційна docs.solana.com | GitHub README та приклади |
| Для кого | Розробники, яким потрібен контроль | Швидкі прототипи та навчання |
Типові помилки
- Залежність від недоступного пакету — community-пакет може бути видалений або залишений без підтримки. Завжди майте запасний план з офіційним SDK.
- Невідповідність версій — Kit може залежати від конкретної мінорної версії web3.js. Перевіряте peerDependencies у package.json пакета.
- Прихована логіка — високорівневі абстракції можуть приховувати важливі деталі, наприклад вибір комітмент-рівня або обробку помилок. Розумійте, що відбувається "під капотом".
Як підключити гаманець до застосунку
Як працюють гаманці у Solana
Браузерні гаманці (Phantom, Solflare, Backpack) ін'ектують свій об'єкт у window.solana (стандарт для Phantom та сумісних гаманців). Цей об'єкт надає методи для підключення, відключення та підписання транзакцій. Гаманець не передає закритий ключ вашому застосунку — він лише підписує дані всередині свого ізольованого контексту.
Виявлення гаманця
Перевірте наявність гаманця в об'єкті window:
const solanaWindow = window as any;
if (solanaWindow.solana?.isPhantom) {
console.log("Phantom виявлено");
} else {
console.log("Гаманець не знайдено. Встановіть Phantom.");
}
Очікуваний результат: повідомлення про виявлення або відсутність гаманця. Перевірку виконуйте після повного завантаження сторінки (у useEffect для React або після події DOMContentLoaded).
Підключення та відключення
Для підключення викличте метод connect, який повертає обіцянку з публічним ключем:
const response = await solanaWindow.solana.connect();
const publicKey = response.publicKey.toString();
console.log("Підключено:", publicKey);
Для відключення:
await solanaWindow.solana.disconnect();
Очікуваний результат: після підключення — рядок з публічним ключем користувача. Після відключення — подія disconnect у гаманці. Зверніть увагу: connect може викинути помилку, якщо користувач скасував дію в спливаючому вікні гаманця.
Підписання транзакцій
Гаманець підписує серіалізовану транзакцію без передачі закритого ключа:
const signedTransaction = await solanaWindow.solana.signTransaction(transaction);
Метод повертає той самий об'єкт Transaction, але з доданим підписом. Після цього транзакцію можна відправити через Connection. Для підписання кількох транзакцій використовуйте signAllTransactions.
Типові помилки
- Гаманець не знайдений у window.solana — користувач не встановив розширення або воно вимкнене. Показуйте повідомлення з посиланням на встановлення.
- Користувач скасував підключення — метод connect викидає помилку. Обов'язково обгортайте виклик у try/catch.
- Зміна акаунта в гаманці — користувач може перемкнутися на інший акаунт під час роботи застосунку. Слухайте подію accountChanged для оновлення стану.
Як надіслати транзакцію через TypeScript
Створення транзакції
Найпростіша транзакція — переказ SOL між двома акаунтами. Для цього потрібні: відправник (Keypair або підпис гаманця), отримувач (PublicKey) та сума в lamports (1 SOL = 1 000 000 000 lamports).
import {
Connection,
Transaction,
SystemProgram,
LAMPORTS_PER_SOL,
PublicKey
} from "@solana/web3.js";
const connection = new Connection("https://api.devnet.solana.com", "confirmed");
const fromKeypair = Keypair.generate(); // концептуальний приклад
const toPublicKey = new PublicKey("7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU");
const transaction = new Transaction().add(
SystemProgram.transfer({
fromPubkey: fromKeypair.publicKey,
toPubkey: toPublicKey,
lamports: 0.01 * LAMPORTS_PER_SOL,
})
);
Цей код створює об'єкт транзакції з однією інструкцією — SystemProgram.transfer. Транзакція ще не підписана і не відправлена.
Підписання транзакції
Для підписання ключовою парою (без гаманця):
transaction.recentBlockhash = (await connection.getLatestBlockhash()).blockhash;
transaction.feePayer = fromKeypair.publicKey;
const signature = await connection.sendTransaction(transaction, [fromKeypair]);
Метод sendTransaction підписує та відправляє транзакцію в одному виклику. Для гаманця використовуйте signTransaction (описано вище), а потім sendRawTransaction.
Відправка та підтвердження
Після відправки дочекайтеся підтвердження:
const signature = await connection.sendTransaction(transaction, [fromKeypair]);
console.log("Підпис:", signature);
await connection.confirmTransaction(signature, "confirmed");
console.log("Транзакція підтверджена");
Очікуваний результат: рядок підпису (Base58, ~87 символів) та повідомлення про підтвердження. Перевірте статус у Solana Explorer на Devnet за адресою https://explorer.solana.com/tx/{signature}?cluster=devnet.
Обробка результату
confirmTransaction повертає об'єкт з інформацією про підтвердження. Перевірте поле value.err:
const { value } = await connection.confirmTransaction(signature, "confirmed");
if (value.err) {
console.error("Транзакція невдала:", value.err);
} else {
console.log("Успіх, слот:", value.slot);
}
Типові помилки
- Insufficient funds for fee — на акаунті відправника немає достатньо SOL для оплати комісії. Навіть якщо ви переказуєте 0 SOL, комісія складає близько 0.000005 SOL.
- Blockhash expired — blockhash дійсний приблизно 60 секунд. Якщо між створенням транзакції та відправкою пройшло більше часу, отримайте свіжий blockhash через getLatestBlockhash.
- Transaction signature verification failure — підпис не відповідає відправнику. Перевірте, що feePayer збігається з ключем, яким підписано транзакцію.
Як читати стан акаунта через клієнт
Отримання інформації про акаунт
Кожен акаунт у Solana має: баланс, власника (програму), дані та стан існування. Запит інформації:
const publicKey = new PublicKey("7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU");
const accountInfo = await connection.getAccountInfo(publicKey);
if (accountInfo) {
console.log("Власник:", accountInfo.owner.toBase58());
console.log("Розмір даних:", accountInfo.data.length, "байт");
console.log("Лампортів:", accountInfo.lamports);
console.log("Executable:", accountInfo.executable);
} else {
console.log("Акаунт не існує");
}
Очікуваний результат: об'єкт з полями lamports, owner, data, executable, rentEpoch, або null, якщо акаунт ніколи не створювався.
Робота з програмними акаунтами
Програмні акаунти (deployed programs) мають executable: true та data, що містить скомпільований BPF-код. Для звичайних данихних акаунтів executable дорівнює false, а data містить серіалізований стан, визначений програмою-власником. Розбір data залежить від конкретної програми — для цього потрібне знання її структури (Borsh або інший формат).
Отримання балансу
Для отримання лише балансу (без повної інформації про акаунт) використовуйте окремий метод — він легший і швидший:
const balance = await connection.getBalance(publicKey);
console.log("Баланс:", balance / LAMPORTS_PER_SOL, "SOL");
Детальніший розбір роботи з балансом наведено в окремому розділі нижче.
Підписка на зміни стану
Замість періодичного опитування можна підписатися на зміни акаунта через WebSocket:
const subscriptionId = connection.onAccountChange(
publicKey,
(updatedAccountInfo) => {
console.log("Акаунт оновлено, лампортів:", updatedAccountInfo.lamports);
},
"confirmed"
);
// Для скасування підписки:
// connection.removeAccountChangeListener(subscriptionId);
Очікуваний результат: колбек викликається щоразу, коли стан акаунта змінюється. Підписка активна, поки ви не скасуєте її або не закриєте з'єднання. У frontend-застосунках скасовуйте підписки при розмонтуванні компонента.
Типові помилки
- getAccountInfo повертає null — акаунт не існує. Це не помилка, а допустимий стан. Завжди перевіряйте на null перед зверненням до полів.
- Неправильне трактування data — байти data не мають сенсу без знання схеми програми-власника. Не намагайтеся інтерпретувати data акаунта, власником якого є SystemProgram — це не ваші дані.
- Витік підписок — кожна onAccountChange створює WebSocket-підключення. Без скасування підписки в React-компоненті ви отримаєте витік пам'яті та дублювання колбеків.
Як підключитися до Devnet з TypeScript
Вибір RPC endpoint
Devnet має публічний RPC endpoint: https://api.devnet.solana.com. Він безкоштовний, але має обмеження на частоту запитів (rate limit). Для інтенсивної розробки розгляньте альтернативні провайдери (Helius, QuickNode, Triton), які надають Devnet-endpoint з вищими лімітами. Конкретні умови та ліміти перевірте на сайтах провайдерів.
Створення Connection
import { Connection } from "@solana/web3.js";
const connection = new Connection(
"https://api.devnet.solana.com",
"confirmed"
);
Один об'єкт Connection може обслуговувати всі запити вашого застосунку. Не створюйте новий Connection для кожного запиту — це призводить до зайвих WebSocket-з'єднань.
Перевірка підключення
try {
const version = await connection.getVersion();
const slot = await connection.getSlot();
console.log("Підключено до Devnet, версія:", version["solana-core"], "слот:", slot);
} catch (error) {
console.error("Помилка підключення:", error);
}
Очікуваний результат: об'єкт з версією solana-core та поточним слотом. Якщо запит зависає або видає помилку мережі — перевірте з'єднання з інтернетом та доступність endpoint.
Отримання тестових SOL
Для роботи на Devnet потрібні тестові SOL (не мають реальної цінності). Отримайте їх через airdrop:
import { LAMPORTS_PER_SOL, PublicKey } from "@solana/web3.js";
const publicKey = new PublicKey("Ваш_публічний_ключ");
const signature = await connection.requestAirdrop(publicKey, 2 * LAMPORTS_PER_SOL);
await connection.confirmTransaction(signature, "confirmed");
console.log("Airdrop виконано, підпис:", signature);
Очікуваний результат: підпис транзакції airdrop та підтвердження. Перевірте баланс після виконання. Якщо airdrop повертає помилку 429 Too Many Requests — ви досягли ліміту. Зачекайте кілька хвилин або використовуйте CLI: solana airdrop 2.
Типові помилки
- Rate limit на публічному RPC — при інтенсивній розробці (часті запити, підписки) публічний endpoint може обмежувати доступ. Використовуйте локальний validator для основної розробки.
- Airdrop не спрацьовує — перевірте, що ви використовуєте Devnet, а не Mainnet. На Mainnet airdrop недоступний. Також перевірте, що акаунт не досяг ліміту airdrop (зазвичай 2-5 SOL на акаунт).
- Таймаут підключення — публічний Devnet іноді буває перевантажений. Додайте таймаут до Connection або використовуйте альтернативний endpoint.
Як створити простий UI для Solana-застосунку
Вибір стеку
Рекомендований стек для початківця: React 18+ з Vite та TypeScript. Цей комбінацію забезпечує швидку збірку, гарну підтримку TypeScript та найбільшу кількість прикладів в екосистемі Solana. Ініціалізація:
npm create vite@latest solana-app -- --template react-ts
cd solana-app
npm install @solana/web3.js@1
Базовий компонент підключення гаманця
Для production-застосунків використовуйте офіційні адаптери гаманців. Встановіть необхідні пакети:
npm install @solana/wallet-adapter-react @solana/wallet-adapter-react-ui @solana/wallet-adapter-base
Обгортка застосунку:
import { WalletAdapterNetwork } from "@solana/wallet-adapter-base";
import { ConnectionProvider, WalletProvider } from "@solana/wallet-adapter-react";
import { WalletModalProvider } from "@solana/wallet-adapter-react-ui";
import "@solana/wallet-adapter-react-ui/styles.css";
const network = WalletAdapterNetwork.Devnet;
const endpoint = "https://api.devnet.solana.com";
function App() {
return (
<ConnectionProvider endpoint={endpoint}>
<WalletProvider wallets={[]} autoConnect>
<WalletModalProvider>
<YourContent />
</WalletModalProvider>
</WalletProvider>
</ConnectionProvider>
);
}
Кнопка підключення гаманця:
import { WalletMultiButton } from "@solana/wallet-adapter-react-ui";
function YourContent() {
return <WalletMultiButton />;
}
Очікуваний результат: кнопка "Select Wallet", яка відкриває модальне вікно з вибором гаманця, підключаєм та відображенням адреси.
Відображення даних з on-chain
import { useConnection, useWallet } from "@solana/wallet-adapter-react";
import { LAMPORTS_PER_SOL } from "@solana/web3.js";
function BalanceDisplay() {
const { connection } = useConnection();
const { publicKey } = useWallet();
const [balance, setBalance] = useState<number | null>(null);
useEffect(() => {
if (!publicKey) {
setBalance(null);
return;
}
connection.getBalance(publicKey).then((bal) => {
setBalance(bal / LAMPORTS_PER_SOL);
});
}, [connection, publicKey]);
if (!publicKey) return <p>Підключіть гаманець</p>;
return <p>Баланс: {balance !== null ? balance : "Завантаження..."} SOL</p>;
}
Форма для відправки транзакції
Концептуальна структура форми переказу SOL:
function TransferForm() {
const { connection } = useConnection();
const { publicKey, sendTransaction } = useWallet();
const [toAddress, setToAddress] = useState("");
const [amount, setAmount] = useState("");
const handleTransfer = async () => {
if (!publicKey) return;
const transaction = new Transaction().add(
SystemProgram.transfer({
fromPubkey: publicKey,
toPubkey: new PublicKey(toAddress),
lamports: parseFloat(amount) * LAMPORTS_PER_SOL,
})
);
const signature = await sendTransaction(transaction, connection);
await connection.confirmTransaction(signature, "confirmed");
};
// JSX з полями вводу та кнопкою
}
Цей код використовує хук sendTransaction з wallet-adapter, який автоматично підписує транзакцію через підключений гаманець. Це концептуальний приклад — у production додайте валідацію адреси, обробку помилок та індикацію завантаження.
Типові помилки
- Відсутність WalletModalProvider — без цього провайдера WalletMultiButton не зможе відкрити модальне вікно. Перевірте, що всі три провайдери вкладені в правильному порядку.
- Не імпортовано styles.css — без @solana/wallet-adapter-react-ui/styles.css кнопка та модальне вікно будуть без стилів.
- Виклик sendTransaction без підключеного гаманця — завжди перевіряйте publicKey перед викликом. Хук sendTransaction викидає помилку, якщо гаманець не підключено.
Як обробляти помилки гаманця у frontend
Типи помилок гаманця
Помилки взаємодії з гаманцем поділяються на три категорії: помилки підключення (користувач скасував, гаманець не встановлено), помилки підпису (користувач відхилив підпис, таймаут) та помилки транзакції (insufficient funds, invalid instruction). Кожна категорія вимагає окремої обробки та повідомлення користувачу.
Обробка помилок підключення
try {
await solanaWindow.solana.connect();
} catch (error: any) {
if (error.code === 4001) {
console.log("Користувач скасував підключення");
} else {
console.error("Помилка підключення:", error.message);
}
}
Код 4001 — стандартний код скасування для Phantom-сумісних гаманців. Показуйте ненав'язливе повідомлення, а не alert.
Обробка помилок підпису
try {
const signed = await solanaWindow.solana.signTransaction(transaction);
} catch (error: any) {
if (error.code === 4001) {
console.log("Користувач відхилив підпис");
} else if (error.message?.includes("timeout")) {
console.log("Таймаут підпису — спробуйте ще раз");
}
}
Обробка помилок транзакції
Після відправки транзакції помилки надходять від кластера:
try {
const signature = await connection.sendRawTransaction(signedTransaction.serialize());
const { value } = await connection.confirmTransaction(signature, "confirmed");
if (value.err) {
console.error("Транзакція невдала:", JSON.stringify(value.err));
}
} catch (error: any) {
if (error.message?.includes("Insufficient funds")) {
console.log("Недостатньо SOL для комісії");
} else {
console.error("Помилка відправки:", error.message);
}
}
Типові помилки
- Показ raw error message користувачу — технічні повідомлення на кшталт "Blockhash not found" не зрозумілі користувачу. Перетворюйте технічні помилки на зрозумілі повідомлення.
- Ігнорування помилки скасування — код 4001 не є помилкою застосунку. Не логуйте його як error і не показуйте повідомлення "Сталася помилка".
- Відсутність обробки в sendTransaction хука — хук sendTransaction з wallet-adapter також викидає помилки. Обгортайте його виклик у try/catch так само, як і прямі виклики гаманця.
Як перевірити баланс SOL через SDK
Запит балансу
import { Connection, PublicKey, LAMPORTS_PER_SOL } from "@solana/web3.js";
const connection = new Connection("https://api.devnet.solana.com", "confirmed");
const publicKey = new PublicKey("7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU");
const lamports = await connection.getBalance(publicKey);
const sol = lamports / LAMPORTS_PER_SOL;
console.log("Баланс:", sol, "SOL");
Очікуваний результат: число з плаваючою точкою, що відображає баланс у SOL. getBalance повертає ціле число в lamports — це точне значення без втрати точності.
Баланс у різних контекстах
У контексті wallet-adapter баланс отримують через хук useConnection та useWallet (приклад наведено вище у розділі UI). У серверному коді (Node.js) — безпосередньо через connection.getBalance. У CLI — командою solana balance. Усі способи звертаються до одного й того ж RPC-методу.
Періодичне оновлення
Для UI, де баланс має оновлюватися автоматично, використовуйте підписку на зміни акаунта замість інтервалу:
useEffect(() => {
if (!publicKey) return;
const fetchBalance = async () => {
const lamports = await connection.getBalance(publicKey);
setBalance(lamports / LAMPORTS_PER_SOL);
};
fetchBalance();
const id = connection.onAccountChange(publicKey, fetchBalance, "confirmed");
return () => { connection.removeAccountChangeListener(id); };
}, [connection, publicKey]);
Очікуваний результат: баланс оновлюється миттєво при будь-якій зміні акаунта (отримання SOL, сплата комісії), без зайвих запитів.
Відображення у UI
Форматування балансу для відображення: використовуйте toFixed(4) або аналогічний метод для обмеження кількості знаків після коми. Не використовуйте toFixed для розрахунків — це призводить до втрати точності. Для розрахунків завжди працюйте в lamports (цілі числа).
Типові помилки
- Втрата точності через ділення — lamports / LAMPORTS_PER_SOL дає число з плаваючою точкою. Зберігайте оригінальне значення в lamports для розрахунків і конвертуйте лише для відображення.
- Показ нульового балансу до завантаження — поки запит виконується, баланс може бути null або 0. Показуйте індикатор завантаження, а не "0 SOL".
- Отримання балансу неіснуючого акаунта — getBalance повертає 0 для акаунтів, які ніколи не створювалися. Це коректна поведінка, а не помилка.
Як запустити локальний validator для розробки
Що таке solana-test-validator
solana-test-validator — це локальний повноцінний вузол Solana, який працює на вашій машині. Він імітує поведінку кластера: підтверджує транзакції, зберігає стан акаунтів, виконує програми. Переваги перед Devnet: немає rate limits, миттєве підтвердження транзакцій, можливість завантажити конкретні програми та акаунти для відтворення сценаріїв.
Запуск validator
Переконайтеся, що Solana CLI встановлено. Запустіть validator:
solana-test-validator
Очікуваний результат: логи запуску в терміналі, включно з повідомленням "Ledger location" та "Genesis hash". Validator працюватиме, поки ви не зупините процес (Ctrl+C). Після запуску в іншому терміналі налаштуйте CLI на localhost:
solana config set --url localhost
Завантаження програм та акаунтів
Ви можете завантажити скомпільовану програму (.so файл) та конкретні акаунти при запуску:
solana-test-validator \
--bpf-program TARGET_PROGRAM_ID ./target/deploy/your_program.so \
--account ACCOUNT_JSON_PATH ./account.json \
--reset
Параметр --reset очищає стан при кожному запуску. TARGET_PROGRAM_ID — це публічний ключ програми (Base58). ACCOUNT_JSON_PATH — шлях до файлу з експортованим акаунтом (отримати через solana account export на Devnet). Це корисно для тестування з конкретним початковим станом.
Робота з локальним validator
Підключення з TypeScript:
const connection = new Connection("http://127.0.0.1:8899", "processed");
Для локального validator використовуйте рівень комітменту processed — це найшвидший рівень, оскільки фіналізація не має сенсу в одному вузлі. Отримання тестових SOL:
solana airdrop 100
На локальному validator airdrop миттєвий і без лімітів. Ви можете отримати будь-яку кількість SOL для тестування.
Типові помилки
- Порт 8899 вже зайнятий — попередній екземпляр validator не був зупинений. Знайдіть і завершіть процес: lsof -i :8899 або solana-test-validator --ledger ./test-ledger з іншим шляхом.
- Недостатньо RAM — validator за замовчуванням використовує значний обсяг пам'яті. Обмежте через --limit-ledger-size або збільште swap.
- Забули переключити CLI з Devnet на localhost — якщо solana config get показує Devnet, а validator працює на localhost, ваші транзакції підуть на Devnet. Завжди перевіряйте конфігурацію.
- Програма не знайдена після завантаження — перевірте, що шлях до .so файлу правильний, а TARGET_PROGRAM_ID збігається з тим, який використовується у вашому TypeScript-коді.
Після опанування цих інструментів ви маєте повне робоче середовище: локальний validator для швидкої ітерації, Devnet для тестування з реальними гаманцями та TypeScript-застосунок, що підключається до гаманця, читає дані та відправляє транзакції. Наступний крок у вашому навчанні — робота з токенами, PDA (Program Derived Addresses) та CPI (Cross-Program Invocation), де ви дізнаєтесь, як створювати та керувати кастомними токенами та взаємодіяти між програмами.