This document provides an overview of the Zig EVM architecture, designed for high-performance L2/Rollup execution with embeddability across multiple languages.
┌─────────────────────────────────────────────────────────────────┐
│ Language Bindings │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Python │ │ Rust │ │ JS │ │ C │ │
│ │ (ctypes) │ │ (FFI) │ │ (N-API) │ │ (native) │ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
└───────┼───────────────┼───────────────┼───────────────┼─────────┘
│ │ │ │
└───────────────┴───────┬───────┴───────────────┘
│
┌───────────────────────────────▼─────────────────────────────────┐
│ FFI Layer (C ABI) │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ src/ffi.zig │ │
│ │ - Opaque handles (EVMHandle, BatchHandle) │ │
│ │ - Error codes enum │ │
│ │ - C-compatible structs │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────┬───────────────────────────────┘
│
┌─────────────────────────────────▼───────────────────────────────┐
│ Core EVM Engine │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐ │
│ │ Stack │ │ Memory │ │ Accounts │ │
│ │ (1024 max) │ │ (dynamic) │ │ (balance, storage) │ │
│ └──────────────┘ └──────────────┘ └──────────────────────┘ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐ │
│ │ Opcodes │ │ Call Stack │ │ Logs │ │
│ │ (96 impl) │ │ (nested) │ │ (LOG0-LOG4) │ │
│ └──────────────┘ └──────────────┘ └──────────────────────┘ │
└─────────────────────────────────┬───────────────────────────────┘
│
┌─────────────────────────────────▼───────────────────────────────┐
│ Parallel Execution Layer │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ BatchExecutor │ │
│ │ - Dependency analysis (O(n) hash-based) │ │
│ │ - Work-stealing thread pool │ │
│ │ - Wave-based parallel execution │ │
│ │ - Speculative execution with rollback │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
The central execution engine containing:
pub const EVM = struct {
// Execution state
stack: Stack, // 256-bit word stack (max 1024)
memory: Memory, // Dynamic byte array
pc: usize, // Program counter
gas: u64, // Remaining gas
code: []const u8, // Bytecode being executed
// Account state
accounts: HashMap([20]u8, Account),
// Environment
current_address: [20]u8,
caller_address: [20]u8,
origin_address: [20]u8,
call_value: BigInt,
calldata: []const u8,
// Block context
block_number: u64,
block_timestamp: u64,
coinbase: [20]u8,
chain_id: u64,
// Execution results
return_data: []u8,
logs: ArrayList(Log),
stop_execution: bool,
execution_reverted: bool,
};LIFO stack for 256-bit EVM words:
- Maximum depth: 1024 items
- Operations: push, pop, peek, dup, swap
- Overflow/underflow protection
Dynamic byte array with word-aligned access:
- Auto-expansion on access
- MLOAD: 32-byte aligned reads
- MSTORE: 32-byte aligned writes
- MSTORE8: Single byte writes
- Gas cost for expansion
256-bit integer arithmetic:
- 4×64-bit word representation
- Full arithmetic: add, sub, mul, div, mod, exp
- Modular arithmetic: addmod, mulmod
- Bitwise operations: and, or, xor, not, shl, shr
- Signed operations: sdiv, smod, signextend
pub const Account = struct {
balance: BigInt,
nonce: u64,
code: []const u8,
storage: HashMap(BigInt, BigInt),
};Nested execution contexts for CALL operations:
pub const CallFrame = struct {
caller: [20]u8,
callee: [20]u8,
value: BigInt,
calldata: []const u8,
gas: u64,
return_offset: usize,
return_size: usize,
is_static: bool,
is_delegate: bool,
depth: u16,
};Opcodes are implemented as individual files in src/opcodes/:
// Example: src/opcodes/add.zig
pub fn getImpl() struct { code: u8, impl: OpcodeImpl } {
return .{
.code = @intFromEnum(Opcode.ADD),
.impl = OpcodeImpl{ .execute = execute },
};
}
fn execute(evm: *EVM) !void {
const a = evm.stack.pop() orelse return error.StackUnderflow;
const b = evm.stack.pop() orelse return error.StackUnderflow;
try evm.stack.push(a.add(b));
}| Category | Opcodes | Count |
|---|---|---|
| Arithmetic | ADD, SUB, MUL, DIV, MOD, EXP, etc. | 17 |
| Comparison | LT, GT, EQ, ISZERO, etc. | 6 |
| Bitwise | AND, OR, XOR, NOT, SHL, SHR, etc. | 9 |
| Stack | PUSH1-32, DUP1-16, SWAP1-16, POP | 66 |
| Memory | MLOAD, MSTORE, MSTORE8, MSIZE | 4 |
| Storage | SLOAD, SSTORE | 2 |
| Flow | JUMP, JUMPI, JUMPDEST, PC, STOP | 5 |
| Environment | ADDRESS, BALANCE, CALLER, etc. | 15 |
| Block | NUMBER, TIMESTAMP, COINBASE, etc. | 8 |
| Logging | LOG0-LOG4 | 5 |
| Call | CALL, DELEGATECALL, STATICCALL, etc. | 6 |
| Create | CREATE, CREATE2 | 2 |
| Return | RETURN, REVERT | 2 |
Gas costs follow Ethereum specifications:
pub fn getGasCost(opcode: Opcode) u64 {
return switch (opcode) {
.STOP => 0,
.ADD, .SUB => 3,
.MUL, .DIV => 5,
.EXP => 10, // + dynamic cost
.SLOAD => 800, // cold access
.SSTORE => 20000, // new value
.CALL => 700, // + memory + value transfer
// ... etc
};
}- Memory expansion:
3 * words + words² / 512 - SSTORE: EIP-2200 gas metering (cold/warm access)
- CALL: Base + memory + value transfer costs
- LOG: 375 + 8 * data_size + 375 * topic_count
Transactions are analyzed for conflicts:
- Address conflicts: Same sender/receiver
- Storage conflicts: Same storage slots accessed
- Nonce ordering: Same sender transactions ordered
Wave 1: [Tx0, Tx2, Tx5] ─── parallel ───▶
Wave 2: [Tx1, Tx3] ─── parallel ───▶
Wave 3: [Tx4, Tx6] ─── parallel ───▶
For optimistic parallelism:
- Execute transactions speculatively
- Track state modifications
- Validate no conflicts occurred
- Rollback and re-execute on conflict
- Arena allocators for execution context
- Memory pools for frequent allocations
- Explicit cleanup on context exit
- Per-thread EVM instances
- Lock-free work queue
- Atomic state updates for shared data
pub const EVMError = error{
OutOfGas,
StackUnderflow,
StackOverflow,
InvalidOpcode,
InvalidJump,
Revert,
StaticCallViolation,
OutOfMemory,
CallDepthExceeded,
InsufficientBalance,
};| Target | Command | Description |
|---|---|---|
| Executable | zig build run |
CLI demo |
| Shared Library | zig build lib |
libzigevm.so/dll |
| Static Library | zig build lib |
libzigevm.a |
| Tests | zig build test |
Unit tests |
| Compliance | zig build compliance |
Ethereum tests |
| Benchmarks | zig build benchmark |
Performance tests |
zig-evm/
├── src/
│ ├── main.zig # Core EVM struct
│ ├── stack.zig # Stack implementation
│ ├── memory.zig # Memory implementation
│ ├── bigint.zig # 256-bit integers
│ ├── call_frame.zig # Call stack
│ ├── ffi.zig # C ABI exports
│ ├── batch_executor.zig # Parallel execution
│ └── opcodes/ # Opcode implementations
│ ├── add.zig
│ ├── sub.zig
│ └── ...
├── include/
│ └── zigevm.h # C header
├── bindings/
│ ├── python/ # Python bindings
│ ├── rust/ # Rust bindings
│ └── js/ # JavaScript bindings
├── tests/
│ ├── test_*.zig # Unit tests
│ ├── eth_compliance.zig # Ethereum test runner
│ └── run_compliance.zig # Compliance test CLI
└── docs/
├── ARCHITECTURE.md # This file
├── FFI.md # C ABI reference
├── BINDINGS.md # Language binding guide
└── PARALLEL.md # Parallel execution guide
- Single execution: ~1M gas/second
- Parallel throughput: 5-6x improvement with 8 threads
- Memory overhead: ~100KB per EVM instance
- Startup time: <1ms for instance creation