Розробка системи оптимізації газу для транзакцій
Для протоколів з постійним потоком транзакцій — автоматичні ліквідації, rebalancing, keeper функції, bridge relayers — стоимість газу напрямки впливає на економіку. При $50 за ліквідацію та gas cost у $30 рентабельність операції критично залежить від того, відправляєте ви транзакцію при 20 Gwei або при 80 Gwei. Система оптимізації газу — це не одноразовий рефактор смарт-контрактів, а комплекс рішень на рівні контрактів, timing стратегії та інфраструктури.
Gas оптимізація на рівні смарт-контрактів
Перший шар оптимізації — самі контракти. Тут прибуток продуктивності може складати 30–70% у порівнянні з наївною реалізацією.
Хранилище (Storage) — найдорожча операція
SSTORE — один з найдорожчих opcodes. EIP-2929 (Istanbul) зробило перший доступ до storage слоту в транзакції ще дорожче (cold vs warm access). Стратегії:
Packing storage variables — змінні одного слоту (32 байти) читаються та пишуться разом. Solidity компілятор автоматично упаковує змінні менше 32 байт, якщо вони об'явлені послідовно:
// ПОГАНО: 3 окремих слота
uint256 amount;
address owner;
bool active;
// ДОБРЕ: 1 слот (address=20 bytes, bool=1 byte, решта uint256 не влізе — вже 2 слота)
address owner; // 20 bytes
bool active; // 1 byte
uint96 amount; // 12 bytes = итого 33 bytes, 2 слота. Пересмотреть порядок:
// ОПТИМАЛЬНО:
bool active; // 1 byte
uint96 amount; // 12 bytes — итого 13 bytes у першому слоту
address owner; // 20 bytes — теж влізе! Итого 33 bytes — не влізе.
// Потрібно аналізувати кожен конкретний випадок через Foundry gas reports
Уникати повторних SLOAD в одній функції. Один раз читаємо в memory змінну, працюємо з нею:
// ПОГАНО: 3× SLOAD
if (balances[user] > 0) {
emit Withdrawal(balances[user]);
total -= balances[user];
balances[user] = 0;
}
// ДОБРЕ: 1× SLOAD
uint256 balance = balances[user];
if (balance > 0) {
emit Withdrawal(balance);
total -= balance;
balances[user] = 0;
}
Custom errors замість require strings — error InsufficientBalance(uint256 available, uint256 required) vs require(balance >= amount, "Insufficient balance"). Custom error економить ~200 gas на деплої та ~50 gas при виклику.
Calldata оптимізація
Zero bytes у calldata коштують 4 gas, non-zero bytes — 16 gas (EIP-2028). При проектуванні ABI функцій це важливо для операцій з великим calldata (batch functions, merkle proofs).
Bitmap для boolean flags замість окремих параметрів:
// Замість (bool useFlashLoan, bool reinvest, bool autoCompound) — 3 параметри
function execute(uint8 flags) external {
bool useFlashLoan = flags & 0x01 != 0;
bool reinvest = flags & 0x02 != 0;
bool autoCompound = flags & 0x04 != 0;
}
Multicall pattern — батчинг кількох вызовів в одну транзакцію. Економія: 21000 gas (base transaction cost) × (N-1) для N операцій. Реалізація через OpenZeppelin Multicall або custom implementation з delegatecall.
Yul/Assembly для критичних шляхів
Для inner loop функцій з тисячами вызовів inline assembly дає 10–40% економії. Приклад — оптимізований трансфер токена:
function _efficientTransfer(address token, address to, uint256 amount) internal {
assembly {
let ptr := mload(0x40)
mstore(ptr, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
mstore(add(ptr, 0x04), and(to, 0xffffffffffffffffffffffffffffffffffffffff))
mstore(add(ptr, 0x24), amount)
if iszero(call(gas(), token, 0, ptr, 0x44, ptr, 0x20)) {
revert(0, 0)
}
}
}
Використовувати лише коли профілювання показує реальний bottleneck — Yul-код складніше аудировать.
Динамічна стратегія timing транзакцій
Другий шар — коли відправляти транзакцію, не лише як її оптимізувати.
Аналіз gas price паттернів
Газ у Ethereum має виражений циклічний паттерн: найнижче у выходні (особливо неділя UTC 02:00–08:00), вище у будні під час торгової сесії США (14:00–22:00 UTC). Для некритичних операцій (treasury rebalancing, harvest yields) відкладання до low-gas періоду економить реальні гроші.
EIP-1559 модель: effectiveGasPrice = min(maxFeePerGas, baseFee + maxPriorityFeePerGas). baseFee алгоритмічно адаптується до навантаження мережі. Передбачуваність baseFee на 1–3 блока вперед достатньо висока — можна чекати зниження.
Gas Oracle: система прийняття рішень
interface GasStrategy {
urgency: "immediate" | "fast" | "standard" | "slow";
maxBaseFee: bigint; // чекати поки baseFee не упадє нижче
maxPriorityFee: bigint;
deadline: number; // блок, після якого відправити в будь-якому разі
}
async function waitForOptimalGas(strategy: GasStrategy): Promise<void> {
while (true) {
const block = await provider.getBlock("latest");
const baseFee = block.baseFeePerGas!;
if (baseFee <= strategy.maxBaseFee ||
block.number >= strategy.deadline) {
break;
}
// Експоненціальний backoff з jitter
const waitMs = Math.min(12000, 3000 * Math.random() + 3000);
await sleep(waitMs);
}
}
Зовнішні data sources для gas prediction: Blocknative Gas Estimator API, Etherscan Gas Oracle, власна модель на основі історичних даних mempool. Для критичних операцій — кілька джерел з агрегацією.
Batch операції та EIP-4337 Account Abstraction
Account Abstraction (AA) через EIP-4337 додає можливості, недоступні в EOA:
UserOperation batching: кілька операцій в одному UserOp з атомарним виконанням. Для keeper систем, які виконують harvest + reinvest + rebalance — вся послідовність в одній транзакції.
Gas sponsorship (Paymaster): протокол може платити за gas користувачів. Для автоматизованих систем корисно інакше — можна платити gas в ERC-20 токенах, а не ETH, якщо на keeper-кошельку нема ETH.
Паралельна відправка через EntryPoint: кілька незалежних UserOp від різних користувачів батчуються bundler'ом, економія base transaction cost ділиться пропорційно.
Інфраструктура мониторінгу та alerting
Система оптимізації газу без мониторингу — це чорний ящик. Потрібні метрики:
-
gas_price_paidvsgas_price_optimal(Prometheus gauge) -
transaction_pending_time— скільки часу транзакція висить у mempool -
gas_savings_usd— розрахункова економія відносно naive стратегії -
stuck_transactions_count— транзакції, не включені за N блоків
Stuck transaction recovery: при різкому зростанні gas price транзакція з занижим maxFeePerGas може не включатися годинами. Потрібен watchdog-сервіс, який через 10–20 блоків відправляє replacement транзакцію з тим же nonce та підвищеним maxFeePerGas (+10% мінімум, краще +20%):
async function speedUpTransaction(originalTx: TransactionResponse) {
const currentBaseFee = (await provider.getBlock("latest"))!.baseFeePerGas!;
const newMaxFee = maxBigInt(
originalTx.maxFeePerGas! * 120n / 100n, // +20% від оригіналу
currentBaseFee * 2n // або 2× поточний baseFee
);
return wallet.sendTransaction({
...originalTx,
maxFeePerGas: newMaxFee,
maxPriorityFeePerGas: originalTx.maxPriorityFeePerGas! * 120n / 100n,
});
}
Итоговий ефект від комплексної системи газ-оптимізації для протоколів з високою транзакційною нагрузкою: 40–70% зниження затрат на газ відносно неоптимізованої baseline. Конкретні цифри залежать від типу операцій, мережи та volatility gas ринку.







