Оптимизация газа смарт-контрактов

Проектируем и разрабатываем блокчейн-решения полного цикла: от архитектуры смарт-контрактов до запуска 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

Оптимизация газа смарт-контрактов

Деплой контракта обошёлся в 0.8 ETH вместо ожидаемых 0.3. Или пользователи платят по $15 за каждый transfer() при gas price 30 gwei, хотя конкуренты — по $4. Это не вопрос «когда у сети нагрузка спадёт». Это вопрос storage layout, неоптимальных opcodes и паттернов, которые компилятор Solidity не исправляет за вас.

Откуда берётся лишний газ

Storage — главный источник потерь

SSTORE стоит 20 000 gas при записи в холодный слот, 2 900 gas при обновлении тёплого. SLOAD — 2 100 gas для холодного, 100 для тёплого (EIP-2929). Именно поэтому архитектура хранилища определяет 60-80% стоимости контракта.

Slot packing — первый инструмент. EVM хранит данные в 32-байтовых слотах. Если объявить переменные так:

uint128 a; // слот 0
uint256 b; // слот 1 — ОТДЕЛЬНЫЙ слот, хотя мог бы упаковаться
uint128 c; // слот 2

Получаем три слота. Переупорядочивание:

uint128 a; // слот 0, байты 0-15
uint128 c; // слот 0, байты 16-31 — упакованы!
uint256 b; // слот 1

Даёт два слота. На контракте с 10 000 деплоями экономия — сотни ETH суммарно по экосистеме.

На практике: один крупный DeFi-проект пришёл к нам с ERC-1155 контрактом, где структура TokenInfo занимала 3 слота вместо 1. Переупорядочивание полей и замена uint256 decimals на uint8 decimals срезали 62% газа на mint().

Mappings vs Arrays

mapping(uint256 => address) — O(1) доступ, gas-эффективен. address[] с поиском по значению — O(n) и в 99% случаев ошибка архитектуры. Если нужна итерация — индексируй через события, читай off-chain через The Graph.

Неочевидный источник: keccak256 на коротких строках

string memory name в функции, которая вызывается тысячи раз — это ABI encoding overhead. Замена строк на bytes32 константы там, где строки известны заранее, даёт 200-500 gas на вызов.

Инструменты анализа

Инструмент Что показывает
Hardhat Gas Reporter Gas на каждый вызов функции в тестах
Foundry forge test --gas-report То же, но быстрее и с diff между коммитами
eth-gas-reporter Детализация по opcodes через --verbose
Tenderly Gas Profiler Breakdown по EVM-трейсу реальной транзакции
Remix Gas Estimation Быстрая проверка без setup

Foundry — предпочтительный выбор. forge snapshot создаёт .gas-snapshot файл, который можно коммитить в репозиторий и отслеживать регрессии газа в CI:

forge snapshot
# изменяем код
forge snapshot --diff

Разница сразу видна построчно по каждой функции.

Конкретные паттерны оптимизации

Custom errors вместо require с строками

// До: 24 000 gas на деплой одной строки
require(amount > 0, "Amount must be positive");

// После: экономия ~200 gas per revert + меньше байткода
error AmountZero();
if (amount == 0) revert AmountZero();

Custom errors (EIP-838) стали стандартом с Solidity 0.8.4. Строки в require — это bytecode, который увеличивает стоимость деплоя и revert.

Unchecked arithmetic

С Solidity 0.8.0 все арифметические операции проверяют overflow по умолчанию. Проверка стоит ~100 gas per operation. Там, где overflow математически невозможен:

unchecked {
    ++i; // в цикле for — стандартный паттерн
    total += amounts[i]; // если суммы ограничены и проверены выше
}

На цикле из 100 итераций — экономия 10 000+ gas.

Immutable и constant

constant — значение встраивается в байткод, SLOAD не нужен. immutable — значение записывается в байткод при деплое, читается как PUSH32. Оба в ~3 раза дешевле чтения из storage. Адрес токена, fee basis points, адрес owner в контракте, который не апгрейдится — всё это кандидаты на immutable.

Calldata vs memory для входных параметров

// memory — копирует данные в память
function process(uint256[] memory ids) external

// calldata — читает напрямую из calldata, не копирует
function process(uint256[] calldata ids) external

Для внешних функций (external), где данные только читаются — calldata дешевле. Разница растёт с размером массива: на массиве из 50 элементов — 3 000-5 000 gas.

Процесс оптимизации

Аудит baseline. Запускаем все тесты с forge test --gas-report, фиксируем baseline. Никаких изменений без измерений до и после.

Профилирование через Tenderly. Берём реальные транзакции из mainnet (если контракт уже задеплоен) или симулируем в Tenderly fork. Смотрим breakdown по EVM opcodes — где процентно больше всего тратится.

Итерации. Применяем изменения по одному, измеряем. Slot packing обычно даёт наибольший эффект — начинаем с него.

Regression testing. forge snapshot в CI. Любой PR, который увеличивает gas более чем на 1%, требует явного обоснования в review.

Ориентиры по срокам

Аудит газа существующего контракта + отчёт с рекомендациями: 1-2 дня. Оптимизация с имплементацией изменений и тестами: 2-3 дня в зависимости от сложности контракта. Полная переработка storage layout (если архитектура изначально неоптимальна): от 1 недели, так как требует миграционных скриптов для существующих данных.

Стоимость рассчитывается после анализа контракта и текущего gas profile.