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:
- Staked RPC — transactions reach the leader’s staked TPU queue.
- Jito bundles — for atomic multi-transaction sequences.
- 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:
| Stage | Target | Notes |
|---|---|---|
| Event detection | <10ms | Geyser gRPC, same region |
| Decision + construction | <5ms | In-memory, pre-computed |
| Signing | <1ms | Ed25519, CPU-bound |
| Network to leader | <10ms | Staked connection, same region |
| Total | <30ms | From 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
| Type | Data source | Execution path | Latency sensitivity |
|---|---|---|---|
| Arbitrage | DEX pool states | Jito bundles | Very high |
| Liquidation | Lending protocol accounts | Jito bundles | High |
| Sniper | New pool creation events | Staked RPC + Jito | Very high |
| Market maker | Order book + oracle | Staked RPC | Medium-high |
| DCA / rebalancer | Price feeds | Standard RPC | Low |
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: