Try it Live
Run Opcode examples in the interactive playground
Usage Patterns
Real-world examples for EVM bytecode analysis, disassembly, and optimization detection.Bytecode Disassembly
Basic Disassembler
Copy
Ask AI
import * as Opcode from '@tevm/primitives/Opcode'
function disassembleContract(bytecode: Uint8Array): void {
const assembly = Opcode.disassemble(bytecode)
console.log("Disassembly:")
console.log("============")
for (const line of assembly) {
console.log(line)
}
}
// Example output:
// 0000: PUSH1 0x80
// 0002: PUSH1 0x40
// 0004: MSTORE
// 0005: CALLVALUE
// 0006: DUP1
// 0007: ISZERO
Annotated Disassembly
Copy
Ask AI
function annotatedDisassembly(bytecode: Uint8Array): string[] {
const instructions = Opcode.parse(bytecode)
const lines: string[] = []
for (const inst of instructions) {
const name = Opcode.name(inst.opcode) ?? `INVALID_${inst.opcode.toString(16)}`
const info = Opcode.info(inst.opcode)
let line = `${inst.offset.toString(16).padStart(4, '0')}: ${name}`
if (inst.immediate) {
const hex = Array(inst.immediate)
.map(b => b.toString(16).padStart(2, '0'))
.join('')
line += ` 0x${hex}`
}
if (info) {
line += ` // gas: ${info.gasCost}, stack: ${info.stackInputs} → ${info.stackOutputs}`
}
lines.push(line)
}
return lines
}
// Example output:
// 0000: PUSH1 0x80 // gas: 3, stack: 0 → 1
// 0002: PUSH1 0x40 // gas: 3, stack: 0 → 1
// 0004: MSTORE // gas: 3, stack: 2 → 0
Jump Analysis
Build Jump Table
Copy
Ask AI
function buildJumpTable(bytecode: Uint8Array): Map<number, number[]> {
const instructions = Opcode.parse(bytecode)
const jumpTable = new Map<number, number[]>()
const validDests = Opcode.jumpDests(bytecode)
// Mark all valid jump destinations
for (const dest of validDests) {
jumpTable.set(dest, [])
}
// Find all JUMP/JUMPI instructions
// Note: This is simplified - real analysis needs symbolic execution
for (const inst of instructions) {
if (Opcode.isJump(inst.opcode)) {
// In real implementation, would trace stack to find jump target
console.log(`Jump instruction at ${inst.offset}`)
}
}
return jumpTable
}
Validate Jump Destinations
Copy
Ask AI
function validateJumpDestinations(bytecode: Uint8Array): {
valid: boolean
errors: string[]
} {
const validDests = Opcode.jumpDests(bytecode)
const errors: string[] = []
// Check if JUMPDEST instructions are actually reachable
// (simplified - real analysis needs control flow graph)
if (validDests.size === 0) {
errors.push("No JUMPDEST instructions found")
}
return {
valid: errors.length === 0,
errors
}
}
Gas Analysis
Estimate Static Gas Cost
Copy
Ask AI
function estimateStaticGas(bytecode: Uint8Array): bigint {
const instructions = Opcode.parse(bytecode)
let total = 0n
for (const inst of instructions) {
const cost = Opcode.getGasCost(inst.opcode)
if (cost !== undefined) {
total += BigInt(cost)
}
}
return total
}
Find Gas-Intensive Operations
Copy
Ask AI
interface GasHotspot {
offset: number
opcode: BrandedOpcode
name: string
gasCost: number
}
function findGasHotspots(bytecode: Uint8Array, threshold: number = 100): GasHotspot[] {
const instructions = Opcode.parse(bytecode)
const hotspots: GasHotspot[] = []
for (const inst of instructions) {
const cost = Opcode.getGasCost(inst.opcode)
const name = Opcode.name(inst.opcode)
if (cost !== undefined && cost >= threshold && name) {
hotspots.push({
offset: inst.offset,
opcode: inst.opcode,
name,
gasCost: cost
})
}
}
return hotspots.sort((a, b) => b.gasCost - a.gasCost)
}
Stack Analysis
Track Stack Depth
Copy
Ask AI
interface StackTrace {
offset: number
opcode: string
depth: number
effect: number
}
function traceStackDepth(bytecode: Uint8Array): StackTrace[] {
const instructions = Opcode.parse(bytecode)
const trace: StackTrace[] = []
let depth = 0
for (const inst of instructions) {
const effect = Opcode.getStackEffect(inst.opcode) ?? 0
const name = Opcode.name(inst.opcode) ?? "UNKNOWN"
depth += effect
trace.push({
offset: inst.offset,
opcode: name,
depth,
effect
})
// Check for stack violations
if (depth < 0) {
throw new Error(`Stack underflow at offset ${inst.offset}`)
}
if (depth > 1024) {
throw new Error(`Stack overflow at offset ${inst.offset}`)
}
}
return trace
}
Find Maximum Stack Depth
Copy
Ask AI
function findMaxStackDepth(bytecode: Uint8Array): number {
const trace = traceStackDepth(bytecode)
return Math.max(...trace.map(t => t.depth))
}
Pattern Detection
Detect Compiler Patterns
Copy
Ask AI
function detectSolidityConstructor(bytecode: Uint8Array): boolean {
const instructions = Opcode.parse(bytecode)
// Solidity constructors typically start with:
// PUSH1 0x80
// PUSH1 0x40
// MSTORE
if (instructions.length < 3) return false
const [inst1, inst2, inst3] = instructions
return (
inst1.opcode === Opcode.PUSH1 &&
inst1.immediate?.[0] === 0x80 &&
inst2.opcode === Opcode.PUSH1 &&
inst2.immediate?.[0] === 0x40 &&
inst3.opcode === Opcode.MSTORE
)
}
Find Function Selectors
Copy
Ask AI
function findFunctionSelectors(bytecode: Uint8Array): Set<string> {
const instructions = Opcode.parse(bytecode)
const selectors = new Set<string>()
// Look for PUSH4 instructions (4-byte selectors)
for (const inst of instructions) {
if (inst.opcode === Opcode.PUSH4 && inst.immediate) {
const selector = '0x' + Array(inst.immediate)
.map(b => b.toString(16).padStart(2, '0'))
.join('')
selectors.add(selector)
}
}
return selectors
}
Detect Optimization Patterns
Copy
Ask AI
interface OptimizationPattern {
name: string
pattern: BrandedOpcode[]
description: string
}
const OPTIMIZATION_PATTERNS: OptimizationPattern[] = [
{
name: "DUP + SWAP elimination",
pattern: [Opcode.DUP1, Opcode.SWAP1],
description: "Redundant DUP1 followed by SWAP1"
},
{
name: "Double PUSH",
pattern: [Opcode.PUSH1, Opcode.POP],
description: "PUSH immediately followed by POP"
}
]
function findOptimizationOpportunities(bytecode: Uint8Array): string[] {
const instructions = Opcode.parse(bytecode)
const opportunities: string[] = []
for (let i = 0; i < instructions.length - 1; i++) {
for (const pattern of OPTIMIZATION_PATTERNS) {
let matches = true
for (let j = 0; j < pattern.pattern.length && i + j < instructions.length; j++) {
if (instructions[i + j].opcode !== pattern.pattern[j]) {
matches = false
break
}
}
if (matches) {
opportunities.push(
`${pattern.name} at offset ${instructions[i].offset}: ${pattern.description}`
)
}
}
}
return opportunities
}
Code Coverage
Track Executed Instructions
Copy
Ask AI
class BytecodeCoverage {
private executed = new Set<number>()
private instructions: Instruction[]
constructor(bytecode: Uint8Array) {
this.instructions = Opcode.parse(bytecode)
}
markExecuted(offset: number): void {
this.executed.add(offset)
}
getCoveragePercent(): number {
return (this.executed.size / this.instructions.length) * 100
}
getUncoveredInstructions(): Instruction[] {
return this.instructions.filter(inst => !this.executed.has(inst.offset))
}
report(): string {
const covered = this.executed.size
const total = this.instructions.length
const percent = this.getCoveragePercent().toFixed(2)
return `Coverage: ${covered}/${total} instructions (${percent}%)`
}
}
Bytecode Comparison
Compare Bytecode
Copy
Ask AI
function compareBytecode(
bytecode1: Uint8Array,
bytecode2: Uint8Array
): {
identical: boolean
differences: Array<{
offset: number
opcode1: string
opcode2: string
}>
} {
const inst1 = Opcode.parse(bytecode1)
const inst2 = Opcode.parse(bytecode2)
if (inst1.length !== inst2.length) {
return {
identical: false,
differences: [{ offset: -1, opcode1: `${inst1.length} instructions`, opcode2: `${inst2.length} instructions` }]
}
}
const differences: Array<{ offset: number; opcode1: string; opcode2: string }> = []
for (let i = 0; i < inst1.length; i++) {
if (inst1[i].opcode !== inst2[i].opcode) {
differences.push({
offset: inst1[i].offset,
opcode1: Opcode.name(inst1[i].opcode) ?? `0x${inst1[i].opcode.toString(16)}`,
opcode2: Opcode.name(inst2[i].opcode) ?? `0x${inst2[i].opcode.toString(16)}`
})
}
}
return {
identical: differences.length === 0,
differences
}
}
Metadata Extraction
Extract Contract Metadata
Copy
Ask AI
function extractMetadata(bytecode: Uint8Array): {
hasMetadata: boolean
metadataHash?: string
codeSize: number
} {
// Solidity adds metadata at end: 0xa1 0x65 'b' 'z' 'z' 'r' '0' 0x58 0x20 <32 bytes> 0x00 0x29
const codeSize = bytecode.length
// Check for metadata marker
if (bytecode.length < 43) {
return { hasMetadata: false, codeSize }
}
const metadataStart = bytecode.length - 43
if (
bytecode[metadataStart] === 0xa1 &&
bytecode[metadataStart + 1] === 0x65 &&
bytecode[metadataStart + 2] === 0x62 && // 'b'
bytecode[metadataStart + 3] === 0x7a && // 'z'
bytecode[metadataStart + 4] === 0x7a && // 'z'
bytecode[metadataStart + 5] === 0x72 && // 'r'
bytecode[metadataStart + 6] === 0x30 // '0'
) {
const hash = bytecode.slice(metadataStart + 9, metadataStart + 41)
const metadataHash = '0x' + Array(hash)
.map(b => b.toString(16).padStart(2, '0'))
.join('')
return {
hasMetadata: true,
metadataHash,
codeSize: metadataStart
}
}
return { hasMetadata: false, codeSize }
}
Bytecode Analysis Performance: For large contracts (>10KB), consider parsing once and caching the instruction array. Reparsing on every analysis is wasteful.
Control Flow Analysis
Build Basic Blocks
Copy
Ask AI
interface BasicBlock {
start: number
end: number
instructions: Instruction[]
endsWithJump: boolean
endsWithTerminator: boolean
}
function buildBasicBlocks(bytecode: Uint8Array): BasicBlock[] {
const instructions = Opcode.parse(bytecode)
const jumpDests = Opcode.jumpDests(bytecode)
const blocks: BasicBlock[] = []
let blockStart = 0
let blockInstructions: Instruction[] = []
for (let i = 0; i < instructions.length; i++) {
const inst = instructions[i]
blockInstructions.push(inst)
// Block ends at: JUMP, JUMPI, terminator, or before JUMPDEST
const isJump = Opcode.isJump(inst.opcode)
const isTerminator = Opcode.isTerminating(inst.opcode)
const nextIsJumpDest = i < instructions.length - 1 && jumpDests.has(instructions[i + 1].offset)
if (isJump || isTerminator || nextIsJumpDest) {
blocks.push({
start: blockStart,
end: inst.offset,
instructions: blockInstructions,
endsWithJump: isJump,
endsWithTerminator: isTerminator
})
blockInstructions = []
if (i < instructions.length - 1) {
blockStart = instructions[i + 1].offset
}
}
}
// Add remaining instructions as final block
if (blockInstructions.length > 0) {
const lastInst = blockInstructions[blockInstructions.length - 1]
blocks.push({
start: blockStart,
end: lastInst.offset,
instructions: blockInstructions,
endsWithJump: false,
endsWithTerminator: false
})
}
return blocks
}
See Also
- Constructors - Parsing bytecode
- Validation - Opcode validation
- Utilities - Metadata and disassembly
- WASM - Performance considerations

