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

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

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

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

Где чаще всего скрывается технический долг

Неоптимизированный storage layout. Solidity упаковывает переменные в 32-байтные слоты. Если переменные объявлены в порядке uint128, uint256, uint128 — это три слота вместо двух. На популярном контракте с тысячами вызовов в день это реальные деньги. Видели контракт, где переупорядочивание 8 переменных под slot packing снизило gas на write-операции на 40%. Это не оптимизация ради оптимизации — это конкретные тысячи долларов экономии пользователей в год.

Unbounded loops как вектор gas griefing. Паттерн for (uint i = 0; i < users.length; i++) в контракте, где users может расти неограниченно — это не просто неэффективность. Злоумышленник добавляет 10 000 адресов, и следующий вызов distribute() улетает за лимит блока (30M gas на mainnet). Функция становится неисполнимой — contract stuck. Рефакторинг на 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 optimization. Конкретные паттерны:

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

Апгрейд Solidity версии

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

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

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

Процесс работы

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

День 2-3. Рефакторинг по приоритетам: критические security issues → gas optimization → readability. Каждый PR — изолированное изменение с тестами. Никаких «один большой коммит с 50 изменениями».

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

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