Skip to main content

Try it Live

Run Opcode examples in the interactive playground

Opcode

EVM opcode utilities for bytecode parsing, disassembly, and instruction analysis.
New to EVM opcodes? Start with Opcode Fundamentals to learn stack machine architecture, gas costs, and common patterns.

Overview

Opcode provides comprehensive utilities for working with EVM opcodes (0x00-0xFF). Supports opcode validation, instruction parsing, bytecode disassembly, jump destination analysis, and opcode categorization (PUSH, DUP, SWAP, LOG, etc.).

Quick Start

import {
  isValid,
  name,
  info,
  isPush,
  isDup,
  isSwap,
  isLog,
  parse,
  disassemble,
  jumpDests,
  isValidJumpDest
} from 'tevm/Opcode'

// Check opcode validity
const valid = isValid(0x60)  // true (PUSH1)

// Get opcode name
const opName = name(0x60)  // "PUSH1"

// Get opcode info
const opInfo = info(0x60)
// { gasCost: 3, stackInputs: 0, stackOutputs: 1, name: "PUSH1" }

// Categorization
isPush(0x60)   // true
isDup(0x80)    // true (DUP1)
isSwap(0x90)   // true (SWAP1)
isLog(0xa0)    // true (LOG0)

// Parse bytecode
const instructions = parse(bytecode)
const assembly = disassemble(bytecode)

// Jump analysis
const dests = jumpDests(bytecode)
const isValidJump = isValidJumpDest(bytecode, 0x0042)

Type Definition

/**
 * Branded type for EVM opcodes (number 0x00-0xFF)
 */
export type BrandedOpcode = number & { readonly __tag: "Opcode" }

/**
 * Instruction with opcode and optional immediate data
 */
export type Instruction = {
  /** Program counter offset */
  offset: number
  /** The opcode */
  opcode: BrandedOpcode
  /** Immediate data for PUSH operations */
  immediate?: Uint8Array
}

/**
 * Opcode metadata structure
 */
export type Info = {
  /** Base gas cost (may be dynamic at runtime) */
  gasCost: number
  /** Number of stack items consumed */
  stackInputs: number
  /** Number of stack items produced */
  stackOutputs: number
  /** Opcode name */
  name: string
}
Source: BrandedOpcode.ts:1-31

Core Methods

info()

Get complete opcode metadata.
const info = Opcode.info(0x01)  // ADD
// {
//   gasCost: 3,
//   stackInputs: 2,
//   stackOutputs: 1,
//   name: "ADD"
// }

name()

Get opcode mnemonic name.
Opcode.name(0x00)  // "STOP"
Opcode.name(0x01)  // "ADD"
Opcode.name(0x60)  // "PUSH1"
Opcode.name(0xFF)  // "SELFDESTRUCT"

isValid()

Check if byte is valid opcode.
Opcode.isValid(0x60)  // true (PUSH1)
Opcode.isValid(0x00)  // true (STOP)
Opcode.isValid(0xFF)  // true (SELFDESTRUCT)
Opcode.isValid(0x0C)  // false (undefined opcode)

isPush()

Check if opcode is PUSH1-PUSH32.
Opcode.isPush(0x60)  // true (PUSH1)
Opcode.isPush(0x7F)  // true (PUSH32)
Opcode.isPush(0x01)  // false (ADD)

pushBytes()

Get number of immediate bytes for PUSH opcode.
Opcode.pushBytes(0x60)  // 1 (PUSH1)
Opcode.pushBytes(0x7F)  // 32 (PUSH32)
Opcode.pushBytes(0x01)  // 0 (not a PUSH)

pushOpcode()

Get PUSH opcode for given byte count.
Opcode.pushOpcode(1)   // 0x60 (PUSH1)
Opcode.pushOpcode(32)  // 0x7F (PUSH32)

isDup()

Check if opcode is DUP1-DUP16.
Opcode.isDup(0x80)  // true (DUP1)
Opcode.isDup(0x8F)  // true (DUP16)
Opcode.isDup(0x01)  // false

dupPosition()

