Рефакторинг смарт-контрактів

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

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

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

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

  • 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

Рефакторинг смарт-контрактів

Контракт написаний рік тому назад, працює, гроші не теряються — але кожна нова функція викликає паніку: незрозуміло, що сломається. Storage layout розпухнув до 30 змінних без логіки групування, функції на 200 рядків, жодного тесту на edge cases. Рефакторинг смарт-контракту відрізняється від рефакторингу звичайного коду тим, що ціна помилки — втрата коштів користувачів.

Де найчастіше ховається технічний борг

Неоптимізований storage layout. Solidity упаковує змінні в 32-байтні слоти. Якщо змінні оголошені у порядку uint128, uint256, uint128 — це три слоти замість двох. На популярному контракті з тисячами викликів на день це реальні гроші. Бачили контракт, де переупорядкування 8 змінних під slot packing скоротило газ на write-операції на 40%. Це не оптимізація заради оптимізації — це конкретні тисячі доларів економії користувачів на рік.

Unbounded loops як вектор gas griefing. Паттерн for (uint i = 0; i < users.length; i++) у контракті, де users може зростати необмежено — це не просто неефективність. Зловмисник додає 10 000 адрес, і наступний виклик distribute() вилітає за ліміт блока (30M газу на mainnet). Функція стає невиконуваною — контракт зависає. Рефакторинг на pull-паттерн з пагінацією або enumerable mapping вирішує це структурно.

Reentrancy без guard на cross-function рівні. ReentrancyGuard від OpenZeppelin захищає одну функцію. Але якщо withdraw() захищена guard, а claim() ні — й обидві змінюють один balance mapping — cross-function reentrancy можлива. Саме так працював Fei Protocol exploit (80M$ у 2022). При рефакторингу аудируємо весь граф викликів, а не тільки "підозрілі" функції.

Як ми підходимо до рефакторингу

Перший крок — статичний аналіз через Slither. Він за 2-3 хвилини знаходить:

  • reentrancy паттерни (включаючи cross-function)
  • неініціалізовані змінні
  • tx.origin авторизацію
  • неправильний порядок операцій (state change після external call)
  • shadow змінні

Slither дає сотні warning-ів на будь-якому реальному контракті — важливо відділити критичні від інформаційних. Далі — Mythril для символічного виконання на ключових функціях.

Coverage аудит. Дивимось, що покрито тестами, а що ні. Як правило: happy path покритий, edge cases — ні. Нема тесту на "що буде, якщо owner викличе цю функцію двічі поспіль". Нема тесту на "що буде з контрактом після emergency pause". Додаємо тести через Foundry — його fuzzer за годину знаходить те, що ручні тести на Hardhat не знайшли за місяць.

Структурний рефакторинг. Виносимо логіку в бібліотеки (Library pattern), розділяємо storage та logic через Diamond pattern (EIP-2535) якщо контракт великий, застосовуємо Check-Effects-Interactions на кожній функції з external call. Переписуємо Events — неправильно індексовані параметри роблять The Graph запити неефективними.

Gas оптимізація. Конкретні паттерни:

  • storagememory для read-only операцій всередині функції
  • uint256 замість uint8 у локальних змінних (EVM оперує 256-бітними словами, downcast дорожче)
  • unchecked { i++ } у лічильниках циклів, де overflow неможливий (Solidity 0.8+)
  • calldata замість memory для параметрів зовнішніх функцій
  • упаковка подій: не емітити зайві поля в events
Паттерн Економія газу (приблизно)
Slot packing змінних 20-40% на SSTORE
memory замість storage у функції 15-30% на читання
unchecked increment 60-80 газу на ітерацію
calldata замість memory 50-100 газу на аргумент
Custom errors замість require strings 50-200 газу на revert

Апгрейд версії Solidity

Рефакторинг часто включає міграцію з 0.6/0.7 на 0.8+. Основні зміни:

  • Arithmetic overflow/underflow перевіряється за замовчуванням (SafeMath можна видалити)
  • Custom errors через error keyword — дешевше та інформативніше ніж revert("string")
  • Immutable змінні — економлять газ на константах, які встановлюються в конструкторі

Міграція з 0.6 на 0.8 — це не просто заміна pragma. ABI encoding змінився, деякі паттерни з assembly перестали працювати, .call.value() замінений на .call{value:}(). Тестуємо кожну зміну ізольовано.

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

День 1. Статичний аналіз (Slither, Mythril), coverage звіт, складання реєстру проблем з пріоритетами.

Дні 2-3. Рефакторинг за пріоритетами: критичні security issues → gas оптимізація → readability. Кожен PR — ізольоване змінення з тестами. Жодних "один великий коміт з 50 змінами".

Фінал. Запуск Foundry fuzz tests на рефакторингових функціях, порівняння gas report до/після через forge snapshot.

Термін — 2-3 дні для контракту до 500 рядків. Складніші системи з кількох контрактів — до тижня.