Try it Live Run Bytecode examples in the interactive playground
GasAnalysis Type
interface GasAnalysis {
/** Total gas for sequential execution */
total : bigint
/** Per-instruction gas breakdown */
byInstruction : InstructionGas []
/** Per-block gas breakdown */
byBlock : BlockGas []
/** Top 10 most expensive instructions */
expensive : ExpensiveInstruction []
/** Alternative execution paths (if control flow analyzed) */
paths ?: {
cheapest : ExecutionPath
mostExpensive : ExecutionPath
average : bigint
}
}
InstructionGas
interface InstructionGas {
pc : number // Program counter
opcode : string // Instruction mnemonic
gas : number // Base gas cost
cumulative : bigint // Cumulative gas up to this point
}
BlockGas
interface BlockGas {
blockIndex : number // Block number
startPc : number // First instruction PC
endPc : number // Last instruction PC (exclusive)
gas : number // Total block gas
percentage : number // % of total gas
}
ExpensiveInstruction
interface ExpensiveInstruction {
pc : number // Program counter
opcode : string // Instruction mnemonic
gas : number // Gas cost
category : string // "storage" | "memory" | "call" | etc.
}
ExecutionPath
interface ExecutionPath {
blocks : number [] // Block indices in path
gas : bigint // Total gas for path
instructions : number // Instruction count
}
Options
interface GasAnalysisOptions {
/** Include control flow path analysis (default: false) */
analyzePaths ?: boolean
/** Maximum paths to explore (default: 100) */
maxPaths ?: number
/** Include dynamic gas estimates (CALL, SSTORE, etc.) (default: false) */
includeDynamic ?: boolean
/** Context for dynamic gas (EIP-2929, etc.) */
context ?: {
warmAddresses ?: Set < string >
warmSlots ?: Set < bigint >
}
}
Usage Patterns
Basic Gas Estimation
const code = Bytecode ( "0x600160020160805260206080f3" );
// PUSH1 1, PUSH1 2, ADD, PUSH1 0x80, MSTORE, PUSH1 0x20, PUSH1 0x80, RETURN
const analysis = code . analyzeGas ();
console . log ( `Total gas: ${ analysis . total } ` );
// Output: Total gas: 27 (3+3+3+3+3+3+3+3+3)
Instruction-Level Breakdown
const analysis = code . analyzeGas ();
analysis . byInstruction . forEach ( inst => {
console . log ( ` ${ inst . pc . toString (). padStart ( 4 ) } : ${ inst . opcode . padEnd ( 10 ) } ${ inst . gas } gas (cumulative: ${ inst . cumulative } )` );
});
// Output:
// 0: PUSH1 3 gas (cumulative: 3)
// 2: PUSH1 3 gas (cumulative: 6)
// 4: ADD 3 gas (cumulative: 9)
// 5: PUSH1 3 gas (cumulative: 12)
// 7: MSTORE 3 gas (cumulative: 15)
// ...
Block-Level Analysis
const analysis = code . analyzeGas ();
analysis . byBlock . forEach ( block => {
console . log ( `Block ${ block . blockIndex } : ${ block . gas } gas ( ${ block . percentage . toFixed ( 1 ) } %)` );
console . log ( ` PC ${ block . startPc } - ${ block . endPc } ` );
});
// Output:
// Block 0: 15 gas (55.6%)
// PC 0 - 9
// Block 1: 12 gas (44.4%)
// PC 9 - 17
Identify Expensive Operations
const analysis = code . analyzeGas ();
console . log ( "Top 10 most expensive instructions:" );
analysis . expensive . forEach (( inst , i ) => {
console . log ( ` ${ i + 1 } . ${ inst . opcode } at PC ${ inst . pc } : ${ inst . gas } gas ( ${ inst . category } )` );
});
// Output:
// 1. SSTORE at PC 45: 20000 gas (storage)
// 2. CALL at PC 123: 700 gas (call)
// 3. SLOAD at PC 12: 100 gas (storage)
Path Analysis
const analysis = code . analyzeGas ({ analyzePaths: true });
if ( analysis . paths ) {
console . log ( `Cheapest path: ${ analysis . paths . cheapest . gas } gas` );
console . log ( ` Blocks: ${ analysis . paths . cheapest . blocks . join ( ' → ' ) } ` );
console . log ( `Most expensive path: ${ analysis . paths . mostExpensive . gas } gas` );
console . log ( ` Blocks: ${ analysis . paths . mostExpensive . blocks . join ( ' → ' ) } ` );
console . log ( `Average: ${ analysis . paths . average } gas` );
}
// Output:
// Cheapest path: 21347 gas
// Blocks: 0 → 1 → 5
// Most expensive path: 45123 gas
// Blocks: 0 → 1 → 2 → 3 → 5
// Average: 32456 gas
Dynamic Gas Estimation
const analysis = code . analyzeGas ({
includeDynamic: true ,
context: {
warmAddresses: new Set ([ '0x742d35Cc6634C0532925a3b844Bc9e7595f51e3e' ]),
warmSlots: new Set ([ 0 n , 1 n ])
}
});
// SLOAD/SSTORE costs depend on warm/cold access (EIP-2929)
// Warm: 100 gas, Cold: 2100 gas
EIP-3860 Initcode Gas
Calculate initcode gas (2 gas per 32-byte word):
const deploymentCode = Bytecode ( "0x608060405234801561001057600080fd5b..." );
const initcodeGas = Bytecode . calculateInitcodeGas ( deploymentCode . size ());
console . log ( `Initcode gas: ${ initcodeGas } ` );
// For 500-byte deployment code:
// 500 bytes ÷ 32 = 15.625 words → 16 words
// 16 × 2 = 32 gas
Post-Shanghai, initcode costs 2 gas per 32-byte word. Include this when deploying contracts.
Gas Cost Categories
Instructions grouped by gas cost category:
Base Cost (3 gas)
Stack: PUSH, DUP, SWAP, POP
Arithmetic: ADD, SUB, MUL
Comparison: LT, GT, EQ
Bitwise: AND, OR, XOR, NOT
Memory Expansion (3+ gas)
MLOAD, MSTORE, MSTORE8: 3 gas + expansion cost
Expansion: 3 * new_words + (new_words)² ÷ 512
Storage (2100/20000 gas)
SLOAD: 100 gas (warm) / 2100 gas (cold)
SSTORE: 100/2900/20000 gas (depends on change)
Calls (700+ gas)
CALL, DELEGATECALL, STATICCALL: 700 base + value transfer + memory expansion
CREATE, CREATE2: 32000 gas + deployment gas
Hashing
KECCAK256: 30 gas + 6 gas per word
SHA256, RIPEMD160: 60 gas + 12 gas per word (precompiles)
Expensive Operations
EXP: 10 gas + 50 gas per byte of exponent
SELFDESTRUCT: 5000 gas + refund
Limitations
Gas estimation is based on static analysis and may not reflect actual execution costs:
Dynamic jumps - Multiple possible paths
Loop gas - Cannot determine iteration count
External calls - Called contract gas unknown
Memory expansion - Depends on runtime values
Storage access - Warm/cold status runtime-dependent
Use includeDynamic: true with context for better estimates, but always test on-chain.
What’s Included
✅ Base opcode costs (ADD, MUL, PUSH, etc.)
✅ JUMPDEST costs (1 gas each)
✅ Block-level aggregation
✅ Sequential execution path
✅ Multiple path exploration (with analyzePaths)
What’s Estimated
⚠️ Memory expansion (requires runtime sizes)
⚠️ Storage access (warm/cold requires context)
⚠️ Call stipend and value transfer
⚠️ CREATE deployment gas
What’s Not Included
❌ Actual loop iteration counts
❌ External contract execution costs
❌ Precompile gas (except fixed-cost ones)
❌ Gas refunds (SELFDESTRUCT, SSTORE)
Optimization Hints
Identify gas optimization opportunities:
const analysis = code . analyzeGas ();
// Find expensive blocks
const expensiveBlocks = analysis . byBlock
. filter ( block => block . gas > 10000 )
. sort (( a , b ) => b . gas - a . gas );
console . log ( "Expensive blocks to optimize:" );
expensiveBlocks . forEach ( block => {
console . log ( `Block ${ block . blockIndex } : ${ block . gas } gas ( ${ block . percentage } %)` );
});
// Find repeated expensive operations
const opcodeGas = new Map < string , number >();
analysis . byInstruction . forEach ( inst => {
opcodeGas . set ( inst . opcode , ( opcodeGas . get ( inst . opcode ) || 0 ) + inst . gas );
});
const sorted = Array ( opcodeGas . entries ())
. sort (( a , b ) => b [ 1 ] - a [ 1 ]);
console . log ( " \n Gas by opcode:" );
sorted . slice ( 0 , 10 ). forEach (([ opcode , gas ]) => {
console . log ( ` ${ opcode } : ${ gas } gas total` );
});
Integration with Opcode Module
Gas costs come from Opcode.getGasCost() :
import * as Opcode from 'tevm/Opcode' ;
const addCost = Opcode . getGasCost ( 'ADD' ); // 3
const sloadCost = Opcode . getGasCost ( 'SLOAD' ); // 100 (warm)
const callCost = Opcode . getGasCost ( 'CALL' ); // 700 (base)
See Also