Розробка смарт-контрактів на Solidity

Проєктуємо та розробляємо блокчейн-рішення повного циклу: від архітектури смарт-контрактів до запуску DeFi-протоколів, NFT-маркетплейсів та криптобірж. Аудит безпеки, токеноміка, інтеграція з наявною інфраструктурою.
Показано 1 з 1Усі 1306 послуг
Розробка смарт-контрактів на Solidity
Середній
~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

Розробка смарт-контрактів на Solidity

Клієнт приносить контракт на аудит — 800 рядків Solidity, розгортання на Ethereum mainnet через тиждень. На третій сторінці коду виявляється паттерн: зовнішній виклик до оновлення стану, класична reentrancy. Не теоретична — така ж конфігурація була у The DAO у 2016-му, 60 мільйонів доларів. Контракт йде на переробку. Це стандартна ситуація, коли Solidity-розробка йде без системного підходу до безпеки.

Де частіше ломається Solidity-контракт

Reentrancy — все ще живе у production

Попри те що атака відома з 2016 року, варіанти reentrancy продовжують з'являтись. Проблема не в невіданні паттерну — більшість розробників знають про Checks-Effects-Interactions. Проблема у cross-function reentrancy, яку ReentrancyGuard з OpenZeppelin не покриває за замовчуванням.

Сценарій: контракт A викликає контракт B через низькорівневий call. B — це токен, що реалізує ERC-777 з гаком tokensReceived. У момент гака у A вже списані токени, але ETH ще не відправлений. Функція виводу у A не заблокована reentrancy-гардом, тому що розробник вважав, що захистив тільки withdraw. Результат — дренаж резервів.

Рішення: nonReentrant на всі публічні функції, які змінюють стан та роблять зовнішні виклики. Для складних систем — окремий ReentrancyGuardUpgradeable з перевіркою на рівні модулю, а не функції.

Storage collision у proxy-паттернах

При використанні Transparent Proxy або UUPS змінні зберігаються у storage слотах за позицією оголошення. Якщо у новій версії імплементації додати змінну перед існуючою — весь storage зміщується. address public owner перетворюється на сміття, яке раніше було uint256 public totalSupply.

Це не гіпотетика: кілька протоколів у 2022-2023 роках виявляли проблему після апгрейду, коли маппінги починали повертати невірні значення. Спасає ERC-7201 (namespaced storage) — змінні імплементації зберігаються у заздалегідь вибраному слоті через keccak256-хеш, ізольовано від proxy-змінних.

Gas griefing через unbounded loops

Функція, яка ітерує по address[] public users без обмежень, безпечна при 50 користувачах та перетворюється на DoS-вектор при 5000. Трансакція упирається у block gas limit та реверсується. Якщо ця функція критична для протоколу — griefing коштує атакуючому дешево, протоколу дорого.

Паттерн рішення: pagination через offset/limit або pull-паттерн замість push (користувач сам забирає награди, а не контракт розсилає всім).

Як ми пишемо Solidity-контракти

Стек та інструменти

Основний інструмент розробки — Foundry. Не через моду, а через конкретні можливості: fuzz-тестування прямо у тестах через vm.fuzz, fork-тести на реальному стані mainnet через vm.createFork, та швидкість компіляції у 4-5 разів вища за Hardhat на великих проектах.

Hardhat залишається у стеку для задач, де важлива екосистема плагінів: hardhat-deploy для воспроизводимых розгортань, hardhat-gas-reporter для звітів по газу в CI, TypeChain інтеграція.

Базові контракти — OpenZeppelin 5.x. Не форкуємо, не модифікуємо внутрішність. Якщо потрібне розширення — успадкування та override з явним super._call().

Статичний аналіз: Slither на кожен PR, Mythril для символічного виконання перед розгортанням. Для фаззингу складної логіки — Echidna з property-based тестами.

Паттерни, які використовуємо

Diamond Pattern (EIP-2535) — для систем, де кількість функцій перевищує ліміт байткоду одного контракту (24 KB). Facet-архітектура дозволяє додавати функціональність без порушення storage. Використовуємо рідко — тільки де дійсно потрібно, через складність аудиту.

Pull payment pattern — ETH ніколи не відправляється напрямик з функцій протоколу. Баланси накопичуються у маппінгу, користувач викликає withdraw(). Це видаляє весь клас reentrancy-векторів та усуває проблеми з контрактами-отримувачами, які реверсують receive().

Multicall — батчинг трансакцій через ERC-2771 або власну реалізацію. Зменшує кількість on-chain викликів, критично при високому газі на mainnet.

Оптимізація газу

Типові місця, де газ йде впустую:

Паттерн Проблема Рішення Економія
bool змінна окремо Займає повний slot (32 байти) Упаковка в struct зі суміжними типами 15-20k газу на розгортання
storage read у loop Кожен SLOAD = 100 газу (EIP-2929) Кеш у memory-змінну перед циклом До 80% на loop
emit Event без індексації Неможливо фільтрувати через The Graph indexed на ключові поля Без економії газу, але критично для DX
string у storage Дорого та неефективно bytes32 для фіксованих строк 3-5x економія

Переупорядкування змінних під slot packing — перше, що робимо при аудиті газу. Контракт з uint128 a; uint256 b; uint128 c; займає 3 слоти. Переставити у uint128 a; uint128 c; uint256 b; — 2 слоти. На розгортанні різниця 20-40k газу, на кожному SLOAD у гарячих контурах — помітна.

Процес роботи

Аналітика (1-3 дні). Розбираємо архітектуру: які ролі, які права, які інваріанти система повинна завжди дотримуватись. Інваріанти — основа для property-based тестів у Echidna.

Проектування (2-5 днів). Діаграма контрактів, storage layout, інтерфейси. На цьому етапі вирішуємо питання апгрейдності: UUPS, Transparent, або immutable. Для DeFi-протоколів з вартістю >1M USD апгрейдність — не завжди перевага з точки зору довіри.

Розробка. Контракти + тести у Foundry. Покриття >95% по рядках, fuzz-тести на всі публічні функції з числовими параметрами. Fork-тести на Ethereum/Polygon mainnet для інтеграцій з Uniswap, Aave, Chainlink.

Внутрішній аудит. Slither, Mythril, ручний review з чеклістом SWC (Smart Contract Weakness Classification). Не замінює зовнішній аудит, але закриває low/medium severity до його початку.

Розгортання. Скрипти через Foundry forge script з автоматичною верифікацією на Etherscan/Polygonscan. Розгортання спочатку на testnet (Sepolia, Mumbai), потім mainnet з мультисигом через Gnosis Safe.

Орієнтири за строками

Простий токен ERC-20 з базовими функціями — 3-5 днів включаючи тести. Стейкинг-контракт з награма та часовими локами — 1-2 тижні. Повноцінний DeFi-протокол з AMM-логікою або ленінгом — від 6 тижнів. Строки залежать від складності логіки та вимог до покриття тестами.

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