Skip to content

[executor] simulate() omits gas field — simulation cannot validate gas_limit; OOG false-pass #161

@obchain

Description

@obchain

PR: #41 (feat/executor: transaction builder + eth_call simulator)
File: crates/charon-executor/src/simulation.rs — simulate()
Refs #41

Problem

The eth_call request in simulate() has no gas field:

let req = TransactionRequest::default()
    .from(self.sender)
    .to(self.liquidator)
    .input(calldata.into());

Without gas, the BSC node defaults to the block gas limit (~30M) for the call. The simulation succeeds even if the gas_limit set in build_tx is insufficient to complete the flash-loan callback (Venus liquidateBorrow + vToken redemption + PancakeSwap V3 swap costs roughly 600k–900k gas). A liquidation passes simulation, gets signed, then reverts on-chain with OOG in the Aave callback. Aave reverts the entire transaction. No funds are lost but the opportunity is wasted.

CLAUDE.md safety invariant

"Every liquidation transaction passes an eth_call simulation gate before broadcast." A gate that ignores gas does not faithfully simulate what broadcast will execute.

Fix

Accept gas_limit in simulate() and forward it to the request:

pub async fn simulate<P>(
    &self,
    provider: &P,
    calldata: Bytes,
    gas_limit: u64,
) -> Result<()>
where
    P: Provider,
{
    let req = TransactionRequest::default()
        .from(self.sender)
        .to(self.liquidator)
        .input(calldata.into())
        .with_gas_limit(gas_limit);
    // ...
}

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