Оптимізація газу смарт-контрактів
Деплой контракту обійшовся в 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.







