Infrastructure

PumpFun Sniper Technical Deep-Dive: Architecture, Performance, and ShredStream Integration

December 10, 2025AllenHark Team

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

  1. Architecture Overview
  2. ShredStream Integration
  3. Detection Pipeline
  4. Transaction Execution
  5. AllenHark Relay Integration
  6. Performance Benchmarks
  7. Configuration Deep-Dive
  8. 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:

  1. Zero-cost abstractions: No runtime overhead for high-level constructs
  2. No garbage collection: Eliminates unpredictable GC pauses (10-50ms in Node.js)
  3. Memory safety: Prevents crashes without runtime checks
  4. Lock-free concurrency: Uses atomic operations and channels for thread communication
  5. LLVM optimization: Produces highly optimized machine code

Benchmark: Transaction Parsing (10,000 iterations)

LanguageAverageP99Memory
Rust12 μs18 μs22 MB
Node.js450 μs2,100 μs450 MB
Python1,200 μs3,500 μs680 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:

  1. Detect transactions before RPC confirmation (200-400ms advantage)
  2. Process transactions in parallel with validators
  3. 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:

  1. HTTP overhead: 20-50ms for connection setup and headers
  2. RPC processing: 10-30ms for validation and forwarding
  3. Gossip propagation: 100-300ms to reach the leader
  4. 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

MethodLatencySuccess RateCost
AllenHark Relay (QUIC)0.1-2ms98%0.001 SOL
AllenHark Relay (HTTPS)5-10ms95%0.001 SOL
Standard RPC200-500ms60%Free
Jito (Public)50-100ms80%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 to false for 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:

  1. Low-level infrastructure access (ShredStream)
  2. Optimized execution (Rust, lock-free architecture)
  3. Direct network connectivity (AllenHark Relay, QUIC)
  4. 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