Simulate a Flashbots Bundle With eth_callBundle (2026)

By Merunas Grincalaitis, founder of MEV DAO and a developer who builds MEV, sniping and trading bots on Ethereum. Last updated: October 2026.

TL;DR: To simulate a Flashbots bundle before broadcasting it to Ethereum block builders, send a signed JSON-RPC request calling eth_callBundle to https://relay.flashbots.net (or simulate locally against an Anvil/Reth fork). The eth_callBundle response gives you the exact gasUsed, coinbaseDiff (net ETH paid to the block builder), ethSentToCoinbase, and per-transaction revert strings—allowing your bot to verify profitability and calculate the exact builder bribe before calling eth_sendBundle or eth_cancelBundle.

Every profitable MEV searcher, sniper bot, and whitehat rescue script on Ethereum relies on one golden rule: never send a bundle to a block builder until you simulate a Flashbots bundle against the target block state. If you skip simulation, a tiny miscalculation in pool reserves, token transfer taxes, or next-block baseFeePerGas will cause your bundle to revert during the builder's auction—meaning your bundle is silently dropped and never lands on-chain.

While we cover high-level bundle submission in Flashbots Bundles Explained: How to Send eth_sendBundle and Why Yours Isn't Landing, this guide focuses specifically on how to simulate a Flashbots bundle using eth_callBundle, how to read the JSON response fields (coinbaseDiff vs ethSentToCoinbase), how to call it in TypeScript and Go (flashbots in golang), and how to cancel or replace in-flight bundles with flashbots cancelbundle.

How eth_callBundle works on the Flashbots RPC

Unlike standard Ethereum eth_call—which only simulates a single unsigned transaction call object—eth_callBundle simulates an ordered array of raw, signed transactions (txs) sequentially inside the exact same EVM block state.

That sequential state persistence is what makes eth_callBundle essential for flashbots crypto workflows:

  • In a Block 0 snipe: Tx 1 is the token developer's pending mempool openTrading() transaction, Tx 2 is your sniper buy, and Tx 3 is a test sell. eth_callBundle applies Tx 1's state change first so Tx 2 and Tx 3 execute against the newly opened pool!
  • In a compromised-wallet rescue: Tx 1 funds gas from your clean wallet, and Tx 2 transfers ERC-20 tokens from the hacked wallet using the ETH received in Tx 1.

Complete eth_callBundle example (JSON-RPC request & response)

When calling https://relay.flashbots.net (flashbots rpc), you must sign the JSON-RPC body with your searcher reputation key in the X-Flashbots-Signature HTTP header (address:0xSignature). Here is a complete eth_callBundle example payload:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "eth_callBundle",
  "params": [
    {
      "txs": [
        "0x02f87401820311843b9aca008506fc23ac0082520894...",
        "0x02f8b1018304a817843b9aca008506fc23ac0083030d4094..."
      ],
      "blockNumber": "0x146a520",
      "stateBlockNumber": "latest",
      "timestamp": 1761336012
    }
  ]
}

Decoding the eth_callBundle response metrics

When the simulation succeeds, relay.flashbots.net returns a JSON object like this:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "bundleGasPrice": "45238095238",
    "bundleHash": "0x7e8a9c...",
    "coinbaseDiff": "19000000000000000",
    "ethSentToCoinbase": "15000000000000000",
    "gasFees": "4000000000000000",
    "results": [
      {
        "coinbaseDiff": "1000000000000000",
        "ethSentToCoinbase": "0",
        "fromAddress": "0xCleanSponsor...",
        "gasFees": "1000000000000000",
        "gasPrice": "47619047619",
        "gasUsed": 21000,
        "toAddress": "0xCompromised...",
        "txHash": "0x111...",
        "value": "0x"
      },
      {
        "coinbaseDiff": "18000000000000000",
        "ethSentToCoinbase": "15000000000000000",
        "fromAddress": "0xSearcher...",
        "gasFees": "3000000000000000",
        "gasPrice": "120000000000",
        "gasUsed": 150000,
        "toAddress": "0xExecutorContract...",
        "txHash": "0x222...",
        "value": "0x0000000000000000000000000000000000000000000000000de0b6b3a7640000"
      }
    ],
    "stateBlockNumber": 21407007,
    "totalGasUsed": 171000
  }
}
Response Field Exact Meaning & Formula Why Searchers Check It
totalGasUsed Sum of gas used by all txs in the bundle Verify your bundle uses >= 42,000 gas (the minimum floor enforced by Flashbots and Titan relays!).
ethSentToCoinbase Direct ETH transferred via block.coinbase.transfer(bribe) Shows the explicit smart-contract bribe paid to the block builder.
gasFees Priority tips (effectivePriorityFee * gasUsed) above baseFeePerGas Shows the priority-fee portion of builder revenue (excludes burned EIP-1559 basefee).
coinbaseDiff ethSentToCoinbase + gasFees Total net ETH value your bundle adds to the block builder's balance.
bundleGasPrice coinbaseDiff / totalGasUsed Effective bundle score builders use to rank non-conflicting bundles!
results[i].error / revert Present only if transaction i reverted Gives you the exact revert reason so you abort before calling eth_sendBundle.

Simulating a Flashbots bundle in TypeScript and Go (flashbots in golang)

1. TypeScript (@flashbots/ethers-provider-bundle)

