gas_analysis/
gas_analysis.rs

1//! Complete examples of using EOT's gas analysis capabilities
2//!
3//! This file demonstrates the gas analysis features for general EVM development:
4//! - Dynamic gas cost calculation
5//! - Gas optimization analysis and recommendations  
6//! - Fork comparison and EIP impact analysis
7//! - Bytecode efficiency analysis
8
9use eot::*;
10
11fn main() -> Result<(), Box<dyn std::error::Error>> {
12    println!("šŸ”„ EOT Gas Analysis Examples\n");
13
14    // Example 1: Basic gas analysis
15    basic_gas_analysis()?;
16    println!("\n{}", "=".repeat(60));
17
18    // Example 2: Dynamic gas calculation with context
19    dynamic_gas_calculation()?;
20    println!("\n{}", "=".repeat(60));
21
22    // Example 3: ERC-20 transfer analysis
23    analyze_erc20_transfer()?;
24    println!("\n{}", "=".repeat(60));
25
26    // Example 4: Fork comparison and EIP impact
27    fork_comparison_analysis()?;
28    println!("\n{}", "=".repeat(60));
29
30    // Example 5: Gas optimization analysis
31    gas_optimization_analysis()?;
32    println!("\n{}", "=".repeat(60));
33
34    // Example 6: Bytecode efficiency comparison
35    bytecode_efficiency_comparison()?;
36
37    Ok(())
38}
39
40/// Example 1: Basic gas analysis using the enhanced traits
41fn basic_gas_analysis() -> Result<(), Box<dyn std::error::Error>> {
42    println!("šŸ“Š Example 1: Basic Gas Analysis");
43
44    // Create a simple bytecode sequence
45    let opcodes = vec![
46        0x60, 0x10, // PUSH1 0x10
47        0x60, 0x20, // PUSH1 0x20
48        0x01, // ADD
49        0x60, 0x00, // PUSH1 0x00
50        0x52, // MSTORE
51        0x60, 0x20, // PUSH1 0x20
52        0x60, 0x00, // PUSH1 0x00
53        0xf3, // RETURN
54    ];
55
56    // Analyze gas usage using the registry
57    use crate::traits::OpcodeAnalysis;
58    let analysis = OpcodeRegistry::analyze_gas_usage(&opcodes, Fork::London);
59
60    println!("Bytecode analysis:");
61    println!("  Total gas: {} gas", analysis.total_gas);
62    println!("  Efficiency score: {}/100", analysis.efficiency_score());
63    println!("  Opcodes analyzed: {}", analysis.breakdown.len());
64
65    // Show gas breakdown
66    println!("\nGas breakdown:");
67    for (opcode, gas_cost) in &analysis.breakdown {
68        println!("  0x{:02x}: {} gas", opcode, gas_cost);
69    }
70
71    // Get optimization suggestions
72    let suggestions = OpcodeRegistry::get_optimization_suggestions(&opcodes, Fork::Shanghai);
73    if !suggestions.is_empty() {
74        println!("\nOptimization suggestions:");
75        for (i, suggestion) in suggestions.iter().enumerate() {
76            println!("  {}. {}", i + 1, suggestion);
77        }
78    }
79
80    // Validate the sequence
81    match OpcodeRegistry::validate_opcode_sequence(&opcodes, Fork::London) {
82        Ok(()) => println!("\nāœ… Opcode sequence is valid"),
83        Err(e) => println!("\nāŒ Validation error: {}", e),
84    }
85
86    Ok(())
87}
88
89/// Example 2: Dynamic gas calculation with execution context
90fn dynamic_gas_calculation() -> Result<(), Box<dyn std::error::Error>> {
91    println!("⚔ Example 2: Dynamic Gas Calculation");
92
93    let calculator = DynamicGasCalculator::new(Fork::Berlin);
94
95    // Create different execution contexts
96    let cold_context = ExecutionContext::new();
97    let mut warm_context = ExecutionContext::new();
98
99    // Pre-warm a storage slot - use fixed-size arrays
100    let address = [0x12u8; 20]; // Fixed-size array for address
101    let storage_key = {
102        let mut key = [0u8; 32]; // Fixed-size array for storage key
103        key[24..32].copy_from_slice(&0x123u64.to_be_bytes()); // Put the value in the last 8 bytes
104        key
105    };
106    warm_context.mark_storage_accessed(&address, &storage_key);
107
108    // Test SLOAD with cold vs warm access
109    println!("SLOAD gas costs (EIP-2929 impact):");
110
111    let cold_cost = calculator.calculate_gas_cost(0x54, &cold_context, &[0x123])?;
112    let warm_cost = calculator.calculate_gas_cost(0x54, &warm_context, &[0x123])?;
113
114    println!("  Cold access: {} gas", cold_cost);
115    println!("  Warm access: {} gas", warm_cost);
116    println!(
117        "  Savings from warming: {} gas ({:.1}%)",
118        cold_cost - warm_cost,
119        (cold_cost - warm_cost) as f64 / cold_cost as f64 * 100.0
120    );
121
122    // Test memory expansion costs
123    println!("\nMemory expansion costs:");
124    let small_memory = calculator.calculate_gas_cost(0x52, &cold_context, &[32])?; // MSTORE at 32
125    let large_memory = calculator.calculate_gas_cost(0x52, &cold_context, &[10000])?; // MSTORE at 10000
126
127    println!("  Small memory access: {} gas", small_memory);
128    println!("  Large memory access: {} gas", large_memory);
129    println!(
130        "  Memory expansion overhead: {} gas",
131        large_memory - small_memory
132    );
133
134    Ok(())
135}
136
137/// Example 3: Analyze an ERC-20 transfer function
138fn analyze_erc20_transfer() -> Result<(), Box<dyn std::error::Error>> {
139    println!("šŸ’° Example 3: ERC-20 Transfer Analysis");
140
141    let calculator = DynamicGasCalculator::new(Fork::London);
142
143    // Simplified ERC-20 transfer sequence
144    let transfer_sequence = vec![
145        (0x54, vec![0x1001]),        // SLOAD - sender balance
146        (0x54, vec![0x1002]),        // SLOAD - receiver balance
147        (0x03, vec![]),              // SUB - subtract from sender
148        (0x01, vec![]),              // ADD - add to receiver
149        (0x55, vec![0x1001, 0x100]), // SSTORE - update sender balance (key, value)
150        (0x55, vec![0x1002, 0x200]), // SSTORE - update receiver balance (key, value)
151        (0xa1, vec![0x40, 0x20]),    // LOG1 - Transfer event (offset, size)
152    ];
153
154    let analysis = calculator.analyze_sequence_gas(&transfer_sequence)?;
155
156    println!("ERC-20 Transfer Gas Analysis:");
157    println!("  Total gas: {} gas", analysis.total_gas);
158    println!("  Base transaction: 21,000 gas");
159    println!("  Transfer logic: {} gas", analysis.total_gas - 21000);
160
161    // Analyze the most expensive operations
162    let expensive_ops = analysis.top_expensive_operations(3);
163    println!("\nMost expensive operations:");
164    for (i, (opcode, cost)) in expensive_ops.iter().enumerate() {
165        let name = match *opcode {
166            0x54 => "SLOAD",
167            0x55 => "SSTORE",
168            0xa1 => "LOG1",
169            _ => "OTHER",
170        };
171        println!("  {}. {}: {} gas", i + 1, name, cost);
172    }
173
174    // Show optimization opportunities
175    if !analysis.optimizations.is_empty() {
176        println!("\nOptimization opportunities:");
177        for (i, opt) in analysis.optimizations.iter().enumerate() {
178            println!("  {}. {}", i + 1, opt);
179        }
180    }
181
182    Ok(())
183}
184
185/// Example 4: Compare gas costs across forks and analyze EIP impact
186fn fork_comparison_analysis() -> Result<(), Box<dyn std::error::Error>> {
187    println!("šŸ“ Example 4: Fork Comparison & EIP Impact");
188
189    // Compare specific opcodes across forks
190    let opcodes_to_compare = vec![
191        (0x54, "SLOAD"),
192        (0x31, "BALANCE"),
193        (0x3b, "EXTCODESIZE"),
194        (0xf1, "CALL"),
195    ];
196
197    let forks = vec![
198        Fork::Istanbul,
199        Fork::Berlin,
200        Fork::London,
201        Fork::Shanghai,
202        Fork::Cancun,
203    ];
204
205    println!("Gas cost evolution across forks:");
206    println!(
207        "{:<12} {:<8} {:<8} {:<8} {:<8} {:<8}",
208        "Opcode", "Istanbul", "Berlin", "London", "Shanghai", "Cancun"
209    );
210    println!("{}", "-".repeat(60));
211
212    for (opcode, name) in opcodes_to_compare {
213        print!("{:<12}", name);
214        for fork in &forks {
215            let calculator = DynamicGasCalculator::new(*fork);
216            let context = ExecutionContext::new();
217
218            match calculator.calculate_gas_cost(opcode, &context, &[0x123]) {
219                Ok(cost) => print!(" {:<8}", cost),
220                Err(_) => print!(" {:<8}", "N/A"),
221            }
222        }
223        println!();
224    }
225
226    // Analyze changes between Berlin and pre-Berlin (EIP-2929 impact)
227    println!("\nEIP-2929 Impact Analysis (Istanbul → Berlin):");
228    use crate::gas::GasComparator;
229    let changes = GasComparator::get_changes_between_forks(Fork::Istanbul, Fork::Berlin);
230
231    for change in changes.iter().take(5) {
232        if let (Some(old), Some(new)) = (change.old_value, change.new_value) {
233            println!(
234                "  0x{:02x}: {} → {} gas ({:+} gas)",
235                change.opcode,
236                old,
237                new,
238                new as i32 - old as i32
239            );
240        }
241    }
242
243    // Generate comparison report
244    let report = GasComparator::generate_comparison_report(Fork::Istanbul, Fork::Berlin);
245    println!("\nFork comparison summary:");
246    println!(
247        "  Opcodes with gas changes: {}",
248        report.summary.gas_cost_changes
249    );
250    println!("  Gas increases: {}", report.summary.gas_increases);
251    println!("  Gas decreases: {}", report.summary.gas_decreases);
252
253    Ok(())
254}
255
256/// Example 5: Gas optimization analysis
257fn gas_optimization_analysis() -> Result<(), Box<dyn std::error::Error>> {
258    println!("šŸ”§ Example 5: Gas Optimization Analysis");
259
260    // Original inefficient contract that reads the same storage slot multiple times
261    let original_contract = vec![
262        (0x54, vec![0x100]),       // SLOAD slot 0x100
263        (0x01, vec![]),            // ADD with something
264        (0x54, vec![0x100]),       // SLOAD same slot again (inefficient!)
265        (0x02, vec![]),            // MUL
266        (0x54, vec![0x100]),       // SLOAD same slot third time!
267        (0x03, vec![]),            // SUB
268        (0x55, vec![0x200, 0x42]), // SSTORE result (key, value)
269    ];
270
271    // Optimized version that caches the storage value
272    let optimized_contract = vec![
273        (0x54, vec![0x100]),       // SLOAD slot 0x100 once
274        (0x80, vec![]),            // DUP1 - duplicate the value
275        (0x01, vec![]),            // ADD
276        (0x81, vec![]),            // DUP2 - use cached value
277        (0x02, vec![]),            // MUL
278        (0x82, vec![]),            // DUP3 - use cached value again
279        (0x03, vec![]),            // SUB
280        (0x55, vec![0x200, 0x42]), // SSTORE result (key, value)
281    ];
282
283    let calculator = DynamicGasCalculator::new(Fork::London);
284
285    let original_analysis = calculator.analyze_sequence_gas(&original_contract)?;
286    let optimized_analysis = calculator.analyze_sequence_gas(&optimized_contract)?;
287
288    println!("Original contract:");
289    println!("  Total gas: {}", original_analysis.total_gas);
290    println!(
291        "  Efficiency score: {}",
292        original_analysis.efficiency_score()
293    );
294
295    println!("\nOptimized contract:");
296    println!("  Total gas: {}", optimized_analysis.total_gas);
297    println!(
298        "  Efficiency score: {}",
299        optimized_analysis.efficiency_score()
300    );
301
302    let savings = original_analysis.total_gas - optimized_analysis.total_gas;
303    let savings_percent = (savings as f64 / original_analysis.total_gas as f64) * 100.0;
304
305    println!("\nOptimization results:");
306    println!("  Gas saved: {} ({:.1}%)", savings, savings_percent);
307
308    if savings > 0 {
309        println!("  āœ… Optimization successful!");
310    } else {
311        println!("  āŒ Optimization made things worse!");
312    }
313
314    Ok(())
315}
316
317/// Example 6: Compare bytecode efficiency between different implementations
318fn bytecode_efficiency_comparison() -> Result<(), Box<dyn std::error::Error>> {
319    println!("āš–ļø  Example 6: Bytecode Efficiency Comparison");
320
321    let calculator = DynamicGasCalculator::new(Fork::Shanghai);
322
323    // Different ways to push zero onto the stack
324    let push_zero_old = vec![(0x60, vec![0])]; // PUSH1 0x00
325    let push_zero_new = vec![(0x5f, vec![])]; // PUSH0 (Shanghai+)
326
327    // Different ways to check if value is zero
328    let iszero_simple = vec![
329        (0x15, vec![]), // ISZERO
330    ];
331    let iszero_complex = vec![
332        (0x80, vec![]), // DUP1
333        (0x80, vec![]), // DUP1
334        (0x14, vec![]), // EQ (compare with itself)
335        (0x15, vec![]), // ISZERO
336    ];
337
338    println!("Efficiency comparison results:");
339
340    // Compare PUSH implementations
341    let old_push_analysis = calculator.analyze_sequence_gas(&push_zero_old)?;
342    let new_push_analysis = calculator.analyze_sequence_gas(&push_zero_new)?;
343
344    println!("\nPush zero implementations:");
345    println!("  PUSH1 0x00: {} gas", old_push_analysis.total_gas);
346    println!("  PUSH0:      {} gas", new_push_analysis.total_gas);
347    println!(
348        "  Savings:    {} gas",
349        old_push_analysis.total_gas - new_push_analysis.total_gas
350    );
351
352    // Compare zero-check implementations
353    let simple_analysis = calculator.analyze_sequence_gas(&iszero_simple)?;
354    let complex_analysis = calculator.analyze_sequence_gas(&iszero_complex)?;
355
356    println!("\nZero-check implementations:");
357    println!(
358        "  Simple ISZERO:  {} gas (efficiency: {}%)",
359        simple_analysis.total_gas,
360        simple_analysis.efficiency_score()
361    );
362    println!(
363        "  Complex check:  {} gas (efficiency: {}%)",
364        complex_analysis.total_gas,
365        complex_analysis.efficiency_score()
366    );
367
368    // General recommendations
369    println!("\nšŸ“‹ General Optimization Recommendations:");
370    println!("  1. Use PUSH0 instead of PUSH1 0x00 (Shanghai+)");
371    println!("  2. Avoid redundant DUP operations");
372    println!("  3. Cache storage reads when accessing the same slot multiple times");
373    println!("  4. Use events instead of storage for data that doesn't need querying");
374    println!("  5. Consider using newer opcodes for better efficiency");
375
376    Ok(())
377}