Skip to main content

Documentation Index

Fetch the complete documentation index at: https://voltaire.tevm.sh/llms.txt

Use this file to discover all available pages before exploring further.

Try it Live

Run Opcode examples in the interactive playground

Opcode.disassemble()

Disassemble bytecode into array of formatted assembly strings.
import { disassemble } from 'tevm/Opcode'

const bytecode = new Uint8Array([
  0x60, 0x01,  // PUSH1 1
  0x60, 0x02,  // PUSH1 2
  0x01,        // ADD
])

const lines = disassemble(bytecode)
// ["0000: PUSH1 0x01", "0002: PUSH1 0x02", "0004: ADD"]

Parameters

  • bytecode: Uint8Array - Raw bytecode to disassemble

Returns

string[] - Array of formatted instruction strings Each line format: "<offset>: <mnemonic> [immediate]"

Output Format

  • Offset: 4-digit hex with leading zeros (e.g., 0000, 002A)
  • Mnemonic: Opcode name (e.g., PUSH1, ADD, SSTORE)
  • Immediate: Hex value for PUSH operations (e.g., 0x80, 0x0102)

Use Cases

import * as Opcode from 'tevm/Opcode'

function printDisassembly(bytecode: Uint8Array): void {
  const lines = Opcode.disassemble(bytecode)

  console.log('=== Disassembly ===')
  for (const line of lines) {
    console.log(line)
  }
  console.log(`\nTotal instructions: ${lines.length}`)
}

Annotated Disassembly with Gas

function disassembleWithGas(bytecode: Uint8Array): string[] {
  const instructions = Opcode.parse(bytecode)
  const lines: string[] = []

  for (const inst of instructions) {
    const formatted = Opcode.format(inst)
    const info = Opcode.info(inst.opcode)
    lines.push(`${formatted}  // gas: ${info?.gasCost ?? 'unknown'}`)
  }

  return lines
}

// Example output:
// 0000: PUSH1 0x80  // gas: 3
// 0002: PUSH1 0x40  // gas: 3
// 0004: MSTORE  // gas: 3

Side-by-Side Comparison

function compareDisassembly(
  bytecode1: Uint8Array,
  bytecode2: Uint8Array
): string[] {
  const lines1 = Opcode.disassemble(bytecode1)
  const lines2 = Opcode.disassemble(bytecode2)
  const maxLen = Math.max(lines1.length, lines2.length)
  const output: string[] = []

  output.push('Original'.padEnd(40) + ' | ' + 'Optimized')
  output.push('-'.repeat(80))

  for (let i = 0; i < maxLen; i++) {
    const left = (lines1[i] ?? '').padEnd(40)
    const right = lines2[i] ?? ''
    output.push(`${left} | ${right}`)
  }

  return output
}

Find Pattern Locations

function findPattern(
  bytecode: Uint8Array,
  pattern: string
): number[] {
  const lines = Opcode.disassemble(bytecode)
  const matches: number[] = []

  lines.forEach((line, index) => {
    if (line.includes(pattern)) {
      matches.push(index)
    }
  })

  return matches
}

// Find all SSTORE operations
const sstoreLocations = findPattern(bytecode, 'SSTORE')

Export to File

import * as fs from 'fs'

function exportDisassembly(
  bytecode: Uint8Array,
  filename: string
): void {
  const lines = Opcode.disassemble(bytecode)
  const content = lines.join('\n')
  fs.writeFileSync(filename, content, 'utf-8')
}

exportDisassembly(contractBytecode, 'contract.asm')

Highlight Instructions

function highlightJumps(bytecode: Uint8Array): string[] {
  const instructions = Opcode.parse(bytecode)
  const jumpDests = Opcode.jumpDests(bytecode)
  const lines: string[] = []

  for (const inst of instructions) {
    let line = Opcode.format(inst)

    if (Opcode.isJump(inst.opcode)) {
      line += '  <-- JUMP'
    }
    if (jumpDests.has(inst.offset)) {
      line += '  <-- JUMPDEST'
    }

    lines.push(line)
  }

  return lines
}

Performance

  • O(n) time where n is number of instructions
  • String allocation for each instruction line
  • Single pass through parsed instructions
  • Uses parse() internally, so includes parsing overhead