Solana відрізняється від більшості блокчейнів тим, що дані, код і виконання організовані навколо єдиної сутності — акаунта. Розуміння цієї моделі є обов'язковою умовою для написання робочого коду, який компілюється, деплоїться і виконується без несподіваних помилок. Ця сторінка дає повну ментальну модель: від структури акаунта до відправки першої транзакції на Devnet і читання логів.
Як стати Solana-розробником
Передумови та дорожня карта
Для розробки на Solana потрібне розуміння базових концепцій блокчейну (транзакції, консенсус, підписи) та впевнене володіння хоча б однією мовою програмування. Solana-програми пишуться переважно на Rust, а клієнтська частина — на TypeScript або Python. Дорожня карта виглядає так:
- Зрозуміти модель акаунтів і виконання (ця сторінка).
- Освоїти Rust на рівні синтаксису, власників та запозичень.
- Вивчити фреймворк Anchor для структурованої розробки програм.
- Практикуватися на Devnet: розгортання, виклик, читання стану.
- Переходити до production-тем: безпека, індексація, діагностика.
Налаштування середовища розробки
Мінімальне робоче середовище для початку:
- ОС: macOS (Apple Silicon або x86_64), Ubuntu 24.04 LTS+ або Windows з WSL2.
- Rust: встановити через rustup (стабільний канал, версію перевірте на офіційному сайті rust-lang.org).
- Solana CLI: встановити через інсталяційний скрипт. Конкретну версію перевірте в офіційному репозиторії Solana на GitHub — на момент написання активно використовується гілка v1.18.x.
- Node.js: версія 18+ для клієнтських скриптів (залежить від обраного SDK, перевірте сумісність у документації).
Після встановлення Solana CLI перевірте:
solana --versionsolana config get
Переконайтеся, що RPC-endpoint вказано на Devnet:
solana config set --url devnet
Перший крок на Devnet
Отримайте тестові SOL для оплати комісій:
solana airdrop 2
Перевірте баланс:
solana balance
Очікуваний результат: баланс близько 2 SOL на Devnet. Якщо airdrop не спрацював, повторіть спробу або використайте альтернативний faucet — обмеження частоти запитів змінюються.
Типові помилки на старті
- Працюєте на Mainnet-beta замість Devnet — реальні витрати SOL.
- Застаріла версія Solana CLI, яка не підтримує поточний протокол Devnet.
- Відсутність тестових SOL — транзакції відхиляються з помилкою недостатнього балансу.
Наступний крок
Після налаштування середовища перейдіть до розуміння моделі акаунтів — це фундамент, без якого будь-який код на Solana буде набором помилок.
Як працює модель акаунтів у Solana
Чому Solana не використовує модель балансів Ethereum
У Ethereum стан — це відображення адрес на баланси та контракти. У Solana стан — це сукупність акаунтів, кожен із яких є незалежною одиницею зберігання. Акаунт може містити баланс SOL, дані програми, код програми або будь-яке поєднання цих компонентів. Це дозволяє паралельне виконання транзакцій, оскільки валідатор бачить, які акаунти читаються, а які змінюються, і може паралелізувати транзакції, що не перетинаються за записом.
Структура акаунта
Кожен акаунт у Solana має такі поля:
| Поле | Опис |
|---|---|
| lamports | Баланс у lamports (1 SOL = 1 000 000 000 lamports). |
| owner | Pubkey програми-власника. Тільки ця програма може змінювати дані акаунта. |
| data | Байтовий масив даних. Максимальний розмір — 10 MB (перевірте актуальне значення в документації, оскільки ліміт може змінюватися). |
| executable | Прапорець: true, якщо акаунт містить скомпільовану програму (BPF bytecode). |
| rent_epoch | Епоха, до якої сплачено rent. Якщо акаунт звільнений від rent, значення велике (max u64). |
Власник акаунта та програма
Поле owner визначає, хто має право змінювати data акаунта. Системна програма (System Program) є власником акаунтів за замовчуванням — вона керує створенням, закриттям та переказом lamports. Якщо ви деплоїте свою програму, вона стає власником акаунтів, які вона створює для зберігання стану. Жодна інша програма не може змінити дані чужого акаунта.
Практичний приклад
Перегляньте будь-який акаунт на Devnet:
solana account <pubkey></pubkey>
Ви побачите всі поля: баланс, власника, розмір даних, прапорець executable. Спробуйте з системним акаунтом (ваш гаманець) та акаунтом програми — різниця у полі executable та owner.
Типові помилки
- Спроба записати дані в акаунт, власником якого є інша програма — транзакція відхилиться з помилкою IllegalOwner.
- Ігнорування rent — акаунт із недостатнім балансом буде видалений після закінчення rent_epoch.
- Конфлікт поняття «власник»: у Solana owner — це програма, а не людина. Право витрачати lamports визначається підписом, а не полем owner.
Що таке Solana program
Означення та роль програми
Solana program (програма) — це скомпільований код (BPF bytecode), розміщений у спеціальному акаунті з прапорцем executable: true. Програма отримує набір акаунтів та інструкцію (instruction) і виконує логіку: читає та модифікує дані акаунтів, які їй передані. Програма не має власного «стану» — весь стан зберігається в окремих акаунтах-даних.
Життєвий цикл програми
- Написання коду на Rust (або C/C++).
- Компіляція у BPF bytecode.
- розгортання на кластер — створення executable-акаунта.
- Виклик через транзакцію з instruction, що вказує на pubkey програми.
- Оновлення — програму можна перезаписати, якщо акаунт програми має відповідний прапорець (upgrade authority).
Типи програм у Solana
- Системна програма (System Program) — створення акаунтів, перекази SOL.
- Програма стейкінгу (Stake Program) — делегування SOL валідаторам.
- Token Program — робота з SPL-токенами.
- Користувацькі програми — ваш код, який деплоїться на кластер.
Як програма взаємодіє з акаунтами
Програма отримує масив акаунтів як параметр. Вона може читати дані будь-якого переданого акаунта, але записувати — лише в ті, де owner збігається з pubkey цієї програми. Це ключове обмеження, яке забезпечує безпеку стану.
Наступні кроки
Після розуміння ролі програми варто вивчити, як саме транзакції доставляють інструкції до програм — це тема наступного розділу.
Транзакції та instructions для розробника
Що таке транзакція у Solana
Транзакція — це атомарна одиниця роботи, яка містить:
- Масив акаунтів, які транзакція зачіпає (читає або записує).
- Одну або кілька інструкцій (instructions).
- Необхідні підписи (signatures).
- Нещодавній blockhash — для захисту від повторного відтворення (replay).
- Ліміт compute units (compute unit limit).
- Пріоритетну комісію (compute unit price) для MEV-захисту.
Усі інструкції всередині транзакції виконуються послідовно. Якщо хоча б одна інструкція завершується помилкою — вся транзакція відхиляється, жодні зміни не застосовуються.
Що таке instruction
Instruction (інструкція) — це конкретна команда до конкретної програми. Інструкція містить:
- program_id — pubkey програми, яка виконує інструкцію.
- accounts — індекси акаунтів із загального масиву транзакції, які потрібні цій інструкції (з вказівкою, чи є акаунт читальним, записуваним, чи є signer).
- data — байтовий масив із аргументами інструкції (як саме інтерпретувати ці байти — вирішує програма).
Порядок виконання instructions
Інструкції виконуються строго в тому порядку, в якому вони зазначені в транзакції. Друга інструкція бачить зміни, які зробила перша. Це дозволяє комбінувати виклики різних програм у одній транзакції (наприклад, переказ SOL через System Program, а потім оновлення стану у вашій програмі).
Signers та підписи
Підписи додаються на рівні транзакції, але кожна інструкція вказує, які саме акаунти мають бути підписані. Детальніше про signers — у відповідному розділі нижче.
Практичний приклад
Створення нового акаунта через System Program — це транзакція з однією інструкцією:
- program_id: System Program (11111111111111111111111111111111).
- accounts: акаунт, що сплачує (signer, writable), новий акаунт (writable).
- data: інструкція CreateAccount із вказаним розміром, кількістю lamports та owner.
Що таке signers у Solana
Означення signer
Signer — це акаунт, чий приватний ключ використано для підписання транзакції. Підпис підтверджує, що власник ключа схвалив транзакцію. На рівні інструкції акаунт позначається як is_signer: true.
Роль signers у безпеці
Signer — це механізм авторизації. Наприклад, переказ SOL вимагає, щоб акаунт-відправник був signer — це гарантує, що ніхто сторонній не може витратити ваші кошти. Програма сама перевіряє прапорець is_signer і відхиляє інструкцію, якщо авторизація відсутня.
Signer у контексті програми
Програма отримує інформацію про те, які акаунти є signers, через метадані інструкції. У Rust (без Anchor) це виглядає як перевірка account.is_signer. У Anchor це автоматизовано через анотацію #[account(mut, signer)]. Важливо: програма не має доступу до приватних ключів — вона лише бачить криптографічний доказ підпису.
Типові помилки
- Передача акаунта без прапорця signer туди, де програма його вимагає — помилка SignatureRequired.
- Плутанина між owner та signer: owner — це програма, яка керує даними; signer — це авторизація дії. Це різні концепції.
- Спроба зробити signer акаунт, який не підписував транзакцію — валідатор відхилить її до виконання програми.
Наступний крок
Після розуміння signers варто зрозуміти, скільки коштують транзакції та як керувати обчислювальними лімітами.
Комісії транзакцій у Solana для початківця
З чого складається комісія
Комісія транзакції в Solana складається з двох частин:
- Базова комісія (base fee) — фіксована сума за кожен підпис у транзакції. Історично це 5000 lamports за підпис, але точне значення перевірте в поточній документації, оскільки воно може змінюватися через пропозиції щодо оновлення мережі.
- Пріоритетна комісія (priority fee) — додаткова сума за compute unit, яка визначає місце транзакції в черзі валідатора. Може бути нульовою.
Як розраховується комісія
Формула: total_fee = base_fee × signers_count + compute_unit_limit × compute_unit_price. Наприклад, транзакція з одним signer, compute_unit_limit = 200 000 та compute_unit_price = 0 lamports обійдеться лише в базову комісію.
Як зменшити комісії
- Мінімізувати кількість signers — кожен підпис додає базову комісію.
- Оптимізувати код програми, щоб витрачати менше compute units.
- Не встановлювати завищений compute_unit_price, якщо швидкість не критична (на Devnet це зазвичай не має значення).
Як перевірити комісію
Після відправки транзакції через CLI ви побачите рядок із комісією:
Transaction signature: <sig>
Fee: 5000 lamports</sig>
Через solana confirm -v <sig></sig> можна побачити детальну інформацію.
Типові помилки
- Нестача lamports на оплату комісії — помилка InsufficientFundsForFee.
- Встановлення занадто високого compute_unit_price на Mainnet — непотрібні витрати.
- Ігнорування того, що комісія стягується навіть за невдалу транзакцію (якщо вона пройшла етап підпису та була включена в блок).
Compute units у Solana: як працюють обчислювальні ліміти
Що таке compute units
Compute unit (CU) — це одиниця обчислювального ресурсу. Кожна інструкція BPF-програми має фіксовану вартість у CU. Це механізм, який запобігає нескінченним циклам та надмірному навантаженню на валідатора. Поточний максимальний ліміт на транзакцію перевірте в документації — історично це 1 400 000 CU, але значення може змінюватися.
Як програма витрачає compute units
Різні операції мають різну вартість: системні виклики (syscalls), доступ до пам'яті, криптографічні операції. Наприклад, виклик solana_sdk::sysvar::clock::Clock::get() коштує менше CU, ніж операція ed25519-перевірки підпису. Точні таблиці вартості перевірте в офіційній документації Solana.
Compute budget
Compute budget — це інструкції, які додаються до транзакції для вказання:
- ComputeUnitLimit — скільки CU виділити транзакції (за замовчуванням 200 000).
- ComputeUnitPrice — пріоритетна комісія за один CU (у мікролампортах).
Якщо ваша програма витрачає більше CU, ніж вказано в ліміті — транзакція відхилиться.
Що робити при ComputeBudgetExceeded
- Збільшити compute_unit_limit у транзакції (до максимуму).
- Оптимізувати код: прибрати зайві цикли, кешувати дані, зменшити кількість CPI-викликів.
- Розбити логіку на кілька транзакцій.
Типові помилки
- Залишити дефолтний ліміт 200 000 CU для програми, яка реально витрачає більше — помилка ComputationalBudgetExceeded.
- Встановити ліміт завищеним без потреби — на Mainnet це може збільшити комісію.
- Ігнорувати CU-вартість CPI-викликів (Cross-Program Invocations) — кожен виклик іншої програми додає CU-навантаження.
Як створити та відправити першу транзакцію на Devnet
Підготовка
Переконайтеся, що:
- Solana CLI встановлено та налаштовано на Devnet (
solana config getпоказує url: devnet). - У вашому гаманці є тестові SOL (
solana balanceпоказує більше 0). - Ви маєте pubkey отримувача (можна згенерувати нову keypair:
solana-keygen new --outfile /tmp/recipient.json).
Створення транзакції через CLI
Найпростіша транзакція — переказ SOL:
solana transfer <recipient_pubkey> 0.01</recipient_pubkey>
CLI автоматично сформує транзакцію: підпише її вашим ключем, додасть blockhash, встановить compute budget і відправить на Devnet.
Відправка та підтвердження
Після виконання команди CLI чекає підтвердження. Очікуваний результат:
Transaction signature: 4xKpN2...
Blockhash: ABC123...
Fee: 5000 lamports
Якщо підтвердження затримується, можна перевірити статус вручну:
solana confirm <signature> -v</signature>
Аналіз результату
Перевірте баланси обох акаунтів:
solana balance
solana balance <recipient_pubkey></recipient_pubkey>
Ваш баланс має зменшитися приблизно на 0.01 SOL + комісія. Баланс отримувача має збільшитися на 0.01 SOL.
Типові помилки
- Blockhash not found — blockhash застарів (живе близько 60 секунд). Спробуйте ще раз.
- InsufficientFundsForFee — навіть якщо ви переказуєте весь баланс, залиште хоча б 5000 lamports на комісію.
- Transaction signature verification failure — проблема з ключем. Перевірте, що використовуєте правильний keypair.
Як зберігаються дані в акаунтах Solana
Формат даних акаунта
Поле data акаунта — це просто байтовий масив ([u8]). Solana не накладає жодної структури на ці байти. Саме програма вирішує, як інтерпретувати дані: як фіксовану структуру, як map, як вектор тощо.
Серіалізація та десеріалізація
Оскільки мережа працює з байтами, а програма — зі структурами, потрібна серіалізація. У Rust-екосистемі Solana стандартом є крейт borsh (Binary Object Representation Serialization for Hashing). Він забезпечує детерміновану серіалізацію: одні й ті ж дані завжди дають однаковий байтовий результат, що критично для детермінованого виконання програм.
Приклад: проста структура даних
Концептуальний приклад структури (без прив'язки до конкретної програми):
#[derive(BorshSerialize, BorshDeserialize)]
struct Counter {
count: u64,
authority: Pubkey,
}
Після серіалізації ця структура займає 8 байт (u64) + 32 байти (Pubkey) = 40 байт. Акаунт для її зберігання має бути створений із розміром даних не менше 40 байт.
Відмінність від Ethereum storage
У Ethereum контракт має storage — ключ-значення сховище, де кожен слот коштує gas. У Solana акаунт — це суцільний байтовий масив фіксованого розміру. Ви самі вирішуєте, як упакувати дані. Збільшити розмір акаунта після створення можна через спеціальну інструкцію (ResizeAccount), але це окрема операція.
Типові помилки
- Створити акаунт із занадто малим розміром даних — при спробі запису програма отримає помилку перевищення розміру.
- Використовувати не детерміновану серіалізацію (наприклад, HashMap без сортування) — різні валідатори отримають різні результати, і транзакція не пройде консенсус.
- Не враховувати 8-байтовий дискримінатор Anchor, якщо використовуєте цей фреймворк — реальний розмір даних буде більший.
Типові помилки транзакцій у Solana та їх вирішення
Помилки підпису
- SignatureRequired — акаунт не позначений як signer, але програма вимагає підпису. Рішення: додати прапорець is_signer в інструкції.
- Transaction signature verification failure — підпис не відповідає транзакції. Рішення: перевірте, що підпис створено для саме цієї транзакції (з правильним blockhash).
Помилки стану акаунтів
- IllegalOwner — програма намагається записати в акаунт, власником якого є інша програма. Рішення: передайте правильний акаунт або створіть новий із вашим програмою як owner.
- AccountNotMutable — акаунт не позначений як writable, але програма намагається його змінити. Рішення: додати прапорець is_writable.
- AccountDataTooSmall — розмір data акаунта менший, ніж потрібно для запису. Рішення: створити акаунт із достатнім розміром.
- AlreadyInUse — спроба створити акаунт з pubkey, який вже існує. Рішення: використайте новий pubkey.
Помилки виконання
- InstructionFallbackNotFound — програма не знайшла обробник для цієї інструкції. Рішення: перевірте, чи правильний індекс інструкції або discriminator.
- InvalidInstructionData — дані інструкції не можуть бути десеріалізовані. Рішення: перевірте формат data, що відправляється з клієнта.
- Custom error — програма повернула власний код помилки. Рішення: читайте логи (про це — наступний розділ).
Помилки комісій та лімітів
- InsufficientFundsForFee — недостатньо lamports для оплати комісії.
- ComputationalBudgetExceeded — програма витратила більше CU, ніж дозволено.
- PrecompileFailure — помилка в попередньо скомпільованій програмі (наприклад, криптографічній операції).
Як діагностувати
Перший інструмент — solana confirm -v <signature></signature>. Другий — читання логів транзакції, що розглядається в наступному розділі.
Як читати логи транзакцій на Devnet
Навіщо читати логи
Логи — це єдине джерело інформації про те, що саме відбулося всередині програми під час виконання. Без логів діагностика помилок зводиться до вгадування.
Читання логів через CLI
Базова команда:
solana logs <signature></signature>
Детальний вивід із усіма логами програм:
solana confirm -v <signature></signature>
Альтернативно, якщо транзакція вже підтверджена:
solana transaction log <signature></signature>
Розуміння формату логів
Кожен рядок логу містить:
- Програму, яка генерує лог (pubkey або назва, якщо це відома системна програма).
- Рівень логування — INFO, WARN, ERROR.
- Текст повідомлення — те, що програма явно логує через
msg!макрос у Rust.
Приклад:
Program 11111111111111111111111111111111 invoke [1]
Program 11111111111111111111111111111111 success
Це означає, що System Program була викликана і завершилася успішно.
Логи CPI-викликів
Коли ваша програма викликає іншу програму (CPI — Cross-Program Invocation), логи показують вкладену структуру:
Program <your_program> invoke [1]
Program <token_program> invoke [2]
Program <token_program> success
Program <your_program> success</your_program></token_program></token_program></your_program>
Рівень вкладеності вказується в квадратних дужках. Якщо помилка сталася всередині CPI — шукайте рядок з fail на відповідному рівні вкладеності.
Типові помилки
- Шукати помилку лише в останньому рядку — справжня причина може бути глибше в стеку CPI-викликів.
- Ігнорувати логи рівня INFO — іноді програма логує корисну діагностичну інформацію до помилки.
- Не використовувати макрос
msg!у власній програмі — без нього ви не побачите проміжний стан у логах.
Тепер у вас є повна ментальна модель того, як Solana зберігає дані, виконує програми та обробляє транзакції. Наступний логічний крок — перейти до Rust і Anchor для початківця, де ця модель стане основою для написання першої програми.