Get stack position for DUP opcode (1-16).
Opcode.dupPosition(0x80)  // 1 (DUP1)
Opcode.dupPosition(0x8F)  // 16 (DUP16)

isSwap()

Check if opcode is SWAP1-SWAP16.
Opcode.isSwap(0x90)  // true (SWAP1)
Opcode.isSwap(0x9F)  // true (SWAP16)

swapPosition()

Get stack position for SWAP opcode (1-16).
Opcode.swapPosition(0x90)  // 1 (SWAP1)
Opcode.swapPosition(0x9F)  // 16 (SWAP16)

isLog()

Check if opcode is LOG0-LOG4.
Opcode.isLog(0xA0)  // true (LOG0)
Opcode.isLog(0xA4)  // true (LOG4)

logTopics()

Get number of topics for LOG opcode (0-4).
Opcode.logTopics(0xA0)  // 0 (LOG0)
Opcode.logTopics(0xA4)  // 4 (LOG4)

isTerminating()

Check if opcode terminates execution (STOP, RETURN, REVERT, INVALID, SELFDESTRUCT).
Opcode.isTerminating(0x00)  // true (STOP)
Opcode.isTerminating(0xF3)  // true (RETURN)
Opcode.isTerminating(0xFD)  // true (REVERT)
Opcode.isTerminating(0xFE)  // true (INVALID)
Opcode.isTerminating(0xFF)  // true (SELFDESTRUCT)

isJump()

Check if opcode is JUMP or JUMPI.
Opcode.isJump(0x56)  // true (JUMP)
Opcode.isJump(0x57)  // true (JUMPI)

parse()

Parse bytecode into instructions.
const bytecode = new Uint8Array([0x60, 0x80, 0x60, 0x40, 0x52])

const instructions = Opcode.parse(bytecode)
// [
//   { offset: 0, opcode: 0x60, immediate: Uint8Array([0x80]) },
//   { offset: 2, opcode: 0x60, immediate: Uint8Array([0x40]) },
//   { offset: 4, opcode: 0x52 }
// ]

format()

Format instruction as human-readable string.
const instruction = { offset: 0, opcode: 0x60, immediate: new Uint8Array([0x80]) }

const formatted = Opcode.format(instruction)
// "0000: PUSH1 0x80"

disassemble()

Disassemble bytecode into array of formatted strings.
const bytecode = new Uint8Array([0x60, 0x80, 0x60, 0x40, 0x52])

const lines = Opcode.disassemble(bytecode)
// [
//   "0000: PUSH1 0x80",
//   "0002: PUSH1 0x40",
//   "0004: MSTORE"
// ]

jumpDests()

Find all valid JUMPDEST positions in bytecode.
const bytecode = contractCode

const dests = Opcode.jumpDests(bytecode)
// Set([0x0042, 0x0089, 0x00b3, ...])

isValidJumpDest()

Check if offset is valid JUMPDEST in bytecode.
const valid = Opcode.isValidJumpDest(bytecode, 0x0042)  // true if JUMPDEST at offset

// Invalid if:
// - Not JUMPDEST opcode (0x5B)
// - Inside PUSH data
// - Out of bounds

Opcode Constants

All EVM opcodes are exported as constants:
import * as Opcode from '@tevm/primitives/Opcode'

// Arithmetic
Opcode.STOP         // 0x00
Opcode.ADD          // 0x01
Opcode.MUL          // 0x02
Opcode.SUB          // 0x03
Opcode.DIV          // 0x04
Opcode.MOD          // 0x05

// Stack operations
Opcode.PUSH1        // 0x60
Opcode.PUSH32       // 0x7F
Opcode.DUP1         // 0x80
Opcode.SWAP1        // 0x90

// Memory/Storage
Opcode.MLOAD        // 0x51
Opcode.MSTORE       // 0x52
Opcode.SLOAD        // 0x54
Opcode.SSTORE       // 0x55

// Flow control
Opcode.JUMP         // 0x56
Opcode.JUMPI        // 0x57
Opcode.JUMPDEST     // 0x5B

