Try it Live Run Bytecode examples in the interactive playground
Instruction Object
Each iteration yields an Instruction object matching the OpcodeData union type :
interface Instruction {
pc : number // Program counter (byte offset)
type : InstructionType // Instruction category
opcode : Opcode | SyntheticOpcode
size : number // Instruction size in bytes
// Type-specific fields (present based on type)
value ?: bigint // For PUSH instructions
gas ?: number // Gas cost (if withGas option)
stackEffect ?: { // Stack effects (if withStack option)
input : number
output : number
}
}
Options
interface ScanOptions {
/** Include gas cost for each instruction (default: false) */
withGas ?: boolean
/** Include stack effect metadata (default: false) */
withStack ?: boolean
/** Detect and yield fusion patterns (default: true) */
detectFusions ?: boolean
/** Start iteration at specific PC (default: 0) */
startPc ?: number
/** Stop iteration at specific PC (default: end) */
endPc ?: number
}
Usage Patterns
Basic Iteration
// Iterate through all instructions
for ( const inst of code . scan ()) {
console . log ( ` ${ inst . pc } : ${ inst . opcode } ` );
}
With Gas Costs
let totalGas = 0 ;
for ( const inst of code . scan ({ withGas: true })) {
totalGas += inst . gas ;
console . log ( ` ${ inst . opcode } : ${ inst . gas } gas` );
}
console . log ( `Total: ${ totalGas } gas` );
With Stack Tracking
let stackDepth = 0 ;
for ( const inst of code . scan ({ withStack: true })) {
stackDepth -= inst . stackEffect . input ;
stackDepth += inst . stackEffect . output ;
console . log ( ` ${ inst . opcode } : stack depth ${ stackDepth } ` );
}
Filter by Type
// Find all JUMP instructions
const jumps = [];
for ( const inst of code . scan ()) {
if ( inst . type === 'jump' || inst . type === 'jumpi' ) {
jumps . push ( inst );
}
}
Detect PUSH Values
// Extract all PUSH values
const pushValues = [];
for ( const inst of code . scan ()) {
if ( inst . type === 'push' ) {
pushValues . push ({ pc: inst . pc , value: inst . value });
}
}
Fusion Detection
// Find all fusion opportunities
for ( const inst of code . scan ({ detectFusions: true })) {
if ( inst . type . endsWith ( '_fusion' )) {
console . log ( `Fusion at PC ${ inst . pc } : ${ inst . type } ` );
}
}
Partial Iteration
// Scan specific range
for ( const inst of code . scan ({ startPc: 100 , endPc: 200 })) {
// Only processes bytes 100-199
}
Early Exit
// Stop at first JUMPDEST
for ( const inst of code . scan ()) {
if ( inst . type === 'jumpdest' ) {
console . log ( `First JUMPDEST at PC ${ inst . pc } ` );
break ;
}
}
Collect to Array
// Materialize all instructions
const instructions = Array ( code . scan ());
// Or with options
const withGas = Array ( code . scan ({ withGas: true }));
scan() uses lazy iteration for memory efficiency:
Lazy evaluation - Instructions computed on demand
O(1) memory - Only current instruction in memory
Streaming - Supports early exit without parsing entire bytecode
Efficient PUSH handling - Skips over immediate data bytes
Bitmap lookups - O(1) JUMPDEST validation
For large bytecode (10KB+), use scan() instead of parseInstructions() which materializes the entire array.
PUSH Data Handling
scan() correctly handles PUSH immediate data:
const code = Bytecode ( "0x60016002605b" ); // PUSH1 1, PUSH1 2, PUSH1 0x5b
for ( const inst of code . scan ()) {
console . log ( inst );
}
// Output:
// { pc: 0, type: 'push', opcode: 'PUSH1', value: 1n, size: 2 }
// { pc: 2, type: 'push', opcode: 'PUSH1', value: 2n, size: 2 }
// { pc: 4, type: 'push', opcode: 'PUSH1', value: 91n, size: 2 }
// Note: 0x5b (JUMPDEST) in last PUSH is treated as data, not instruction
Fusion Patterns
When detectFusions: true (default), multi-instruction patterns are detected:
const code = Bytecode ( "0x60016001015b" );
// PUSH1 1, PUSH1 1, ADD, JUMPDEST
for ( const inst of code . scan ({ detectFusions: true })) {
console . log ( ` ${ inst . pc } : ${ inst . type } ` );
}
// Output may include:
// 0: push_add_fusion (if PUSH+ADD pattern detected)
// Or:
// 0: push
// 2: push
// 4: regular (ADD)
// 5: jumpdest
See Fusion Detection for all 20+ patterns.
Error Handling
try {
for ( const inst of code . scan ()) {
// Process instruction
}
} catch ( error ) {
if ( error instanceof BytecodeError ) {
console . error ( `Invalid bytecode at PC ${ error . pc } ` );
}
}
scan() validates bytecode structure during iteration. Malformed PUSH instructions (truncated data) will throw during iteration at the problematic position.
Comparison with parseInstructions
Use scan() when:
Processing large bytecode (memory efficiency)
Need early exit capability
Streaming analysis
Real-time disassembly
Use parseInstructions() when:
Need random access to instructions
Multiple passes over same data
Small bytecode (<1KB)
Caching full instruction list
// Memory efficient (lazy)
for ( const inst of code . scan ()) { /* process */ }
// Materializes all instructions
const instructions = code . parseInstructions ();
instructions . forEach ( inst => { /* process */ });
See Also