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

Проєктуємо та розробляємо блокчейн-рішення повного циклу: від архітектури смарт-контрактів до запуску DeFi-протоколів, NFT-маркетплейсів та криптобірж. Аудит безпеки, токеноміка, інтеграція з наявною інфраструктурою.
Показано 1 з 1Усі 1306 послуг
Розробка смарт-контрактів на Cairo (StarkNet)
Складний
~3-5 днів
Часті запитання

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

Етапи блокчейн-розробки

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

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

Розроблення смарт-контрактів на Cairo (StarkNet)

Cairo — це не «ще одна мова для смарт-контрактів». Це мова, написана з нуля під одну задачу: генерувати доказуємі вичислення для STARK-пруверів. Якщо ви звикли до Solidity, перші тижні з Cairo будуть болісні. Якщо звикли до Rust — трохи простіше, але все одно потребуватимуть перестройки мислення.

Чому Cairo — не просто «StarkNet Solidity»

Головне, що потрібно зрозуміти: Cairo 1 (поточна версія) компілюється у Sierra — проміжне представлення, яке потім компілюється у CASM (Cairo Assembly). Sierra гарантує, що будь-яка програма завершима — це убирає клас атак, коли контракт спеціально потребує весь газ та откатується, залишаючи секвенсер без винагороди.

На практиці це значит: немає infinite loops без явного лічильника, немає довільних jumps. Це обмеження, з яким приходиться працювати.

Друга принципіальна річ: StarkNet — це ZK-роллап, та кожна транзакція у кінці кінців підтверджується STARK-доказом на L1 (Ethereum). Це дає дешевизну виконання на L2 при гарантіях безпеки L1. Але модель стоимості газу в StarkNet відрізняється від EVM: газ вважається у «кроках Cairo VM», а не у EVM opcodes.

Типові ошибки при переході з Solidity на Cairo

Storage — не mapping, а окремий світ

У Solidity mapping(address => uint256) — привична конструкція. У Cairo storage працює через StorageMap з явною сериалізацією. Проблема виникає, коли розробник намагається зберігати складні структури з вложеними колекціями: Cairo потребує ручної реалізації Store трейту для кастомних типів, іначе не скомпілюється.

Реальний кейс: контракт токена з balances: LegacyMap<ContractAddress, u256> працює штатно. Контракт з positions: LegacyMap<ContractAddress, UserPosition>, де UserPosition — кастомна структура, потребує #[derive(Store)] та коректну реалізацію. Якщо структура містить вложений Array<u256>, зберігати її напрямо в storage неможливо — Cairo не підтримує динамічні типи в storage. Це ломить паттерни з Solidity, де mapping(address => uint256[]) працює з коробки.

Рішення: декомпозувати структури в плоскі маппинги. positions_amount: LegacyMap<ContractAddress, u256>, positions_token: LegacyMap<ContractAddress, ContractAddress> — замість одного маппінга з nested struct.

Reentrancy у StarkNet — інша механіка

В EVM reentrancy працює через call stack: контракт A викликає контракт B, B викликає назад до A до завершення першого вклику. У StarkNet у поточній архітектурі (версія протоколу 0.13+) reentrancy теж можлива через call_contract_syscall. Але є нюанс: стан storage оновлюється негайно при записі, а не в кінці транзакції (на відміну від деяких інтерпретацій).

Паттерн захисту такий же: checks-effects-interactions. Спочатку оновлюємо balance користувача, потім робимо зовнішній виклик. ReentrancyGuard у Cairo реалізується через storage-флаг:

#[storage]
struct Storage {
    _reentrancy_guard: bool,
}

fn _lock(ref self: ContractState) {
    assert(!self._reentrancy_guard.read(), 'ReentrancyGuard: reentrant call');
    self._reentrancy_guard.write(true);
}

fn _unlock(ref self: ContractState) {
    self._reentrancy_guard.write(false);
}

OpenZeppelin Cairo (github.com/OpenZeppelin/cairo-contracts) надає готовий ReentrancyGuardComponent. Використовуємо його, а не вигадуємо свій.

Целочисленні типи та overflow

Cairo 1 використовує u256, u128, u64, u32, u8, felt252. Тип felt252 — поле простого числа (порядку 2^251), не знаковое цілое. Арифметика за модулем: якщо складаєте два felt252 та результат виходить за границю поля, отримуєте wrapping — без паніки, без помилки. Це пастка для тих, хто переносить логіку з Solidity, де uint256 за замовчуванням паникує при overflow у останніх версіях.

Для фінансової логіки використовуємо u256 з явними перевірками через u256_overflow_add, або checked_add на целочисленних типах. Ніколи не зберігаємо суми токенів у felt252.

Компоненти OpenZeppelin Cairo — архітектурний паттерн

Cairo-контракти в екосистемі StarkNet будуються на компонентній архітектурі. OpenZeppelin Cairo надає компоненти для ERC-20, ERC-721, ERC-1155, Ownable, AccessControl, Upgradeable та інших паттернів.

Компонент — це переиспользуємої одиниця логіки зі своїм storage-простором, яка підмішується в контракт через #[starknet::contract] та component!() макрос. Важлива деталь: у кожного компоненту своє storage-namespace, колізій не виникає. Це вирішує проблему storage collision, знайому з proxy-паттернами у Solidity.

Приклад ініціалізації ERC-20 з Ownable:

