The arbitrage loop: see, decide, land

Strip the branding off any Solana arbitrage bot and it is the same loop, bracketed by two legs of pure network distance:

arb-loop.txt
        [ cluster ]                                  [ leader ]
            │  ① network leg                             ▲
            ▼                                            │ ④ network leg
   ┌────────────────┐   ┌──────────┐   ┌──────────┐   ┌──┴───────┐
   │ ② SEE          │──▶│  decode  │──▶│ ③ DECIDE │──▶│  LAND    │
   │ shreds + gRPC  │   │ pool Δ   │   │ price arb│   │ Jito/TPU │
   └────────────────┘   └──────────┘   └──────────┘   └──────────┘


                                                ⑤ reconcile vs finality

The boxes in the middle are your code - you optimize them with engineering. The arrows on the outside are network legs - source-to-you and you-to-leader - and they are pure geography. An arb bot that is fast in the middle and slow on the legs still loses the race to a co-located competitor who saw the same opportunity sooner and landed it faster. This is the same shape we lay out in Building an HFT Data Pipeline on Solana; arbitrage is that pipeline with a tighter loop and a harder deadline.

An arbitrage bot is mostly infrastructure, not strategy. The price math is the easy part - everyone can see the same arb. The bot that wins is the one that saw it first and landed it fastest.

See: first-seen signal plus live state

The arb you can act on is the one you saw before everyone else. Two sources do that job, and you want both:

  • Decoded shreds for the trigger. Shreds are the fragments of a block as it propagates across the cluster, before confirmation. Decoded server-side, they hand you the swap that just moved a pool the moment it hits the wire - not seconds later when an RPC gets around to reporting it. That is how bots see opportunities first. We go deep on reading pre-confirmation data in Reading Decoded Transactions Before Confirmation.
  • Yellowstone gRPC for live state. Arbitrage is a function of pool reserves, and you cannot poll your way to a current view fast enough. gRPC pushes account and slot updates to you as they change, so your model of every market stays warm without a single RPC round-trip on the hot path.

Shreds tell you something just happened; gRPC tells you what the book looks like now. Aggregate shreds from multiple upstreams so you take whichever copy of a slot arrives first - redundancy that doubles as latency insurance on the leg that matters most. Keep a JSON-RPC endpoint around too, but for settlement truth and backfill, not the hot path.

Decode and decide: keep it lean

Everything before this exists to deliver clean events to your decision box as early as possible. Do not waste that head start. Decode the relevant program layouts - Raydium, Orca, Meteora, whatever you trade - into a normalized internal shape, ideally decoded close to the data so you receive ready-to-use objects instead of raw bytes. Normalize every source into one event shape early, so the strategy never branches on where data came from.

The decision itself should be doing decision work and nothing else: no parsing, no waiting on I/O, no re-deriving state it could have cached. Keep pool state hot in memory, pre-compute routes and fee math where you can, and treat every allocation on the hot path as latency you are choosing to spend. The arb math - cross-venue price delta minus fees, slippage, and tip - is small. The infrastructure that feeds it clean inputs in time is the hard part.

Land: direct leader delivery plus Jito bundles

Seeing the arb is worthless if you cannot land it. Output is a delivery race, and arbitrage adds one hard requirement: atomicity. Most arbs are multi-leg, and a half-filled multi-leg arb is not a smaller profit, it is an open position you did not want.

  • Direct leader delivery for speed. Resolve the current and upcoming leaders from the schedule and send straight to their TPUs over QUIC, with stake-weighted QoS so your packets are accepted when the network is saturated. No RPC hops to add latency and drop risk.
  • Jito bundles for atomicity and bidding. A bundle executes all-or-nothing, in the exact order you specify, and you attach a tip to bid in the Block Engine's auction. That is the right tool for a two- or three-leg arb where partial execution leaves you exposed - and the mechanism by which you win the inclusion auction against everyone else who saw the same opportunity.

rpc edge's transaction sender does both, co-located, so your send leg is as short as the schedule allows. Fee dynamically, own your retries, and never blindly resubmit an arb your model no longer wants - the failure modes here are the same delivery problems we break down in Why Solana Transactions Fail.

The latency budget: why co-location dominates

Add it up. Your end-to-end edge is: cluster-to-you network + decode + decide + you-to-leader network. You shave decode and decide with good engineering, and you should. But the two network legs are set by geography, and in arbitrage they routinely dominate, because everyone competing already wrote tight code. When the strategy is commoditized, the race collapses to the legs.

This is the uncomfortable truth most "fast RPC" pitches skip: a brilliantly optimized arb bot a region away from the cluster loses to an ordinary one that is co-located. And the latency you care about is not the average, it is the tail - arbitrage is decided in the slow percentiles, which is why a shared node whose latency is hostage to other tenants is the wrong substrate. A dedicated node beside the stake clusters and the Jito Block Engine gives you predictable, no-noisy-neighbor latency on both legs you cannot refactor away.

