sendTransaction vs Jito sendBundle: A Dual-Submission Landing Strategy
A practical guide to Solana dual transaction submission with Jito: race raw sendTransaction against sendBundle, de-dupe on-chain, and raise your land rate.
Losing a market-making fill because your transaction sat in a validator's mempool while the leader rotated is the kind of failure that doesn't show up in backtests but quietly eats your edge in production. On Solana there's no mempool in the Ethereum sense, but there is a very real question of how your signed transaction reaches the current leader — and whether it arrives in time to land in the slot you were targeting. The honest answer for any latency-sensitive bot is: don't pick one path. Send it two ways at once and let the network decide which one wins.
This is the dual-submission pattern. You race a raw sendTransaction against a Jito sendBundle, accept that one of them will land first, and build the rest of your pipeline to tolerate the duplicate that shows up on the other rail. Done right, your land rate climbs and your tail latency shrinks. Done carelessly, you double-spend fees, spam validators, and confuse your own accounting.
Why two rails beat one
Raw sendTransaction against a good RPC forwards your transaction to the leader's TPU over QUIC. It's fast, it's cheap, and if you're paying a competitive priority fee you'll land in normal market conditions. But it's best-effort. During congestion, QUIC connection limits and per-IP stake weighting decide who gets through, and an unstaked or lightly-staked RPC endpoint loses that fight. We wrote up the staked-connection side of this in detail in our guide to sWQoS and staked RPC, and it's the single biggest lever on plain RPC land rates.
Jito's sendBundle takes a different route entirely. Your transaction (optionally with a tip) goes to a Jito block engine, gets bundled, and is delivered to Jito-Solana leaders with an all-or-nothing atomicity guarantee. Bundles land or they don't — there's no partial execution — and the tip buys you priority over the regular fee market during the moments that actually matter. The tradeoff: bundles only help when the current leader runs Jito-Solana, and the block engine adds a hop of latency that raw TPU submission doesn't have.
Neither rail dominates across all slots. Leader schedule, congestion, whether you need atomicity, how much you're willing to tip — all of it moves the winner around. So you send both.
The de-dupe problem
The moment you fire the same transaction down two paths, you've created a race with a nasty edge case: both can land. If your transaction is idempotent at the program level, that's merely wasteful. If it isn't, you've just executed twice.
The clean solution is a shared, deterministic nonce or a program-level guard so that the second landing is a no-op or reverts cheaply. Three approaches, roughly in order of preference:
- Same signature, both rails. If you submit the identical signed transaction (same blockhash, same signature) on both
sendTransactionand inside the Jito bundle, Solana's transaction dedup at the leader will reject the second one as a duplicate signature. This is the simplest and safest — you're not double-executing, you're just giving the same transaction two delivery trucks. The catch: a Jito bundle wraps your transaction, and if the bundle also carries a separate tip transaction, the bundle is atomic but your core transaction still has one signature that the runtime dedups. Keep the tip as a separate instruction inside the same transaction where you can, so there's exactly one signature to collapse on. - On-chain guard account. For anything non-idempotent, gate the effect behind a PDA you flip once. First landing sets it; second landing sees it set and returns early. Costs you a few hundred CU and a bit of account contention, buys you correctness.
- Client-side confirmation gate. Watch for the signature via a WebSocket or gRPC stream and cancel the losing rail the instant the winner confirms. This doesn't prevent a double-land that already happened in-flight; it just stops you from re-sending on retries.
In practice you use the first for read-mostly and idempotent flows, and the second for anything that moves inventory. The confirmation gate is layered on top of both to shut down retry loops early.
A worked submission racer
Here's the shape of the racer we run inside client landing pipelines. The key detail is that both rails share one signed transaction, so signature dedup does the heavy lifting and the guard PDA is a backstop, not the primary mechanism.
async function landDual(tx: VersionedTransaction, tip: number) {
const raw = signAndSerialize(tx); // one signature
const sig = bs58.encode(tx.signatures[0]);
const rpcSend = rpc.sendRawTransaction(raw, {
skipPreflight: true,
maxRetries: 0, // WE own retries
});
const jitoSend = jito.sendBundle([
withTipInstruction(tx, tip), // same core tx + tip inst
]);
// fire both, don't await the loser
const [rpcRes, jitoRes] = await Promise.allSettled([rpcSend, jitoSend]);
// single confirmation watcher keyed on the shared signature
return watchSignature(sig, { commitment: "confirmed", timeoutMs: 8000 });
}
Two things engineers get wrong here. First, maxRetries: 0 — let the RPC's built-in rebroadcast run and you'll be resending stale transactions past the blockhash window, polluting the network and skewing your metrics. Own your retry loop and re-sign with a fresh blockhash on each attempt. Second, watch one signature, not two futures. The rails are just transport; confirmation is a property of the chain, so you poll the ledger, not the submission RPC's opinion of what it forwarded.
Tuning the race in production
Static tip sizing is the mistake that separates a demo from a system that pays for itself. Tips should track the current fee market, and the raw-RPC priority fee should track a percentile of recent landed transactions in your target program — not a hardcoded number you set three months ago. Pull both from a live fee oracle and adjust per slot. If you're building that telemetry layer, the streaming plumbing behind it — gRPC connection pooling over Yellowstone — is what keeps your fee model fed without falling behind the tip of the chain.
Some tactical numbers from real deployments:
- Cap your dual-send fan-out. Two rails, not five. Every extra path is more duplicate risk and more spend for diminishing land-rate gains past the second rail.
- Set the confirmation timeout to roughly 2–3x your target slot time (about 4–8 seconds at
confirmed), then hard-cancel and re-sign. Waiting onfinalizedfor the landing decision is too slow for anything that trades. - When the current leader isn't a Jito leader, the bundle is dead weight — skip it and save the tip. If you're forwarding directly to the leader's TPU, the QUIC-based TPU client path is worth pairing with raw send so you're not bottlenecked on your RPC's own forwarding.
- Log which rail won, per slot, and feed that back. Over a week you'll see a clear split by congestion regime, and you can bias tip spend toward whichever rail is actually landing your fills.
That per-rail attribution is the part most teams skip and later wish they hadn't. Without it you can't tell whether your Jito tips are buying anything, or whether you're paying block-engine latency for slots your raw send would have won anyway. Wiring it into a live view — the kind of real-time trading dashboard that shows land rate and win-by-rail as they happen — turns tip tuning from guesswork into a dial you can turn.
Dual submission isn't exotic. It's just refusing to bet your fills on a single delivery path when the network gives you two, and being disciplined enough to de-dupe cleanly so the redundancy costs you almost nothing. The engineering that matters is all downstream of the send: the guard account, the shared signature, the single confirmation watcher, the retry loop you actually control.
If you want that landing pipeline built and tuned against your own leader-schedule and fee data, that's the core of what our infrastructure and data engineering work delivers.
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