// Logs
Opcode.LOG0         // 0xA0
Opcode.LOG1         // 0xA1
Opcode.LOG2         // 0xA2
Opcode.LOG3         // 0xA3
Opcode.LOG4         // 0xA4

// System
Opcode.CREATE       // 0xF0
Opcode.CALL         // 0xF1
Opcode.RETURN       // 0xF3
Opcode.REVERT       // 0xFD
Opcode.INVALID      // 0xFE
Opcode.SELFDESTRUCT // 0xFF

Use Cases

Bytecode Analysis

import * as Opcode from '@tevm/primitives/Opcode'

// Analyze bytecode structure
const instructions = Opcode.parse(bytecode)

let totalGas = 0
for (const inst of instructions) {
  const info = Opcode.info(inst.opcode)
  totalGas += info.gasCost
  console.log(`${Opcode.name(inst.opcode)}: ${info.gasCost} gas`)
}

Jump Validation

// Build valid jump destination map
const validDests = Opcode.jumpDests(bytecode)

// During execution, validate jumps
function executeJump(targetOffset: number) {
  if (!validDests.has(targetOffset)) {
    throw new Error("Invalid jump destination")
  }
  // Proceed with jump
}

Disassembler

// Create human-readable disassembly
function disassembleContract(bytecode: Uint8Array): string {
  const lines = Opcode.disassemble(bytecode)
  return lines.join('\n')
}

const output = disassembleContract(contractCode)
// 0000: PUSH1 0x80
// 0002: PUSH1 0x40
// 0004: MSTORE
// 0005: CALLVALUE
// ...

Stack Depth Analysis

// Track stack depth through execution
let stackDepth = 0

for (const inst of Opcode.parse(bytecode)) {
  const info = Opcode.info(inst.opcode)
  stackDepth -= info.stackInputs
  stackDepth += info.stackOutputs

  if (stackDepth < 0) {
    console.error(`Stack underflow at ${inst.offset}`)
  }
  if (stackDepth > 1024) {
    console.error(`Stack overflow at ${inst.offset}`)
  }
}

Code Coverage

// Track which opcodes are executed
const coverage = new Set<number>()

for (const inst of executedInstructions) {
  coverage.add(inst.offset)
}

// Find uncovered code
const allOffsets = Opcode.parse(bytecode).map(i => i.offset)
const uncovered = allOffsets.filter(o => !coverage.has(o))

Tree-Shaking

Import only what you need for optimal bundle size:
// Import specific operations
import { parse, disassemble, isPush, isValid } from '@tevm/primitives/Opcode'

// Or import entire namespace
import * as Opcode from '@tevm/primitives/Opcode'

// Selective imports for bytecode analysis
const instructions = parse(bytecode)
const assembly = disassemble(bytecode)
Each parser, categorization check, and disassembly function is independently exported. Import only the bytecode analysis utilities you need (e.g., just parse without disassemble or jump analysis).

Opcode Categories

Stack Operations

  • PUSH1-PUSH32 (0x60-0x7F): Push 1-32 bytes onto stack
  • DUP1-DUP16 (0x80-0x8F): Duplicate stack item at position 1-16
  • SWAP1-SWAP16 (0x90-0x9F): Swap top stack item with item at position 1-16
  • POP (0x50): Remove top stack item

Arithmetic

  • ADD, MUL, SUB, DIV, MOD (0x01-0x05)
  • ADDMOD, MULMOD (0x08-0x09)
  • EXP (0x0A)
  • SIGNEXTEND (0x0B)

Comparison & Logic

  • LT, GT, SLT, SGT, EQ (0x10-0x14)
  • ISZERO, AND, OR, XOR, NOT (0x15-0x19)
  • BYTE, SHL, SHR, SAR (0x1A-0x1D)

Memory & Storage

  • MLOAD, MSTORE, MSTORE8 (0x51-0x53)
  • SLOAD, SSTORE (0x54-0x55)
  • MSIZE (0x59)

Flow Control

  • JUMP, JUMPI, JUMPDEST (0x56-0x5B)
  • PC (0x58)
  • STOP, RETURN, REVERT (0x00, 0xF3, 0xFD)

