Skip to main content

Try it Live

Run Bytecode examples in the interactive playground

    Output Format

    ╔══════════════════════════════════════╗
    ║     EVM Bytecode Disassembly         ║
    ╚══════════════════════════════════════╝
    
    📊 Length: 145 bytes
    📍 Legend: ●=JUMPDEST  ⚡=Fusion  ✗=Invalid Jump
    
     #   | PC   | Stack | Hex          | Opcode    | Jump | Details
    -----|------|-------|--------------|-----------|------|------------------
       1 |    0 |     0 | 60 01        | PUSH1     |      | 0x1 (1) [gas: 3]
       2 |    2 |     1 | 60 02        | PUSH1     |      | 0x2 (2) [gas: 3]
       3 |    4 |     2 | 01           | ADD       |      | [gas: 3] [stack: -2, +1]
    
    [Block 0] gas: 9 stack: [0, 2] len: 3
    
       4 |    5 |     1 | ● 5b         | JUMPDEST  | ←●   | [target: PC=5] [gas: 1]
       5 |    6 |     1 | 00           | STOP      |      | [gas: 0]
    
    ╔══════════════════════════════════════╗
    ║            Summary                   ║
    ╚══════════════════════════════════════╝
    
    📈 Basic blocks: 2
    🎯 Jump destinations: 1
    ➡️  Unconditional jumps: 0
    ❓ Conditional jumps: 0
    ⚡ Fusion candidates: 0
    📝 Total instructions: 5
    

    Options

    interface PrettyPrintOptions {
      /** Use ANSI colors (default: true if terminal supports) */
      colors?: boolean
    
      /** Show gas costs inline (default: true) */
      showGas?: boolean
    
      /** Show stack depth column (default: true) */
      showStack?: boolean
    
      /** Show block boundaries and metadata (default: true) */
      showBlocks?: boolean
    
      /** Show jump arrows and targets (default: true) */
      showJumpArrows?: boolean
    
      /** Show fusion annotations (default: true) */
      showFusions?: boolean
    
      /** Show line numbers (default: true) */
      lineNumbers?: boolean
    
      /** Include summary footer (default: true) */
      showSummary?: boolean
    
      /** Maximum width for output (default: 120) */
      maxWidth?: number
    
      /** Compact mode (fewer columns) (default: false) */
      compact?: boolean
    }
    

    Usage Patterns

    Basic Disassembly

    const code = Bytecode("0x60016002015b00");
    
    // Simple output (no colors, no annotations)
    console.log(code.prettyPrint({
      colors: false,
      showGas: false,
      showStack: false,
      showBlocks: false
    }));
    
    // Full output (all annotations)
    console.log(code.prettyPrint());
    

    Gas-Focused View

    const output = code.prettyPrint({
      showGas: true,
      showStack: false,
      showJumpArrows: false,
      showFusions: false
    });
    
    // Highlights gas costs for optimization analysis
    

    Stack-Focused View

    const output = code.prettyPrint({
      showStack: true,
      showGas: false,
      showBlocks: true
    });
    
    // Shows stack depth evolution for validation
    

    Compact Mode

    const output = code.prettyPrint({
      compact: true,
      maxWidth: 80
    });
    
    // Fits in narrow terminals (80 columns)
    

    Save to File

    import { writeFileSync } from 'fs';
    
    const disassembly = code.prettyPrint({ colors: false });
    writeFileSync('bytecode.asm', disassembly);
    
    Disable colors when saving to file to avoid ANSI escape sequences in text files.

    Color Coding

    Instructions are color-coded by category:
    CategoryColorOpcodes
    PUSHBluePUSH0-PUSH32
    ArithmeticCyanADD, SUB, MUL, DIV, MOD, EXP
    ComparisonMagentaLT, GT, EQ, ISZERO
    BitwiseBright CyanAND, OR, XOR, NOT, SHL, SHR
    MemoryBright BlueMLOAD, MSTORE, MSTORE8
    StorageBright MagentaSLOAD, SSTORE, TLOAD, TSTORE
    StackGreenDUP1-16, SWAP1-16, POP
    Jump (JUMP)YellowJUMP
    Jump (JUMPI)GoldJUMPI
    JUMPDESTGreen BGJUMPDEST (green background)
    Flow ControlRedSTOP, RETURN, REVERT, INVALID
    SystemBright YellowCALL, CREATE, SELFDESTRUCT, etc.
    Color scheme optimized for dark terminals. Colors automatically disabled if stdout is not a TTY.

    Block Visualization

    Basic blocks shown with metadata header:
    [Block 0] gas: 21 stack: [0, 3] len: 7
       1 |    0 |     0 | 60 01        | PUSH1     |      | ...
       2 |    2 |     1 | 60 02        | PUSH1     |      | ...
       ...
    
    [Block 1] gas: 156 stack: [1, 5] len: 12
     ● Begin
       8 |   23 |     1 | ● 5b         | JUMPDEST  | ←●   | ...
       ...
    
    Block Header Format:
    • gas: N - Total gas for block (sequential execution)
    • stack: [min, max] - Min required, max reached
    • len: N - Instruction count
    Block Markers:
    • ● Begin - Marks JUMPDEST blocks (entry points)
    • Blank line separates blocks visually

    Jump Visualization

    Jump instructions show arrows connecting to targets:
      12 |   45 |     2 | 60 32        | PUSH1     | ┐→50 | 0x32 (50)
      13 |   47 |     3 | 56           | JUMP      |     | [unconditional jump]
      ...
      15 |   50 |     2 | ● 5b         | JUMPDEST  | ←●  | [target: PC=50]
    
    Jump Symbols:
    • ┐→N - Forward jump (down in code) to PC N
    • ┘→N - Backward jump (up in code) to PC N
    • ←● - Jump target (valid JUMPDEST)
    • ←✗ - Invalid jump target (not JUMPDEST) - highlights bugs!
    • →? - Jump target unknown or outside bytecode
    Pretty print highlights jumps to non-JUMPDEST locations with red marker. This indicates malformed bytecode that will revert at runtime.

    Fusion Annotations

    Detected fusion patterns annotated inline:
       1 |    0 |     0 | ⚡ 60 01        | PUSH1     |      | 0x1 ⚡ PUSH+ADD
       2 |    2 |     1 | 01             | ADD       |      | [gas: 3]
    
    Fusion Markers:
    • prefix on hex column - Fusion opportunity
    • ⚡ PUSH+ADD suffix - Pattern type
    • Green background for fusion marker
    See Fusion Detection for all 20+ patterns.

    Inline Metadata

    PUSH Values

    PUSH1     |      | 0x42 (66) [gas: 3]
    
    Shows both hex (0x42) and decimal (66) for values ≤ 0xFFFF.

    Gas Costs

    SSTORE    |      | [gas: 20000]
    
    Base gas cost from Opcode.getGasCost().

    Stack Effects

    ADD       |      | [gas: 3] [stack: -2, +1]
    
    Shows inputs consumed (-2) and outputs produced (+1).

    Jump Targets

    JUMPDEST  | ←●   | [target: PC=50] [gas: 1]
    
    Shows this is a valid jump destination at PC 50.

    Summary Section

    Footer shows aggregate statistics:
    ╔══════════════════════════════════════╗
    ║            Summary                   ║
    ╚══════════════════════════════════════╝
    
    📈 Basic blocks: 5
    🎯 Jump destinations: 3
    ➡️  Unconditional jumps: 2
    ❓ Conditional jumps: 1
    ⚠️  Invalid jumps: 1 (!)
    ⚡ Fusion candidates: 4
    📝 Total instructions: 47
    ⚠️  Final stack depth: 2 (expected 0)
    
    Warnings:
    • ⚠️ Invalid jumps - Jumps to non-JUMPDEST (red, critical)
    • ⚠️ Final stack depth - Non-zero final depth (yellow, warning)
    • ❌ Stack underflow - Popping from empty stack (red, critical)

    Comparison with formatInstructions

    FeatureprettyPrint()formatInstructions()
    ANSI colors
    Gas costs
    Stack depth
    Block boundaries
    Jump arrows
    Fusion detection
    Summary stats
    Array output
    Parseable
    Use prettyPrint() for:
    • Debugging and visualization
    • Terminal output
    • Understanding control flow
    • Gas analysis
    • Educational purposes
    Use formatInstructions() for:
    • Programmatic parsing
    • Simple disassembly
    • Integration with other tools
    • Machine-readable output

    Advanced Usage

    Debug Specific Range

    // Extract and print specific block
    const instructions = code.parseInstructions();
    const blockInsts = instructions.filter(inst =>
      inst.position >= 100 && inst.position < 150
    );
    
    // Create temporary bytecode from range
    const range = code.raw().slice(100, 150);
    const blockCode = Bytecode(range);
    console.log(blockCode.prettyPrint());
    

    Compare Bytecode Versions

    const v1 = Bytecode(contractV1Bytecode);
    const v2 = Bytecode(contractV2Bytecode);
    
    console.log("=== Version 1 ===");
    console.log(v1.prettyPrint({ colors: false }));
    
    console.log("\n=== Version 2 ===");
    console.log(v2.prettyPrint({ colors: false }));
    

    Extract Function Bodies

    // Find function dispatch pattern
    const functions = new Map<number, number>();
    for (const inst of code.scan({ detectFusions: true })) {
      if (inst.type === 'function_dispatch') {
        functions.set(inst.selector, Number(inst.target));
      }
    }
    
    // Print each function
    functions.forEach((target, selector) => {
      console.log(`\n=== Function 0x${selector.toString(16)} ===`);
    
      // Find block ending at target
      const blocks = code.analyzeBlocks();
      const funcBlock = blocks.find(b => b.startPc === target);
    
      if (funcBlock) {
        const funcCode = code.raw().slice(funcBlock.startPc, funcBlock.endPc);
        console.log(Bytecode(funcCode).prettyPrint());
      }
    });
    

    Integration with Logger

    import { createLogger } from 'your-logger';
    
    const logger = createLogger();
    
    // Conditional pretty print based on log level
    if (logger.level === 'debug') {
      logger.debug('Bytecode analysis:\n' + code.prettyPrint());
    } else {
      logger.info(`Bytecode size: ${code.size()} bytes`);
    }
    

    Terminal Compatibility

    Color Support Detection

    import { supportsColor } from 'supports-color';
    
    const useColors = supportsColor.stdout !== false;
    
    const output = code.prettyPrint({ colors: useColors });
    

    Width Detection

    const termWidth = process.stdout.columns || 120;
    
    const output = code.prettyPrint({
      maxWidth: termWidth,
      compact: termWidth < 100
    });
    

    Unicode Fallback

    If terminal doesn’t support Unicode box-drawing characters:
    const output = code.prettyPrint({ unicode: false });
    
    // Uses ASCII instead:
    // +======================================+
    // |     EVM Bytecode Disassembly         |
    // +======================================+
    

    Performance

    Pretty printing is optimized for readability over speed:
    • Small bytecode (<1KB): <5ms
    • Medium bytecode (10KB): ~50ms
    • Large bytecode (24KB): ~120ms
    Use formatInstructions() if performance is critical and visual output not needed.
    TypeScript-only: Pretty printing requires complex string formatting with ANSI colors and Unicode drawing. Use simpler APIs like Scanner in Zig for bytecode iteration.

    See Also