Skip to content
Learning Platform
Advanced
35 minutes
Gas EIP-1559 Base Fee Priority Fee Опкоды Оптимизация EIP-2929

Prerequisites:

  • 04-evm-stack-memory-storage

Газ и выполнение транзакций

Зачем нужен газ

Каждая операция в EVM стоит газ — единицу вычислительной работы. Газ решает две фундаментальные проблемы:

  1. Halting problem: Тьюринг-полная машина может зациклиться. Газ гарантирует, что любое выполнение завершится (когда газ закончится)
  2. DoS protection: Без газа злоумышленник мог бы бесплатно нагружать сеть тяжелыми вычислениями

Цель урока: Вы сможете объяснить EIP-1559, рассчитать стоимость транзакции, сравнить газовую стоимость опкодов и применить основные приемы оптимизации.

Интуитивная аналогия: бензин и поездка

КонцептАналогияEthereum
Gas limitРазмер бакаМаксимум газа, который вы готовы потратить
Gas usedСколько бензина сожглиФактический расход газа при выполнении
Base feeЦена бензина на заправкеЦена за единицу газа, установленная протоколом
Priority fee (tip)Чаевые водителюДоплата валидатору за приоритет
Max feeМаксимум, который готовы заплатитьПотолок цены за единицу газа
Total costИтого за поездкуgas_used * effective_gas_price

EIP-1559: динамическая модель комиссий

До обновления London (август 2021) Ethereum использовал аукцион первой цены: кто больше заплатил — того транзакцию включили первой. Это приводило к непредсказуемым комиссиям и переплатам.

EIP-1559 ввел новую модель с базовой комиссией (base fee), которая:

  • Автоматически подстраивается под нагрузку сети
  • Сжигается (burned) — не достается валидатору
  • Дополняется приоритетной комиссией (priority fee / tip) — идет валидатору

Три поля комиссии в транзакции

Поля транзакции (тип 2, EIP-1559):
  maxFeePerGas          -- максимум за единицу газа (gwei)
  maxPriorityFeePerGas  -- максимальный tip валидатору (gwei)
  gasLimit              -- максимум единиц газа

Как вычисляется baseFee

Базовая комиссия корректируется каждый блок в зависимости от заполненности предыдущего блока:

  • Target = 15M gas (50% от максимума 30M gas)
  • Блок заполнен >50% → baseFee растет (до +12.5% за блок)
  • Блок заполнен <50% → baseFee падает (до -12.5% за блок)
  • Блок заполнен ровно 50% → baseFee не меняется

Формула:

new_base_fee = old_base_fee * (1 + (gas_used - target) / target / 8)

Попробуйте перемещать ползунок заполненности блока и наблюдайте, как меняется baseFee:

EIP-1559: динамическая комиссия
Block N-1
50% full
baseFee: 10.00 gwei
7.5M / 30M gas
Block N
60% full
baseFee: 10.25 gwei
18.0M / 30M gas
Block N+1
predicted
baseFee: 10.51 gwei
baseFee УВЕЛИЧИВАЕТСЯ (+2.5% за блок)
Расчет стоимости транзакции (Block N, transfer 21000 gas)
maxFeePerGas = 30 gwei
maxPriorityFeePerGas = 2 gwei
baseFeePerGas = 10.25 gwei
effectiveGasPrice = min(30, 10.25 + 2) = 12.25 gwei
totalCost = 21,000 * 12.25 = 257,250 gwei
Сожжено (burned)215,250 gwei
Валидатору (tip)42,000 gwei
Итого257,250 gwei
Распределение комиссии
burned
tip
Target = 15M gas (50% от max 30M). Если блок заполнен больше target -- baseFee растет (до +12.5% за блок).

Расчет стоимости транзакции

Пошаговый расчет для конкретного примера:

Дано:
  maxFeePerGas          = 30 gwei
  maxPriorityFeePerGas  = 2 gwei
  baseFeePerGas         = 12 gwei  (установлено протоколом)
  gasLimit              = 100000
  gasUsed               = 21000    (простой перевод ETH)

Вычисляем:
  1. effectiveGasPrice = min(maxFeePerGas, baseFeePerGas + maxPriorityFeePerGas)
                       = min(30, 12 + 2)
                       = 14 gwei

  2. totalCost = gasUsed * effectiveGasPrice
               = 21000 * 14
               = 294000 gwei
               = 0.000294 ETH

  3. burned = gasUsed * baseFeePerGas
            = 21000 * 12
            = 252000 gwei (сожжено)

  4. validatorTip = gasUsed * (effectiveGasPrice - baseFeePerGas)
                  = 21000 * (14 - 12)
                  = 42000 gwei (валидатору)

  5. refund = (gasLimit - gasUsed) * effectiveGasPrice
            = 79000 * 14
            = 1106000 gwei (возвращено отправителю)

