Skip to content

[Infrastructure] Gas cost modeling and optimization analysis #80

@Th0rgal

Description

@Th0rgal

Summary

Add gas cost tracking and optimization analysis to enable production deployment readiness and cost-efficient contract verification.

Motivation

Current State:

  • No gas modeling in verification
  • Unknown gas costs for verified contracts
  • No optimization guidance
  • Can't prove gas bounds

Production Need:

  • Gas costs affect deployment viability
  • Users care about transaction costs
  • Optimization opportunities unknown
  • Competitive disadvantage vs hand-written Solidity

Proposed Implementation

Phase 1: Gas Cost Model (from EVMYulLean)

Reuse EVMYulLean's gas model:

-- File: DumbContracts/EVM/GasCosts.lean

import EVMYulLean.EVM.GasCosts

def gasCost : YulStmt → Nat
  | .assign _ _ => 3              -- PUSH, DUP, SWAP
  | .storageLoad _ _ => 100       -- SLOAD (warm)
  | .storageStore _ _ => 100      -- SSTORE (warm, no change)
  | .if_ cond body => 
      gasCost cond + 10 + gasCost body  -- JUMPI
  | .for_ init cond post body =>
      gasCost init + n * (gasCost cond + gasCost body + gasCost post)

Phase 2: Gas Tracking in Compiler

Add gas annotations:

-- File: Compiler/GasAnnotation.lean

structure GasAnnotatedStmt where
  stmt : YulStmt
  estimatedGas : Nat
  worstCaseGas : Nat

def annotateGas : YulStmt → GasAnnotatedStmt := ...

theorem gas_bounded (stmt : YulStmt) :
    actualGas stmt ≤ worstCaseGas (annotateGas stmt)

Phase 3: Optimization Analysis

Identify optimization opportunities:

-- File: Compiler/Optimization/Analysis.lean

inductive Optimization where
  | StoragePackaging : List (Nat × Uint256) → Optimization  -- Pack into fewer slots
  | MemoryReuse : List Identifier → Optimization            -- Reuse memory slots
  | ConstantFolding : YulExpr → Optimization                 -- Fold at compile time
  | DeadCodeElimination : YulStmt → Optimization             -- Remove unused code

def findOptimizations : YulProgram → List Optimization := ...

Prove optimizations preserve semantics:

theorem optimization_preserves_semantics (opt : Optimization) :
    let optimized := applyOptimization opt program
    ∀ state, exec state program = exec state optimized

Phase 4: Gas Profiling in Tests

Add gas measurement:

// test/Gas*.t.sol

function test_transfer_gas() public {
    uint256 gasBefore = gasleft();
    ledger.transfer(recipient, amount);
    uint256 gasUsed = gasBefore - gasleft();
    
    // Assert gas bounds
    assertLt(gasUsed, EXPECTED_GAS_BOUND);
}

Generate gas report:

# scripts/gas_report.py

def generate_gas_report():
    """Generate gas usage report for all functions"""
    for contract in contracts:
        for function in contract.functions:
            print(f"{function}: {avg_gas} ± {std_dev}")

Gas Cost Categories

Storage Operations (Most Expensive)

Operation Cold Cost Warm Cost Note
SLOAD 2,100 100 First access vs subsequent
SSTORE (zero→non-zero) 20,000 - Most expensive
SSTORE (non-zero→non-zero) 5,000 - Modify existing
SSTORE (non-zero→zero) -15,000 - Gas refund

Memory Operations

Operation Cost Note
MLOAD 3 Load from memory
MSTORE 3 Store to memory
Memory expansion 3 + quadratic Increases with size

Computation

Operation Cost Note
ADD, SUB, MUL 3-5 Basic arithmetic
DIV, MOD 5 Division
SHA3 (KECCAK) 30 + 6/word Hash function
ECRECOVER 3,000 Signature verification

Optimization Strategies

Strategy 1: Storage Packing

Problem: Each storage slot costs ~2,100 gas (cold)

Solution: Pack multiple values in one slot

-- Before: 3 slots (6,300 gas cold)
slot0: totalSupply (256 bits)
slot1: paused (8 bits)  -- Waste!
slot2: owner (160 bits) -- Waste!

-- After: 1 slot (2,100 gas cold)
slot0: [totalSupply (96 bits) | paused (8 bits) | owner (160 bits)]
-- Savings: 4,200 gas

Strategy 2: Memory Reuse

Problem: Memory expansion is quadratic

Solution: Reuse memory slots

-- Reuse temporary memory locations
let temp1 := mload(0x00)
-- ... use temp1 ...
let temp2 := mload(0x00)  -- Reuse same slot

Strategy 3: Constant Folding

Problem: Runtime computation costs gas

Solution: Compute at compile time

-- Before: Runtime computation
let x := add(mul(5, 3), 7)  -- 3 + 5 = 8 gas

-- After: Compile-time fold
let x := 22  -- 3 gas
-- Savings: 5 gas per occurrence

Strategy 4: Short-Circuit Evaluation

Problem: Expensive checks execute even when unnecessary

Solution: Order checks by cost (cheap first)

-- Before:
require(expensiveCheck() && cheapCheck())  -- Both execute

-- After:
require(cheapCheck() && expensiveCheck())  -- Short-circuit if cheap fails

Benefits

✅ Production deployment readiness
✅ Cost-competitive with hand-written Solidity
✅ Optimization opportunities identified
✅ Proven gas bounds (safety property)
✅ User confidence (predictable costs)

Implementation Plan

Phase 1: Gas Model (1 week)

  • Import EVMYulLean gas costs
  • Implement gas tracking

Phase 2: Compiler Integration (2 weeks)

  • Annotate generated Yul
  • Prove gas bounds

Phase 3: Optimization (2 weeks)

  • Implement optimizations
  • Prove semantic preservation

Phase 4: Testing & Reporting (1 week)

  • Gas profiling tests
  • Generate reports
  • Documentation

Files Affected

Create:

  • DumbContracts/EVM/GasCosts.lean
  • Compiler/GasAnnotation.lean
  • Compiler/Optimization/Analysis.lean
  • scripts/gas_report.py
  • test/Gas*.t.sol

Update:

  • Compiler/YulGeneration.lean (add gas annotations)

Dependencies

Estimated Effort

6 weeks

Success Criteria

  • Gas costs tracked for all operations
  • Gas bounds proven
  • Optimizations preserve semantics
  • Gas reports generated
  • Competitive with hand-written Solidity

Related Issues

Future Work

  • Auto-optimization (apply proven optimizations)
  • Gas budgeting (fail compilation if too expensive)
  • Gas attack resistance (prove DoS bounds)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions