Skip to content

Implement gas estimation for UserOperations #32

Description

@ch4r10t33r

Title

Implement gas estimation for UserOperations

Labels

enhancement, account-abstraction, gas, estimation

Description

Add accurate gas estimation for UserOperations, accounting for all ERC-4337 components including verification, execution, and paymaster gas.

Requirements

UserOperation Gas Components

  • Estimate callGasLimit:

    • Simulate main execution call
    • Add buffer for safety (10-20%)
  • Estimate verificationGasLimit:

    • Account's validateUserOp gas cost
    • Handle different account implementations
    • Standard accounts: ~100k gas
    • Complex validation: higher limits
  • Calculate preVerificationGas:

    • Fixed overhead for bundler
    • calldata cost calculation
    • Signature length consideration
    • Formula: 21000 + calldataGas + bundlerOverhead
  • Estimate paymaster gas (if used):

    • paymasterVerificationGasLimit
    • paymasterPostOpGasLimit

Estimation Strategies

  • Via Bundler RPC:

    • Use eth_estimateUserOperationGas
    • Parse bundler response
    • Apply safety margins
  • Local Simulation:

    • Fallback if bundler doesn't support estimation
    • Simulate via eth_call to EntryPoint
    • Extract gas usage from trace
  • Heuristic-based:

    • Default values for known account types
    • Simple account: 100k verification, based on call for execution
    • Configurable defaults

Gas Optimization

  • Add optimization helpers:
    • optimizeUserOpGas(userOp: *UserOperation) - Reduce gas where safe
    • Check if init_code is needed
    • Minimize signature size
    • Optimize calldata packing

Estimation Middleware

  • Create UserOpGasMiddleware:
    • Automatic gas estimation before submission
    • Caching of estimation results
    • Dynamic adjustment based on network conditions

Gas Calculation Formulas

// Pre-verification gas
pub fn calculatePreVerificationGas(
    userOp: UserOperation,
    bundler_overhead: u256,
) u256 {
    const calldata_cost = calcdataGasCost(userOp);
    return 21000 + calldata_cost + bundler_overhead;
}

// Calldata gas cost
fn calcdataGasCost(userOp: UserOperation) u256 {
    var cost: u256 = 0;
    const packed = packUserOp(userOp);
    for (packed) |byte| {
        cost += if (byte == 0) 4 else 16;
    }
    return cost;
}

File Structure

src/account_abstraction/
  ├── gas_estimation.zig       # Core gas estimation logic
  └── gas_middleware.zig       # Middleware for auto-estimation

Example Usage

// Automatic estimation
var userOp = try UserOperationBuilder.init(allocator)
    .setSender(account)
    .setCallData(data)
    .estimateGas(provider, entrypoint)  // <-- automatic
    .build();

// Manual estimation
const gas_estimate = try estimateUserOperationGas(
    provider,
    userOp,
    entrypoint_address,
);
userOp.callGasLimit = gas_estimate.callGasLimit;
userOp.verificationGasLimit = gas_estimate.verificationGasLimit;
userOp.preVerificationGas = gas_estimate.preVerificationGas;

References

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