in-solana by Supanode

Reducing Latency for Solana Bots

TL;DR

  • - Colocation in the same data center as validators saves 10–50ms per transaction
  • - Pre-compute everything possible before the opportunity arrives
  • - Leader-aware routing sends directly to the current and next leader

In competitive Solana trading, every millisecond matters. A bot that detects an opportunity 10ms earlier and lands a transaction 10ms faster wins the arbitrage, liquidation, or snipe. This guide covers practical techniques to minimize latency at every layer.

Latency breakdown

A transaction’s lifecycle from event detection to confirmation:

StageTypical latencyOptimized latency
Event detection (Geyser gRPC)5–20ms1–5ms
Decision logic5–50ms<1ms
Transaction construction2–10ms<1ms
Signing (Ed25519)<1ms<1ms
Network to leader (unstaked)20–100ms5–15ms
Network to leader (staked, colocated)5–15ms1–5ms
Total37–191ms8–27ms

Colocation

Why it matters

Network latency between your bot and the validator leader is irreducible physics. Light through fiber travels at roughly 200km/ms. Running in a data center that hosts many Solana validators reduces this to near-zero.

Key data center regions

Major Solana validator concentrations (as of 2026-10-21):

  • North America: multiple US data centers (NY, Chicago, Dallas, LA).
  • Europe: Frankfurt, Amsterdam.
  • Asia: Tokyo.

Choose a location with the highest concentration of high-stake validators, as these produce the most leader slots.

Bare metal vs. cloud

For lowest latency, bare metal servers outperform cloud VMs:

  • No hypervisor overhead.
  • Dedicated NIC (no shared networking).
  • Consistent performance (no noisy neighbors).

Cloud VMs add 0.5–5ms of variable latency from virtualization and shared networking.

Connection optimization

Keep connections warm

Establishing a new TCP/TLS/QUIC connection adds 1–3 round trips (5–15ms). Reuse connections:

// Keep a persistent HTTP/2 connection
const agent = new https.Agent({
  keepAlive: true,
  maxSockets: 10,
});

// Reuse for all RPC calls
const response = await fetch(rpcUrl, {
  agent,
  method: "POST",
  body: JSON.stringify(rpcRequest),
});

Pre-connect to upcoming leaders

Monitor the leader schedule and establish connections to the next 2–4 leaders before their slots begin:

const leaderSchedule = await connection.getSlotLeaders(currentSlot, 8);
for (const leader of leaderSchedule) {
  preConnect(getTPUAddress(leader));
}

Pre-computation

Pre-build transaction templates

Construct transaction skeletons with all fixed fields. At execution time, fill only the variable data:

// Pre-built at startup
const template = buildSwapTransaction({
  pool: poolAddress,
  userAccounts: myAccounts,
  program: dexProgram,
});

// At execution time (~0.1ms instead of ~5ms)
template.setAmount(computedAmount);
template.setBlockhash(latestBlockhash);
signer.sign(template);

Cache account data locally

Maintain an in-memory cache of frequently accessed accounts, updated via Geyser or WebSocket subscriptions:

const accountCache = new Map<string, AccountInfo>();

// Updated in real-time via Geyser
geyserStream.on("accountUpdate", (pubkey, info) => {
  accountCache.set(pubkey, info);
});

// Decision logic reads from cache (0ms) instead of RPC (20ms+)
const poolState = accountCache.get(poolAddress);

Pre-compute blockhash

Keep a fresh blockhash ready:

let latestBlockhash = await connection.getLatestBlockhash();

// Refresh every 20 seconds (blockhash is valid for ~60–90 seconds)
setInterval(async () => {
  latestBlockhash = await connection.getLatestBlockhash();
}, 20000);

Leader-aware routing

Direct TPU connection

Instead of sending through the RPC node (which adds a hop), connect directly to the leader’s TPU:

Standard: Bot → RPC → Leader TPU (2 hops, 20–40ms)
Direct:   Bot → Leader TPU (1 hop, 5–15ms)

Multi-leader sending

Send to both the current and next leader for redundancy:

const leaders = await connection.getSlotLeaders(currentSlot, 2);
await Promise.all([
  sendToTPU(leaders[0], signedTx),
  sendToTPU(leaders[1], signedTx),
]);

Language and runtime

Rust vs. TypeScript

For latency-critical paths:

AspectRustTypeScript
Decision logic<0.1ms1–5ms
JSON parsing<0.1ms0.5–2ms
Transaction construction<0.1ms1–3ms
Ed25519 signing~0.05ms~0.1ms
GC pausesNone0–50ms

Rust eliminates garbage collection pauses and provides predictable, low-latency execution. Many competitive bots use Rust for the hot path and TypeScript for management and monitoring.

Zero-allocation patterns

In Rust, avoid heap allocations in the hot path:

  • Pre-allocate buffers for serialization.
  • Use stack-allocated arrays for account lists.
  • Reuse transaction buffers between iterations.

Measuring latency

Timestamps at every stage

const t0 = performance.now(); // Event received
// ... decision logic
const t1 = performance.now(); // Decision made
// ... transaction built
const t2 = performance.now(); // Transaction sent
// ... confirmation received
const t3 = performance.now(); // Confirmed

metrics.record({
  decisionMs: t1 - t0,
  buildMs: t2 - t1,
  landingMs: t3 - t2,
  totalMs: t3 - t0,
});

Track p50, p95, and p99 latencies. Optimize for p95 — consistent performance matters more than best-case speed.

FAQ

How much does colocation improve latency? Typically 10–50ms reduction, depending on your current distance from validators. From a residential connection, the improvement can be 50–100ms.

Is it worth using Rust instead of TypeScript? For competitive strategies (arbitrage, sniping), yes. Rust reduces decision and construction latency from 5–10ms to <1ms and eliminates GC pauses. For lower-frequency strategies, TypeScript is usually sufficient.

Can I reduce latency by running my own validator? Running a staked validator gives you direct SWQoS access and eliminates the RPC hop. However, it requires significant capital (staked SOL) and operational effort. A managed staked RPC connection provides most of the benefit.

What is the theoretical minimum latency? Physics limits: light through fiber at ~200km/ms. If you are in the same data center as the leader, network latency is <1ms. The practical minimum is roughly 5–10ms including computation and signing.

Updated: