EVM opcodes library with fork-aware gas costs, static metadata, and bytecode analysis.
eot provides a single OpCode(u8) type backed by a static [Option<OpCodeInfo>; 256] lookup table for O(1) opcode lookups with zero heap allocation. Every opcode carries its name, stack I/O, gas cost, group, introduction fork, EIP reference, immediate size, and termination flag.
Fork-specific gas costs (EIP-150, EIP-1884, EIP-2929) are resolved at query time via OpCode::gas_cost(fork), so there's no need for per-fork enum types. A DynamicGasCalculator handles warm/cold access, memory expansion, and complex call pricing for more precise analysis.
use eot::{OpCode, Fork};
// O(1) lookup
let add = OpCode::ADD;
assert_eq!(add.gas_cost(Fork::Frontier), 3);
assert!(add.is_valid_in(Fork::Frontier));
// Parse from byte
let op = OpCode::new(0x60).unwrap();
assert_eq!(op, OpCode::PUSH1);
assert_eq!(op.info().unwrap().immediate_size, 1);
// Parse from string
let op: OpCode = "KECCAK256".parse().unwrap();
assert_eq!(op.byte(), 0x20);
// Gas cost evolution
assert_eq!(OpCode::SLOAD.gas_cost(Fork::Frontier), 50);
assert_eq!(OpCode::SLOAD.gas_cost(Fork::TangerineWhistle), 200);
assert_eq!(OpCode::SLOAD.gas_cost(Fork::Istanbul), 800);
assert_eq!(OpCode::SLOAD.gas_cost(Fork::Berlin), 2100); // cold access- 149 opcodes from Frontier through Prague with full metadata
- Zero-allocation lookups via a static 256-element array
- Fork-aware gas costs encoding EIP-150, EIP-1884, EIP-2929 repricing
- Dynamic gas calculator for warm/cold access, memory expansion, call pricing
- Sequence analysis with optimization suggestions
- Fork comparison to see what changed between any two forks
- Validation of the opcode table against known historical facts
- Optional serde support behind the
serdefeature flag
| Fork | Date | Key changes |
|---|---|---|
| Frontier | Jul 2015 | Base set (130+ opcodes) |
| Homestead | Mar 2016 | DELEGATECALL |
| Tangerine Whistle | Oct 2016 | EIP-150 gas repricing |
| Spurious Dragon | Nov 2016 | EIP-161/170 |
| Byzantium | Oct 2017 | REVERT, RETURNDATASIZE, RETURNDATACOPY, STATICCALL |
| Constantinople | Feb 2019 | SHL, SHR, SAR, CREATE2, EXTCODEHASH |
| Petersburg | Feb 2019 | Reverted EIP-1283 |
| Istanbul | Dec 2019 | CHAINID, SELFBALANCE, EIP-1884 repricing |
| Berlin | Apr 2021 | EIP-2929 warm/cold access costs |
| London | Aug 2021 | BASEFEE (EIP-1559) |
| Paris | Sep 2022 | DIFFICULTY becomes PREVRANDAO |
| Shanghai | Apr 2023 | PUSH0 (EIP-3855) |
| Cancun | Mar 2024 | TLOAD, TSTORE, MCOPY, BLOBHASH, BLOBBASEFEE |
| Prague | May 2025 | — |
| Fusaka | Dec 2025 | EOF (EIP-7692), CLZ, RJUMP, CALLF, DUPN, SWAPN, EXTCALL, +13 more |
use eot::gas::{DynamicGasCalculator, ExecutionContext};
use eot::Fork;
let calc = DynamicGasCalculator::new(Fork::Berlin);
let mut ctx = ExecutionContext::new();
// Cold SLOAD
let cold = calc.calculate_gas_cost(0x54, &ctx, &[0x100]).unwrap();
// Warm the slot, then re-read
let key = {
let mut k = [0u8; 32];
k[24..32].copy_from_slice(&0x100u64.to_be_bytes());
k
};
let addr = ctx.current_address;
ctx.mark_storage_accessed(&addr, &key);
let warm = calc.calculate_gas_cost(0x54, &ctx, &[0x100]).unwrap();
assert!(warm < cold);src/
lib.rs OpCode, OpCodeInfo, Fork, Group, OPCODE_TABLE, gas_cost_for_fork()
gas.rs Gas constants, GasCostCategory, GasAnalysis
gas/calculator.rs DynamicGasCalculator (warm/cold, memory, calls)
gas/context.rs ExecutionContext, Address/StorageKey types
gas/analysis.rs GasAnalyzer, GasComparator, GasOptimizationAdvisor
validation.rs Static table validation
The entire opcode table is a single static [Option<OpCodeInfo>; 256] initialized at compile time. No HashMap, no heap allocation, no per-fork enum duplication.
- Adding an opcode or fork: edit the
opcodes!invocation insrc/lib.rsand add the fork variant to theForkenum. Runcargo ttto verify. - Fixing gas costs: update
gas_cost_for_fork()insrc/lib.rsfor historical changes, or thebase_gasin the opcode table for base costs. - Adding analysis features: extend
gas/analysis.rsorgas/calculator.rs.