Опкод EVM (opcode) — это одна байтовая команда виртуальной машины Ethereum, которая задаёт конкретное действие: сложить числа, прочитать переменную из хранилища, вызвать другой контракт, выкинуть лог и т.д. Смарт-контракт на Solidity или другом языке в итоге компилируется в последовательность опкодов — байткод, который выполняет EVM.
У каждого опкода есть:
- однобайтовый код (0x00–0xFF);
- имя (например, ADD, SLOAD, CALL);
- набор параметров (берутся из стека или из байткода);
- стоимость по газу, которая влияет на цену исполнения транзакции.
EVM как стековая машина
EVM — это стековая виртуальная машина. Это означает, что опкоды читают и записывают данные в стек:
- стек — LIFO-структура (последний пришёл — первый вышел), каждый элемент — 256-битное слово;
- большинство опкодов берут аргументы из стека и кладут результат обратно в стек;
- кроме стека есть память (временное хранилище в рамках вызова) и storage (постоянное хранилище контракта).
Пример простейшей последовательности опкодов:
- PUSH1 0x02 — кладёт число 2 в стек;
- PUSH1 0x03 — кладёт 3 в стек;
- ADD — снимает два верхних элемента (3 и 2), складывает и кладёт 5 обратно в стек.
Байт-код контракта — это просто цепочка таких команд с параметрами. Высокоуровневый код (Solidity, Vyper) компилируется в такую последовательность опкодов.
Основные группы опкодов
Опкоды EVM логически делятся на несколько групп:
- Арифметика и логика
Операции над числами и битами: ADD, SUB, MUL, DIV, MOD, AND, OR, XOR, NOT, SHL, SHR.
Эти опкоды работают только со стеком и не трогают память/хранилище.
- Сравнение и условные переходы
LT, GT, EQ, ISZERO и условный переход JUMPI.
На их основе реализуются if, while и другие конструкции в коде смарт-контракта.
- Работа со стеком
POP, DUP1…DUP16, SWAP1…SWAP16.
Нужны для перестановки элементов в стеке, чтобы подать их на нужные опкоды.
- Память и хранилище
- MLOAD, MSTORE, MSTORE8 — операции с временной памятью (memory);
- SLOAD, SSTORE — чтение и запись в постоянное хранилище (storage).
Именно SSTORE и SLOAD дают основной вклад в стоимость газа — см. Газ в Ethereum.
- Окружение и блокчейн
Опкоды, которые дают доступ к данным транзакции и блока:
- CALLER, CALLVALUE, CALLDATALOAD — информация о вызове;
- ADDRESS, BALANCE — данные текущего контракта и баланса;
- TIMESTAMP, NUMBER, GASPRICE — параметры блока и транзакции.
Через них контракт «видит» внешнее окружение сети.
- Вызовы и создание контрактов
CALL, STATICCALL, DELEGATECALL, CALLCODE, CREATE, CREATE2.
С их помощью контракт взаимодействует с другими контрактами и создаёт новые. Расширенные риски таких операций разбираются на страницах DELEGATECALL и риски прокси и CREATE2.
- Логи (events)
LOG0…LOG4 записывают события в лог-журнал, который индексируется по топикам (в том числе с использованием Keccak-256).
- Системные опкоды
RETURN, REVERT, INVALID, STOP, SELFDESTRUCT — управление завершением исполнения и ошибками.
Опкоды и стоимость газа
Каждый опкод имеет свою базовую стоимость по газу, которая определяет, сколько «вычислительных ресурсов» сети он потребляет.
Основные идеи:
- дешёвые операции (арифметика, стек, простая память) стоят мало газа;
- операции с постоянным хранилищем (SSTORE, SLOAD) стоят значительно дороже, так как меняют состояние сети;
- некоторые опкоды могут иметь динамическую стоимость, зависящую от контекста (например, первый доступ к слоту storage в рамках транзакции может стоить дороже, чем последующие «тёплые» обращения).
Общее потребление газа транзакцией — это сумма затрат всех опкодов плюс расходы на передачу данных в calldata и изменения состояния. Подробнее тема раскрывается в комиссиях за газ в Ethereum.
Зачем разработчику понимать опкоды
Хотя в повседневной работе разработчики чаще пишут на Solidity, понимание опкодов EVM полезно:
- Оптимизация газа
Зная, какие опкоды «дорогие», легче переписать участок кода так, чтобы использовать меньше SSTORE или сократить обращения к памяти.
- Отладка и аудит
При аудите сложных контрактов важно уметь смотреть на байткод: проверять, как реально реализованы проверки, нет ли неожиданных DELEGATECALL или опасных шаблонов.
- Работа с низкоуровневым кодом
Языки вроде Yul дают более прямой доступ к опкодам и позволяют писать вручную оптимизированные участки логики.
- Реверс-инжиниринг
При анализе чужих контрактов без исходников приходится смотреть именно на последовательность опкодов, чтобы восстановить логику.
