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

A comprehensive technical analysis of the PumpFun Sniper bot architecture, including ShredStream integration, pre-confirmation detection, AllenHark Relay optimization, and design tradeoffs.

  • December 10, 2025
  • Infrastructure
  • AllenHark Engineering
A quiet stack of pagesAn abstract drawing of light on the page's dark ground. A neat stack of thin, solid pages, the top one lit, with faint streams of light drifting toward it from the left.A quiet stack of pagesAn abstract drawing of light on the page's dark ground. A neat stack of thin, solid pages, the top one lit, with faint streams of light drifting toward it from the left.

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 let it act on a launch while the block is still being produced, before RPC confirmation.

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

Text
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]

Where the time goes, in order:

  • ShredStream ingestion: receiving the entry from the shred feed
  • Transaction parsing: decoding it into instructions
  • Filter evaluation: program ID, discriminator and spam checks
  • Signal generation: deciding to buy
  • Transaction building: filling the pre-built template and signing
  • Relay submission: handing the signed transaction to AllenHark Relay

Each stage is instrumented, so you can see your own budget rather than trusting ours.

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, which Node.js and Python both have
  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

Transaction parsing by language

LanguageGarbage collectorMemory layout
RustNoneStructs, no boxing
Node.jsYes, with pausesObjects on the heap
PythonYes, with pausesObjects on the heap

ShredStream Integration

ShredStream provides access to raw Turbine protocol data - the UDP packets validators exchange before block finalization. This lets the bot see a transaction while its block is still being built, before any RPC reports it.

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
  2. Process transactions in parallel with validators
  3. Avoid RPC polling overhead (no waiting for the next poll)

Integration Code

The bot connects to a local ShredStream proxy via gRPC:

Rust
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

  • Timing: data arrives while the block is still being produced
  • Throughput: the full shred stream of every slot, votes included
  • Delivery: pushed to you, no polling

Detection Pipeline

The detection pipeline uses a multi-stage filter, cheapest check first, so almost no CPU is spent on transactions that are not PumpFun creates.

Stage 1: Program ID Filter

Rust
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 throws away nearly every transaction (votes, transfers, other programs) with one lookup in the account keys.

Stage 2: Instruction Discriminator

Rust
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:

Rust
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:

Rust
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 has to build and submit a transaction before everyone else who saw the same create.

Pre-computed Transaction Template

To minimize latency, we pre-build a transaction template with placeholder accounts:

Rust
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:

Rust
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 straight to the leader's TPU.

Why Use a Relay?

Standard RPC submission has several bottlenecks:

  1. HTTP overhead: connection setup and headers on every request
  2. RPC processing: validation and forwarding by the node
  3. Gossip propagation: the transaction has to find the leader
  4. Shared path: Transactions may pass through several nodes on the way

AllenHark Relay eliminates these bottlenecks:

  • QUIC protocol: a persistent connection, so no handshake when the transaction is ready
  • Straight to the leader: private connections to the current leader, never public gossip
  • Jito bundles: up to five transactions, atomic and in order
  • Persistent connections: No connection setup overhead

QUIC Integration

Rust
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("88.216.36.108: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)?;
    
    // Build the relay packet and send it
    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

MethodDispatch costCost
AllenHark Relay (QUIC)No per-request handshake — the connection is already openRelay tip per transaction, from 0.001 SOL
AllenHark Relay (HTTPS)One HTTPS request per transactionRelay tip per transaction, from 0.001 SOL
Standard RPCValidation, then gossip propagationFree
Jito (Public)Auction round before submissionVariable

What to Measure

We don't publish latency or win-rate figures for this bot, because the numbers that matter are yours: they depend on where your box sits relative to the relay and the stake, how aggressive your filters are, and what the rest of the field is doing on the day. Anyone quoting a P99 without telling you the vantage point is selling you something.

Instrument these yourself and you'll know whether your setup is actually competitive:

  • Detection: time from the entry arriving on the shred feed to your filter firing.
  • End-to-end: detection → signed transaction handed to the relay.
  • Landing: what fraction of attempts land in the intended slot, not just eventually.
  • Filter yield: how many launches survive your filters, and how many of those you'd actually have wanted.

Configuration Deep-Dive

Trading Parameters

TOML
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 basis points is one hundredth of the price. Increase for volatile tokens.
  • live: Set to false for dry-run mode (no real transactions).

Dev Buy Filter

TOML
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:

Rust
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:

Rust
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:

Rust
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 for load balancing and redundancy:

Rust
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 detects a launch while the block is still being produced — before RPC confirmation — and goes for first-block execution, which an RPC-polling bot structurally cannot do.

Download the PumpFun Sniper: Latest Release

Full Documentation: PumpFun Sniper Docs

AllenHark Services: ShredStream | Relay | Co-Location