High-performance Rust library for parsing Solana DEX events with microsecond-level latency
δΈζ | English | Website | Telegram | Discord
β Support This Project
This SDK is completely free and open source. However, maintaining and continuously updating it requires significant AI computing resources and token consumption. If this SDK helps with your development, consider making a monthly SOL donation β any amount is appreciated and helps keep this project alive!
Donation Wallet:
6oW7AXz1yRb57pYSxysuXnMs2aR1ha5rzGzReZ1MjPV8
This SDK is available in multiple languages:
| Language | Repository | Description |
|---|---|---|
| Rust | sol-parser-sdk | Ultra-low latency with SIMD optimization |
| Node.js | sol-parser-sdk-nodejs | TypeScript/JavaScript for Node.js |
| Python | sol-parser-sdk-python | Async/await native support |
| Go | sol-parser-sdk-golang | Concurrent-safe with goroutine support |
- 10-20ΞΌs parsing latency in release mode
- Zero-copy parsing with stack-allocated buffers
- SIMD-accelerated pattern matching (memchr)
- Lock-free ArrayQueue for event delivery
| Mode | Latency | Description |
|---|---|---|
| Unordered | 10-20ΞΌs | Immediate output, ultra-low latency |
| MicroBatch | 50-200ΞΌs | Micro-batch ordering with time window |
| StreamingOrdered | 0.1-5ms | Stream ordering with continuous sequence release |
| Ordered | 1-50ms | Full slot ordering, wait for complete slot |
- β Zero heap allocation for hot paths
- β SIMD pattern matching for all protocol detection
- β Static pre-compiled finders for string search
- β Inline functions with aggressive optimization
- β Event type filtering for targeted parsing
- β Conditional Create detection (only when needed)
- β Multiple order modes for latency vs ordering trade-off
Clone the repository:
cd your_project_dir
git clone https://github.com/0xfnzero/sol-parser-sdkAdd to your Cargo.toml:
[dependencies]
# Default: Borsh parser
sol-parser-sdk = { path = "../sol-parser-sdk" }
# Or: Zero-copy parser (maximum performance)
sol-parser-sdk = { path = "../sol-parser-sdk", default-features = false, features = ["parse-zero-copy"] }# Add to your Cargo.toml
sol-parser-sdk = "0.4.4"Or with the zero-copy parser (maximum performance):
sol-parser-sdk = { version = "0.4.4", default-features = false, features = ["parse-zero-copy"] }Test parsing latency with the optimized examples:
# PumpFun with detailed metrics (per-event + 10s stats)
cargo run --example pumpfun_with_metrics --release
# PumpSwap with detailed metrics (per-event + 10s stats)
cargo run --example pumpswap_with_metrics --release
# PumpSwap ultra-low latency test
cargo run --example pumpswap_low_latency --release
# PumpSwap with MicroBatch ordering
cargo run --example pumpswap_ordered --release
# Expected output:
# gRPC receive time: 1234567890 ΞΌs
# Event receive time: 1234567900 ΞΌs
# Latency: 10 ΞΌs <-- Ultra-low latency!| Description | Run Command | Source Code |
|---|---|---|
| PumpFun | ||
| PumpFun event parsing with metrics | cargo run --example pumpfun_with_metrics --release |
examples/pumpfun_with_metrics.rs |
| PumpFun trade type filtering | cargo run --example pumpfun_trade_filter --release |
examples/pumpfun_trade_filter.rs |
| PumpFun trade with ordered mode | cargo run --example pumpfun_trade_filter_ordered --release |
examples/pumpfun_trade_filter_ordered.rs |
| Quick PumpFun connection test | cargo run --example pumpfun_quick_test --release |
examples/pumpfun_quick_test.rs |
| Parse PumpFun tx by signature | TX_SIGNATURE=<sig> cargo run --example parse_pump_tx --release |
examples/parse_pump_tx.rs |
| Debug PumpFun transaction | cargo run --example debug_pump_tx --release |
examples/debug_pump_tx.rs |
| PumpSwap | ||
| PumpSwap events with metrics | cargo run --example pumpswap_with_metrics --release |
examples/pumpswap_with_metrics.rs |
| PumpSwap ultra-low latency | cargo run --example pumpswap_low_latency --release |
examples/pumpswap_low_latency.rs |
| PumpSwap with MicroBatch ordering | cargo run --example pumpswap_ordered --release |
examples/pumpswap_ordered.rs |
| Parse PumpSwap tx by signature | TX_SIGNATURE=<sig> cargo run --example parse_pumpswap_tx --release |
examples/parse_pumpswap_tx.rs |
| Debug PumpSwap transaction | cargo run --example debug_pumpswap_tx --release |
examples/debug_pumpswap_tx.rs |
| Meteora DAMM | ||
| Meteora DAMM V2 events | cargo run --example meteora_damm_grpc --release |
examples/meteora_damm_grpc.rs |
| Parse Meteora DAMM tx by signature | TX_SIGNATURE=<sig> cargo run --example parse_meteora_damm_tx --release |
examples/parse_meteora_damm_tx.rs |
| Account subscription | ||
| Token account balance updates | TOKEN_ACCOUNT=<pubkey> cargo run --example token_balance_listen --release |
examples/token_balance_listen.rs |
| Nonce account state changes | NONCE_ACCOUNT=<pubkey> cargo run --example nonce_listen --release |
examples/nonce_listen.rs |
| Mint account info | MINT_ACCOUNT=<pubkey> cargo run --example token_decimals_listen --release |
examples/token_decimals_listen.rs |
| PumpSwap pool accounts via memcmp | cargo run --example pumpswap_pool_account_listen --release |
examples/pumpswap_pool_account_listen.rs |
| All ATAs for mints | cargo run --example mint_all_ata_account_listen --release |
examples/mint_all_ata_account_listen.rs |
| ShredStream | ||
| Jito ShredStream subscription | cargo run --example shredstream_example --release |
examples/shredstream_example.rs |
| Utility | ||
| Dynamic subscription filters | cargo run --example dynamic_subscription --release |
examples/dynamic_subscription.rs |
| Debug PumpSwap account filling | cargo run --example test_account_filling --release |
examples/test_account_filling.rs |
use sol_parser_sdk::grpc::{YellowstoneGrpc, ClientConfig, OrderMode, EventTypeFilter, EventType};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create gRPC client with default config (Unordered mode)
let grpc = YellowstoneGrpc::new(
"https://solana-yellowstone-grpc.publicnode.com:443".to_string(),
None,
)?;
// Or with custom config for ordered events
let config = ClientConfig {
order_mode: OrderMode::MicroBatch, // Low latency + ordering
micro_batch_us: 100, // 100ΞΌs batch window
..ClientConfig::default()
};
let grpc = YellowstoneGrpc::new_with_config(
"https://solana-yellowstone-grpc.publicnode.com:443".to_string(),
None,
config,
)?;
// Filter for PumpFun Trade events only (ultra-fast path)
let event_filter = EventTypeFilter::include_only(vec![
EventType::PumpFunTrade
]);
// Subscribe and get lock-free queue
let queue = grpc.subscribe_dex_events(
vec![transaction_filter],
vec![account_filter],
Some(event_filter),
).await?;
// Consume events with minimal latency
tokio::spawn(async move {
let mut spin_count = 0;
loop {
if let Some(event) = queue.pop() {
spin_count = 0;
// Process event (10-20ΞΌs latency!)
println!("{:?}", event);
} else {
// Hybrid spin-wait strategy
spin_count += 1;
if spin_count < 1000 {
std::hint::spin_loop();
} else {
tokio::task::yield_now().await;
spin_count = 0;
}
}
}
});
Ok(())
}ShredStream provides ultra-low latency (~50-100ms faster than gRPC) by directly subscribing to Jito's ShredStream service:
use sol_parser_sdk::shredstream::{ShredStreamClient, ShredStreamConfig};
use sol_parser_sdk::DexEvent;
use std::sync::Arc;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create ShredStream client
let client = ShredStreamClient::new("http://127.0.0.1:10800").await?;
// Or with custom config
let config = ShredStreamConfig {
connection_timeout_ms: 5000,
request_timeout_ms: 30000,
max_decoding_message_size: 1024 * 1024 * 1024,
reconnect_delay_ms: 1000,
max_reconnect_attempts: 0, // 0 = infinite reconnect
};
let client = ShredStreamClient::new_with_config("http://127.0.0.1:10800", config).await?;
// Subscribe and get lock-free queue
let queue = client.subscribe().await?;
// Consume events
loop {
if let Some(event) = queue.pop() {
match &event {
DexEvent::PumpFunTrade(e) => {
println!("PumpFun Trade: mint={}, is_buy={}", e.mint, e.is_buy);
}
DexEvent::PumpSwapBuy(e) => {
println!("PumpSwap Buy: pool={}", e.pool);
}
_ => {}
}
} else {
std::hint::spin_loop();
}
}
}ShredStream Limitations:
- Only
static_account_keys()- transactions using ALT may have incorrect accounts - No Inner Instructions - cannot parse CPI calls
- No block_time - always 0
- tx_index is entry-level, not slot-level
- β PumpFun - Meme coin trading (ultra-fast zero-copy path, incl. v2 instructions)
- β PumpSwap - PumpFun swap protocol
- β Raydium AMM V4 - Automated Market Maker
- β Raydium CLMM - Concentrated Liquidity
- β Raydium CPMM - Concentrated Pool
- β Orca Whirlpool - Concentrated liquidity AMM
- β Meteora AMM - Dynamic AMM
- β Meteora DAMM - Dynamic AMM V2
- β Meteora DLMM - Dynamic Liquidity Market Maker
- β Bonk Launchpad - Token launch platform
Each protocol supports:
- π Trade/Swap Events - Buy/sell transactions
- π§ Liquidity Events - Deposits/withdrawals
- π Pool Events - Pool creation/initialization
- π― Position Events - Open/close positions (CLMM)
// Stack-allocated 512-byte buffer for PumpFun Trade
const MAX_DECODE_SIZE: usize = 512;
let mut decode_buf: [u8; MAX_DECODE_SIZE] = [0u8; MAX_DECODE_SIZE];
// Decode directly to stack, no heap allocation
general_purpose::STANDARD
.decode_slice(data_part.as_bytes(), &mut decode_buf)
.ok()?;// Pre-compiled SIMD finders (initialized once)
static PUMPFUN_FINDER: Lazy<memmem::Finder> =
Lazy::new(|| memmem::Finder::new(b"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"));
// 3-10x faster than .contains()
if PUMPFUN_FINDER.find(log_bytes).is_some() {
return LogType::PumpFun;
}// Ultra-fast path for single event type
if include_only.len() == 1 && include_only[0] == EventType::PumpFunTrade {
if log_type == LogType::PumpFun {
return parse_pumpfun_trade( // Zero-copy path
log, signature, slot, block_time, grpc_recv_us, is_created_buy
);
}
}// ArrayQueue with 100,000 capacity
let queue = Arc::new(ArrayQueue::<DexEvent>::new(100_000));
// Non-blocking push/pop (no mutex overhead)
let _ = queue.push(event);
if let Some(event) = queue.pop() {
// Process event
}Reduce processing overhead by filtering specific events:
let event_filter = EventTypeFilter::include_only(vec![
EventType::PumpFunTrade,
EventType::RaydiumAmmV4Swap,
EventType::RaydiumClmmSwap,
EventType::OrcaWhirlpoolSwap,
]);let event_filter = EventTypeFilter::include_only(vec![
EventType::PumpFunCreate,
EventType::RaydiumClmmCreatePool,
EventType::OrcaWhirlpoolInitialize,
]);Performance Impact:
- 60-80% reduction in processing
- Lower memory usage
- Reduced network bandwidth
Automatically detects when a token is created and immediately bought in the same transaction:
// Detects "Program data: GB7IKAUcB3c..." pattern
let has_create = detect_pumpfun_create(logs);
// Sets is_created_buy flag on Trade events
if has_create {
trade_event.is_created_buy = true;
}The SDK recognizes Pump.fun's new v2 trading instructions introduced in the Bonding Curve upgrade. Event logs from buy_v2, sell_v2, and buy_exact_quote_in_v2 are parsed with the same zero-copy path and mapped to the existing event types:
| ix_name in TradeEvent | DexEvent Variant |
|---|---|
"buy" / "buy_v2" |
DexEvent::PumpFunBuy |
"sell" / "sell_v2" |
DexEvent::PumpFunSell |
"buy_exact_sol_in" / "buy_exact_quote_in_v2" |
DexEvent::PumpFunBuyExactSolIn |
No changes are required in your event handling code β v2 events arrive through the same PumpFunTradeEvent struct with the correct ix_name field populated. Instruction discriminators for buy_v2 ([184, 23, 238, 97, 103, 197, 211, 61]), sell_v2 ([93, 246, 130, 60, 231, 233, 64, 178]), and buy_exact_quote_in_v2 ([194, 171, 28, 70, 104, 77, 91, 47]) are recognized at the instruction parser level.
Update filters without reconnecting:
grpc.update_subscription(
vec![new_transaction_filter],
vec![new_account_filter],
).await?;Choose the right balance between latency and ordering:
use sol_parser_sdk::grpc::{ClientConfig, OrderMode};
// Ultra-low latency (no ordering guarantee)
let config = ClientConfig {
order_mode: OrderMode::Unordered,
..ClientConfig::default()
};
// Low latency with micro-batch ordering (50-200ΞΌs)
let config = ClientConfig {
order_mode: OrderMode::MicroBatch,
micro_batch_us: 100, // 100ΞΌs batch window
..ClientConfig::default()
};
// Stream ordering with continuous sequence release (0.1-5ms)
let config = ClientConfig {
order_mode: OrderMode::StreamingOrdered,
order_timeout_ms: 50, // Timeout for incomplete sequences
..ClientConfig::default()
};
// Full slot ordering (1-50ms, wait for complete slot)
let config = ClientConfig {
order_mode: OrderMode::Ordered,
order_timeout_ms: 100,
..ClientConfig::default()
};let config = ClientConfig {
enable_metrics: true,
..ClientConfig::default()
};
let grpc = YellowstoneGrpc::new_with_config(endpoint, token, config)?;src/
βββ core/
β βββ events.rs # Event definitions
βββ grpc/
β βββ client.rs # Yellowstone gRPC client
β βββ buffers.rs # SlotBuffer & MicroBatchBuffer
β βββ types.rs # OrderMode, ClientConfig, filters
βββ shredstream/
β βββ client.rs # Jito ShredStream client
β βββ config.rs # ShredStreamConfig
β βββ proto/ # Protobuf definitions
βββ logs/
β βββ optimized_matcher.rs # SIMD log detection
β βββ zero_copy_parser.rs # Zero-copy parsing
β βββ pumpfun.rs # PumpFun parser
β βββ raydium_*.rs # Raydium parsers
β βββ orca_*.rs # Orca parsers
β βββ meteora_*.rs # Meteora parsers
βββ instr/
β βββ *.rs # Instruction parsers
βββ warmup/
β βββ mod.rs # Parser warmup (auto-called)
βββ lib.rs
- Replaced all
.contains()withmemmem::Finder - 3-10x performance improvement
- Pre-compiled static finders
- Stack-allocated buffers (512 bytes)
- No heap allocation in hot path
- Inline helper functions
- Early filtering at protocol level
- Conditional Create detection
- Single-type ultra-fast path
- ArrayQueue (100K capacity)
- Spin-wait hybrid strategy
- No mutex overhead
#[inline(always)]
fn read_u64_le_inline(data: &[u8], offset: usize) -> Option<u64> {
if offset + 8 <= data.len() {
let mut bytes = [0u8; 8];
bytes.copy_from_slice(&data[offset..offset + 8]);
Some(u64::from_le_bytes(bytes))
} else {
None
}
}| Protocol | Avg Latency | Min | Max |
|---|---|---|---|
| PumpFun Trade (zero-copy) | 10-15ΞΌs | 8ΞΌs | 20ΞΌs |
| Raydium AMM V4 Swap | 15-20ΞΌs | 12ΞΌs | 25ΞΌs |
| Orca Whirlpool Swap | 15-20ΞΌs | 12ΞΌs | 25ΞΌs |
| Operation | Before (contains) | After (SIMD) | Speedup |
|---|---|---|---|
| Protocol detection | 50-100ns | 10-20ns | 3-10x |
| Create event detection | 150ns | 30ns | 5x |
MIT License
- Repository: https://github.com/0xfnzero/solana-streamer
- Telegram: https://t.me/fnzero_group
- Discord: https://discord.gg/vuazbGkqQE
- Use Event Filtering - Filter at the source for 60-80% performance gain
- Run in Release Mode -
cargo build --releasefor full optimization - Test with sudo -
sudo cargo run --example basic --releasefor accurate timing - Monitor Latency - Check
grpc_recv_usand queue latency in production - Tune Queue Size - Adjust ArrayQueue capacity based on your throughput
- Spin-Wait Strategy - Tune spin count (default: 1000) for your use case
# Run tests
cargo test
# Build release binary
cargo build --release
# Generate docs
cargo doc --open