Разработка системы оптимизации газа для транзакций
Для протоколов с постоянным потоком транзакций — автоматические ликвидации, 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 рынка.