Что происходит при высокой baseFee?

Если baseFeePerGas + maxPriorityFeePerGas > maxFeePerGas:

  baseFeePerGas         = 29 gwei
  maxPriorityFeePerGas  = 2 gwei
  maxFeePerGas          = 30 gwei

  effectiveGasPrice = min(30, 29 + 2) = 30 gwei
  Фактический tip   = 30 - 29 = 1 gwei (меньше заявленных 2 gwei)

Если baseFeePerGas > maxFeePerGas — транзакция не будет включена в блок. Она ждет в мемпуле, пока baseFee не снизится.

Стоимость опкодов

Каждый опкод EVM имеет фиксированную стоимость в газе. Некоторые операции имеют переменную стоимость в зависимости от данных или контекста.

EIP-2929: Cold и Warm Access

С обновления Berlin (апрель 2021, EIP-2929) введено разделение на cold и warm доступ:

  • Cold access — первое обращение к адресу или слоту хранилища в транзакции. Дороже
  • Warm access — повторное обращение к тому же адресу/слоту. Дешевле

Это отражает реальную стоимость: первое чтение требует загрузки данных с диска, повторное — из кеша.

Примеры:
  SLOAD slot_X (первый раз)  = 2100 gas  (cold)
  SLOAD slot_X (второй раз)  = 100 gas   (warm)

  Экономия при повторном доступе: 2000 gas!

Изучите стоимость всех основных опкодов:

Стоимость опкодов EVM (gas)
OpcodeCold GasWarm Gas
ADD / SUB
3--
MUL / DIV
5--
EXP
10--
KECCAK256
30--
SLOAD
2,100100
SSTORE (0->non-0)
22,100100
SSTORE (non-0->non-0)
5,000100
SSTORE (non-0->0)
5,000100
CALL
2,600100
DELEGATECALL
2,600100
CREATE
32,000--
LOG1
750--
MLOAD / MSTORE
3--
<10 gas (дешево)
10-1000 gas (средне)
>1000 gas (дорого)
Нажмите на строку для подробностей. Cold/Warm (EIP-2929): первый доступ к адресу или слоту стоит дороже.

Почему SSTORE такой дорогой?

SSTORE (запись в хранилище) — самая дорогая базовая операция EVM:

СценарийGasПочему
0 → non-0 (cold)22100Создание нового слота. Данные записываются на диск навсегда
non-0 → non-0 (cold)5000Обновление. Слот уже существует, но первый доступ
non-0 → 05000 - 4800 refundУдаление слота. Refund стимулирует очистку
Warm access100Слот уже в кеше

Главный вывод: Минимизируйте запись в хранилище. Одна операция SSTORE (22100 gas) = 7367 операций ADD (3 gas).

Жизненный цикл транзакции

Шаг 1: Проверка

1. Подпись валидна (ECDSA, см. CRYPTO-11)
2. Nonce совпадает с текущим nonce аккаунта
3. Баланс >= gasLimit * maxFeePerGas + value
4. gasLimit >= intrinsic gas (21000 для трансфера)

Шаг 2: Предварительное списание

Списание = gasLimit * maxFeePerGas
Баланс -= списание
Nonce += 1

Шаг 3: Выполнение

EVM выполняет байткод контракта (или просто перевод ETH).
Каждый опкод уменьшает gas counter.
Если gas == 0 -- выполнение откатывается (revert).

Шаг 4: Финализация

refund = min(gasUsed / 5, accumulated_refund)
effective_refund = (gasLimit - gasUsed + refund) * effectiveGasPrice
Баланс += effective_refund

burned = gasUsed * baseFeePerGas
tip = gasUsed * (effectiveGasPrice - baseFeePerGas)
Валидатор получает: tip
Протокол сжигает:   burned

Intrinsic Gas

Каждая транзакция имеет базовую стоимость до выполнения байткода:

КомпонентGasОписание
Базовая стоимость21000Любая транзакция
Нулевой байт calldata4За каждый 0x00 байт
Ненулевой байт calldata16За каждый non-zero байт
Создание контракта32000Дополнительно к 21000
Access list entry2400/1900EIP-2930 (адрес/слот)
Пример: простой перевод ETH
  intrinsic_gas = 21000 + 0 (нет calldata)
  Итого: 21000 gas

Пример: вызов функции transfer(address,uint256)
  calldata = 4 байта selector + 32 байта address + 32 байта amount = 68 байт
  intrinsic_gas = 21000 + 68 * 16 = 22088 gas (если нет нулевых байт)

