Skip to main content

Try it Live

Run Opcode examples in the interactive playground

Opcode.format()

Format single instruction as human-readable assembly string.
import { format, PUSH1 } from 'tevm/Opcode'

const instruction = {
  offset: 42,
  opcode: PUSH1,
  immediate: new Uint8Array([0xFF])
}

const line = format(instruction)
// "002A: PUSH1 0xff"

Parameters

  • instruction: Instruction - Parsed instruction to format
type Instruction = {
  offset: number           // Program counter offset
  opcode: BrandedOpcode    // Opcode byte
  immediate?: Uint8Array   // Immediate data for PUSH
}

Returns

string - Formatted instruction string Format: "<offset>: <mnemonic> [immediate]"

Output Format

  • Offset: 4-digit hex with leading zeros (e.g., 0000, 002A, 00FF)
  • Mnemonic: Uppercase opcode name (e.g., PUSH1, ADD, SSTORE)
  • Immediate: Lowercase hex with 0x prefix for PUSH operations

Examples

Format Different Opcodes

import * as Opcode from 'tevm/Opcode'

// Simple opcode (no immediate)
const add = Opcode.format({
  offset: 0,
  opcode: Opcode.ADD
})
// "0000: ADD"

// PUSH1
const push1 = Opcode.format({
  offset: 2,
  opcode: Opcode.PUSH1,
  immediate: new Uint8Array([0x42])
})
// "0002: PUSH1 0x42"

// PUSH32 (32 bytes)
const push32 = Opcode.format({
  offset: 100,
  opcode: Opcode.PUSH32,
  immediate: Bytes32().fill(0xFF)
})
// "0064: PUSH32 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"

Custom Formatting

function formatWithComment(inst: Instruction, comment: string): string {
  const base = Opcode.format(inst)
  return `${base}  // ${comment}`
}

const formatted = formatWithComment(
  { offset: 0, opcode: Opcode.SSTORE },
  'Store counter'
)
// "0000: SSTORE  // Store counter"

Colorized Output

function formatWithColor(inst: Instruction): string {
  const base = Opcode.format(inst)
  const name = Opcode.name(inst.opcode)

  // ANSI color codes
  if (Opcode.isPush(inst.opcode)) {
    return `\x1b[32m${base}\x1b[0m`  // Green for PUSH
  } else if (Opcode.isJump(inst.opcode)) {
    return `\x1b[33m${base}\x1b[0m`  // Yellow for JUMP
  } else if (name === 'SSTORE' || name === 'SLOAD') {
    return `\x1b[31m${base}\x1b[0m`  // Red for storage
  }
  return base
}

Format with Stack Effect

function formatWithStack(inst: Instruction): string {
  const base = Opcode.format(inst)
  const effect = Opcode.getStackEffect(inst.opcode) ?? 0
  const arrow = effect > 0 ? '↑' : effect < 0 ? '↓' : '→'
  return `${base.padEnd(30)} ${arrow}${Math.abs(effect)}`
}

// Example output:
// "0000: PUSH1 0x01            ↑1"
// "0002: PUSH1 0x02            ↑1"
// "0004: ADD                   ↓1"

Batch Format

function formatInstructions(bytecode: Uint8Array): string[] {
  const instructions = Opcode.parse(bytecode)
  return instructions.map(inst => Opcode.format(inst))
}

// Or use disassemble() directly
const lines = Opcode.disassemble(bytecode)

Use Cases

Debug Output

function debugInstruction(inst: Instruction): void {
  const formatted = Opcode.format(inst)
  const info = Opcode.info(inst.opcode)

  console.log('Instruction:', formatted)
  console.log('  Gas:', info?.gasCost)
  console.log('  Stack In:', info?.stackInputs)
  console.log('  Stack Out:', info?.stackOutputs)
}

Build Control Flow Graph

interface BasicBlock {
  start: number
  instructions: string[]
}

function buildBlocks(bytecode: Uint8Array): BasicBlock[] {
  const instructions = Opcode.parse(bytecode)
  const blocks: BasicBlock[] = []
  let current: BasicBlock = { start: 0, instructions: [] }

  for (const inst of instructions) {
    current.instructions.push(Opcode.format(inst))

    if (Opcode.isJump(inst.opcode) || Opcode.isTerminating(inst.opcode)) {
      blocks.push(current)
      current = { start: inst.offset + 1, instructions: [] }
    }
  }

  if (current.instructions.length > 0) {
    blocks.push(current)
  }

  return blocks
}

Performance

  • O(1) time complexity
  • String allocation for output
  • Hex conversion for offset and immediate data
  • Very fast - just string formatting