The PumpFun Sniper represents a significant advancement in Solana trading bot architecture. This technical deep-dive explores the engineering decisions, performance optimizations, and infrastructure integrations that enable first-block token sniping with sub-millisecond detection times.
Table of Contents
- Architecture Overview
- ShredStream Integration
- Detection Pipeline
- Transaction Execution
- AllenHark Relay Integration
- Performance Benchmarks
- Configuration Deep-Dive
- Advanced Features
Architecture Overview
The PumpFun Sniper is built on a lock-free, event-driven architecture designed to minimize latency at every stage of the detection-to-execution pipeline.
Core Components
graph LR
A[ShredStream] -->|UDP Packets| B[Shred Processor]
B -->|Parsed Transactions| C[Filter Layer]
C -->|PumpFun Creates| D[Signal Generator]
D -->|Token Mint| E[Transaction Builder]
E -->|Signed TX| F[AllenHark Relay]
F -->|QUIC/UDP| G[Validator TPU]
Latency Budget:
- ShredStream ingestion: 20-50 μs
- Transaction parsing: 10-15 μs
- Filter evaluation: 5-10 μs
- Signal generation: 30-50 μs
- Transaction building: 80-100 μs
- Relay submission: 100-200 μs
Total internal latency: < 500 μs (0.5ms)
Why Rust?
The bot is implemented in Rust for several critical reasons:
- Zero-cost abstractions: No runtime overhead for high-level constructs
- No garbage collection: Eliminates unpredictable GC pauses (10-50ms in Node.js)
- Memory safety: Prevents crashes without runtime checks
- Lock-free concurrency: Uses atomic operations and channels for thread communication
- LLVM optimization: Produces highly optimized machine code
Benchmark: Transaction Parsing (10,000 iterations)
| Language | Average | P99 | Memory |
|---|---|---|---|
| Rust | 12 μs | 18 μs | 22 MB |
| Node.js | 450 μs | 2,100 μs | 450 MB |
| Python | 1,200 μs | 3,500 μs | 680 MB |
ShredStream Integration
ShredStream provides access to raw Turbine protocol data - the UDP packets validators exchange before block finalization. This enables 0-slot detection: seeing transactions in the same slot they're created.
How ShredStream Works
Solana validators use the Turbine protocol to propagate blocks as "shreds" (fragments). By tapping into this stream, we can:
- Detect transactions before RPC confirmation (200-400ms advantage)
- Process transactions in parallel with validators
- Avoid RPC polling overhead (eliminates 500ms+ latency)
Integration Code
The bot connects to a local ShredStream proxy via gRPC:
use tonic::transport::Channel;
use shredstream_proto::shred_stream_client::ShredStreamClient;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Connect to local ShredStream proxy
let channel = Channel::from_static("http://127.0.0.1:9999")
.connect()
.await?;
let mut client = ShredStreamClient::new(channel);
// Subscribe to transaction stream
let request = SubscribeRequest {
filter_accounts: vec![PUMPFUN_PROGRAM_ID.to_string()],
..Default::default()
};
let mut stream = client.subscribe(request).await?.into_inner();
// Process shreds in real-time
while let Some(shred) = stream.message().await? {
process_shred(shred).await;
}
Ok(())
}
Performance Characteristics
- Latency: 20-50 μs from validator broadcast to bot receipt
- Throughput: 50,000+ shreds/second
- Reliability: Direct UDP multicast, no intermediary hops
Detection Pipeline
The detection pipeline uses a multi-stage filter to minimize CPU overhead while maintaining 100% detection accuracy.
Stage 1: Program ID Filter
const PUMPFUN_PROGRAM: Pubkey = pubkey!("6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P");
fn quick_filter(tx: &VersionedTransaction) -> bool {
// Fast path: check if PumpFun program is in account keys
tx.message.static_account_keys().contains(&PUMPFUN_PROGRAM)
}
This eliminates 99.9% of transactions (votes, transfers, etc.) in < 5 μs.
Stage 2: Instruction Discriminator
const CREATE_DISCRIMINATOR: [u8; 8] = [24, 30, 200, 40, 5, 28, 7, 119];
fn is_create_instruction(ix: &CompiledInstruction) -> bool {
ix.data.len() >= 8 && ix.data[..8] == CREATE_DISCRIMINATOR
}
The discriminator is computed as sha256("global:create")[..8] and identifies token creation instructions.
Stage 3: Spam Filter
The spam filter evaluates multiple heuristics to avoid rug pulls:
pub struct SpamFilter {
min_buy_sol: f64,
max_buy_sol: f64,
min_tokens: u64,
min_accounts: usize,
}
impl SpamFilter {
fn evaluate(&self, tx: &ParsedCreate) -> bool {
// Check dev buy amount
if tx.dev_buy_sol < self.min_buy_sol || tx.dev_buy_sol > self.max_buy_sol {
return false;
}
// Check token amount
if tx.token_amount < self.min_tokens {
return false;
}
// Check account count (malformed txs have fewer accounts)
if tx.accounts.len() < self.min_accounts {
return false;
}
true
}
}
Stage 4: Blacklist Check
The blacklist is stored in a lock-free concurrent hash set for O(1) lookups:
use dashmap::DashSet;
static BLACKLIST: Lazy<DashSet<Pubkey>> = Lazy::new(DashSet::new);
fn is_blacklisted(creator: &Pubkey) -> bool {
BLACKLIST.contains(creator)
}
The blacklist is reloaded every 10 seconds from blacklist.jsonl without blocking the hot path.
Transaction Execution
Once a token passes all filters, the bot must build and submit a transaction in < 100 μs.
Pre-computed Transaction Template
To minimize latency, we pre-build a transaction template with placeholder accounts:
struct TransactionTemplate {
instructions: Vec<Instruction>,
recent_blockhash: Hash,
payer: Pubkey,
}
impl TransactionTemplate {
fn instantiate(&self, mint: Pubkey) -> Transaction {
// Clone template and replace mint address
let mut tx = self.clone();
tx.instructions[0].accounts[MINT_INDEX] = AccountMeta::new(mint, false);
// Sign transaction
tx.sign(&[&PAYER_KEYPAIR], self.recent_blockhash);
tx
}
}
Blockhash Management
A background thread maintains a cache of the latest blockhash:
static BLOCKHASH_CACHE: Lazy<RwLock<Hash>> = Lazy::new(|| RwLock::new(Hash::default()));
async fn blockhash_updater(rpc_client: Arc<RpcClient>) {
loop {
if let Ok(hash) = rpc_client.get_latest_blockhash().await {
*BLOCKHASH_CACHE.write().unwrap() = hash;
}
tokio::time::sleep(Duration::from_millis(400)).await; // Update every slot
}
}
This ensures we always have a fresh blockhash without blocking the critical path.
AllenHark Relay Integration
The AllenHark Relay provides optimized transaction submission with sub-millisecond dispatch times.
Why Use a Relay?
Standard RPC submission has several bottlenecks:
- HTTP overhead: 20-50ms for connection setup and headers
- RPC processing: 10-30ms for validation and forwarding
- Gossip propagation: 100-300ms to reach the leader
- No priority routing: Transactions may take multiple hops
AllenHark Relay eliminates these bottlenecks:
- QUIC protocol: 0-RTT connection resumption (0.1ms)
- Direct TPU connection: Physical cables to validator racks
- Priority routing: Jito bundle integration
- Persistent connections: No connection setup overhead
QUIC Integration
use quinn::{ClientConfig, Endpoint};
async fn send_via_relay(tx: &Transaction) -> Result<(), Box<dyn std::error::Error>> {
// Create QUIC endpoint
let mut endpoint = Endpoint::client("0.0.0.0:0".parse()?)?;
endpoint.set_default_client_config(ClientConfig::with_native_roots());
// Connect to AllenHark Relay
let connection = endpoint
.connect("185.191.117.237:4433".parse()?, "relay.allenhark.com")?
.await?;
// Open bidirectional stream
let (mut send, mut recv) = connection.open_bi().await?;
// Serialize transaction
let tx_bytes = bincode::serialize(tx)?;
// Send transaction with tip
let packet = build_relay_packet(&tx_bytes, TIP_LAMPORTS)?;
send.write_all(&packet).await?;
send.finish().await?;
// Read response
let response = recv.read_to_end(1024).await?;
Ok(())
}
Performance Comparison
| Method | Latency | Success Rate | Cost |
|---|---|---|---|
| AllenHark Relay (QUIC) | 0.1-2ms | 98% | 0.001 SOL |
| AllenHark Relay (HTTPS) | 5-10ms | 95% | 0.001 SOL |
| Standard RPC | 200-500ms | 60% | Free |
| Jito (Public) | 50-100ms | 80% | Variable |
Performance Benchmarks
We benchmarked the bot under realistic conditions: 1,000 token launches over 24 hours.
Detection Latency
- Median: 234 μs
- P95: 450 μs
- P99: 680 μs
- Max: 1.2 ms
End-to-End Latency (Detection → Submission)
- Median: 1.8 ms
- P95: 3.2 ms
- P99: 5.1 ms
- Max: 12 ms
Success Metrics
- Tokens detected: 1,000/1,000 (100%)
- Passed filters: 127/1,000 (12.7%)
- Successful buys: 121/127 (95.3%)
- Same-block execution: 118/121 (97.5%)
Configuration Deep-Dive
Trading Parameters
purchase_amount_sol = 0.001
slippage_basis_points = 100
live = true
purchase_amount_sol: Position size per snipe. Start with 0.001 SOL for testing.slippage_basis_points: 100 = 1%. Increase for volatile tokens.live: Set tofalsefor dry-run mode (no real transactions).
Dev Buy Filter
min_dev_buy_sol = 0.5
max_dev_buy_sol = 4.5
Filters tokens based on the developer's initial buy:
- Too low (< 0.5 SOL): Likely spam or low-effort launch
- Too high (> 4.5 SOL): Risk of immediate dump
User Volume Accumulator
The user_volume_accumulator is a PDA required by PumpFun to track trading volume:
fn derive_accumulator(user: &Pubkey) -> Pubkey {
let pumpfun = pubkey!("6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P");
let fee_program = pubkey!("pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ");
Pubkey::find_program_address(
&[
b"user-volume-accumulator",
user.as_ref(),
pumpfun.as_ref(),
],
&fee_program,
).0
}
Advanced Features
Automatic Blacklisting
The bot tracks creator performance and automatically blacklists unprofitable wallets:
fn update_blacklist(creator: Pubkey, profit: f64) {
if profit < -0.001 { // Lost more than 0.001 SOL
BLACKLIST.insert(creator);
append_to_file("blacklist.jsonl", &creator);
}
}
Profit Tracking
After each sell, the bot calculates profit including rent recovery:
fn calculate_profit(initial_balance: u64, final_balance: u64, rent: u64) -> f64 {
let profit_lamports = (final_balance + rent) as i64 - initial_balance as i64;
profit_lamports as f64 / 1e9 // Convert to SOL
}
RPC Pool Rotation
The bot rotates through multiple RPC endpoints every 20ms for load balancing and redundancy:
struct RpcPool {
endpoints: Vec<String>,
current: AtomicUsize,
}
impl RpcPool {
fn next(&self) -> &str {
let idx = self.current.fetch_add(1, Ordering::Relaxed) % self.endpoints.len();
&self.endpoints[idx]
}
}
Conclusion
The PumpFun Sniper demonstrates that competitive Solana trading requires:
- Low-level infrastructure access (ShredStream)
- Optimized execution (Rust, lock-free architecture)
- Direct network connectivity (AllenHark Relay, QUIC)
- Intelligent filtering (spam detection, blacklisting)
By combining these elements, the bot achieves sub-millisecond detection and first-block execution - a 1,000x improvement over traditional RPC-based bots.
Download the PumpFun Sniper: Latest Release
Full Documentation: PumpFun Sniper Docs
AllenHark Services: ShredStream | Relay | Co-Location