Математическое описание

EIP-1559 формально

Пусть BnB_n — baseFee блока nn, gng_n — gasUsed блока nn, T=15,000,000T = 15{,}000{,}000 — target gas:

Bn+1=Bn(1+gnTT8)B_{n+1} = B_n \cdot \left(1 + \frac{g_n - T}{T \cdot 8}\right)

Максимальное изменение за блок: ±12.5%\pm 12.5\% (при gn=30Mg_n = 30M или gn=0g_n = 0).

При постоянной загрузке gn=α2Tg_n = \alpha \cdot 2T (доля от максимума):

Bn+k=Bn(1+2α18)kB_{n+k} = B_n \cdot \left(1 + \frac{2\alpha - 1}{8}\right)^k

Effective gas price

peff=min(pmax,Bn+ppriority)p_{eff} = \min(p_{max}, B_n + p_{priority})

Где pmaxp_{max} = maxFeePerGas, ppriorityp_{priority} = maxPriorityFeePerGas.

Total transaction cost

C=gusedpeffC = g_{used} \cdot p_{eff}

Распределение: Cburned=gusedBnC_{burned} = g_{used} \cdot B_n Ctip=gused(peffBn)C_{tip} = g_{used} \cdot (p_{eff} - B_n)

Оптимизация газа: практические советы

1. Минимизируйте SSTORE

// Плохо: 2 записи в storage
function bad(uint256 x) external {
    counter += x;     // SSTORE
    total += x;       // SSTORE
}

// Лучше: кешируйте в memory
function better(uint256 x) external {
    uint256 _counter = counter;  // SLOAD
    uint256 _total = total;      // SLOAD
    _counter += x;
    _total += x;
    counter = _counter;  // SSTORE
    total = _total;      // SSTORE
}
// Экономия: если counter/total читались ранее (warm), нет разницы.
// Но если были доп. чтения -- memory дешевле.

2. Упаковка переменных (Slot Packing)

// Плохо: 3 слота
contract Bad {
    uint128 a;   // slot 0
    uint256 b;   // slot 1
    uint128 c;   // slot 2
}

// Хорошо: 2 слота
contract Good {
    uint128 a;   // slot 0 (lower)
    uint128 c;   // slot 0 (upper) -- packed!
    uint256 b;   // slot 1
}

3. Calldata вместо Memory

// Дороже: копирует массив в memory
function bad(uint256[] memory data) external { ... }

// Дешевле: читает прямо из calldata
function better(uint256[] calldata data) external { ... }

4. Custom Errors вместо require строк

// Дороже: строка хранится в байткоде
require(balance >= amount, "Insufficient balance");

// Дешевле: custom error
error InsufficientBalance(uint256 available, uint256 required);
if (balance < amount) revert InsufficientBalance(balance, amount);

5. Используйте unchecked для безопасных операций

// С проверкой переполнения (по умолчанию в Solidity 0.8+)
for (uint256 i = 0; i < length; i++) { ... }

// Без проверки (i не может overflow в реалистичном цикле)
for (uint256 i = 0; i < length; ) {
    ...
    unchecked { i++; }
}

Практика

  1. Используйте слайдер в диаграмме EIP-1559 чтобы исследовать:

    • Как baseFee реагирует на 100% загрузку блока?
    • При какой загрузке baseFee стабилен?
    • Что происходит с tip при высокой baseFee?
  2. В таблице стоимости опкодов найдите:

    • Самый дешевый опкод
    • Самый дорогой (без CREATE)
    • Разницу между cold и warm SLOAD
  3. Рассчитайте вручную стоимость транзакции:

    • maxFeePerGas = 25 gwei, maxPriorityFeePerGas = 3 gwei
    • baseFeePerGas = 20 gwei, gasUsed = 50000
    • Сколько сожжено? Сколько получил валидатор?

Итоги

  • Газ — единица вычислительной работы, решает halting problem и защищает от DoS
  • EIP-1559: baseFee (сжигается, автоподстройка) + priorityFee (валидатору)
  • baseFee растет при загрузке >50%, падает при <50%. Макс. изменение: 12.5% за блок
  • effectiveGasPrice = min(maxFee, baseFee + priorityFee)
  • Cold/warm access (EIP-2929): первый доступ к слоту/адресу дороже
  • SSTORE cold (0->non-0) = 22100 gas — самая дорогая базовая операция
  • Оптимизация: минимизируйте SSTORE, пакуйте переменные, используйте calldata и custom errors

Finished the lesson?

Mark it as complete to track your progress