const signedBundle = await flashbotsProvider.signBundle([
  { signedTransaction: rawPendingMempoolTx },
  { signer: searcherWallet, transaction: populatedArbTx },
]);

const simulation = await flashbotsProvider.simulate(signedBundle, targetBlockNumber);
if ("error" in simulation || simulation.firstRevert) {
  console.error("Bundle reverted:", simulation);
  return;
}
console.log("Effective bundleGasPrice (gwei):", simulation.effectiveGasPrice.toString());

2. Using flashbots in golang (flashbotsrpc / go-ethereum)

Many backend searchers write their relay dispatcher in Go (flashbots in golang) using github.com/metachris/flashbotsrpc or custom net/http clients with crypto.Sign:

// Flashbots in Golang: eth_callBundle example using metachris/flashbotsrpc
rpc := flashbotsrpc.New("https://relay.flashbots.net")
callBundleArgs := flashbotsrpc.FlashbotsCallBundleParam{
    Txs:              []string{rawSignedTx1, rawSignedTx2},
    BlockNumber:      fmt.Sprintf("0x%x", targetBlock),
    StateBlockNumber: "latest",
}
res, err := rpc.FlashbotsCallBundle(searcherPrivKey, callBundleArgs)
if err != nil || len(res.Results) == 0 {
    log.Fatalf("flashbots simulate bundle failed: %v", err)
}
fmt.Printf("CoinbaseDiff: %s wei, TotalGasUsed: %d\n", res.CoinbaseDiff, res.TotalGasUsed)

Why fast searchers simulate locally (Anvil / Reth) instead of calling relay.flashbots.net

While eth_callBundle on https://relay.flashbots.net is convenient, it requires a network round trip (30–120ms) and is subject to relay rate limits during volatile blocks.

In production, we simulate locally in 1 to 5 milliseconds using either:

  • Foundry Anvil (anvil --fork-url ... --auto-impersonate): Perfect for development, token rescue verification, and CI testing. Clone our flashbots-bundle-simulator-anvil GitHub repo and read How to Test an MEV Bot on a Mainnet Fork With Anvil to run this on your laptop.
  • Local Reth eth_callBundle RPC or embedded revm: Both Reth and mev-geth expose eth_callBundle natively over local IPC sockets (< 2ms latency), letting you simulate hundreds of candidate bundles per slot before sending only the winning bundle to external builders.

How to update or cancel an in-flight bundle (flashbots cancelbundle & replacementUuid)

What if you simulate and send a bundle at t = 2s into the slot, and at t = 5s a large whale trade in the mempool invalidates your price or you want to raise your builder bribe?

Use replacementUuid and eth_cancelBundle (flashbots cancelbundle):

  1. When calling eth_sendBundle, include a standard UUID v4 string in "replacementUuid": "550e8400-e29b-41d4-a716-446655440000".
  2. To update/raise your bid: Call eth_sendBundle again with the same replacementUuid and your new txs. The builder overwrites your earlier bundle in memory.
  3. To cancel before block inclusion (eth_cancelBundle): Send a JSON-RPC call with "method": "eth_cancelBundle" and "params": [{"replacementUuid": "550e8400-e29b-41d4-a716-446655440000"}]. As long as the builder has not sealed the block yet, your bundle is removed from the auction queue.

Frequently asked questions

What is the difference between eth_call and eth_callBundle?

Standard eth_call simulates a single transaction in isolation without persisting state to a second transaction. eth_callBundle simulates an ordered list of signed transactions (txs) one after another in the same block context and computes builder accounting metrics (coinbaseDiff, ethSentToCoinbase, bundleGasPrice).

Why does eth_callBundle succeed, but my eth_sendBundle still isn't included in the block?

eth_callBundle simulates your bundle against stateBlockNumber: "latest" (the top of the previous block), proving your transactions are valid in isolation. However, in the live block auction, your bundle may still be excluded if (1) its bundleGasPrice was lower than competing bundles touching the same state, (2) your bundle used less than 42,000 total gas, or (3) the block was won by a builder you didn't send to (such as Titan or Beaverbuild).

How do I calculate the right maxFeePerGas for the next block when simulating?

Under EIP-1559, the maximum possible baseFeePerGas in blockNumber + 1 is currentBaseFee * 1.125 (+12.5%). Always sign your bundle transactions with maxFeePerGas >= currentBaseFee * 1.15 so a full preceding block never invalidates your bundle signature.

Does eth_callBundle expose my strategy to the public mempool?

No. Requests sent to https://relay.flashbots.net via eth_callBundle are simulated privately on the Flashbots relay server and never broadcast to the P2P mempool. For 100% privacy and zero network latency, run eth_callBundle on your own local Reth node or Anvil fork.

How does eth_cancelBundle (flashbots cancelbundle) work?

If you included a replacementUuid field when submitting your bundle via eth_sendBundle, you can send an eth_cancelBundle RPC request passing that exact replacementUuid to Flashbots, Titan, or Beaverbuild to withdraw the bundle before the slot's block proposal cutoff.

Related guides


Want to use production Flashbots bundle tooling for Block 0 sniping and token rescues—or hire us to engineer a custom multi-builder searcher? Start a 3-day free trial at mevdao.org/subscribe, or message me directly on Telegram (@merunas).

← All articles