Building a Solana trading bot is one of the most practical ways to learn blockchain development while solving a real problem. Instead of manually swapping tokens through a UI, you can write code that monitors prices, executes trades, and manages positions automatically.
This guide walks you through building a simple trading bot from scratch using TypeScript, the Jupiter swap API, and Helius webhooks for real-time event monitoring. This is not a guide for building a high-frequency MEV bot — it is an introduction to the core concepts you need to automate trading on Solana.
What You Need Before Starting
Technical Requirements
- Node.js 18+ installed
- TypeScript (we will use it throughout — types prevent expensive bugs)
- A Solana wallet with some SOL for testing (start on devnet, move to mainnet when ready)
- An RPC endpoint — free tiers from Helius or QuickNode work for development
- Basic understanding of async/await and HTTP APIs
Architecture Overview
Your bot will have three components:
- Price monitoring: Watches token prices via Jupiter's price API or Helius webhooks
- Decision logic: Determines when to buy or sell based on your strategy
- Execution engine: Submits swap transactions through Jupiter's swap API
Let's build each component.
Step 1: Project Setup
Initialize a TypeScript project with the required dependencies:
mkdir solana-bot && cd solana-bot
npm init -y
npm install @solana/web3.js @solana/spl-token bs58 dotenv
npm install -D typescript @types/node ts-node
npx tsc --init
Create a .env file for your configuration:
PRIVATE_KEY=your_base58_private_key_here
RPC_URL=https://mainnet.helius-rpc.com/?api-key=YOUR_KEY
HELIUS_API_KEY=your_helius_api_key
Security warning: Never commit your private key to Git. Never use a wallet with significant funds for bot development. Start with a fresh wallet containing only what you are willing to lose.
Basic Connection Setup
// src/config.ts
import { Connection, Keypair } from '@solana/web3.js';
import bs58 from 'bs58';
import dotenv from 'dotenv';
dotenv.config();
export const connection = new Connection(process.env.RPC_URL!, {
commitment: 'confirmed',
});
export const wallet = Keypair.fromSecretKey(
bs58.decode(process.env.PRIVATE_KEY!)
);
export const USDC_MINT = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v';
export const SOL_MINT = 'So11111111111111111111111111111111111111112';
Step 2: Price Monitoring with Jupiter
Jupiter provides a free price API that returns current token prices across all Solana DEXs. This is the simplest way to track prices.
// src/price.ts
interface JupiterPrice {
id: string;
type: string;
price: string;
}
export async function getTokenPrice(mintAddress: string): Promise<number> {
const response = await fetch(
`https://api.jup.ag/price/v2?ids=${mintAddress}`
);
const data = await response.json();
return parseFloat(data.data[mintAddress]?.price || '0');
}
export async function monitorPrice(
mintAddress: string,
intervalMs: number,
callback: (price: number) => void
): Promise<NodeJS.Timeout> {
const interval = setInterval(async () => {
try {
const price = await getTokenPrice(mintAddress);
callback(price);
} catch (error) {
console.error('Price fetch error:', error);
}
}, intervalMs);
return interval;
}
This polls the Jupiter price API at your specified interval. For a simple bot, polling every 5-10 seconds is reasonable. If you need real-time data (sub-second), you will need websockets or gRPC — which we cover later.
Step 3: Executing Swaps via Jupiter API
Jupiter's swap API handles route finding and transaction construction. Your bot sends the swap parameters, Jupiter returns a transaction, and you sign and submit it.
// src/swap.ts
import { connection, wallet } from './config';
import { VersionedTransaction } from '@solana/web3.js';
interface SwapQuote {
inputMint: string;
outputMint: string;
amount: string;
slippageBps: number;
}
export async function getQuote(params: SwapQuote) {
const url = new URL('https://quote-api.jup.ag/v6/quote');
url.searchParams.set('inputMint', params.inputMint);
url.searchParams.set('outputMint', params.outputMint);
url.searchParams.set('amount', params.amount);
url.searchParams.set('slippageBps', params.slippageBps.toString());
const response = await fetch(url.toString());
return response.json();
}
export async function executeSwap(
inputMint: string,
outputMint: string,
amountLamports: string,
slippageBps: number = 300
): Promise<string> {
// 1. Get quote
const quote = await getQuote({
inputMint,
outputMint,
amount: amountLamports,
slippageBps,
});
// 2. Get swap transaction
const swapResponse = await fetch('https://quote-api.jup.ag/v6/swap', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
quoteResponse: quote,
userPublicKey: wallet.publicKey.toBase58(),
wrapAndUnwrapSol: true,
prioritizationFeeLamports: 100000, // 0.0001 SOL priority fee
}),
});
const { swapTransaction } = await swapResponse.json();
// 3. Deserialize, sign, and send
const transactionBuf = Buffer.from(swapTransaction, 'base64');
const transaction = VersionedTransaction.deserialize(transactionBuf);
transaction.sign([wallet]);
const signature = await connection.sendRawTransaction(
transaction.serialize(),
{
skipPreflight: true,
maxRetries: 3,
}
);
// 4. Confirm
const confirmation = await connection.confirmTransaction(signature, 'confirmed');
if (confirmation.value.err) {
throw new Error(`Transaction failed: ${JSON.stringify(confirmation.value.err)}`);
}
return signature;
}
Key Parameters to Understand
- slippageBps: Slippage tolerance in basis points. 300 = 3%. For volatile tokens, you may need 1000+ (10%). See our Solana Slippage Explained guide.
- prioritizationFeeLamports: Extra fee to increase transaction priority. During congestion, increase this to ensure your transaction lands.
- skipPreflight: Skips local simulation before sending. Set to true for speed, but you lose pre-submission error checking.
Step 4: Real-Time Monitoring with Helius Webhooks
Polling prices works for basic bots, but Helius webhooks let you react to on-chain events in real time — token transfers, DEX swaps, new liquidity pools, and more.
Setting Up a Webhook
// src/webhook.ts
import express from 'express';
const app = express();
app.use(express.json());
app.post('/webhook', (req, res) => {
const transactions = req.body;
for (const tx of transactions) {
// Process each transaction
console.log('New transaction:', tx.signature);
console.log('Type:', tx.type);
// Check for swap events, transfers, etc.
if (tx.type === 'SWAP') {
handleSwapEvent(tx);
}
}
res.status(200).send('OK');
});
function handleSwapEvent(tx: any) {
// Extract swap details
const { tokenInputs, tokenOutputs } = tx;
console.log('Swap detected:', {
in: tokenInputs,
out: tokenOutputs,
signature: tx.signature,
});
}
app.listen(3333, () => console.log('Webhook server running on :3333'));
Register the webhook with Helius via their API or dashboard. You can filter by account address, transaction type, or program ID. For a trading bot, common webhook triggers include:
- Whale wallet monitoring: Get notified when a specific wallet makes a trade
- New pool creation: Detect when new tokens are listed on Raydium or Orca
- Large swaps: Monitor for significant volume on tokens you are watching
Step 5: Putting It Together — A Simple Strategy
Here is a basic bot that monitors a token's price and buys when it drops below a threshold:
// src/bot.ts
import { SOL_MINT } from './config';
import { getTokenPrice, monitorPrice } from './price';
import { executeSwap } from './swap';
const TARGET_TOKEN = 'TOKEN_MINT_ADDRESS_HERE';
const BUY_THRESHOLD = 0.005; // Buy when price drops below $0.005
const BUY_AMOUNT = '100000000'; // 0.1 SOL in lamports
const CHECK_INTERVAL = 10000; // Check every 10 seconds
let hasBought = false;
async function main() {
console.log('Bot starting...');
console.log(`Monitoring ${TARGET_TOKEN}`);
console.log(`Buy threshold: $${BUY_THRESHOLD}`);
monitorPrice(TARGET_TOKEN, CHECK_INTERVAL, async (price) => {
console.log(`Current price: $${price}`);
if (price > 0 && price < BUY_THRESHOLD && !hasBought) {
console.log(`Price below threshold! Executing buy...`);
try {
const sig = await executeSwap(SOL_MINT, TARGET_TOKEN, BUY_AMOUNT);
console.log(`Buy executed: ${sig}`);
hasBought = true;
} catch (error) {
console.error('Buy failed:', error);
}
}
});
}
main().catch(console.error);
This is intentionally simple. A production bot would include position tracking, multiple strategy conditions, risk limits, error recovery, and logging.
Common Pitfalls and How to Avoid Them
Transaction Failures
Solana transactions fail more often than you might expect, especially during congestion. Your bot must handle failures gracefully:
- Retry logic: Implement exponential backoff for failed transactions
- Stale blockhash: Transactions expire after roughly 60 seconds. If your transaction takes too long to build and submit, it will fail with a stale blockhash error. Always get a fresh blockhash just before signing.
- Insufficient SOL: Always check your balance before attempting a swap. Keep a reserve for transaction fees.
Rate Limits
Free RPC endpoints have rate limits. Helius free tier allows a generous number of requests, but if your bot polls aggressively, you will hit limits. Solutions:
- Use websocket subscriptions instead of polling where possible
- Cache price data between API calls
- Upgrade to a paid RPC plan if needed — Helius, QuickNode, and Triton all offer developer-tier plans
If your bot submits transactions with low priority fees, MEV searchers can detect your pending transaction and front-run it (buying before you, pushing the price up, then selling after your purchase). Mitigations:
- Use sufficient priority fees
- Consider using Jito bundles for atomic execution (advanced)
- Set reasonable slippage limits — never use unlimited slippage
Testing on Devnet First
Always test your bot on Solana devnet before using real SOL. Jupiter's API works on devnet, and you can get free devnet SOL from faucets. The execution flow is identical, just with worthless tokens.
Going Further: Advanced Concepts
Once your basic bot works, these are the natural next steps:
Jito Bundles for MEV Protection
Jito bundles let you submit transactions that either all execute atomically or none execute. This prevents partial execution and provides some MEV protection. See our Jito Bundles Explained guide.
gRPC Streams for Real-Time Data
For sub-second price monitoring, HTTP polling is too slow. Solana gRPC streams (available from providers like Helius and Triton) push transaction data to your bot in real time. This is what professional trading bots use.
On-Chain Data for Smarter Signals
Instead of just watching price, you can monitor:
- New wallet holders (growing or declining)
- Liquidity changes (additions or removals)
- Social signals via APIs from Birdeye or DEXScreener
- Whale wallet activity via Helius webhooks
AI-Powered Decision Making
The Solana Agent Kit enables building AI agents that interact with Solana protocols. You can combine LLM-based analysis with your trading bot for more sophisticated decision-making — though this adds significant complexity.
Legal and Risk Considerations
- You are responsible for your bot's trades. If it buys a scam token or loses money due to a bug, that is on you.
- Tax implications: Every swap is a taxable event in most jurisdictions. Your bot could generate hundreds of taxable transactions per day. See our Solana Tax Guide.
- API terms of service: Respect rate limits and ToS for any API you use. Abusing free tiers can get your keys revoked.
- Start small: Test with amounts you can afford to lose completely. Bugs in financial code are expensive.
Useful Resources
Final Thoughts
Building a Solana trading bot is an excellent way to learn blockchain development with immediate, tangible results. Start with the simplest possible version — a bot that monitors one token and executes one type of trade. Get that working reliably before adding complexity.
The most common failure mode for bot builders is overengineering before the basics work. A simple bot that reliably executes a straightforward strategy will outperform a complex bot that crashes every few hours. Get the foundation right, then iterate.