Try it Live Run Bytecode examples in the interactive playground
╔══════════════════════════════════════╗
║ 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:
Category Color Opcodes PUSH Blue PUSH0-PUSH32 Arithmetic Cyan ADD, SUB, MUL, DIV, MOD, EXP Comparison Magenta LT, GT, EQ, ISZERO Bitwise Bright Cyan AND, OR, XOR, NOT, SHL, SHR Memory Bright Blue MLOAD, MSTORE, MSTORE8 Storage Bright Magenta SLOAD, SSTORE, TLOAD, TSTORE Stack Green DUP1-16, SWAP1-16, POP Jump (JUMP) Yellow JUMP Jump (JUMPI) Gold JUMPI JUMPDEST Green BG JUMPDEST (green background) Flow Control Red STOP, RETURN, REVERT, INVALID System Bright Yellow CALL, 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.
PUSH Values
PUSH1 | | 0x42 (66) [gas: 3]
Shows both hex (0x42) and decimal (66) for values ≤ 0xFFFF.
Gas Costs
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)
Feature prettyPrint() 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 }));
// 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 |
// +======================================+
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