All articles
MEV·May 10, 2026·6 min read

Jito ShredStream Explained: Sub-Slot Data for Solana Searchers

Jito ShredStream Solana delivers shreds before block confirmation, cutting opportunity-detection latency for searchers. How it beats Geyser feeds, and where it bites.

A Solana block isn't a single object that lands on your RPC node all at once. It's assembled from thousands of small UDP packets called shreds, streamed by the leader as it packs the block. By the time your Geyser plugin fires an account-update or transaction notification, that block is already replayed, verified, and committed locally. ShredStream lets you tap the stream one layer earlier: you receive the raw shreds as they propagate across the network, before your validator has finished reconstructing the block. For a searcher, that gap is where the money is.

What a shred actually is

The leader takes the entries it's producing, serializes them, and splits the byte stream into fixed-size fragments. Each fragment is a shred, roughly 1,228 bytes of payload to fit inside a single UDP datagram without IP fragmentation. Shreds come in two flavors: data shreds carry the actual transaction bytes, and coding shreds are Reed-Solomon parity used to recover data shreds that get dropped in flight. They're grouped into FEC sets, and once you have enough shreds in a set (data plus coding), you can reconstruct every data shred even if some never arrived.

This matters because Solana's Turbine protocol fans shreds out through a tree of validators. A shred for slot N might reach you 100-400ms before that slot is fully replayed and its transactions surface in a normal feed. Jito's ShredStream Proxy hooks into that fan-out and forwards a copy of the shreds to you over the open internet, so you don't need to be a staked node sitting inside Turbine to see them.

Where this sits relative to Geyser

If you've built on Solana MEV before, your mental model is probably a Yellowstone gRPC Geyser stream feeding your mempool and account watchers. Geyser is excellent and it's what most infrastructure runs on, but by construction it emits notifications after the bank has processed the update. The ordering is clean, the data is typed and deserialized, and you get account deltas for free. You pay for that convenience with time.

ShredStream is the opposite tradeoff:

  • Earlier: you see transaction bytes as they land, not after replay.
  • Rawer: you get shreds. You reconstruct entries yourself, deserialize the transactions yourself, and there is no account state attached. If you need the pool reserves, you carry your own shadow state.
  • Messier: shreds arrive out of order, some are missing until parity fills them in, and you'll occasionally reconstruct a slot that later gets skipped. Equivocation is real — a leader can produce conflicting versions of the same slot.

A useful way to think about it: Geyser answers "what is the confirmed state," ShredStream answers "what is probably about to be confirmed." Serious searchers run both. ShredStream drives the fast path for detection; Geyser (or replayed bank state) is the source of truth you settle against.

The reconstruction pipeline

Running the proxy gets you a UDP firehose. The work starts after that. A realistic pipeline looks like:

  1. Receive shreds, deduplicate by (slot, index), and bucket into FEC sets.
  2. When a set has enough shreds, run Reed-Solomon recovery to fill gaps.
  3. Reassemble data shreds in order into the entry byte stream, then deserialize entries into transactions.
  4. Filter to the programs you care about (a specific AMM, a lending market) and hand matches to your strategy loop.

The solana-ledger crate already has the shredder and entry-recovery logic, so you don't hand-roll Reed-Solomon. A stripped-down receive loop:

let sock = UdpSocket::bind("0.0.0.0:20000")?;
let mut buf = [0u8; 1500];
loop {
    let (n, _src) = sock.recv_from(&mut buf)?;
    let shred = Shred::new_from_serialized_shred(buf[..n].to_vec())?;
    let slot = shred.slot();

    let set = fec_sets.entry((slot, shred.fec_set_index())).or_default();
    set.push(shred);

    if let Some(entries) = try_recover_and_deshred(set) {
        for tx in entries.into_iter().flat_map(|e| e.transactions) {
            if touches_target_program(&tx) {
                strategy_tx.send((slot, tx)).ok(); // hot path
            }
        }
    }
}

The gotcha nobody warns you about: your try_recover_and_deshred must be non-blocking on the receive thread, or you'll drop packets. UDP has no backpressure. If your socket buffer fills while you're busy deserializing, the kernel silently discards incoming shreds and you lose exactly the data you're paying latency to get. Bump SO_RCVBUF hard (several MB), pin the receive thread, and push everything heavier onto a second core.

How much time you actually save

Measure it, don't trust a blog. On a well-placed node — same region as the current leader schedule, ideally colocated in the Frankfurt or Amsterdam clusters where a lot of Solana stake lives — I've seen shreds for a slot arrive 150-300ms ahead of the same transactions showing up on a commitment-processed Geyser feed. That's not a rounding error. In a contested backrun against a large swap, 200ms is the difference between landing in the same block via a Jito bundle and eating the next slot's price.

But raw detection speed only helps if the rest of your stack keeps up. Two things eat the advantage:

  • Deserialization cost. Deshredding and decoding thousands of transactions per second is not free. Profile it. If your parser adds 50ms, you've handed back a third of your edge.
  • Submission latency. Seeing the opportunity early is useless if your bundle takes 80ms to reach the block engine. Colocate your sender, and understand the tradeoffs between Jito, Temporal, and Helius fast-path submission before you commit to one.

Where ShredStream earns its keep

It's most valuable for strategies where being first to know is the whole game. Backrunning large DEX swaps is the obvious one — you want to construct and submit your bundle the instant the victim transaction is visible. It also sharpens JIT liquidity plays on Meteora DLMM and similar concentrated-liquidity venues, where you're racing to place liquidity in the exact bin a swap will cross.

It helps less for pure statistical arb across pools, where you're reacting to confirmed reserve changes and a clean Geyser feed with account deltas is genuinely easier to work with. Building that kind of low-latency detection-and-execution loop is the bulk of what goes into a production Solana MEV and arbitrage bot, and the shred pipeline is only worth it once the rest is already tight.

The honest caveats

  • You will process slots that get skipped or forked away. Never treat a shred-derived transaction as final. It's a signal, not a fact.
  • No account state. You maintain your own view of every pool you touch, updated from confirmed data, and use shreds only to trigger action.
  • Operational weight. This is another UDP service to run, monitor, and keep colocated with shifting leader schedules. That's the same class of problem as running your own low-latency data and infra layer for any serious searcher — it's ongoing work, not a set-and-forget dependency.
  • Equivocation and spoofing. You're parsing bytes off the network. Validate signatures before you act on anything, and be paranoid about conflicting slot versions.

ShredStream isn't a shortcut around building a good bot — it's a sharp tool that rewards a stack already engineered for microseconds and punishes one that isn't. Get the detection loop, shadow state, and submission path right first; the shreds are the last few milliseconds you shave off after everything else is fast.

If you're weighing whether a shred-driven detection layer is worth the operational cost for your strategy, that's exactly the kind of decision we work through when we build a Solana MEV and arbitrage bot.

Need a bot like this built?

We design, build and run trading bots on Solana, Hyperliquid and Polymarket.

Start a project
#jito#shredstream#solana-mev#searchers#low-latency