#[starknet::contract]
mod MyToken {
    use openzeppelin::token::erc20::{ERC20Component, ERC20HooksEmptyImpl};
    use openzeppelin::access::ownable::OwnableComponent;

    component!(path: ERC20Component, storage: erc20, event: ERC20Event);
    component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);

    #[storage]
    struct Storage {
        #[substorage(v0)]
        erc20: ERC20Component::Storage,
        #[substorage(v0)]
        ownable: OwnableComponent::Storage,
    }
}

Цей паттерн відрізняється від Solidity-спадкування: тут немає ієрархії, є композиція. Спроба реалізувати спадкування через виклик функцій батьківського контракту у Cairo не працює так само, як у Solidity.

Апгрейдабельність: proxy або replace_class_syscall

У StarkNet є нативний механізм апгрейду: replace_class_syscall. Контракт може замінити власний клас-хеш на новий, при цьому storage залишається. Це подібно на UUPS-паттерн, але без окремого proxy-контракту.

Ризики ті ж: якщо нова версія класу змінює layout storage (порядок змінних у #[storage]), дані можуть бути інтерпретовані неверно. У Cairo storage addresses обчислюються з імен змінних (хеш від імені), тому переименування змінної = втрата даних у ній після апгрейду.

OpenZeppelin надає UpgradeableComponent, який обмежує виклик upgrade() тільки owner-ом та еміттує событие:

fn upgrade(ref self: ContractState, new_class_hash: ClassHash) {
    self.ownable.assert_only_owner();
    self.upgradeable.upgrade(new_class_hash);
}

Перед апгрейдом у mainnet — обов'язковий тест на fork. Перевіряємо, що новий клас читає старий storage коректно.

Тестування Cairo-контрактів

Екосистема інструментів молодша, ніж EVM, але швидко растет.

Starknet Foundry (snforge) — основной інструмент тестування. Синтаксис тестів близкий до Foundry для EVM:

#[cfg(test)]
mod tests {
    use snforge_std::{declare, ContractClassTrait, start_cheat_caller_address};

    #[test]
    fn test_transfer() {
        let contract = declare("MyToken").unwrap();
        let (contract_address, _) = contract.deploy(@array![]).unwrap();
        // тесты
    }
}

Fuzz-тестування у snforge підтримується через #[fuzzer] атрибут. Менш розвинене, ніж Foundry fuzzing на EVM, але базові сценарії покриває.

Тестові мережі. Sepolia — поточний рекомендований тестнет StarkNet. Goerli виведений з експлуатації. Для локальної розроблення використовуємо Katana (з пакету dojo) або starknet-devnet-rs.

Інструмент Призначення Статус
snforge Unit/integration тесты Активно розвивається
sncast CLI для деплоя та взаємодії Стабільний
Katana Локальна нода StarkNet Активно розвивається
starknet-devnet-rs Альтернативна локальна нода Стабільна
Voyager Block explorer mainnet/testnet Production

Account Abstraction за замовчуванням

У StarkNet немає понятия EOA (Externally Owned Account) у смислі EVM. Кожен аккаунт — це смарт-контракт, що реалізує інтерфейс IAccount. Це Account Abstraction (AA) за замовчуванням, без EIP-4337.

Для розробника це значить: у контракті не можна використовувати tx.origin у смислі EOA (його просто немає), нема ECDSA-підписів hardcoded в протоколі на рівні аккаунту. Аккаунт може реалізувати будь-яку схему підпису — мультисиг, passkey, сессійні ключі.

Паттерн сессійних ключів особливо цікав для ігрових контрактів: користувач підписує один раз видачу сессійного ключа, потім гра робить транзакції від його імені у межах дозволеного scope. Без накладних витрат EIP-4337 bundler-інфраструктури.

Процес роботи над Cairo-контрактом

Аналітика. Розбираємо вимоги під Cairo-специфіку: які дані йдуть у storage, яка логіка потребує межконтрактних викликів (вони дорожче, ніж internal виклики), потрібна ли апгрейдабельність.

Проектування storage. Найбільш критичний етап. Неправильний layout storage неможливо виправити після деплоя без апгрейду.

Розроблення. Cairo 2.x, Scarb як build tool, OpenZeppelin Cairo як база. Для DeFi-логіки — вивчаємо існуючі аудировані контракти в екосистемі (Ekubo, JediSwap, zkLend).

Тестування. Snforge unit-тесты, integration тесты на Katana, manual тесты на Sepolia.

Аудит. Екосистема аудиторів для Cairo менше, ніж для EVM. Спеціалізовані команди: Trail of Bits (мають Cairo практику), ChainSecurity, Nethermind Security.

Деплой. Через sncast або starknet.js. Declare (публікація класу) + Deploy (створення инстанса) — два кроки замість одного у EVM.

Часові рамки

Розроблення Cairo-контракту сопостави сложності займає у 1.5-2 рази довше, ніж аналог на Solidity, якщо команда переходить з EVM вперше. Це час на освоєння Cairo-специфіки, а не показатель складності самої мови.

Оцінювально: базовий ERC-20 з кастомною логікою — 3-5 днів. DeFi-протокол (AMM, lending) — 3-8 тижнів. Повна розроблення з аудитом та деплоєм на mainnet — від 2 місяців.

Вартість розраховується після технічного брифінгу та аналізу вимог.