Creating a Solana copy trading bot has become increasingly accessible thanks to powerful developer tools like the Pump.fun API and Yellowstone gRPC. In this comprehensive guide, you'll learn how to build a fully functional trading bot that monitors specific wallet addresses on the Pump.fun DEX and automatically replicates their buy trades on Solana in real time.
Whether you're an experienced blockchain developer or diving into DeFi automation for the first time, this step-by-step walkthrough will equip you with the skills to deploy your own Solana trading bot using modern gRPC streaming, transaction parsing, and automated swap execution.
Why Build a Solana Copy Trading Bot?
Copy trading bots allow developers and traders to mirror the moves of high-performing wallets—often referred to as "whales"—on decentralized exchanges. By automating this process, you can react faster than manual trading and potentially capitalize on early momentum in new token launches.
With Pump.fun being one of the most active meme coin launchpads on Solana, and Yellowstone gRPC enabling real-time blockchain monitoring, this combination offers a robust foundation for building responsive, low-latency trading strategies.
👉 Discover how top traders automate their Solana strategies with powerful tools.
Key Features of This Bot
- Real-time monitoring of wallet transactions via Yellowstone gRPC
- Automatic replication of Pump.fun buy trades above a minimum threshold
- Configurable test mode for safe simulation before live deployment
- Transaction logging for performance tracking and debugging
- Built using Node.js, Solana Web3.js, and secure environment variables
Prerequisites
Before diving into the code, ensure you have the following:
- Intermediate knowledge of Solana development and DeFi concepts
- Experience with JavaScript and Node.js
- A funded Solana wallet (e.g., Phantom, Backpack) with SOL for transaction fees
A QuickNode account with:
- Solana Mainnet RPC endpoint
- Metis Jupiter V6 Swap API enabled
- Yellowstone gRPC plugin activated
⚠️ Educational Purpose Only: This guide is for learning purposes and does not constitute financial advice. Trading bots carry risks, including potential loss of funds. Always test thoroughly in a safe environment before using real assets.
Understanding the Core Tools
What Is Pump.fun?
Pump.fun is a popular decentralized exchange (DEX) on Solana designed for launching and trading meme tokens. It simplifies token creation and provides liquidity through a bonding curve model. The platform has gained traction due to its low barriers to entry and rapid token listing capabilities.
By leveraging the Pump.fun API via Metis, we can programmatically execute swaps without interacting directly with smart contracts—making integration faster and more reliable.
What Is Yellowstone gRPC?
Yellowstone is a high-performance gRPC-based plugin that streams real-time blockchain data from Solana nodes. Unlike traditional RPC polling, which introduces delays, Yellowstone delivers instant updates on transactions, account changes, and slot confirmations.
This makes it ideal for building:
- High-frequency trading bots
- On-chain analytics dashboards
- Real-time DeFi alert systems
With fine-grained filtering, we can subscribe only to transactions involving specific programs or wallets—reducing noise and improving efficiency.
👉 Access real-time blockchain data with advanced infrastructure tools.
Setting Up Your Development Environment
Step 1: Initialize the Project
Create a new directory and initialize a Node.js project:
mkdir solana-copy-bot
cd solana-copy-bot
npm init -yStep 2: Install Dependencies
Install the required packages:
npm install @solana/web3.js@1 bs58 dotenv @triton-one/yellowstone-grpc node-fetch| Package | Purpose |
|---|---|
@solana/web3.js | Interact with the Solana blockchain |
bs58 | Handle Base58 encoding for signatures and addresses |
dotenv | Load environment variables securely |
@triton-one/yellowstone-grpc | Connect to real-time transaction streams |
node-fetch | Make HTTP requests to the Pump.fun API |
Step 3: Configure Environment Variables
Create a .env file in your project root:
SOLANA_RPC=https://your-quiknode-endpoint.quiknode.pro/abc123/
SECRET_KEY=[1,2,3,...] # Your wallet's secret key array
METIS_ENDPOINT=https://jupiter-swap-api.quiknode.pro/YOUR_METIS_ID/
YELLOWSTONE_ENDPOINT=https://your-yellowstone-endpoint.solana-mainnet.quiknode.pro:10000
YELLOWSTONE_TOKEN=your_yellowstone_api_tokenReplace placeholders with actual values from your QuickNode dashboard.
🔐 Never commit.envfiles to version control. Add.envto your.gitignore.
Building the Copy Trading Bot
We'll structure our bot as a class called CopyTradeBot, encapsulating all logic for monitoring, decision-making, and execution.
Core Configuration
require("dotenv").config();
const fs = require("fs");
const fetch = require("node-fetch");
const bs58 = require("bs58").default;
const {
Connection,
Keypair,
VersionedTransaction,
LAMPORTS_PER_SOL,
PublicKey,
} = require("@solana/web3.js");
const Client = require("@triton-one/yellowstone-grpc").default;
const { CommitmentLevel } = require("@triton-one/yellowstone-grpc");
class CopyTradeBot {
config = {
WATCH_LIST: ["YourWalletAddressHere"], // Wallets to monitor
PUMP_FUN: {
PROGRAM_ID: "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P",
FEE_ACCOUNT: "CebN5WGQ4jvEPvsVU4EoHEpgzq1VV7AbicfhtW4xC9iM",
BUY_DISCRIMINATOR: Buffer.from([102, 6, 61, 18, 1, 218, 235, 234]),
SELL_DISCRIMINATOR: Buffer.from([51, 230, 133, 164, 1, 127, 131, 173]),
TOKEN_DECIMALS: 6,
TARGET_ACCOUNTS: {
BUY: [{ name: "mint", index: 2 }, { name: "user", index: 6 }],
SELL: [{ name: "mint", index: 2 }, { name: "user", index: 6 }],
},
},
MIN_TX_AMOUNT: LAMPORTS_PER_SOL / 1000, // Minimum SOL to trigger copy (0.001 SOL)
BUY_AMOUNT: LAMPORTS_PER_SOL / 1000, // Amount to spend when copying
LOG_FILE: "pump_fun_swaps.json",
COMMITMENT: CommitmentLevel.CONFIRMED,
TEST_MODE: true,
};
constructor() {
this.validateEnv();
this.connection = new Connection(process.env.SOLANA_RPC);
this.wallet = Keypair.fromSecretKey(Uint8Array.from(JSON.parse(process.env.SECRET_KEY)));
console.log("🤖 Bot Wallet:", this.wallet.publicKey.toBase58());
console.log("Monitoring addresses:");
this.config.WATCH_LIST.forEach((addr) => console.log(" -", addr));
}
}Validate Environment Variables
Ensure all required credentials are present:
validateEnv = () => {
const requiredEnvs = [
"SOLANA_RPC",
"SECRET_KEY",
"METIS_ENDPOINT",
"YELLOWSTONE_ENDPOINT",
"YELLOWSTONE_TOKEN",
];
requiredEnvs.forEach((env) => {
if (!process.env[env]) {
throw new Error(`Missing required environment variable: ${env}`);
}
});
};Fetch Swap Transaction from Pump.fun API
Use the Metis-powered Pump.fun endpoint to generate a valid swap transaction:
fetchSwapTransaction = async ({ wallet, type, mint, inAmount }) => {
const body = JSON.stringify({
wallet,
type,
mint,
inAmount,
priorityFeeLevel: "high",
slippageBps: "300",
});
const res = await fetch(`${process.env.METIS_ENDPOINT}/pump-fun/swap`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body,
});
if (!res.ok) throw new Error(`Swap fetch error: ${await res.text()}`);
return res.json();
};Sign and Serialize Transactions
Locally sign the transaction using your bot’s wallet:
signTransaction = async (swapTransaction) => {
const transaction = VersionedTransaction.deserialize(Buffer.from(swapTransaction, "base64"));
const latestBlockHash = await this.connection.getLatestBlockhash();
transaction.message.recentBlockhash = latestBlockHash.blockhash;
transaction.sign([this.wallet]);
return Buffer.from(transaction.serialize()).toString("base64");
};Broadcast Transaction to Solana Network
Send and confirm the signed transaction:
sendAndConfirmTransaction = async (signedTxBase64) => {
try {
const txid = await this.connection.sendEncodedTransaction(signedTxBase64, {
skipPreflight: false,
encoding: 'base64'
});
const timeout = 30000;
const pollInterval = 3000;
const start = Date.now();
while (Date.now() - start < timeout) {
const response = await this.connection.getSignatureStatuses([txid]);
const status = response?.value?.[0];
if (status?.err) throw new Error(`Transaction failed: ${JSON.stringify(status.err)}`);
if (status?.confirmationStatus === 'confirmed' || status?.confirmationStatus === 'finalized') {
return txid;
}
await new Promise(resolve => setTimeout(resolve, pollInterval));
}
throw new Error(`Transaction confirmation timeout after ${timeout}ms`);
} catch (error) {
throw error;
}
};Implementing Real-Time Monitoring with Yellowstone
Create Subscription Request
Filter only relevant transactions from monitored wallets and Pump.fun program:
createSubscribeRequest = () => {
const { WATCH_LIST, PUMP_FUN, COMMITMENT } = this.config;
return {
transactions: {
pumpFun: {
accountInclude: WATCH_LIST,
accountRequired: [PUMP_FUN.FEE_ACCOUNT, PUMP_FUN.PROGRAM_ID],
},
},
commitment: COMMITMENT,
};
};Handle Incoming Data Stream
Parse incoming transactions and detect valid buy events:
handleData = (data) => {
if (!this.isSubscribeUpdateTransaction(data)) return;
const transaction = data.transaction.transaction;
const message = transaction.message;
const meta = transaction.meta;
if (meta?.err) return;
const allInstructions = [
...message.instructions,
...(meta.innerInstructions?.flatMap(ix => ix.instructions || []) || [])
];
const matchingIx = allInstructions.find(this.matchesInstructionDiscriminator);
if (!matchingIx) return;
const { amount, solAmount } = this.getInstructionData(matchingIx.data);
if (solAmount < this.config.MIN_TX_AMOUNT) return;
const txType = this.getTransactionType(matchingIx.data);
const formattedSig = bs58.encode(Buffer.from(data.transaction.signature));
console.log(`${txType === 'BUY' ? '🎯' : '📉'} - ${txType} - TxID: ${formattedSig}`);
if (txType === "BUY") {
const accountKeys = message.accountKeys;
const accountsToInclude = this.config.PUMP_FUN.TARGET_ACCOUNTS.BUY;
const includedAccounts = accountsToInclude.reduce((acc, { name, index }) => {
const accountIndex = matchingIx.accounts[index];
acc[name] = new PublicKey(accountKeys[accountIndex]).toBase58();
return acc;
}, {});
this.handleWhaleBuy(includedAccounts.user, includedAccounts.mint, solAmount, formattedSig);
}
};Execute Copy Trade Logic
Trigger replication when conditions are met:
handleWhaleBuy = async (whalePubkey, tokenMint, lamportsSpent, copiedTxid) => {
try {
const response = await this.fetchSwapTransaction({
wallet: this.wallet.publicKey.toBase58(),
type: "BUY",
mint: tokenMint,
inAmount: this.config.BUY_AMOUNT,
});
const signedTx = await this.signTransaction(response.tx);
let txid = 'simulated-TxID';
if (!this.config.TEST_MODE) {
txid = await this.sendAndConfirmTransaction(signedTx);
}
console.log("✅ Copied Buy - TxID:", txid);
this.logSwap({
event: "COPY_BUY",
txid,
copiedTxid,
tokenMint,
lamportsSpent,
whalePubkey,
timestamp: new Date().toISOString(),
});
} catch (err) {
console.error("Failed to copy trade:", err);
this.logSwap({
event: "COPY_BUY_ERROR",
error: err.message || String(err),
copiedTxid,
timestamp: new Date().toISOString(),
});
}
};Launch and Test Your Bot
Start the Bot
Add the entry point:
start = async () => {
console.log("🚀 Starting Pump.fun Copy Trading Bot...");
await this.monitorWhales();
};
async function main() {
const bot = new CopyTradeBot();
await bot.start();
}
main().catch(console.error);Run it:
node bot.jsTesting Steps
- Fund your bot wallet with at least 0.01 SOL
- Set
TEST_MODE=trueinitially - Use a wallet from
WATCH_LISTto buy a token on Pump.fun - Observe console logs for detected buys and simulated copies
- Review
pump_fun_swaps.jsonfor recorded actions
Once confident, set TEST_MODE=false for live trading—but only with small amounts at first.
Frequently Asked Questions (FAQ)
Can I use TypeScript instead of JavaScript?
Yes! While this guide uses JavaScript for simplicity, you can easily convert it to TypeScript by adding type definitions for transactions, responses, and configuration objects. Many developers prefer TypeScript for better tooling and compile-time safety when working with complex blockchain logic.
How do I avoid frontrunning or failed transactions?
To improve success rates:
- Use higher priority fees (
priorityFeeLevel: "high") - Monitor network congestion
- Implement retry logic with exponential backoff
- Consider using Jito bundles or MEV protection if scaling up
👉 Explore advanced execution strategies used by professional traders.
Is it safe to run a bot with real funds?
Only after extensive testing in simulation mode. Always:
- Start with minimal stakes
- Audit all dependencies
- Keep secret keys offline whenever possible
- Monitor logs continuously during early live runs
Can I monitor multiple wallets or programs?
Absolutely. Expand the WATCH_LIST array with additional public keys. You can also modify the subscription filter to include other DeFi protocols like Raydium or Orca by adjusting accountRequired fields.
Does this work on devnet or localnet?
Yes. Replace your RPC and Yellowstone endpoints with devnet equivalents. This allows risk-free testing of logic and integration without spending real SOL.
How can I improve the bot’s strategy?
Advanced enhancements include:
- Dynamic position sizing based on whale activity
- Automatic sell triggers after price targets
- Integration with price oracles for stop-loss logic
- Multi-wallet diversification to reduce risk
Final Thoughts
You've now built a functional Solana copy trading bot capable of detecting and mirroring Pump.fun trades in real time using Yellowstone gRPC and the Pump.fun API. This setup demonstrates how modern blockchain tooling enables powerful automation in decentralized finance.
As you continue refining your bot, consider integrating additional signals, improving error handling, and exploring portfolio-level risk management.
Happy coding—and trade safely!