sol-shred-sdk is a low-latency Rust SDK for Solana raw shred decoding, ShredStream ingestion, and multi-DEX transaction event parsing. It turns Solana UDP shreds or Jito-style ShredStream entries into typed DexEvent data for trading bots, snipers, indexers, and real-time analytics.
The default, lowest-latency path is:
UDP packet -> Solana/Agave Shred -> FEC recovery -> deshred -> Entry -> VersionedTransaction -> event parser
Source/decode mode is selected with ShredDecodeMode. Native raw UDP shreds are the recommended path; Jito-style ShredStream gRPC entries are retained as a compatibility source while Jito service availability lasts.
| Area | Coverage |
|---|---|
| Input | Raw UDP Solana shred payloads, or Jito-style ShredStream gRPC entries |
| Decode | ShredDecodeMode::RawUdp uses solana-ledger Shred parsing, Reed-Solomon recovery, Shredder::deshred, and bincode Vec<Entry> decode; ShredDecodeMode::JitoGrpc receives prebuilt entry batches |
| Transactions | Entry-to-transaction flattening with slot context |
| Events | DexEvent parser migrated from sol-parser-sdk ShredStream handling |
| Extensibility | TransactionEventParser trait for custom parser plug-ins |
| Compatibility | Preserves selected generated proto/type re-exports used by older code |
Migrated parser families match the sol-parser-sdk parser surface:
- PumpFun, PumpFun v2/Mayhem, Pump fees, PumpSwap
- Raydium LaunchLab, CPMM, CLMM, AMM V4
- Orca Whirlpool
- Meteora Pools, DAMM V2, DBC, DLMM
- Token accounts, nonce accounts, selected DEX account state events, and block metadata types
- Solana low-latency trading bots and sniper bots
- Real-time PumpFun, PumpSwap, Raydium, Orca, and Meteora event feeds
- Raw shred UDP ingestion pipelines without relying on Jito ShredStream
- Jito ShredStream gRPC compatibility while migrating to raw shreds
- Multi-DEX indexers, analytics, and alerting systems
- Higher-level parser SDKs that want to reuse one shred/entry ingestion layer
Raw shred subscriptions parse transaction-visible instruction data directly from
Entry transactions. Log-only and account-update event parsers are included for
SDK compatibility, but raw shreds do not carry execution logs or account update
payloads by themselves.
[dependencies]
sol-shred-sdk = "3.0.1"Choose the source/decoder when creating the client:
use sol_shred_sdk::{RawShredConfig, ShredDecodeMode, ShredStreamClient};
let raw = ShredStreamClient::new_with_decode_mode(
ShredDecodeMode::raw_udp(RawShredConfig::default()),
).await?;
let jito = ShredStreamClient::new_with_decode_mode(
ShredDecodeMode::jito_grpc("http://127.0.0.1:10000"),
).await?;Bind a UDP socket where your Solana shred source sends raw shred datagrams:
use sol_shred_sdk::shredstream::{ShredStreamClient, ShredStreamConfig};
use sol_shred_sdk::{DexEvent, EventType, EventTypeFilter};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let config = ShredStreamConfig::low_latency()
.with_udp_bind("0.0.0.0:8001".parse()?);
let client = ShredStreamClient::new_with_config(config).await?;
client
.subscribe_with_filter_callback(
Some(EventTypeFilter::include_only(vec![
EventType::PumpFunCreate,
EventType::RaydiumCpmmSwap,
EventType::OrcaWhirlpoolSwap,
])),
|event| {
match event {
DexEvent::PumpFunCreate(create) => println!("pump create: {create:?}"),
DexEvent::RaydiumCpmmSwap(swap) => println!("cpmm swap: {swap:?}"),
DexEvent::OrcaWhirlpoolSwap(swap) => println!("orca swap: {swap:?}"),
other => println!("{other:?}"),
}
},
)
.await?;
tokio::signal::ctrl_c().await?;
client.stop().await;
Ok(())
}For consumers that prefer polling:
use sol_shred_sdk::shredstream::ShredStreamClient;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let client = ShredStreamClient::new("0.0.0.0:8001").await?;
let queue = client.subscribe().await?;
loop {
while let Some(event) = queue.pop() {
println!("{event:?}");
}
tokio::task::yield_now().await;
}
}For legacy PumpFun/Bonk-only consumers, subscribe_pumpfun,
subscribe_pumpfun_callback, and the generic subscribe_with_parser APIs are
still available.
Use the low-level API when you already own the UDP receive loop:
use std::time::Instant;
use sol_shred_sdk::{RawShredConfig, RawShredDecoder};
fn handle_packet(decoder: &mut RawShredDecoder, packet: &[u8]) {
for batch in decoder.push_packet(packet, Instant::now()) {
for entry in batch.entries {
for transaction in entry.transactions {
println!("slot={} sig={:?}", batch.slot, transaction.signatures.first());
}
}
}
}
let mut decoder = RawShredDecoder::new(RawShredConfig::default());sol-shred-sdk owns networking, shred reassembly, and transaction traversal. Higher-level crates can plug in event parsing without reimplementing shredstream handling:
use sol_shred_sdk::common::AnyResult;
use sol_shred_sdk::parser::TransactionEventParser;
use solana_sdk::transaction::VersionedTransaction;
struct MyParser;
impl TransactionEventParser for MyParser {
type Event = String;
fn parse_transaction_events<F>(
&mut self,
transaction: &VersionedTransaction,
slot: u64,
tx_index: u64,
recv_us: i64,
mut emit: F,
) -> AnyResult<usize>
where
F: FnMut(Self::Event),
{
if let Some(signature) = transaction.signatures.first() {
emit(format!("slot={slot} tx_index={tx_index} recv_us={recv_us} sig={signature}"));
return Ok(1);
}
Ok(0)
}
}RawShredConfig::udp_payload_prefix_skipshould stay0for native raw shreds.RawShredConfig::forward_slot_watermarkdefaults tofalseso out-of-order completed slots are not dropped. Enable it only when you explicitly prefer forward-only latency over completeness.ShredStreamConfig::low_latency()favors shorter reassembly waits and faster restart.ShredStreamConfig::high_throughput()increases receive buffering, tracked slots, and queue capacity.ShredDecodeMode::JitoGrpckeeps the old entries-gRPC source but still uses the same unifiedDexEventparser.- The UDP receive buffer is requested with
socket2; the OS may cap the actual value.
The raw decoder has an ignored release-mode microbench that generates real Solana merkle shreds with solana-ledger and decodes them through RawShredDecoder:
RAW_SHRED_BENCH_ITERS=10000 cargo test --release --lib bench_decode_generated_shreds -- --ignored --nocaptureCurrent local reference result:
packets_per_sec=1172196 slots_per_sec=36631 tx_per_sec=1172196
This project is licensed under the MIT License - see the LICENSE file for details.