Pipeline stageWhat you need
See (trigger)Decoded shreds, aggregated from multiple upstreams, decoded server-side
See (state)Yellowstone gRPC streaming live pool and account state, no polling
DecodeProgram-layout decoding close to the data, normalized to one event shape
DecideHot in-memory state, pre-computed routes, zero I/O on the path
LandDirect TPU delivery + Jito bundles for atomic multi-leg execution
SubstrateDedicated, co-located node for predictable tail latency on both legs
ReconcileFinalized reads to confirm landed, on the winning fork, at assumed price

Put your arb bot next to the cluster.

rpc edge gives you co-located decoded shreds, Yellowstone gRPC, and a transaction sender on dedicated nodes - the ingest and execution legs, already short.

View plans & pricing →

Reconcile: do not settle on first-seen

The same head start that lets you take the arb can burn you if you trust it too far. First-seen data is a signal, not a settlement. A transaction you saw on the wire can land on a fork that loses, or get reordered, or revert. Act on first-seen for speed, but tag every action as provisional and reconcile it against a finalized read: did the arb land? On the winning fork? At the price your model assumed? Anything that did not needs an explicit path - unwind, hedge, or cancel.

Bound your exposure so a reorg is survivable, and make the loop idempotent so a re-seen event converges instead of double-firing a trade. Separating signal from settlement is what lets you be aggressive on speed without being reckless with capital.

Wiring it: shreds in, bundle out

The whole loop, sketched - first-seen signal and live state in, an atomic bundle out:

arb.ts
// see: first-seen signal from shreds, live pool state from gRPC
const swaps = await rpcedge.subscribe({ source: "shreds", decode: true,
  filter: { programs: [RAYDIUM, ORCA, METEORA] } });
const pools = await rpcedge.subscribe({ source: "geyser",
  accounts: { amms: { owner: [RAYDIUM_AMM, ORCA_WHIRLPOOL] } }, commitment: "processed" });
 
for await (const swap of swaps) {
  const edge = priceArb(swap, pools.snapshot()); // decide: lean, hot state, no I/O
  if (edge.bps < MIN_BPS) continue;
 
  // land: atomic multi-leg via Jito bundle, bid to win inclusion
  await rpcedge.sendBundle(edge.legs, {
    tip: edge.tipLamports,   // bid for inclusion in the Block Engine auction
    leaders: 3,              // resolve current + upcoming leaders
    swqos: true,             // stake-weighted ingest under congestion
    maxRetries: 0,           // you own retries; never resend a stale arb
  });
}

The takeaway

A Solana arbitrage bot is an infrastructure problem wearing a strategy costume. You need to see the opportunity first (decoded shreds for the trigger, Yellowstone gRPC for live state), decide in a lean decode path that does nothing but math, and land atomically with direct leader delivery and Jito bundles. Reconcile every fill against finality so first-seen never becomes first-settled. Then put the whole loop on a dedicated, co-located node, because the two network legs that bracket it are pure geography and they decide the race once everyone's code is fast. Get the infrastructure right and your strategy is not chasing arbs - it is early to them, every slot.

Frequently asked questions

What infrastructure does a Solana arbitrage bot need?
Three layers: ingest that sees opportunities first (decoded shreds for first-seen signal, Yellowstone gRPC for live pool state), a lean decode-and-decide path, and execution that lands fast (direct leader delivery plus Jito bundles). All of it co-located with the cluster so the network legs are short. The strategy is the small part; the infrastructure around it is the edge.
How do bots see opportunities first?
By reading the network before confirmation. Decoded shreds expose transactions as they propagate across the cluster, before a block is confirmed, so you see a swap that moves a pool the moment it lands on the wire rather than waiting for an RPC to report it. That head start is the difference between taking the arb and watching someone else take it.
Do I need Jito for arbitrage?
Usually yes. Most arbitrage is multi-leg and must execute atomically: either every leg fills or none does, or you are left holding inventory. Jito bundles give all-or-nothing execution, guaranteed ordering, and a tip-based auction to bid for inclusion. Direct TPU delivery still matters for simpler single-transaction sends, but atomic multi-leg arbitrage is what bundles are built for.
Shared or dedicated nodes for arbitrage?
Dedicated, once you are competing for real. On a shared node your latency is hostage to every other tenant's load, and arbitrage is decided in the tail of the latency distribution. A dedicated, co-located node gives you predictable latency and no noisy neighbors, which is exactly what a latency-sensitive bot needs.
What latency do I need?
Think in legs, not a single number. Your edge is source-to-you network time plus decode plus decide plus you-to-leader send time. The compute legs you fix with lean code; the two network legs are pure distance and are set by where you run. Co-location compresses both, which is why a co-located ordinary bot beats a brilliant one a region away.