Skip to main content

Overview

The Ethereum Virtual Machine is a stack-based virtual machine that executes smart contract bytecode. Voltaire provides type-first EVM primitives - strongly-typed execution types, instruction handlers, and precompiled contracts in both TypeScript and Zig. Every execution primitive is a branded type:
  • Opcode - Branded number (0x00-0xFF)
  • Instruction - Opcode + offset + immediate data
  • BrandedFrame - Execution frame (stack, memory, gas, state)
  • InstructionHandler - Opcode handler function signature
  • CallParams / CallResult - Cross-contract call types
  • CreateParams / CreateResult - Contract deployment types
This section documents 166 instruction handlers across 11 categories plus 21 precompiled contracts, all implemented with:
  • Type safety - Branded types prevent passing wrong values
  • Zero-copy operations - Direct Uint8Array manipulation
  • Tree-shakeable exports - Import only what you need
  • WASM compilation support - High-performance native execution
  • Comprehensive test coverage - Every opcode tested against official vectors
For complete spec-compliant EVM implementations that use these primitives, see evmts/guillotine and evmts/tevm-monorepo.

EVM Components

Types

Strongly-typed execution primitives:
  • Opcode - Branded number type for instructions (0x00-0xFF)
  • Instruction - Opcode with offset and immediate data
  • BrandedFrame - Complete execution state (stack, memory, gas, context)
  • BrandedHost - Pluggable state backend interface
  • InstructionHandler - Function signature for opcode implementations
  • CallParams/CallResult - Cross-contract call types
  • CreateParams/CreateResult - Contract deployment types
  • EvmError - Execution error types
See EVM Types for complete type reference with examples.

Instructions

166 opcode handlers organized by function:
  • Arithmetic (11): ADD, MUL, SUB, DIV, SDIV, MOD, SMOD, ADDMOD, MULMOD, EXP, SIGNEXTEND
  • Comparison (6): LT, GT, SLT, SGT, EQ, ISZERO
  • Bitwise (8): AND, OR, XOR, NOT, BYTE, SHL, SHR, SAR
  • Keccak (1): SHA3
  • Context (16): ADDRESS, BALANCE, ORIGIN, CALLER, CALLVALUE, CALLDATALOAD, etc.
  • Block (11): BLOCKHASH, COINBASE, TIMESTAMP, NUMBER, DIFFICULTY, GASLIMIT, CHAINID, etc.
  • Stack (86): POP, PUSH0-32, DUP1-16, SWAP1-16
  • Memory (4): MLOAD, MSTORE, MSTORE8, MCOPY
  • Storage (4): SLOAD, SSTORE, TLOAD, TSTORE
  • Control Flow (7): STOP, JUMP, JUMPI, PC, JUMPDEST, RETURN, REVERT
  • Log (5): LOG0-4
  • System (7): CREATE, CALL, CALLCODE, DELEGATECALL, CREATE2, STATICCALL, SELFDESTRUCT

Precompiles

21 precompiled contracts at addresses 0x01-0x13:
  • Cryptography (1): ECRECOVER
  • Hashing (3): SHA256, RIPEMD160, BLAKE2F
  • Data (1): IDENTITY
  • Mathematics (1): MODEXP
  • zkSNARKs (3): BN254_ADD, BN254_MUL, BN254_PAIRING
  • Blob Data (1): POINT_EVALUATION (EIP-4844)
  • BLS12-381 (9): G1/G2 operations for Ethereum 2.0 consensus

Architecture

Type-First Design

All EVM operations use strongly-typed primitives:
import {
  type BrandedFrame,
  type BrandedHost,
  type InstructionHandler,
  type CallParams,
  type CallResult,
  Opcode,
} from '@tevm/voltaire/evm';

// Execution frame (stack, memory, gas, state)
const frame: BrandedFrame = {
  stack: [],
  memory: new Map(),
  memorySize: 0,
  pc: 0,
  gasRemaining: 1000000n,
  bytecode: code,
  caller: callerAddress,
  address: contractAddress,
  value: 0n,
  calldata: input,
  // ... other fields
};

// Host interface (pluggable state backend)
const host: BrandedHost = {
  getBalance: (addr) => balances.get(addr) || 0n,
  setBalance: (addr, bal) => balances.set(addr, bal),
  getStorage: (addr, slot) => storage.get(`${addr}:${slot}`) || 0n,
  setStorage: (addr, slot, val) => storage.set(`${addr}:${slot}`, val),
  // ... other methods
};

