Разработка смарт-контрактов на 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 — пишите, поможем избежать типовых ошибок. Свяжитесь с нами, чтобы оценить ваш проект и получить план разработки.