Розробка смарт-контрактів на Rust (Solana) під ключ

Розробка смарт-контрактів на Rust (Solana) Ми розробляємо смарт-контракти на Rust для Solana під ключ — від проектування accounts model до деплою з multisig. Solana приваблює розробників субсекундними фінальностями та вартістю транзакції в $0.00025. Але за цією швидкістю стоїть принципово інша мо

Напрямки блокчейн-розробки

Часті запитання

Останні роботи

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1441
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1301
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    998
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1267
  • image_logo-advance_0.webp
    Розробка логотипу компанії B2B Advance
    713
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    1003

Розробка смарт-контрактів на Rust (Solana)

Ми розробляємо смарт-контракти на Rust для Solana під ключ — від проектування accounts model до деплою з multisig. Solana приваблює розробників субсекундними фінальностями та вартістю транзакції в $0.00025. Але за цією швидкістю стоїть принципово інша модель виконання: accounts model замість contract storage, stateless programs, PDA-деривати замість mapping. Розробник, який прийшов з EVM, перші два тижні думає, що Solana зламана — тому що звичні паттерни Solidity тут або не працюють, або призводять до вразливостей іншого класу. Ми навчилися обходити ці граблі за 5 років практики.

Чому Solana-програми ламають EVM-розробника

Згідно з документацією Solana, програми не володіють даними — вони лише авторизують операції над переданими акаунтами. У EVM контракт зберігає свій стан всередині себе. У Solana програма — це stateless executable, а дані живуть в окремих account-ах, якими програма не володіє — вона тільки авторизує операції над ними. Це означає, що кожен виклик інструкції вимагає явної передачі всіх accounts, які будуть задіяні.

Missing signer check — розробка смарт контрактів

Перша грабля: missing signer check. Програма отримує account через AccountInfo, але не перевіряє, що переданий authority дійсно підписав транзакцію. В Anchor це ловиться через атрибут #[account(signer)] або тип Signer<'info>. У нативному Rust — через явну перевірку if !authority.is_signer { return Err(...) }. Пропустити це легко, коли пишеш перші програми.

Account substitution

Друга класична вразливість — account substitution. Програма приймає token_account і authority, але не перевіряє, що token_account.owner збігається з переданим authority. Атакуючий передає свій token_account і чужий authority — програма виконується без помилок і дренує чужі токени.

PDA derivation mismatch

Третя біль — PDA derivation mismatch. Program Derived Address обчислюється з seeds + program_id. Якщо seeds не перевірені явно через find_program_address з constraint-ом в Anchor (seeds = [b"vault", user.key().as_ref()]), атакуючий може передати довільний account, який випадково збігається з PDA за адресою, але не є легітимним.

Уразливість Причина Запобігання
Missing signer Не перевірено підпис Signer<'info> або #[account(signer)]
Account substitution Не перевірено owner Перевірка token_account.owner == authority
PDA mismatch Seeds не валідовано seeds = [...] constraint в Anchor

Як Anchor змінює рівняння безпеки?

Працюємо переважно через Anchor framework (поточна версія 0.30.x). Anchor генерує discriminator для кожного account type і перевіряє його при десеріалізації — це автоматично закриває цілий клас type confusion атак, де атакуючий передає account не того типу.

Типова структура інструкції в нашій кодовій базі:

#[derive(Accounts)] pub struct Deposit<'info> { #[account( mut, seeds = [b"vault", user.key().as_ref()], bump, constraint = vault.authority == user.key() @ ErrorCode::Unauthorized )] pub vault: Account<'info, VaultState>, #[account( mut, associated_token::mint = mint, associated_token::authority = user )] pub user_token_account: Account<'info, TokenAccount>, pub user: Signer<'info>, pub mint: Account<'info, Mint>, pub token_program: Program<'info, Token>, pub system_program: Program<'info, System>, } 

Constraint-и в #[account(...)] — це не просто цукор. Вони компілюються в явні перевірки перед виконанням інструкції. Якщо constraint порушено — транзакція реверт-иться до того, як логіка інструкції виконалася.

Як тестувати Solana-програми без запуску валідатора?

Для більшості unit-тестів використовуємо solana-bankrun — він підіймає синтетичний runtime в пам'яті без запуску validator-а. Тест, який на localnet зайняв би 3 секунди (слот кожні 400 мс), виконується за 50 мс — у 60 разів швидше. Це критично для fuzzing-а.

Інтеграційні тести — на localnet через anchor test. Додаємо --skip-local-validator там, де тестовий сценарій вимагає реальних програм (Associated Token Program, Metaplex) — клонуємо їх стан з mainnet через --clone.

Для фаззингу SPL-програм використовуємо Trident (Ackee Blockchain's fuzzer). Він генерує випадкові послідовності інструкцій і шукає panics, unexpected account state, integer overflow. На одному з проєктів Trident за 4 години знайшов сценарій, де послідовність init → close → reinit призводила до повторної ініціалізації account з чужими даними — Anchor discriminator при цьому проходив, тому що reinit використовував той самий тип.

Оптимізація compute units

Solana обмежує кожну транзакцію в 1.4M compute units за замовчуванням (запросити можна до 1.4M через SetComputeUnitLimit). Serialization/deserialization через Borsh — дороге задоволення для великих структур.

Практика: розбиваємо великі state структури на кілька account-ів. Замість одного ProgramState з 50 полями — кілька спеціалізованих account-ів. Менше даних десеріалізується на кожен виклик — менше CU витрачається.

Другий прийом: zero_copy акаунти через #[account(zero_copy)] в Anchor. Дані читаються напряму з пам'яті без Borsh-десеріалізації. На структурах >1KB економія 30-50% CU.

Підхід CU на десеріалізацію 1KB Мутабельність
Стандартний Borsh ~8000 CU Повна
zero_copy (bytemuck) ~500 CU Обмежена (repr(C))

Процес розробки

Аналітика та проектування (2-5 днів). Розбираємо accounts model під задачу: які PDA потрібні, які seeds, де потрібен CPI в Token Program або Associated Token Program. Проектуємо state до написання коду — переробка accounts структури на етапі тестування коштує дорого.

Розробка (3-10 днів залежно від складності). Anchor + Rust stable. Покриваємо кожну інструкцію тестами через Bankrun. Складні сценарії (PDA lifecycle, CPI chains) — на localnet.

Security review. Проганяємо через Soteria (статичний аналіз Solana-програм) і ручний review за чеклистом: missing signer, ownership checks, PDA validation, integer arithmetic (використовуємо checked_add, checked_mul всюди).

Чеклист безпеки для Solana-програм
  • [ ] Всі signer перевірені
  • [ ] Ownership checks на кожному account
  • [ ] PDA derivation з seeds constraint
  • [ ] Арифметика з checked_ops
  • [ ] Відсутність reinit-уразливостей
  • [ ] Upgrade authority задана через multisig

Деплой. anchor deploy з multisig upgrade authority (Squads Protocol). Upgrade authority не повинна бути EOA — якщо приватник витече, програму перепишуть.

Що входить в роботу

  • Проектування accounts model і PDA-структури
  • Написання програми на Rust з Anchor
  • Написання unit-тестів (Bankrun) та інтеграційних тестів (localnet)
  • Security review (статичний аналіз + ручний аудит)
  • Деплой з multisig upgrade authority
  • Документація інструкцій та accounts

Наша команда має 5+ років досвіду в блокчейн-розробці та більше 10 реалізованих проєктів на Solana. Якщо ви розробляєте DeFi-протокол або NFT-маркетплейс на Solana — пишіть, допоможемо уникнути типових помилок. Зв'яжіться з нами, щоб оцінити ваш проєкт і отримати план розробки.