Simulating Jito Bundles Before Submission: simulateBundle Deep Dive
Use Jito simulateBundle on Solana to validate atomic MEV bundles, catch reverts, and confirm tip logic before you ever pay a validator tip.
A Jito bundle either lands whole or not at all, and you pay the tip regardless of whether the profitable leg actually executed the way you assumed. That asymmetry is the whole reason simulateBundle exists. Send a three-transaction arb bundle that reverts on the second leg because the pool moved, and the block engine still forwards your tip transaction — you burned lamports for nothing. Simulating the entire bundle against a real bank slot before submission is how you stop paying to lose.
Most Solana devs know simulateTransaction. Far fewer use simulateBundle, an RPC exposed by Jito-patched validators that runs an ordered list of transactions sequentially, against the same account state, exactly the way the block engine would attempt them. That sequential-state property is the entire point. simulateTransaction runs each transaction against a fresh snapshot; it cannot tell you whether transaction #2 succeeds given the writes from transaction #1. For atomic MEV — sandwiches, backruns, multi-hop arbs, liquidations — that's the only question that matters.
What simulateBundle actually does
The method lives on a Jito-enabled RPC node (your own validator with the Jito patch, or a provider that exposes it — not every public endpoint does). You hand it a fully signed, ordered set of transactions plus optional pre/post account configs, and it replays them in order on a recent bank.
{
"jsonrpc": "2.0",
"id": 1,
"method": "simulateBundle",
"params": [
{
"encodedTransactions": ["<base64 tx0>", "<base64 tx1>", "<base64 tx2>"]
},
{
"preExecutionAccountsConfigs": [null, null, {"encoding": "base64", "addresses": ["<pool state pubkey>"]}],
"postExecutionAccountsConfigs": [null, null, {"encoding": "base64", "addresses": ["<pool state pubkey>"]}],
"skipSigVerify": false,
"replaceRecentBlockhash": false
}
]
}
The response gives you per-transaction results: compute units consumed, program logs, an error field, and — this is the useful part — the raw account data before and after each transaction for the addresses you asked about. You get to read the pool's post-swap reserves directly out of the simulation instead of guessing.
A few flags worth knowing cold:
skipSigVerify— settruewhile iterating so you don't have to re-sign on every parameter tweak. Set itfalsefor a final pre-flight; a bad signature is a real reason the bundle would be dropped.replaceRecentBlockhash— handy during development, but if you replace the blockhash you're no longer simulating the exact transactions you'll submit. For a true pre-flight, keep your real blockhash and eat the occasional "blockhash not found" as a signal to refresh.
The workflow that catches reverts before you tip
Here's the sequence I run for an arb bundle. It's boring and it works.
- Build the profit legs (the swaps) and the tip transaction. Order matters: tip usually goes last, so a revert on any profit leg means the tip never executes either.
- Call
simulateBundlewithpreExecutionAccountsConfigs/postExecutionAccountsConfigsset on the pool accounts you care about. - Walk the results array. If any entry has a non-null
err, the bundle would fail atomically — abort, don't submit. - Decode the post-execution account data and recompute realized profit from actual reserves, not from the quote you built the transaction against.
- Only if simulated profit still clears your threshold after subtracting the tip and priority fee do you submit through
sendBundle.
Step 4 is where the money hides. A quote from a minute-old snapshot might promise 0.4 SOL of edge; the live simulation shows the pool already moved and the real number is 0.02 SOL, which the tip eats entirely. simulateTransaction on the individual swap won't reveal this because it never sees the intermediate state your earlier legs create. The bundle simulation does.
A gotcha with tip accounts
If your tip transaction pays a Jito tip account, the simulation will debit that lamport transfer — it's a normal SystemProgram transfer as far as the bank is concerned. Don't be surprised when the tipping wallet's post-execution balance drops in the sim. What you're checking is that the transfer succeeds (sufficient balance, valid tip account) and that it lands last. A tip transaction that itself reverts because the wallet is short is a genuinely common bug this catches.
Where it fits in a real stack
Simulation is a filter, not a guarantee. Between your sim slot and the slot the leader actually builds, state moves — someone else's transaction lands, the pool shifts, your edge evaporates. That's inherent to a competitive mempool-free chain, and it's why bundle simulation pairs so tightly with the delivery side of the stack. If you haven't tuned how your transactions reach the leader, read our breakdown of stake-weighted QoS and how SWQoS shapes transaction priority, and the companion piece on QUIC connection throttling and why bot transactions get dropped. Simulating a bundle you can't reliably deliver is polishing a car with no engine.
The other half is freshness. Your simulation is only as good as the account state you feed it, which means the recency of your data feed sets a hard ceiling on accuracy. Teams running serious MEV and arbitrage bots on Solana generally back simulateBundle with a low-latency shred feed so the bank state they simulate against is at most a slot or two stale — our write-up on Jito ShredStream and low-latency shred feeds covers that plumbing. The tighter your feed, the smaller the gap between simulated profit and realized profit.
Practical notes and failure modes
- Not all providers expose it.
simulateBundleis a Jito-specific extension. Verify your RPC actually implements it before building around it; a plain Solana node returns a method-not-found error. - Compute-unit budgets are load-bearing. The sim reports CU consumed per transaction. If a leg is near its requested limit, bump the CU limit before you submit — a bundle that simulates fine at the edge of its budget can fail live when the pool's swap path is a hair longer.
- Simulate against a recent slot, not a stale one. Re-simulate right before submission. A sim from 800ms ago is a different chain state than the one your bundle competes in.
- Read the logs, not just the err field. Program logs surface slippage-tolerance failures, insufficient-funds conditions, and custom program errors that a bare success/fail flag hides.
For high-frequency work — snipers landing in-block, copytrading systems mirroring a wallet, market makers quoting both sides — this pre-flight step is the difference between a strategy that leaks tips and one that only pays when the math already cleared.
If you're building atomic bundles and want the simulation-plus-delivery pipeline done right the first time, that's exactly the kind of system we build for Solana MEV and arbitrage bots.
Need a bot like this built?
We design, build and run trading bots on Solana, Hyperliquid and Polymarket.
Start a projectMore from the blog
Public vs Paid RPC: How to Run a Fair Benchmark
A fair comparison of public and paid RPC endpoints must account for quotas, workload, freshness and support guarantees.
Read articleRPC Benchmark Percentiles Explained: p50, p95 and p99
Why averages hide the latency spikes that break trading bots, wallets and production blockchain applications.
Read articleHyperliquid REST vs WebSocket Benchmark: What to Measure
A benchmark plan for Hyperliquid market data that separates request latency from streaming freshness and recovery.
Read article