DEV Community

TateLyman
TateLyman

Posted on

How to Build a Solana Copy Trading Bot That Actually Works

Copy trading on Solana is one of the most requested features in crypto trading bots. Here's how I implemented it in @solscanitbot and the architectural decisions behind it.

The Problem

You want to automatically mirror another wallet's trades. When they buy a token, you buy it. When they sell, you sell. Simple concept, surprisingly tricky to implement.

Two Approaches

1. WebSocket Monitoring (Complex)

  • Subscribe to account changes via Helius WebSocket
  • Parse transaction logs in real-time
  • High complexity, many edge cases

2. Snapshot-Diff (Simple, What I Use)

  • Take snapshot of target wallet's token holdings
  • Wait 60 seconds
  • Take new snapshot
  • Diff the two: new tokens = buys, missing tokens = sells

I chose snapshot-diff. Here's why:

Why Snapshot-Diff Wins

  1. Simpler code — ~50 lines vs 200+ for WebSocket
  2. More reliable — WebSocket connections drop; polling always works
  3. Easier to debug — you can log each snapshot
  4. 60-second latency is fine — for copy trading, seconds don't matter

Implementation

async function checkCopyTrades() {
  for (const [chatId, targets] of Object.entries(copyTargets)) {
    for (const target of targets) {
      const currentHoldings = await getWalletTokens(target.wallet);
      const prevHoldings = target.lastSnapshot || {};

      // Find new tokens (buys)
      for (const [mint, amount] of Object.entries(currentHoldings)) {
        if (!prevHoldings[mint]) {
          await executeCopyBuy(chatId, mint, target.buyAmount);
        }
      }

      // Find removed tokens (sells)
      for (const [mint, amount] of Object.entries(prevHoldings)) {
        if (!currentHoldings[mint]) {
          await executeCopySell(chatId, mint);
        }
      }

      target.lastSnapshot = currentHoldings;
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Edge Cases

  • Partial sells: If they reduce position but don't fully exit, compare amounts
  • Multiple targets: Users can copy multiple wallets simultaneously
  • Position sizing: Don't copy the whale's 100 SOL buy with your 0.5 SOL
  • Failed trades: Retry once, then notify the user

Getting Wallet Token Holdings

Use getTokenAccountsByOwner RPC call:

const accounts = await fetch(RPC_URL, {
  method: 'POST',
  body: JSON.stringify({
    jsonrpc: '2.0', id: 1,
    method: 'getTokenAccountsByOwner',
    params: [walletAddress, { programId: TOKEN_PROGRAM_ID }, { encoding: 'jsonParsed' }]
  })
});
Enter fullscreen mode Exit fullscreen mode

Live in Action

Try it free: /copy <wallet_address> in @solscanitbot

Or get the full source code with all 42 commands for 2 SOL.

Top comments (0)