// Instruction handler (opcode implementation)
const addHandler: InstructionHandler = (frame, host) => {
  if (frame.stack.length < 2) return { type: "StackUnderflow" };
  if (frame.gasRemaining < 3n) return { type: "OutOfGas" };

  const b = frame.stack.pop()!;
  const a = frame.stack.pop()!;
  frame.stack.push((a + b) % 2n**256n); // Mod 2^256 overflow
  frame.gasRemaining -= 3n;
  frame.pc += 1;

  return { type: "Success" };
};

// Cross-contract call parameters
const callParams: CallParams = {
  callType: "CALL",
  target: targetAddress,
  value: 1000000000000000000n, // 1 ether
  gasLimit: 100000n,
  input: calldata,
  caller: frame.address,
  isStatic: false,
  depth: 1,
};

TypeScript Implementation

import * as EVM from '@tevm/voltaire/evm';

// Execute instruction
const result = EVM.Instructions.Arithmetic.add(stack);

// Execute precompile
const precompileResult = EVM.Precompiles.execute(
  PrecompileAddress.ECRECOVER,
  input,
  gasLimit,
  Hardfork.CANCUN
);

Opcode Categories

Computational Operations

Stack manipulation and arithmetic form the foundation of EVM computation:
  • Stack: 1024 elements max, 256-bit words
  • Arithmetic: Big-integer operations with overflow semantics
  • Bitwise: Bit manipulation for flags, masks, compression

State Access

Instructions for reading/modifying blockchain state:
  • Storage: Persistent (SLOAD/SSTORE) and transient (TLOAD/TSTORE)
  • Memory: Volatile scratch space within transaction
  • Context: Access to msg.sender, msg.value, block data

Control Flow

Program counter manipulation and execution flow:
  • Jumps: JUMP, JUMPI require JUMPDEST validation
  • Termination: STOP, RETURN, REVERT for execution halting
  • PC: Program counter inspection for dynamic code

External Interactions

Cross-contract calls and logging:
  • Calls: CALL, STATICCALL, DELEGATECALL with gas forwarding
  • Creation: CREATE, CREATE2 for contract deployment
  • Logs: LOG0-4 for event emission
  • Destruction: SELFDESTRUCT for contract removal

Gas Metering

All operations have precise gas costs defined in the Yellow Paper:
CategoryCost RangeExamples
Zero0STOP, RETURN (base)
Base2ADDRESS, ORIGIN, CALLER
Very Low3ADD, SUB, NOT, LT, GT
Low5MUL, DIV, MOD, BYTE
Mid8ADDMOD, MULMOD
High10JUMPI, balance check
Ext20-700BALANCE, SLOAD, LOG
Memory3/word + expansionMLOAD, MSTORE, CREATE
Storage100-20000SLOAD, SSTORE (complex)

Dynamic Gas

Some operations have variable costs:
  • Memory expansion: Quadratic growth prevents DoS
  • Storage changes: SSTORE pricing based on cold/warm, zero/nonzero transitions
  • Call gas: 63/64 rule for subcall forwarding
  • Precompiles: Dynamic based on input size (MODEXP, MSM)

Hardfork Evolution

EVM instructions and precompiles introduced over time:
HardforkInstructionsPrecompilesNotable Additions
Frontier1400x01-0x04Core opcodes, ECRECOVER, SHA256
Homestead1400x01-0x04DELEGATECALL behavior change
Byzantium1430x01-0x08RETURNDATASIZE, STATICCALL, MODEXP, BN254
Istanbul1440x01-0x09CHAINID, SELFBALANCE, BLAKE2F
Berlin1440x01-0x09Access list gas changes
London1450x01-0x09BASEFEE
Shanghai1470x01-0x09PUSH0, transient storage (TLOAD/TSTORE)
Cancun1480x01-0x0AMCOPY, BLOBHASH, BLOBBASEFEE, POINT_EVALUATION
Prague1480x01-0x13BLS12-381 precompiles (9 new)

Implementation Status

Zig: Complete

All 166 instructions and 21 precompiles fully implemented with:
  • Native C library integration (blst, c-kzg-4844, arkworks)
  • Comprehensive test coverage
  • WASM compilation support

TypeScript: Functional

  • Instructions: All 166 handlers implemented in pure TypeScript
  • Precompiles: Production-ready for most, stubs for BLS12-381 (use WASM)
For security-critical operations, always use Zig/WASM implementations.

Security

Validation

All implementations validate:
  • Stack depth (1024 max)
  • Memory bounds
  • Gas sufficiency
  • Input lengths
  • Opcode validity for hardfork

Constant-Time Operations

Cryptographic operations use constant-time algorithms to prevent timing attacks on sensitive data.

DoS Protection

Gas metering prevents computational DoS:
  • Memory expansion costs grow quadratically
  • Storage operations priced to prevent abuse
  • Call depth limited to 1024
  • Precompile gas checked before execution

References