Skip to content

[executor] estimate_gas_units has no safety margin — flash-loan liquidation can OOG on-chain #182

@obchain

Description

@obchain

Summary

estimate_gas_units is a direct pass-through to eth_estimateGas with no multiplier applied to the result. The gas estimate is computed against the simulation block's state. Between simulation and on-chain inclusion (1-3 blocks on BSC), the following can change:

  • A partial liquidation by another bot reduces collateral, altering storage-access patterns and adding cold SLOADs
  • A price oracle update writes a new slot
  • Flash-loan protocol re-entrancy guard flag uses additional storage

Industry standard for liquidation bots is to apply a 20-30% gas buffer:

let gas_limit = estimate * 120 / 100;

Without this buffer, the flash-loan callback can OOG mid-execution. In a flash-loan, an OOG revert causes the lender to reclaim the loan atomically, but all gas spent in the callback is consumed. The net effect: gas lost, opportunity missed.

The CLAUDE.md requirement that the simulation gate must not be bypassed implies the gas limit passed to simulation must match the gas limit used for broadcast. An underestimated limit that passes simulation but fails on-chain is a functional bypass of the gate.

File

crates/charon-executor/src/gas.rsestimate_gas_units()

Fix

pub async fn estimate_gas_units(...) -> Result<u64, GasError> {
    let estimate = provider.estimate_gas(&tx).await.map_err(GasError::Provider)?;
    Ok(estimate.saturating_mul(12) / 10)  // 20% buffer
}

Refs #43

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workinglayer:rustRust crates (core / scanner / protocols / executor / cli)pr-reviewFindings from PR review processpriority:p1-coreCore MVP scopestatus:readyScoped and ready to pick up

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions