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.