Logging

  • LOG0-LOG4 (0xA0-0xA4): Emit log with 0-4 topics

System Operations

  • CREATE, CREATE2 (0xF0, 0xF5)
  • CALL, CALLCODE, DELEGATECALL, STATICCALL (0xF1-0xF4, 0xFA)
  • SELFDESTRUCT (0xFF)
PUSH Data Skipping: When parsing bytecode, PUSH instructions consume 1-32 immediate bytes. These bytes must be skipped during parsing - they are data, not opcodes. The parse() and jumpDests() functions handle this correctly.

API Reference

Core

  • info - Get complete opcode metadata (gas cost, stack effects, name)
  • name - Get opcode mnemonic name (PUSH1, ADD, etc.)
  • isValid - Check if byte is valid opcode
  • parse - Parse bytecode into instructions
  • disassemble - Disassemble bytecode to human-readable strings
  • format - Format instruction as human-readable string

Categorization

  • isPush - Check if opcode is PUSH1-PUSH32
  • pushBytes - Get number of immediate bytes for PUSH opcode
  • pushOpcode - Get PUSH opcode for given byte count
  • isDup - Check if opcode is DUP1-DUP16
  • dupPosition - Get stack position for DUP opcode (1-16)
  • isSwap - Check if opcode is SWAP1-SWAP16
  • swapPosition - Get stack position for SWAP opcode (1-16)

Jump Analysis

Documentation

  • Reference Guide - Complete opcode reference with execution traces and stack diagrams
  • Fundamentals - Learn stack machine architecture, gas costs, and patterns
  • Usage Patterns - Real-world bytecode analysis and gas estimation
  • Constructors - Opcode constants and construction utilities
  • Validation - Opcode validation and category checks
  • Utilities - Metadata, stack effects, and gas cost utilities
  • WASM Implementation - Performance analysis (pure TS is faster)
  • Bytecode - Contract bytecode representation and manipulation
  • Transaction - Transactions that deploy and execute bytecode
  • State - EVM state operations accessed by opcodes
  • GasConstants - Gas costs for EVM operations

Opcode Reference Tables

Complete Opcode Listing

HexNameGasStack InStack OutNotes
0x01ADD321a + b
0x02MUL521a * b
0x03SUB321a - b
0x04DIV521a / b (0 if b=0)
0x05SDIV521Signed division
0x06MOD521a % b (0 if b=0)
0x07SMOD521Signed modulo
0x08ADDMOD831(a + b) % N
0x09MULMOD831(a * b) % N
0x0AEXP10*21a ** b (dynamic gas)
0x0BSIGNEXTEND521Sign extend byte
Legend:
  • Gas: Base gas cost (dynamic marked with *)
  • EIP: Ethereum Improvement Proposal adding the opcode
  • Warm/Cold: Access list (EIP-2929) affects gas

Stack Effect Diagrams

Common stack patterns: Binary Operations (ADD, MUL, SUB, etc)
Before: [..., a, b]
After:  [..., result]
DUP (Duplicate)
Before: [..., x₁₆, ..., x₁]
After:  [..., x₁₆, ..., x₁, ..., x₁]  // DUP[i] duplicates item at depth i
SWAP (Exchange)
Before: [..., a, b]
After:  [..., b, a]  // SWAP1 exchanges top 2
Memory Operations
Before: [..., offset, value]
MSTORE: Store value at memory[offset:offset+32]
After:  [...]

Before: [..., offset]
MLOAD:  Load value from memory[offset:offset+32]
After:  [..., value]
Storage Operations
Before: [..., slot]
SLOAD:  Load value from storage[slot]
After:  [..., value]

Before: [..., slot, value]
SSTORE: Store value to storage[slot]
After:  [...]

Gas Cost Variations

OperationBaseDynamicReason
SLOAD100Warm: 100, Cold: 2100Access list tracking (EIP-2929)
SSTORE1000-2900Storage write patterns, refunds
CALL100100-2600Warm/cold + contract creation
KECCAK25630+6/wordInput size
EXP10+50/byteExponent size
LOG375+375/topicTopics emitted

Specification References