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

Проектируем и разрабатываем блокчейн-решения полного цикла: от архитектуры смарт-контрактов до запуска DeFi-протоколов, NFT-маркетплейсов и криптобирж. Аудит безопасности, токеномика, интеграция с существующей инфраструктурой.
Показано 1 из 1 услугВсе 1306 услуг
Разработка смарт-контрактов на Rust (Solana)
Сложная
~3-5 рабочих дней
Часто задаваемые вопросы
Направления блокчейн-разработки
Этапы блокчейн-разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1221
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1163
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    855
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1056
  • image_logo-advance_0.png
    Разработка логотипа компании B2B Advance
    561
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    828

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

Solana привлекает разработчиков субсекундными финальностями и стоимостью транзакции в $0.00025. Но за этой скоростью стоит принципиально другая модель исполнения: accounts model вместо contract storage, stateless programs, PDA-дериваты вместо mapping. Разработчик, пришедший с EVM, первые две недели думает, что Solana сломана — потому что привычные паттерны Solidity здесь либо не работают, либо приводят к уязвимостям другого класса.

Почему Solana-программы ломают EVM-разработчика

В EVM контракт хранит своё состояние внутри себя. В Solana программа — это stateless executable, а данные живут в отдельных account-ах, которые программа не владеет — она только авторизует операции над ними. Это означает, что каждый вызов инструкции требует явной передачи всех accounts, которые будут задействованы.

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

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

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

Как 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 нарушен — транзакция реверт-ится до того, как логика инструкции выполнилась.

Тестирование: Bankrun vs localnet

Для большинства unit-тестов используем solana-bankrun — он поднимает синтетический runtime в памяти без запуска validator-а. Тест, который на localnet занял бы 3 секунды (слот каждые 400мс), выполняется за 50мс. Это критично для 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 везде).

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

Сроки: 3-5 дней для стандартного SPL-совместимого контракта, до 3 недель для сложного протокола с несколькими программами и CPI-цепочками.