Skip to content

About

Solana ShredStream SDK/Decoder for JavaScript/TypeScript, enabling ultra-low latency Solana transaction streaming via UDP shreds from https://www.shredstream.com

Topics

Resources

Stars

3 stars

Watchers

0 watching

Forks

Latest commit

ย 

History

6 Commits

Folders and files

NameName
Last commit message
Last commit date
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

Solana ShredStream SDK for JavaScript / TypeScript

Solana ShredStream SDK/Decoder for JavaScript/TypeScript, enabling ultra-low latency Solana transaction streaming via UDP shreds from ShredStream.com

Part of the ShredStream.com ecosystem โ€” ultra-low latency Solana shred streaming via UDP.

License: MIT TypeScript Node

๐Ÿ“‹ Prerequisites

  1. Create an account on ShredStream.com
  2. Launch a Shred Stream and pick your region (Frankfurt, Amsterdam, Singapore, Chicago, and more)
  3. Enter your server's IP address and the UDP port where you want to receive shreds
  4. Open your firewall for inbound UDP traffic on that port (e.g. configure your cloud provider's security group)
  5. Install Node.js 18+ and npm:
    # Linux (Ubuntu/Debian)
    curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
    sudo apt install -y nodejs
    
    # macOS
    brew install node

๐ŸŽ Want to try before you buy? Open a ticket on our Discord to request a free trial.

๐Ÿ“ฆ Installation

npm install shredstream

โšก Quick Start

Create a file index.ts:

import { ShredListener } from 'shredstream';
import { VersionedTransaction } from '@solana/web3.js';

const PORT = parseInt(process.env.SHREDSTREAM_PORT || '8001');
const listener = ShredListener.bind(PORT);

while (true) {
  const batch = listener.nextTransactionSync();
  if (batch === null) {
    throw new Error(`shredstream listener stopped: ${listener.lastIoErrorKind ?? 'unknown'}`);
  }
  for (const raw of batch.transactions) {
    const tx = VersionedTransaction.deserialize(new Uint8Array(raw));
    console.log(`slot ${batch.slot}: ${tx.signatures[0]}`);
  }
}

Transactions may be version 1 (leading byte 0x81); decode them with a library that supports it.

nextTransactionSync() is the lowest-latency path โ€” recommended for dedicated MEV / sniping consumers. A null return is a terminal state (socket closed, fatal I/O like BrokenPipe or NetworkDown); inspect listener.lastIoErrorKind and let your process supervisor (systemd, Docker, k8s) restart you. If you need to mix with other Node async I/O (dgram.send, HTTP, timers), use for await (const batch of listener) instead โ€” see Performance.

Run it:

npx tsx index.ts

๐Ÿ“– API Reference

ShredListener

  • ShredListener.bind(port) โ€” Bind with defaults (64 MB recv buf, 3 slot window, FEC enabled)
  • ShredListener.bindWithOptions(port, opts) โ€” Custom configuration
  • ShredListener.offline() โ€” Bind on ephemeral port; drive via handlePacket()
  • ShredListener.fromFd(fd, opts) โ€” Adopt an existing UDP file descriptor
  • listener.nextTransactionSync() โ€” Sync single-pull (lowest latency, MEV-grade โ€” see Performance)
  • for await (const { slot, transactions } of listener) โ€” Async iterator, compatible with other Node async I/O
  • await listener.nextTransaction() โ€” Async single-pull, resolves to { slot, transactions } | null
  • await listener.nextShred() โ€” Raw shred header { slot, index, payloadLen }
  • listener.handlePacket(buf: Buffer) โ€” Inject an externally-received UDP datagram
  • listener.close() โ€” Release the socket

Options

interface ListenerOptions {
  recvBuf?: number;
  maxAge?: bigint;
  busyPollUs?: number;
  disableBusyPoll?: boolean;
  poolSize?: number;
  enableFec?: boolean;
  disableSalvageDelivery?: boolean;
  accumulator?: { maxFecSetsPerSlot?: number; stuckBatchTimeoutMs?: number };
}
Field Default Description
recvBuf 64 MB SO_RCVBUF size. Kernel rmem_max must allow it
maxAge 3n Slot retention window
busyPollUs 200 Linux SO_BUSY_POLL ยตs (best-effort)
disableBusyPoll false Pass true to disable busy poll explicitly
poolSize 4096 Number of 2 KiB buffers in the zero-copy pool
enableFec true Reed-Solomon recovery on dropped data shreds
disableSalvageDelivery false Drop salvaged tail txs for lowest p99
accumulator.maxFecSetsPerSlot 32 Per-slot FEC buffer cap
accumulator.stuckBatchTimeoutMs 50 Force-finalize a stuck batch after this delay

Metrics

Read-only getters mirroring the Rust crate. All u64 counters are bigint:

Group Getters
Throughput dataShredCountTotal, codeShredCountTotal, bytesReceived, slotCount
Decoder batchesDecodedStreamingTotal, batchesDecodedFallbackTotal, batchesSkippedTotal, decodeErrorsTotal
FEC fecRecoveriesTotal, fecRecoveryFailuresTotal, fecSetsDiscardedUnusedTotal, fecSetsEvictedEarlyTotal
Unparseable unparseablePackets, unparseableTooShort, unparseableVariant, unparseablePayload, unparseableSlotRange
Slot lifecycle slotsCompletedTotal, slotsEvictedByAge, droppedKnownSlots, harvestedBatchesTotal, salvagedTailTxTotal
Tail control batchesForceFinalizedCorruptedTotal, batchesForceFinalizedTimeoutTotal
Pool / I-O poolExhaustedCount, lastIoErrorKind, busyPollActive, localAddress

Helpers

  • classifyVariant(byte: number) โ†’ 'DataLegacy' | 'CodeLegacy' | 'DataMerkleUnchained' | 'DataMerkleResigned' | 'CodeMerkleUnchained' | 'CodeMerkleResigned' | null
  • variantProofSize(byte) / variantResigned(byte) / variantMerkleSuffix(byte) โ€” Merkle metadata
  • pinCurrentThreadToCpu(cpuId: number) โ€” Best-effort thread pinning. Linux: sched_setaffinity; macOS: hint; other: no-op

๐ŸŽฏ Use Cases

ShredStream.com shred data powers a wide range of latency-sensitive strategies โ€” HFT, MEV extraction, token sniping, copy trading, liquidation bots, on-chain analytics, and more.

๐Ÿ’Ž PumpFun Token Sniping

ShredStream.com SDK detects PumpFun token creations ~499ms before they appear on PumpFun's live feed โ€” tested across 25 consecutive detections:

ShredStream.com SDK vs PumpFun live feed โ€” ~499ms advantage

Ready-to-run example included: see examples/pumpfun_creates.ts. Run with npx tsx examples/pumpfun_creates.ts [port].

๐ŸŽ๏ธ Performance

Two delivery APIs, depending on what your process does between batches.

listener.nextTransactionSync() โ€” MEV-grade hot loop (default in Quick Start)

Synchronous pull. Returns { slot, transactions } | null directly โ€” no Promise allocation, no Buffer.from round-trip, no event-loop hop. Fastest possible delivery.

while (true) {
  const batch = listener.nextTransactionSync();
  if (batch === null) {
    throw new Error(`shredstream listener stopped: ${listener.lastIoErrorKind ?? 'unknown'}`);
  }
  for (const raw of batch.transactions) {
    // synchronous decode + outbound send (e.g. raw-socket, RDMA, etc.)
  }
}

โš ๏ธ Sync mode blocks the libuv event loop while parked on UDP recv. Any other async I/O in this process (dgram.send, HTTP, fs.readFile, setTimeout) will not progress while sync mode is parked. Use sync mode only in a dedicated process or a worker_thread whose only job is to consume shreds. For mixed workloads, switch to for await below.

for await / await listener.nextTransaction() โ€” mixed async workloads

Async-await flow. Compatible with any other Node async I/O (dgram, fs, HTTP clients, timers). The iterator parks on the libuv event loop while waiting on UDP.

for await (const { slot, transactions } of listener) {
  // safe to await other async work here
}

โš™๏ธ Configuration

OS Tuning

# Linux
sudo sysctl -w net.core.rmem_max=67108864
sudo sysctl -w net.core.busy_read=200

# macOS
sudo sysctl -w kern.ipc.maxsockbuf=67108864

๐Ÿš€ Launch a Shred Stream

Need a feed? Launch a Solana Shred Stream on ShredStream.com โ€” sub-millisecond delivery, multiple global regions, 5-minute setup.

๐Ÿ”— Links

๐Ÿ“„ License

MIT โ€” ShredStream.com

About

Solana ShredStream SDK/Decoder for JavaScript/TypeScript, enabling ultra-low latency Solana transaction streaming via UDP shreds from https://www.shredstream.com

Topics

Resources

Stars

3 stars

Watchers

0 watching

Forks

Contributors

Languages