in-solana by Supanode

Trading Bot Architecture on Solana

TL;DR

  • - Separate data ingestion, decision logic, and execution into distinct components
  • - Use Geyser gRPC for data, staked connections + Jito for execution
  • - Latency matters at every layer — colocation and connection reuse are essential

A Solana trading bot has three core components: data ingestion (what is happening on-chain), decision logic (what to do about it), and execution (submitting transactions). Each layer has distinct infrastructure requirements and optimization strategies.

Architecture overview

┌─────────────────────────────────────────────────────┐
│  Data Ingestion                                      │
│  ├─ Geyser gRPC (account updates, tx stream)        │
│  ├─ WebSocket (slot, signature notifications)        │
│  └─ RPC polling (fallback, price feeds)              │
└──────────────────────┬──────────────────────────────┘
                       │ events
┌──────────────────────▼──────────────────────────────┐
│  Decision Engine                                     │
│  ├─ Opportunity detection                            │
│  ├─ Profitability calculation                        │
│  ├─ Risk checks                                      │
│  └─ Transaction construction                         │
└──────────────────────┬──────────────────────────────┘
                       │ signed tx / bundle
┌──────────────────────▼──────────────────────────────┐
│  Execution Layer                                     │
│  ├─ Staked RPC (SWQoS path)                          │
│  ├─ Jito Block Engine (bundles)                      │
│  ├─ Direct TPU (leader forwarding)                   │
│  └─ Retry + confirmation tracking                    │
└─────────────────────────────────────────────────────┘

Data ingestion

Geyser gRPC for real-time data

Geyser gRPC provides the lowest-latency access to on-chain state changes. Subscribe to:

  • Account updates: monitor specific accounts (DEX pools, order books, token accounts).
  • Transaction streams: see all transactions for a program as they are processed.
  • Slot notifications: track leader schedule and slot progression.
const stream = client.subscribe({
  accounts: {
    pools: {
      owner: [DEX_PROGRAM_ID],
      memcmp: [{ offset: 0, bytes: poolDiscriminator }],
    },
  },
});

WebSocket for targeted monitoring

Use WebSocket subscriptions for specific accounts or signatures when Geyser gRPC is not available:

connection.onAccountChange(poolAddress, (accountInfo) => {
  // Pool state changed — check for opportunity
});

RPC polling as fallback

For data that does not need sub-second latency (token prices from oracles, global state), periodic RPC polling is sufficient.

Decision engine

The decision engine evaluates incoming data and decides whether to act. Key considerations:

Speed vs. accuracy tradeoff

  • Simpler checks first: filter obvious non-opportunities before expensive computation.
  • Pre-compute when possible: cache pool states, pre-calculate swap routes, maintain local order books.
  • Avoid RPC calls in the hot path: every RPC roundtrip adds 20–100ms latency.

Transaction construction

Pre-build transaction templates and fill in variable data (amounts, prices, accounts) at execution time:

// Pre-built instruction template
const swapIx = createSwapInstruction({
  pool: poolAddress,
  userSource: sourceAta,
  userDestination: destAta,
  amount: 0, // filled at execution time
});

// At execution time, update only the variable field
swapIx.data.writeUInt64LE(computedAmount, AMOUNT_OFFSET);

Profitability calculation

Account for all costs:

  • Base fee (5,000 lamports per signature).
  • Priority fee (variable, market-dependent).
  • Jito tip (if using bundles).
  • Price impact / slippage.
  • Account rent (if creating accounts).

Execution layer

Multi-path sending

Send through multiple paths simultaneously for highest landing probability:

  1. Staked RPC — transactions reach the leader’s staked TPU queue.
  2. Jito bundles — for atomic multi-transaction sequences.
  3. Direct TPU — connect directly to the next leader’s TPU endpoint.

Leader-aware routing

Send transactions to the current and upcoming leader nodes, not just any validator:

const leaders = await connection.getSlotLeaders(currentSlot, 4);
// Send directly to leader[0] (current) and leader[1] (next)

Confirmation tracking

Track transaction status without blocking:

const sig = await sendTransaction(tx);
// Non-blocking confirmation check
connection.onSignature(sig, (result) => {
  if (result.err) handleFailure(sig, result.err);
  else handleSuccess(sig);
});

Infrastructure considerations

Latency budget

A competitive bot’s latency budget from event to transaction landing:

StageTargetNotes
Event detection<10msGeyser gRPC, same region
Decision + construction<5msIn-memory, pre-computed
Signing<1msEd25519, CPU-bound
Network to leader<10msStaked connection, same region
Total<30msFrom event to leader receipt

Colocation

Running your bot in the same data center as major validators reduces network latency. Key regions:

  • North America (many Solana validators).
  • Europe (growing validator presence).

Connection management

  • Keep connections warm: reuse HTTP/2 and QUIC connections.
  • Pre-connect to leaders: establish connections to upcoming leaders before their slot.
  • Connection pooling: maintain pools to multiple RPC endpoints.

Common bot types

TypeData sourceExecution pathLatency sensitivity
ArbitrageDEX pool statesJito bundlesVery high
LiquidationLending protocol accountsJito bundlesHigh
SniperNew pool creation eventsStaked RPC + JitoVery high
Market makerOrder book + oracleStaked RPCMedium-high
DCA / rebalancerPrice feedsStandard RPCLow

FAQ

What programming language should I use? Rust for lowest latency (zero-cost abstractions, no GC pauses). TypeScript for prototyping and lower-frequency strategies. Many teams prototype in TypeScript and rewrite hot paths in Rust.

How much SOL do I need to start? Depends on the strategy. Arbitrage requires capital for the trade. Liquidation bots need capital to repay loans. A simple monitoring bot needs only enough for transaction fees (~0.1 SOL).

Should I use Jito for every transaction? No. Jito bundles only land during Jito validator slots (~15–20% of slots). Use Jito for atomic sequences and standard sending for single transactions that do not require ordering guarantees.

How do I test a trading bot safely? Start on devnet with faucet SOL. Then test on mainnet with tiny amounts. Monitor landed vs. failed transactions, slippage, and profitability before scaling capital.

Updated: