Skill: youthaiagent/solana-copy-trader (version 1.0.0)

--- name: solana-copy-trader description: > Solana whale copy trading bot. Track any wallet, copy trades in real-time via Jupiter + Pump.fun APIs, with paper trading simulation and live execution. Use when user wants to copy trade a Solana wallet, track whale transactions, scan for arbitrage opportunities, monitor pump.fun token launches, or set up an autonomous Solana trading bot. --- # Solana Copy Trader Real-time Solana whale copy trader using Helius WebSocket + Jupiter API + Pump.fun. ## Quick Start ```bash cd solana-bot npm install cp .env.example .env # fill in keys node index.js copy # paper mode (safe) node index.js watch # whale tracker only node index.js scan # arb scanner ``` ## Modes | Mode | Command | Description | |------|---------|-------------| | `copy` | `node index.js copy 0.01` | Copy whale trades (paper by default) | | `watch` | `node index.js watch` | Watch whale txs live | | `scan` | `node index.js scan` | Scan arb opportunities | | `paper` | `node index.js paper` | Full paper trading sim | | `analyze` | `node index.js analyze` | Wallet pattern analysis | | `safety` | `node index.js safety <mint>` | Token rug check | ## .env Setup ```env PRIVATE_KEY=your_base58_private_key # leave blank for watch-only RPC_URL=https://mainnet.helius-rpc.com/?[REDACTED_SECRET] HELIUS_API_KEY=your_helius_key # free at dev.helius.xyz BOT_TOKEN=telegram_bot_token # for alerts CHAT_ID=your_telegram_chat_id MAX_TRADE_SOL=0.01 # safety limit per trade MIN_PROFIT_PCT=0.5 ``` ## Architecture ``` Helius WebSocket → whale tx detected ↓ parseTransaction() → decode token changes ↓ Jupiter quote → can we route? ↓ (if no route) Pump.fun DAS check → bonding curve token? ↓ safety check → price impact < 50%? ↓ paper: log trade | live: executeRealSwap() ↓ Telegram alert sent ``` ## Key Files - `src/copy_trade.js` — Core copy trader engine - `src/wallet_tracker.js` — Helius WebSocket + tx parsing - `src/arbitrage.js` — Jupiter arb scanner - `src/pumpfun.js` — Pump.fun token metadata via Helius DAS - `src/sniper.js` — New token sniper (paper mode) - `src/config.js` — Wallet + connection setup - `src/alerts.js` — Telegram notifications ## Live → Paper Switch In `copy_trade.js` `startCopyTrader()`: ```js paper: true // paper mode (safe, no real money) paper: false // LIVE mode — real trades ``` Or use `index.js` mode `copy` (always paper) vs direct `startCopyTrader({ paper: false })`. ## Safety Limits - `MAX_TRADE_SOL` — max SOL per trade (default 0.01) - `maxPositions: 3` — max open positions at once - `priceImpact > 50%` → skip (rug protection) - Pump safety score < 40 → skip ## Whale to Copy Default whale: [REDACTED_SECRET] - Confirmed MEV bot: 477 SOL, 172K txs/day, $40K/day - Change in `src/copy_trade.js` → `WHALE` constant ## Requirements - Node.js 18+ - Free Helius API key (1000 req/day free tier) - Solana wallet (optional — watch-only without) - Telegram bot (optional — for alerts) See `references/api-setup.md` for getting free API keys. See `references/trading-concepts.md` for how Solana MEV/arb works.

path: scripts/index.js
content: /**
 * IRONMAN Solana Bot — Main Entry Point
 * 
 * EDUCATIONAL PURPOSE — Learn how bots work
 * 
 * Modes:
 *   node index.js analyze   → Wallet analysis only
 *   node index.js scan      → Arbitrage scanner (no trades)
 *   node index.js watch     → Watch whale wallet
 *   node index.js paper     → Paper trading simulation
 *   node index.js learn     → Full explanation mode
 */

const { analyzeWalletPattern, watchWallet, CopyTradeSimulator, WHALE_WALLET } = require('./src/wallet_tracker');
const { scanArbitrageOpportunities, startArbitrageMonitor, explainArbitrage } = require('./src/arbitrage');
const { sendTelegram, formatArbitrageAlert, formatWhaleTrade } = require('./src/alerts');
const { getSolPrice } = require('./src/price_monitor');
const { startSniper, safetyCheck } = require('./src/sniper');
const { startCopyTrader } = require('./src/copy_trade');
const { wallet, config } = require('./src/config');

const MODE = process.argv[2] || 'learn';
const TRADE_AMOUNT = parseFloat(process.argv[3] || '0.01'); // SOL amount

console.log(`
╔═══════════════════════════════════════╗
║   IRONMAN SOLANA BOT — Educational    ║
║   Mode: ${MODE.padEnd(30)}║
╚═══════════════════════════════════════╝
`);

async function main() {
  // Current SOL price
  const solPrice = await getSolPrice();
  console.log(`[Main] SOL Price: $${solPrice.toFixed(2)}\n`);

  switch (MODE) {
    
    // ═══════════════════════════════════════
    case 'analyze':
    // Whale wallet ka full analysis
    // LEARN: Transaction patterns, bot detection
    // ═══════════════════════════════════════
      console.log('[Mode] WALLET ANALYSIS\n');
      const report = await analyzeWalletPattern(WHALE_WALLET, 20);
      
      await sendTelegram(`
<b>Whale Wallet Analysis Complete</b>

Strategy: ${report.strategy}
Avg tx gap: ${report.avgGapSec}s
Min tx gap: ${report.minGapSec}s
Is Bot: ${report.isBot ? 'YES' : 'NO'}
Total txs analyzed: ${report.totalTxs}

Top DEXes: ${JSON.stringify(report.dexUsage)}
`.trim());
      break;

    // ═══════════════════════════════════════
    case 'scan':
    // Arbitrage opportunities scan karo
    // LEARN: Price differences across DEXes
    // ═══════════════════════════════════════
      console.log('[Mode] ARBITRAGE SCANNER (Watch Only)\n');
      explainArbitrage();
      
      console.log('Starting continuous scan... Press Ctrl+C to stop\n');
      
      const monitor = await startArbitrageMonitor(8000, async (opp) => {
        console.log(`\n🔥 OPPORTUNITY: ${opp.profitPct.toFixed(3)}% profit!`);
        await sendTelegram(formatArbitrageAlert(opp));
      });
      
      // Run for 5 minutes then report
      setTimeout(async () => {
        const stats = monitor.getStats();
        console.log(`\n[Stats] Scans: ${stats.scanCount} | Opportunities found: ${stats.totalOpportunities}`);
        await sendTelegram(`Bot ran ${stats.scanCount} scans, found ${stats.totalOpportunities} opportunities`);
        monitor.stop();
        process.exit(0);
      }, 5 * 60 * 1000);
      
      // Keep alive
      await new Promise(() => {});
      break;

    // ═══════════════════════════════════════
    case 'watch':
    // Whale wallet real-time watch
    // LEARN: WebSocket monitoring, copy trading logic
    // ═══════════════════════════════════════
      console.log(`[Mode] WHALE WATCHER — ${WHALE_WALLET.slice(0,20)}...\n`);
      console.log('Watching for transactions... Press Ctrl+C to stop\n');
      
      await sendTelegram(`🐋 Whale Watch STARTED\nWatching: ${WHALE_WALLET.slice(0,20)}...`);
      
      watchWallet(WHALE_WALLET, async (tx) => {
        console.log('\n🚨 WHALE TRANSACTION DETECTED!');
        console.log(JSON.stringify(tx, null, 2));
        await sendTelegram(formatWhaleTrade(tx));
      });
      
      // Keep alive
      await new Promise(() => {});
      break;

    // ═══════════════════════════════════════
    case 'paper':
    // Paper trading — ZERO real money
    // LEARN: Copy trading logic, P&L tracking
    // ═══════════════════════════════════════
      console.log('[Mode] PAPER TRADING SIMULATION\n');
      console.log('⚠️  NO REAL MONEY — Simulation only\n');
      
      const sim = new CopyTradeSimulator(1.0); // Start with 1 SOL (fake)
      
      await sendTelegram('📊 Paper Trading Started\nSimulating copy trades with 1 SOL (fake)');
      
      // Watch whale and simulate copy trades
      watchWallet(WHALE_WALLET, async (tx) => {
        if (tx.tokenChanges.length > 0) {
          await sim.onWhaleTrade(tx);
          
          const pnl = sim.getPnL();
          console.log(`\n[PnL] Balance: ${pnl.currentBalance.toFixed(4)} SOL | P&L: ${pnl.pnlPct.toFixed(2)}%`);
          
          // Report every 10 trades
          if (pnl.trades % 10 === 0 && pnl.trades > 0) {
            await sendTelegram(`
<b>Paper Trading Update</b>
Trades: ${pnl.trades}
Balance: ${pnl.currentBalance.toFixed(4)} SOL
P&L: ${pnl.pnl >= 0 ? '+' : ''}${pnl.pnlPct.toFixed(2)}%
${pnl.pnl >= 0 ? '✅ Profitable' : '❌ Losing'}
`.trim());
          }
        }
      });
      
      // Keep alive
      await new Promise(() => {});
      break;

    // ═══════════════════════════════════════
    case 'copy':
    // Copy trade whale wallet in paper mode
    // ═══════════════════════════════════════
      console.log('[Mode] COPY TRADER — Paper Mode\n');
      
      const copyBot = await startCopyTrader({
        solPerTrade: TRADE_AMOUNT,  // e.g. 0.01 SOL
        maxPositions: 3,
        takeProfitPct: 50,
        stopLossPct: 20,
        paper: true,              // change to false for real
        startSOL: 0.5,
      });
      
      console.log('Copying whale... Press Ctrl+C to stop\n');
      await new Promise(() => {});
      break;

    // ═══════════════════════════════════════
    case 'snipe':
    // Token Sniper — new launches detect + buy
    // LEARN: WebSocket monitoring, safety checks, auto sell
    // ═══════════════════════════════════════
      console.log('[Mode] SNIPER BOT (Paper Trading)\n');
      console.log(`Trade amount: ${TRADE_AMOUNT} SOL per snipe\n`);
      console.log('⚠️  Running in PAPER mode — no real money\n');
      
      await sendTelegram(`
🎯 <b>IRONMAN Sniper Bot</b>
Mode: Paper Trading
Per snipe: ${TRADE_AMOUNT} SOL (simulated)
Watching: Raydium + Pump.fun
Safety filters: ON
`.trim());

      const sniper = await startSniper({
        solPerTrade: TRADE_AMOUNT,
        simulated: true,  // Always paper first!
        maxPositions: 3,
      });
      
      // Keep alive
      await new Promise(() => {});
      break;

    // ═══════════════════════════════════════
    case 'safety':
    // Safety check a specific token
    // LEARN: How to detect rugs before buying
    // ═══════════════════════════════════════
      const tokenToCheck = process.argv[3];
      if (!tokenToCheck) {
        console.log('Usage: node index.js safety <TOKEN_MINT_ADDRESS>');
        break;
      }
      console.log(`[Mode] SAFETY CHECK: ${tokenToCheck}\n`);
      const safety = await safetyCheck(tokenToCheck);
      console.log('\n=== SAFETY REPORT ===');
      console.log(JSON.stringify(safety, null, 2));
      if (safety.passed) {
        console.log('\n✅ Token passed safety checks');
      } else {
        console.log('\n❌ Token FAILED — DO NOT BUY');
        console.log('Reasons:', safety.reasons.join('\n - '));
      }
      break;

    // ═══════════════════════════════════════
    case 'learn':
    default:
    // Learning mode — sab explain karo
    // ═══════════════════════════════════════
      console.log('[Mode] LEARNING MODE\n');
      
      explainArbitrage();
      
      console.log('\n[Learning] Quick arbitrage scan (1 round)...\n');
      const opps = await scanArbitrageOpportunities(0.01, 0.1);
      
      console.log(`\n[Learning] Found ${opps.length} opportunities above 0.1% threshold`);
      
      if (opps.length > 0) {
        console.log('\nBest opportunity:');
        console.log(JSON.stringify(opps[0], null, 2));
      }
      
      console.log('\n[Learning] Quick whale analysis...\n');
      await analyzeWalletPattern(WHALE_WALLET, 10);
      
      console.log(`
═══════════════════════════════════════
NEXT STEPS TO LEARN:
═══════════════════════════════════════
1. node index.js scan    → Live arbitrage scanning
2. node index.js watch   → Watch whale in real-time  
3. node index.js analyze → Deep wallet analysis
4. node index.js paper   → Simulate copy trading

ADD YOUR WALLET to .env to enable:
→ Real execution (start with tiny amounts!)
→ Actual P&L tracking
═══════════════════════════════════════
`);

      await sendTelegram(`
<b>IRONMAN Bot Learning Session Complete!</b>

SOL Price: $${solPrice.toFixed(2)}
Arb opportunities (>0.1%): ${opps.length}
Whale strategy: Confirmed BOT

Bot is ready. Add private key to .env for real trading.
`.trim());
      break;
  }
}

main().catch(console.error);


path: scripts/package.json
content: {
  "name": "solana-bot",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@jup-ag/api": "^6.0.48",
    "@solana/web3.js": "^1.98.4",
    "axios": "^1.13.5",
    "bs58": "^6.0.0",
    "dotenv": "^17.3.1"
  }
}


path: scripts/src/alerts.js
content: /**
 * IRONMAN Solana Bot — Alert System
 * OpenClaw + Telegram integration
 */

const axios = require('axios');
const { config } = require('./config');

async function sendTelegram(msg) {
  try {
    await axios.post(
      `https://api.telegram.org/bot${config.botToken}/sendMessage`,
      { chat_id: config.chatId, text: msg, parse_mode: 'HTML' },
      { timeout: 8000 }
    );
  } catch (e) {
    console.error('[Alert] Telegram error:', e.message);
  }
}

function formatArbitrageAlert(opp) {
  return `
🔴 <b>ARBITRAGE OPPORTUNITY DETECTED</b>

[REDACTED_SECRET],20)}...</code>
Input: ${opp.inputSol} SOL
Output: ${opp.outputSol.toFixed(6)} SOL
Profit: <b>${opp.profitPct.toFixed(3)}%</b> = ${opp.profitSol.toFixed(6)} SOL
USD: ~$${opp.profitUSD?.toFixed(4) || '?'}

Route:
Buy:  ${opp.buyRoute}
Sell: ${opp.sellRoute}

⚡ Act fast — opportunity lasts <2 seconds!
`.trim();
}

function formatWhaleTrade(tx) {
  const changes = tx.tokenChanges
    .map(c => `${c.action}: ${Math.abs(c.change).toFixed(2)} of ${c.mint.slice(0,12)}...`)
    .join('\n');
    
  return `
🐋 <b>WHALE ACTIVITY DETECTED</b>

Wallet: AgmLJBM...zN51
DEX: ${tx.dex}
SOL Change: ${tx.solChange.toFixed(4)} SOL

Tokens:
${changes}

Time: ${new Date(tx.timestamp * 1000).toLocaleTimeString()}
`.trim();
}

module.exports = { sendTelegram, formatArbitrageAlert, formatWhaleTrade };


path: scripts/src/arbitrage.js
content: /**
 * IRONMAN Solana Bot — Arbitrage Scanner
 * 
 * LESSON 4: Arbitrage — How it actually works
 * 
 * SIMPLE EXPLANATION:
 * ─────────────────
 * Maan le SOL = $100
 * 
 * Jupiter route 1 (via Raydium):
 *   0.1 SOL → 10.05 USDC → 0.1005 SOL back
 *   Profit: 0.0005 SOL = 0.5%
 * 
 * Jupiter route 2 (via Orca):
 *   0.1 SOL → 9.99 USDC → 0.0999 SOL back
 *   Loss: -0.01% ❌
 * 
 * Hum route 1 ko dhundh ke execute karte hain!
 * 
 * WHY IT EXISTS:
 * ─────────────
 * Different DEXes mein liquidity pools hote hain
 * Jab koi bada trade hota hai, price temporarily off ho jaata hai
 * Arbitrageurs yeh gap fill karte hain
 * Result: Market efficient rehta hai
 * Arbitrageur: Free profit!
 */

const { checkArbitrage, getMultiplePrices, getSolPrice } = require('./price_monitor');
const { config } = require('./config');
const axios = require('axios');

// Popular tokens for arbitrage scanning
// Yeh high-liquidity tokens hain jahan opportunities zyada hoti hain
const SCAN_TOKENS = [
  [REDACTED_SECRET], // USDC
  [REDACTED_SECRET],  // USDT
  [REDACTED_SECRET],  // mSOL
  [REDACTED_SECRET], // BONK
  [REDACTED_SECRET],  // JUP
  [REDACTED_SECRET], // ETH (Wormhole)
];

/**
 * Full arbitrage scan — sabhi tokens check karo
 * @param {number} solAmount - kitne SOL se scan karna hai
 * @param {number} minProfit - minimum profit % threshold
 */
async function scanArbitrageOpportunities(solAmount = 0.01, minProfit = 0.3) {
  console.log(`\n[Arb] Scanning ${SCAN_TOKENS.length} tokens for ${solAmount} SOL...`);
  
  const opportunities = [];
  const solPrice = await getSolPrice();
  console.log(`[Arb] SOL price: $${solPrice.toFixed(2)}`);
  
  // Parallel check — sab ek saath
  const results = await Promise.allSettled(
    SCAN_TOKENS.map(mint => checkArbitrage(mint, solAmount))
  );
  
  for (let i = 0; i < results.length; i++) {
    const result = results[i];
    if (result.status === 'fulfilled' && result.value) {
      const arb = result.value;
      
      if (arb.profitable && arb.profitPct >= minProfit) {
        const profitUSD = arb.profitSol * solPrice;
        opportunities.push({
          ...arb,
          profitUSD,
          [REDACTED_SECRET],
        });
        
        console.log(`[Arb] ✅ OPPORTUNITY FOUND!`);
        console.log(`     [REDACTED_SECRET],20)}...`);
        console.log(`     Profit: ${arb.profitPct.toFixed(3)}% = ${arb.profitSol.toFixed(6)} SOL ($${profitUSD.toFixed(4)})`);
        console.log(`     Route: SOL → [${arb.buyRoute}] → Token → [${arb.sellRoute}] → SOL`);
      } else if (arb.profitPct > -1) {
        // Close to profit — log for learning
        console.log(`[Arb] 📊 ${SCAN_TOKENS[i].slice(0,12)}... | ${arb.profitPct.toFixed(3)}% | ${arb.buyRoute} → ${arb.sellRoute}`);
      }
    }
  }
  
  // Best opportunity
  opportunities.sort((a, b) => b.profitPct - a.profitPct);
  
  return opportunities;
}

/**
 * Continuous arbitrage monitor
 * Every N seconds scan karo
 * 
 * @param {number} intervalMs - kitne ms baad scan karo
 * @param {Function} onOpportunity - callback when opportunity found
 */
async function startArbitrageMonitor(intervalMs = 5000, onOpportunity) {
  console.log(`[Arb Monitor] Starting... interval: ${intervalMs/1000}s`);
  
  let scanCount = 0;
  let totalOpportunities = 0;
  
  const scan = async () => {
    scanCount++;
    const startTime = Date.now();
    
    try {
      const opportunities = await scanArbitrageOpportunities(
        config.maxTradeSol,
        config.minProfitPct
      );
      
      const elapsed = Date.now() - startTime;
      console.log(`[Arb] Scan #${scanCount} done in ${elapsed}ms | Found: ${opportunities.length} opps`);
      
      if (opportunities.length > 0) {
        totalOpportunities += opportunities.length;
        
        for (const opp of opportunities) {
          if (onOpportunity) {
            await onOpportunity(opp);
          }
        }
      }
      
    } catch (e) {
      console.error('[Arb] Scan error:', e.message);
    }
  };
  
  // First scan immediately
  await scan();
  
  // Then repeat
  const interval = setInterval(scan, intervalMs);
  
  return {
    stop: () => clearInterval(interval),
    getStats: () => ({ scanCount, totalOpportunities }),
  };
}

/**
 * LESSON: Why arbitrage is hard in practice
 * 
 * Problems:
 * 1. By the time you see it, others execute it faster
 * 2. Transaction fees eat into profits (0.000005 SOL per tx)
 * 3. Slippage: Large trades move the price against you
 * 4. MEV bots front-run your transaction
 * 
 * Solutions (that whale uses):
 * 1. Private RPC (faster mempool access)
 * 2. Jito bundles (guaranteed execution order)
 * 3. Pre-signed transactions (ready to go)
 * 4. Multiple wallets (parallel execution)
 */
function explainArbitrage() {
  const explanation = `
═══════════════════════════════════════
ARBITRAGE — HOW IT REALLY WORKS
═══════════════════════════════════════

STEP 1: PRICE DISCOVERY
  - Jupiter API se quote lo (free)
  - SOL → USDC → SOL route test karo
  - Profit calculate karo

STEP 2: OPPORTUNITY CHECK
  - Profit > fees? (fees ~$0.0001 on Solana)
  - Profit > slippage? (usually 0.1-0.5%)
  - Execute within 1-2 blocks? (0.4 seconds)

STEP 3: EXECUTION (Advanced)
  - Jito bundle = multiple txs together
  - Private RPC = faster than public
  - Atomic = buy AND sell in same block

REAL NUMBERS:
  ✅ Opportunity: 0.5% on 1 SOL
  → Gross: 0.005 SOL ($0.40)
  → Network fee: 0.00001 SOL
  → Net: ~0.0049 SOL ($0.39)
  
  At 100 opportunities/day:
  → $39/day theoretical
  → Reality: ~30% hit rate = $12/day

WHALE REALITY (AgmLJBM...):
  → 172,000 txs/day
  → 0.3% average profit
  → ~500 SOL/day = $40,000/day
  → Using Jito MEV + private infra
═══════════════════════════════════════
`;
  console.log(explanation);
}

module.exports = {
  scanArbitrageOpportunities,
  startArbitrageMonitor,
  explainArbitrage,
  SCAN_TOKENS,
};


path: scripts/src/config.js
content: /**
 * IRONMAN Solana Bot — Config Module
 * 
 * LESSON 1: Environment variables se sensitive data store karte hain
 * Code mein kabhi private key mat likho
 */

require('dotenv').config();
const { Connection, PublicKey, Keypair } = require('@solana/web3.js');
const bs58 = require('bs58');

// ============================================
// CONNECTION SETUP
// ============================================
// Solana network se connect karna
// mainnet-beta = real money, devnet = test money
const connection = new Connection(
  process.env.RPC_URL || 'https://api.mainnet-beta.solana.com',
  {
    commitment: 'confirmed',  // transaction confirm hone ka level
    wsEndpoint: process.env.RPC_WS,
  }
);

// ============================================
// WALLET SETUP  
// ============================================
// Private key se Keypair banate hain
// Keypair = public key (wallet address) + private key (signing)
let wallet = null;
if (process.env.PRIVATE_KEY && process.env.PRIVATE_KEY !== 'your_private_key_here') {
  try {
    const secretKey = bs58.default.decode(process.env.PRIVATE_KEY);
    wallet = Keypair.fromSecretKey(secretKey);
    console.log(`[Config] Wallet loaded: ${wallet.publicKey.toString()}`);
  } catch (e) {
    console.log('[Config] No wallet configured — running in WATCH ONLY mode');
  }
} else {
  console.log('[Config] No wallet configured — running in WATCH ONLY mode');
}

// ============================================
// KNOWN DEX PROGRAM IDs
// ============================================
// Yeh Solana pe DEX protocols ke addresses hain
// Jab koi in addresses ke saath transaction karta hai
// toh hum samajh jaate hain kaunsa DEX use hua
const DEX_PROGRAMS = {
  JUPITER:    [REDACTED_SECRET],
  RAYDIUM:    [REDACTED_SECRET],
  ORCA:       [REDACTED_SECRET],
  PUMP_FUN:   [REDACTED_SECRET],
  METEORA:    [REDACTED_SECRET],
};

// ============================================
// IMPORTANT TOKEN ADDRESSES
// ============================================
const TOKENS = {
  SOL:  [REDACTED_SECRET],
  USDC: [REDACTED_SECRET],
  USDT: [REDACTED_SECRET],
};

module.exports = {
  connection,
  wallet,
  DEX_PROGRAMS,
  TOKENS,
  config: {
    maxTradeSol: parseFloat(process.env.MAX_TRADE_SOL || '0.1'),
    minProfitPct: parseFloat(process.env.MIN_PROFIT_PCT || '0.5'),
    jitoTip: parseFloat(process.env.JITO_TIP || '0.001'),
    botToken: process.env.BOT_TOKEN,
    chatId: process.env.CHAT_ID,
  }
};


path: scripts/src/copy_trade.js
content: /**
 * IRONMAN — Copy Trade Engine
 * 
 * Whale ke trades real-time copy karo
 * Paper mode: simulate only
 * Live mode: actual execution (needs wallet)
 * 
 * HOW IT WORKS:
 * 1. WebSocket se whale ki activity detect karo
 * 2. Transaction parse karo — kya buy/sell hua
 * 3. Same trade execute karo (smaller size)
 * 4. Auto take-profit + stop-loss manage karo
 */

const { Connection, PublicKey, VersionedTransaction } = require('@solana/web3.js');
const axios = require('axios');
const { connection, wallet, TOKENS, config } = require('./config');
const { getJupiterQuote } = require('./price_monitor');
const { sendTelegram } = require('./alerts');
const { parseTransaction, getRecentTransactions } = require('./wallet_tracker');
const { getPumpToken, pumpSafetyCheck, calcBuy } = require('./pumpfun');

// Whale to copy
const WHALE = [REDACTED_SECRET];

// Skip these — whale's internal routing, not real trades
const SKIP_MINTS = new Set([
  TOKENS.SOL,
  TOKENS.USDC, 
  TOKENS.USDT,
  [REDACTED_SECRET],
]);

/**
 * Paper Trading State
 */
class PaperPortfolio {
  constructor(startSOL = 0.5) {
    this.sol = startSOL;
    this.startSOL = startSOL;
    this.positions = new Map(); // mint => {tokens, buyPrice, solSpent, time}
    this.closedTrades = [];
    this.log = [];
  }

  addLog(msg) {
    const entry = `[${new Date().toLocaleTimeString()}] ${msg}`;
    this.log.push(entry);
    console.log('[Portfolio]', entry);
  }

  buy(mint, solAmount, tokensReceived, price) {
    if (this.sol < solAmount) {
      this.addLog(`Insufficient balance: ${this.sol.toFixed(4)} SOL`);
      return false;
    }
    this.sol -= solAmount;
    const existing = this.positions.get(mint) || { tokens: 0, solSpent: 0 };
    this.positions.set(mint, {
      tokens: existing.tokens + tokensReceived,
      solSpent: existing.solSpent + solAmount,
      buyPrice: price,
      time: Date.now(),
    });
    this.addLog(`BUY ${mint.slice(0,12)}... | ${solAmount} SOL → ${tokensReceived.toFixed(0)} tokens`);
    return true;
  }

  sell(mint, solReceived, tokensSold) {
    const pos = this.positions.get(mint);
    if (!pos) return false;
    this.sol += solReceived;
    const pnl = solReceived - pos.solSpent;
    const pnlPct = (pnl / pos.solSpent) * 100;
    this.closedTrades.push({ mint, pnl, pnlPct, solReceived, solSpent: pos.solSpent });
    this.positions.delete(mint);
    this.addLog(`SELL ${mint.slice(0,12)}... | ${solReceived.toFixed(4)} SOL | P&L: ${pnlPct.toFixed(1)}%`);
    return true;
  }

  getStats() {
    const totalPnL = this.closedTrades.reduce((s, t) => s + t.pnl, 0);
    const winRate = this.closedTrades.filter(t => t.pnl > 0).length / Math.max(this.closedTrades.length, 1);
    return {
      currentSOL: this.sol,
      startSOL: this.startSOL,
      totalPnL,
      totalPnLPct: (totalPnL / this.startSOL) * 100,
      openPositions: this.positions.size,
      closedTrades: this.closedTrades.length,
      winRate: (winRate * 100).toFixed(0) + '%',
    };
  }
}

/**
 * Core: Process whale transaction + decide action
 */
async function processWhaleTx(tx, portfolio, options = {}) {
  const { 
    maxPositions = 3,
    solPerTrade = 0.01,
    takeProfitPct = 50,   // sell at +50%
    stopLossPct = 20,     // sell at -20%
    paper = true
  } = options;

  if (!tx || !tx.success) return;

  // Find meaningful token trades (not SOL/USDC routing)
  const realTrades = tx.tokenChanges.filter(c => 
    !SKIP_MINTS.has(c.mint) && 
    (c.mint.endsWith('pump') || Math.abs(c.change) > 1000)  // Pump.fun tokens OR big trades
  );

  if (realTrades.length === 0) return;

  for (const trade of realTrades) {
    const mint = trade.mint;

    if (trade.action === 'BUY') {
      // Whale bought — we buy too
      if (portfolio.positions.size >= maxPositions) {
        console.log(`[CopyTrade] Max positions (${maxPositions}) — skipping`);
        continue;
      }

      if (portfolio.positions.has(mint)) {
        console.log(`[CopyTrade] Already have position in ${mint.slice(0,12)}`);
        continue;
      }

      console.log(`\n[CopyTrade] 🐋 Whale BUY detected: ${mint.slice(0,20)}...`);

      // Get our buy quote
      const lamports = Math.floor(solPerTrade * 1e9);
      // Try Jupiter first
      let quote = await getJupiterQuote(TOKENS.SOL, mint, lamports, 5000);
      if (!quote) quote = await getJupiterQuote(TOKENS.SOL, mint, lamports, 9900);

      let tokensOut, pricePerToken, priceImpact, routeLabel;
      let isPumpDirect = false;

      if (!quote) {
        // Jupiter failed — try pump.fun bonding curve
        console.log('[CopyTrade] Jupiter no route — checking pump.fun...');
        const safety = await pumpSafetyCheck(mint);
        
        if (!safety.pass) {
          console.log(`[CopyTrade] Pump safety FAIL (score: ${safety.score}) — skip`);
          continue;
        }

        if (safety.token.complete) {
          console.log('[CopyTrade] Token graduated but no Jupiter route — skip');
          continue;
        }

        // Estimate tokens (bonding curve data unavailable — use placeholder for paper mode)
        tokensOut = Math.floor(lamports / 1000); // rough estimate: 1 lamport = 0.001 token units
        if (tokensOut === 0) { console.log('[CopyTrade] Token estimate failed — skip'); continue; }
        
        pricePerToken = solPerTrade / tokensOut;
        priceImpact = 0; // unknown for bonding curve
        routeLabel = 'Pump.fun Bonding Curve';
        isPumpDirect = true;
        console.log(`[CopyTrade] Pump.fun route found! ${tokensOut.toLocaleString()} tokens out | mcap: $${safety.token.marketCap?.toFixed(0)}`);
        
      } else {
        tokensOut = parseInt(quote.outAmount);
        pricePerToken = solPerTrade / tokensOut;
        priceImpact = parseFloat(quote.priceImpactPct || 0);
        routeLabel = quote.routePlan?.map(r => r.swapInfo?.label).join('→') || 'Jupiter';

        if (priceImpact > 50) {
          console.log(`[CopyTrade] Price impact too high: ${priceImpact}% — skip`);
          continue;
        }
      }

      if (paper) {
        portfolio.buy(mint, solPerTrade, tokensOut, pricePerToken);
        
        await sendTelegram(`
🔴 <b>COPY TRADE — PAPER BUY</b>

Copying whale: AgmLJBM...
[REDACTED_SECRET],25)}...</code>
SOL spent: ${solPerTrade} SOL
Tokens: ${tokensOut.toLocaleString()}
Impact: ${priceImpact.toFixed(2)}%
Route: ${routeLabel}
${isPumpDirect ? '🎯 Pump.fun direct trade' : ''}

Portfolio: ${portfolio.sol.toFixed(4)} SOL left
Positions: ${portfolio.positions.size}/${maxPositions}
`.trim());

      } else {
        // REAL EXECUTION
        if (!wallet) {
          console.log('[CopyTrade] No wallet configured — paper only!');
          continue;
        }
        await executeRealSwap(mint, TOKENS.SOL, lamports, quote);
      }

    } else if (trade.action === 'SELL') {
      // Whale sold — we sell too
      if (!portfolio.positions.has(mint)) continue;

      console.log(`\n[CopyTrade] 🐋 Whale SELL detected: ${mint.slice(0,20)}...`);

      const pos = portfolio.positions.get(mint);
      const sellQuote = await getJupiterQuote(mint, TOKENS.SOL, Math.floor(pos.tokens), 2000);
      
      if (!sellQuote) continue;
      
      const solBack = parseInt(sellQuote.outAmount) / 1e9;
      
      if (paper) {
        portfolio.sell(mint, solBack, pos.tokens);
        const pnl = solBack - pos.solSpent;
        const pnlPct = (pnl / pos.solSpent) * 100;
        
        await sendTelegram(`
📊 <b>COPY TRADE — PAPER SELL</b>

[REDACTED_SECRET],25)}...</code>
SOL received: ${solBack.toFixed(4)}
P&L: ${pnl >= 0 ? '+' : ''}${pnlPct.toFixed(1)}% ${pnl >= 0 ? '✅' : '❌'}

Portfolio total: ${portfolio.sol.toFixed(4)} SOL
`.trim());
      }
    }
  }
}

/**
 * Real swap execution via Jupiter
 * Only runs when wallet is configured
 */
async function executeRealSwap(inputMint, outputMint, lamports, quote) {
  if (!wallet) throw new Error('No wallet configured');
  
  try {
    // Get swap transaction from Jupiter
    const { data: swapData } = await axios.post('https://quote-api.jup.ag/v6/swap', {
      quoteResponse: quote,
      userPublicKey: wallet.publicKey.toString(),
      wrapAndUnwrapSol: true,
      prioritizationFeeLamports: 10000, // ~0.00001 SOL priority fee
    });

    // Deserialize + sign + send
    const swapTx = VersionedTransaction.deserialize(
      Buffer.from(swapData.swapTransaction, 'base64')
    );
    swapTx.sign([wallet]);
    
    const sig = await connection.sendRawTransaction(swapTx.serialize(), {
      skipPreflight: false,
      maxRetries: 3,
    });
    
    await connection.confirmTransaction(sig, 'confirmed');
    
    console.log(`[CopyTrade] ✅ REAL SWAP EXECUTED: ${sig}`);
    await sendTelegram(`✅ REAL SWAP: https://solscan.io/tx/${sig}`);
    return sig;
    
  } catch (e) {
    console.error('[CopyTrade] Swap failed:', e.message);
    await sendTelegram(`❌ Swap failed: ${e.message}`);
    return null;
  }
}

/**
 * MAIN: Start copy trading bot
 */
async function startCopyTrader(options = {}) {
  const opts = {
    solPerTrade: 0.01,
    maxPositions: 3,
    takeProfitPct: 50,
    stopLossPct: 20,
    paper: true,
    ...options
  };

  const portfolio = new PaperPortfolio(opts.startSOL || 0.5);
  
  console.log(`
╔══════════════════════════════════════════╗
║  IRONMAN COPY TRADER                     ║
║  Copying: AgmLJBMDCqWynYnQiPCu...       ║
║  Mode: ${opts.paper ? 'PAPER (No real money)    ' : '⚠️  LIVE - REAL MONEY     '}║
║  Per trade: ${opts.solPerTrade} SOL${' '.repeat(25)}║
║  Max positions: ${opts.maxPositions}${' '.repeat(25)}║
╚══════════════════════════════════════════╝
  `);

  await sendTelegram(`
🤖 <b>Copy Trader STARTED</b>

Copying: AgmLJBMDCqWynYnQiPCu...zN51
Mode: ${opts.paper ? 'PAPER TRADING' : '⚠️ LIVE'}
Per trade: ${opts.solPerTrade} SOL
Balance: ${portfolio.sol} SOL
Max positions: ${opts.maxPositions}
`.trim());

  let lastSig = null;

  // Poll for new transactions (WebSocket alternative — more reliable)
  const pollInterval = setInterval(async () => {
    try {
      const sigs = await getRecentTransactions(WHALE, 3);
      if (!sigs.length) return;

      const newestSig = sigs[0].signature;
      if (newestSig === lastSig) return; // No new tx
      
      lastSig = newestSig;
      console.log(`\n[CopyTrade] New tx: ${newestSig.slice(0,20)}...`);
      
      const tx = await parseTransaction(newestSig);
      if (tx) {
        await processWhaleTx(tx, portfolio, opts);
      }
      
    } catch (e) {
      if (!e.message?.includes('429')) {
        console.error('[CopyTrade] Poll error:', e.message);
      }
    }
  }, 3000); // Poll every 3 seconds

  // Stats report every 5 minutes
  const statsInterval = setInterval(async () => {
    const stats = portfolio.getStats();
    console.log('\n[Stats]', stats);
    await sendTelegram(`
📊 <b>Copy Trader Update</b>

Balance: ${stats.currentSOL.toFixed(4)} SOL
P&L: ${stats.totalPnL >= 0 ? '+' : ''}${stats.totalPnLPct.toFixed(2)}%
Trades: ${stats.closedTrades} closed | ${stats.openPositions} open
Win rate: ${stats.winRate}
`.trim());
  }, 5 * 60 * 1000);

  return {
    stop: () => { clearInterval(pollInterval); clearInterval(statsInterval); },
    portfolio,
  };
}

module.exports = { startCopyTrader, PaperPortfolio, processWhaleTx };


path: scripts/src/price_monitor.js
content: /**
 * IRONMAN Solana Bot — Price Monitor
 * 
 * LESSON 2: Price Fetching
 * 
 * Jupiter API use karke kisi bhi token ka price milta hai
 * Jupiter = Solana ka best DEX aggregator
 * Woh automatically best route dhundta hai across all DEXes
 * 
 * ARBITRAGE CONCEPT:
 * Token X ka price:
 *   Raydium pe = $1.000
 *   Orca pe    = $1.005
 * 
 * Agar tu Raydium se buy kare aur Orca pe sell kare
 * → $0.005 profit per token (0.5%)
 * → Isse arbitrage kehte hain
 * → Risk-free profit (in theory)
 */

const axios = require('axios');
const { connection, TOKENS } = require('./config');

// Jupiter Price API — free, no auth needed
const JUPITER_PRICE_API = 'https://api.jup.ag/price/v2';
const JUPITER_QUOTE_API = 'https://quote-api.jup.ag/v6/quote';

/**
 * Kisi bhi token ka current price fetch karo (in USD)
 * @param {string} tokenMint - token ka mint address
 */
async function getTokenPrice(tokenMint) {
  try {
    const res = await axios.get(`${JUPITER_PRICE_API}?ids=${tokenMint}`, {
      timeout: 5000
    });
    const data = res.data.data[tokenMint];
    if (data) {
      return {
        price: parseFloat(data.price),
        mint: tokenMint,
        type: data.type,
      };
    }
  } catch (e) {
    // silent fail
  }
  return null;
}

/**
 * Multiple tokens ke prices ek saath fetch karo
 * @param {string[]} mints - array of token mint addresses
 */
async function getMultiplePrices(mints) {
  try {
    const ids = mints.join(',');
    const res = await axios.get(`${JUPITER_PRICE_API}?ids=${ids}`, {
      timeout: 8000
    });
    return res.data.data;
  } catch (e) {
    return {};
  }
}

/**
 * CORE FUNCTION: Jupiter se quote lo
 * 
 * Quote = "Agar main X token se Y token buy karun
 *          toh mujhe kitna milega?"
 * 
 * @param {string} inputMint  - jis token se pay karenge
 * @param {string} outputMint - jis token ko buy karenge  
 * @param {number} amountLamports - kitna (in lamports, 1 SOL = 1B lamports)
 * @param {number} slippage - max price slip allowed (50 = 0.5%)
 */
async function getJupiterQuote(inputMint, outputMint, amountLamports, slippage = 50) {
  try {
    const res = await axios.get(JUPITER_QUOTE_API, {
      params: {
        inputMint,
        outputMint,
        amount: amountLamports,
        slippageBps: slippage,
        onlyDirectRoutes: false,  // false = best route across all DEXes
      },
      timeout: 8000,
    });
    return res.data;
  } catch (e) {
    return null;
  }
}

/**
 * ARBITRAGE SCANNER
 * 
 * CONCEPT:
 * SOL → Token → SOL
 * Agar end mein zyada SOL mile toh profit!
 * 
 * Example:
 * 1 SOL se USDC buy karo (Jupiter best route)
 * Phir woh USDC se SOL buy karo (Jupiter best route)
 * Agar > 1 SOL mile = arbitrage opportunity!
 */
async function checkArbitrage(tokenMint, solAmount = 0.01) {
  const lamports = Math.floor(solAmount * 1e9); // SOL to lamports
  
  // Step 1: SOL → Token (kharidna)
  const buyQuote = await getJupiterQuote(
    TOKENS.SOL,
    tokenMint,
    lamports
  );
  
  if (!buyQuote) return null;
  
  const tokenReceived = parseInt(buyQuote.outAmount);
  const buyRoute = buyQuote.routePlan?.map(r => r.swapInfo?.label).join(' → ');
  
  // Step 2: Token → SOL (bechna)
  const sellQuote = await getJupiterQuote(
    tokenMint,
    TOKENS.SOL,
    tokenReceived
  );
  
  if (!sellQuote) return null;
  
  const solReceived = parseInt(sellQuote.outAmount);
  const sellRoute = sellQuote.routePlan?.map(r => r.swapInfo?.label).join(' → ');
  
  // Profit calculate karo
  const profitLamports = solReceived - lamports;
  const profitSol = profitLamports / 1e9;
  const profitPct = (profitLamports / lamports) * 100;
  
  return {
    [REDACTED_SECRET],
    inputSol: solAmount,
    outputSol: solReceived / 1e9,
    profitSol,
    profitPct,
    profitable: profitPct > 0,
    buyRoute,
    sellRoute,
    buyQuote,
    sellQuote,
  };
}

/**
 * SOL price in USD
 */
async function getSolPrice() {
  try {
    // CoinGecko free API - no auth needed
    const res = await axios.get(
      'https://api.coingecko.com/api/v3/simple/price?ids=solana&vs_currencies=usd',
      { timeout: 8000 }
    );
    return res.data?.solana?.usd || 0;
  } catch (e) {
    const data = await getMultiplePrices([TOKENS.SOL]);
    return data[TOKENS.SOL]?.price || 0;
  }
}

/**
 * Token price ka history track karna
 * Price spike = potential opportunity
 */
class PriceTracker {
  constructor() {
    this.prices = new Map(); // mint => [price1, price2, ...]
    this.maxHistory = 60;    // last 60 readings
  }
  
  update(mint, price) {
    if (!this.prices.has(mint)) {
      this.prices.set(mint, []);
    }
    const history = this.prices.get(mint);
    history.push({ price, time: Date.now() });
    if (history.length > this.maxHistory) history.shift();
  }
  
  getChange(mint, windowMs = 60000) {
    const history = this.prices.get(mint);
    if (!history || history.length < 2) return 0;
    
    const now = Date.now();
    const old = history.find(h => now - h.time >= windowMs);
    const current = history[history.length - 1];
    
    if (!old) return 0;
    return ((current.price - old.price) / old.price) * 100;
  }
  
  isSpike(mint, threshold = 5) {
    return Math.abs(this.getChange(mint)) >= threshold;
  }
}

module.exports = {
  getTokenPrice,
  getMultiplePrices,
  getJupiterQuote,
  checkArbitrage,
  getSolPrice,
  PriceTracker,
};


path: scripts/src/pumpfun.js
content: /**
 * IRONMAN — Pump.fun Direct API
 * 
 * New pump tokens directly trade via bonding curve
 * Jupiter doesn't route them until they graduate (~$69K mcap)
 * 
 * This module:
 * 1. Checks if token exists on pump.fun
 * 2. Gets token metadata + price
 * 3. Simulates buy via bonding curve math
 */

const axios = require('axios');
require('dotenv').config();

const HELIUS_URL = `https://mainnet.helius-rpc.com/?[REDACTED_SECRET]`;

/**
 * Get pump.fun token info via Helius DAS API
 */
async function getPumpToken(mint) {
  try {
    const res = await axios.post(HELIUS_URL, {
      jsonrpc: '2.0', id: 1, method: 'getAsset', params: { id: mint }
    }, { timeout: 8000 });

    const d = res.data?.result;
    if (!d) return null;

    const meta = d.content?.metadata || {};
    const isPump = mint.endsWith('pump');

    return {
      mint,
      name: meta.name || 'Unknown',
      symbol: meta.symbol || '???',
      description: meta.description || '',
      marketCap: null,          // not in DAS
      virtualSolReserves: null, // not in DAS
      virtualTokenReserves: null,
      totalSupply: d.token_info?.supply,
      complete: !isPump,        // non-pump suffix = likely graduated
      createdAt: Date.now(),    // unknown from DAS
      twitter: null,
      website: null,
      creatorWallet: d.authorities?.[0]?.address,
      replies: 0,
      decimals: d.token_info?.decimals || 6,
      isPump,
    };
  } catch (e) {
    return null;
  }
}

/**
 * Bonding curve price calc
 * Returns tokens you get for X SOL
 */
function calcBuy(virtualSolReserves, virtualTokenReserves, solAmountLamports) {
  // Pump.fun uses constant product: x * y = k
  const sol = BigInt(Math.floor(virtualSolReserves));
  const tokens = BigInt(Math.floor(virtualTokenReserves));
  const solIn = BigInt(Math.floor(solAmountLamports));
  
  // tokens_out = tokens * sol_in / (sol + sol_in)
  const tokensOut = (tokens * solIn) / (sol + solIn);
  return Number(tokensOut);
}

/**
 * Get recent pump.fun launches
 * Sorted by creation time — freshest first
 */
async function getRecentLaunches(limit = 20) {
  try {
    const res = await axios.get(`${PUMP_API}/coins/latest`, {
      params: { offset: 0, limit, sort: 'creation_time', order: 'DESC', includeNsfw: false },
      timeout: 8000,
    });
    return res.data.map(d => ({
      mint: d.mint,
      name: d.name,
      symbol: d.symbol,
      marketCap: d.usd_market_cap,
      createdAt: d.created_timestamp,
      ageSeconds: Math.floor((Date.now() - d.created_timestamp) / 1000),
      replies: d.reply_count,
      complete: d.complete,
    }));
  } catch (e) {
    return [];
  }
}

/**
 * Get top trending pump tokens (by volume)
 */
async function getTrending(limit = 10) {
  try {
    const res = await axios.get(`${PUMP_API}/coins`, {
      params: { offset: 0, limit, sort: 'last_trade_timestamp', order: 'DESC', includeNsfw: false },
      timeout: 8000,
    });
    return res.data.map(d => ({
      mint: d.mint,
      name: d.name,
      symbol: d.symbol,
      marketCap: d.usd_market_cap,
      lastTrade: d.last_trade_timestamp,
      replies: d.reply_count,
      complete: d.complete,
    }));
  } catch (e) {
    return [];
  }
}

/**
 * Safety check for pump token
 * Returns score 0-100 (higher = safer)
 */
async function pumpSafetyCheck(mint) {
  const token = await getPumpToken(mint);
  if (!token) return { score: 0, pass: false, reasons: ['Token metadata not found'] };
  
  const reasons = [];
  let score = 50; // base score — token exists

  // Is it a pump.fun token? (ends in 'pump')
  if (token.isPump) {
    score += 10;
    reasons.push('Pump.fun token ✅');
  }

  // Has name/symbol?
  if (token.name && token.name !== 'Unknown') score += 10;
  if (token.symbol && token.symbol !== '???') score += 10;

  // Already graduated to Raydium?
  if (token.complete) {
    score += 20;
    reasons.push('Graduated to Raydium ✅');
  }

  return {
    score: Math.min(100, Math.max(0, score)),
    pass: score >= 40,
    token,
    reasons,
  };
}

module.exports = { getPumpToken, calcBuy, getRecentLaunches, getTrending, pumpSafetyCheck };


path: scripts/src/sniper.js
content: /**
 * IRONMAN Solana Bot — Token Sniper
 * 
 * LESSON: How sniping works
 * 
 * New token launch hota hai Pump.fun / Raydium pe
 * → Hum WebSocket se detect karte hain
 * → Instantly buy karte hain (0.1-0.5 sec)
 * → Price pump hone pe sell karte hain
 * 
 * RISK WARNING:
 * → 90% new tokens = rugpull / honeypot
 * → Always small amounts only
 * → Never invest what you can't lose
 * 
 * SAFETY FILTERS we use:
 * → Minimum liquidity check
 * → No mint authority (can't print more)
 * → No freeze authority (can't freeze your tokens)
 * → Liquidity locked check
 */

const { Connection, PublicKey } = require('@solana/web3.js');
const axios = require('axios');
const { connection, DEX_PROGRAMS, TOKENS, config } = require('./config');
const { getJupiterQuote } = require('./price_monitor');
const { sendTelegram } = require('./alerts');

// Pump.fun program ID
const PUMP_FUN_PROGRAM = [REDACTED_SECRET];
// Raydium pool program
const RAYDIUM_POOL_PROGRAM = [REDACTED_SECRET];

/**
 * SAFETY CHECK — Token ke metadata verify karo
 * 
 * Ye sabse important step hai!
 * Bina safety check ke snipe karna = guaranteed loss
 */
async function safetyCheck(mintAddress) {
  const checks = {
    hasMintAuthority: false,   // true = bad (can print more tokens)
    hasFreezeAuthority: false, // true = bad (can freeze your wallet)
    hasLiquidity: false,       // false = bad (can't sell)
    liquidityUSD: 0,
    passed: false,
    reasons: [],
  };

  try {
    // Jupiter API se token info
    const res = await axios.get(
      `https://tokens.jup.ag/token/${mintAddress}`,
      { timeout: 5000 }
    );
    const tokenInfo = res.data;

    // Minimum liquidity check ($1000 minimum)
    // Kam liquidity = rug easily possible
    if (tokenInfo.daily_volume > 1000) {
      checks.hasLiquidity = true;
      checks.liquidityUSD = tokenInfo.daily_volume;
    } else {
      checks.reasons.push(`Low liquidity: $${tokenInfo.daily_volume}`);
    }

  } catch (e) {
    checks.reasons.push('Token not found on Jupiter');
  }

  try {
    // Solana RPC se mint account check
    const mintPubkey = new PublicKey(mintAddress);
    const mintInfo = await connection.getParsedAccountInfo(mintPubkey);
    
    if (mintInfo.value) {
      const data = mintInfo.value.data?.parsed?.info;
      if (data) {
        // Mint authority check
        if (data.mintAuthority) {
          checks.hasMintAuthority = true;
          checks.reasons.push('Has mint authority — dev can print tokens!');
        }
        
        // Freeze authority check
        if (data.freezeAuthority) {
          checks.hasFreezeAuthority = true;
          checks.reasons.push('Has freeze authority — dev can freeze your tokens!');
        }
      }
    }
  } catch (e) {
    checks.reasons.push('Could not verify mint authority');
  }

  // PASS only if: no mint auth + no freeze auth + has liquidity
  checks.passed = !checks.hasMintAuthority && 
                  !checks.hasFreezeAuthority && 
                  checks.hasLiquidity;

  return checks;
}

/**
 * NEW POOL DETECTOR
 * 
 * Raydium pe jab naya pool create hota hai
 * toh ek specific transaction pattern hota hai
 * Hum woh detect karte hain
 */
async function detectNewPools(onNewPool) {
  console.log('[Sniper] Watching for new pools on Raydium + Pump.fun...');
  
  // Raydium pool program ke logs monitor karo
  const subId = connection.onLogs(
    new PublicKey(RAYDIUM_POOL_PROGRAM),
    async (logs) => {
      // "initialize2" = new pool creation signature
      if (logs.logs.some(log => log.includes('initialize2'))) {
        console.log('\n[Sniper] 🚨 NEW RAYDIUM POOL DETECTED!');
        
        try {
          const tx = await connection.getParsedTransaction(logs.signature, {
            maxSupportedTransactionVersion: 0,
            commitment: 'confirmed',
          });
          
          if (tx) {
            // Token mint address dhundho
            const accounts = tx.transaction.message.accountKeys;
            const mints = [];
            
            for (const acc of accounts) {
              const pubkey = typeof acc === 'object' ? acc.pubkey?.toString() : acc.toString();
              // SOL aur USDC ko skip karo
              if (pubkey !== TOKENS.SOL && pubkey !== TOKENS.USDC) {
                try {
                  const info = await connection.getParsedAccountInfo(new PublicKey(pubkey));
                  if (info.value?.data?.parsed?.type === 'mint') {
                    mints.push(pubkey);
                  }
                } catch (e) {}
              }
            }
            
            if (mints.length > 0) {
              const newTokenMint = mints[0];
              console.log(`[Sniper] New token mint: ${newTokenMint}`);
              onNewPool({
                mint: newTokenMint,
                signature: logs.signature,
                timestamp: Date.now(),
                dex: 'RAYDIUM',
              });
            }
          }
        } catch (e) {
          console.error('[Sniper] Parse error:', e.message);
        }
      }
    },
    'confirmed'
  );
  
  // Pump.fun monitor (simpler — all txs are launches)
  const pumpSubId = connection.onLogs(
    new PublicKey(PUMP_FUN_PROGRAM),
    async (logs) => {
      if (logs.logs.some(log => log.includes('MintTo') || log.includes('InitializeMint'))) {
        console.log('\n[Sniper] 🚨 NEW PUMP.FUN TOKEN DETECTED!');
        
        // Quick grab the mint from logs
        const mintLog = logs.logs.find(l => l.includes('mint'));
        if (mintLog) {
          onNewPool({
            signature: logs.signature,
            timestamp: Date.now(),
            dex: 'PUMP_FUN',
            raw: mintLog,
          });
        }
      }
    },
    'confirmed'
  );
  
  return { raydiumSub: subId, pumpSub: pumpSubId };
}

/**
 * SNIPE EXECUTION LOGIC (Paper mode)
 * 
 * Real execution ke liye wallet private key chahiye
 * Abhi sirf simulate kar rahe hain
 */
async function executeSniperBuy(mint, solAmount, isSimulated = true) {
  console.log(`\n[Sniper] ${isSimulated ? 'SIMULATED' : 'REAL'} BUY: ${mint.slice(0,20)}... | ${solAmount} SOL`);
  
  // Safety check first!
  console.log('[Sniper] Running safety checks...');
  const safety = await safetyCheck(mint);
  
  console.log(`[Sniper] Safety: ${safety.passed ? '✅ PASSED' : '❌ FAILED'}`);
  if (!safety.passed) {
    console.log(`[Sniper] Reasons: ${safety.reasons.join(', ')}`);
    return { success: false, reason: safety.reasons.join(', ') };
  }
  
  // Get quote
  const lamports = Math.floor(solAmount * 1e9);
  const quote = await getJupiterQuote(TOKENS.SOL, mint, lamports, 1000); // 10% slippage for new tokens
  
  if (!quote) {
    return { success: false, reason: 'No route found' };
  }
  
  const tokensOut = parseInt(quote.outAmount);
  const priceImpact = parseFloat(quote.priceImpactPct || 0);
  
  console.log(`[Sniper] Quote: ${solAmount} SOL → ${tokensOut} tokens`);
  console.log(`[Sniper] Price impact: ${priceImpact.toFixed(2)}%`);
  
  if (priceImpact > 20) {
    return { success: false, reason: `Price impact too high: ${priceImpact.toFixed(2)}%` };
  }
  
  if (isSimulated) {
    // Paper trade only
    const result = {
      success: true,
      simulated: true,
      mint,
      solSpent: solAmount,
      tokensReceived: tokensOut,
      priceImpact,
      buyTime: Date.now(),
      safetyChecks: safety,
    };
    
    await sendTelegram(`
🎯 <b>SNIPER - PAPER BUY EXECUTED</b>

[REDACTED_SECRET],20)}...</code>
SOL spent: ${solAmount}
Tokens: ${tokensOut.toLocaleString()}
Price impact: ${priceImpact.toFixed(2)}%
Safety: ✅ PASSED

⚠️ SIMULATED — No real money
`.trim());
    
    return result;
  }
  
  // REAL EXECUTION — Only with configured wallet
  // TODO: Add @jup-ag/api swap execution
  // Requires: wallet private key + enough SOL
  return { success: false, reason: 'Real execution not yet configured' };
}

/**
 * AUTO SELL — Take profit / stop loss
 * 
 * Target: Sell at 2x (100% profit)
 * Stop:   Sell at -30% (stop loss)
 */
async function autoSell(position, currentPrice) {
  const { mint, buyPrice, solSpent, tokensReceived } = position;
  const pnlPct = ((currentPrice - buyPrice) / buyPrice) * 100;
  
  console.log(`[Sniper] Position ${mint.slice(0,12)}: ${pnlPct.toFixed(1)}% P&L`);
  
  // Take profit at 2x
  if (pnlPct >= 100) {
    console.log('[Sniper] 🎯 TAKE PROFIT triggered!');
    return { action: 'SELL', reason: 'Take profit 2x' };
  }
  
  // Stop loss at -30%
  if (pnlPct <= -30) {
    console.log('[Sniper] 🛑 STOP LOSS triggered!');
    return { action: 'SELL', reason: 'Stop loss -30%' };
  }
  
  return { action: 'HOLD', pnlPct };
}

/**
 * FULL SNIPER BOT — Put it all together
 */
async function startSniper(options = {}) {
  const {
    solPerTrade = 0.01,  // TINY amount for testing
    simulated = true,    // Always start in paper mode!
    maxPositions = 3,    // Max 3 positions at once
  } = options;
  
  console.log(`
[Sniper] ==========================================
[Sniper] IRONMAN SNIPER BOT STARTING
[Sniper] Mode: ${simulated ? 'PAPER (Simulated)' : '⚠️ REAL MONEY'}
[Sniper] Per trade: ${solPerTrade} SOL
[Sniper] Max positions: ${maxPositions}
[Sniper] ==========================================
  `);
  
  await sendTelegram(`
🎯 <b>Sniper Bot Started</b>
Mode: ${simulated ? 'Paper Trading' : 'LIVE'}
Per trade: ${solPerTrade} SOL
Watching: Raydium + Pump.fun new launches
`.trim());
  
  const positions = new Map();
  let totalSnipes = 0;
  
  // Start watching for new pools
  const subs = await detectNewPools(async (newToken) => {
    console.log(`\n[Sniper] New token detected: ${newToken.dex}`);
    console.log(`[Sniper] Signature: ${newToken.signature?.slice(0,20)}...`);
    
    // Max positions check
    if (positions.size >= maxPositions) {
      console.log('[Sniper] Max positions reached — skipping');
      return;
    }
    
    if (!newToken.mint) {
      console.log('[Sniper] No mint address found — skipping');
      return;
    }
    
    // Execute snipe
    totalSnipes++;
    const result = await executeSniperBuy(newToken.mint, solPerTrade, simulated);
    
    if (result.success) {
      positions.set(newToken.mint, {
        ...result,
        buyPrice: solPerTrade / result.tokensReceived,
      });
      console.log(`[Sniper] Position opened! Total: ${positions.size}`);
    }
  });
  
  // Position monitor — check prices every 30 seconds
  const positionMonitor = setInterval(async () => {
    if (positions.size === 0) return;
    
    console.log(`\n[Sniper] Checking ${positions.size} positions...`);
    
    for (const [mint, position] of positions) {
      try {
        // Get current sell quote
        const sellQuote = await getJupiterQuote(
          mint,
          TOKENS.SOL,
          position.tokensReceived,
          500
        );
        
        if (sellQuote) {
          const currentSOL = parseInt(sellQuote.outAmount) / 1e9;
          const currentPrice = currentSOL / position.tokensReceived;
          
          const decision = await autoSell(position, currentPrice);
          
          if (decision.action === 'SELL') {
            console.log(`[Sniper] Selling ${mint.slice(0,12)}... Reason: ${decision.reason}`);
            positions.delete(mint);
            
            await sendTelegram(`
📊 <b>Sniper Position Closed</b>
[REDACTED_SECRET],20)}...</code>
Reason: ${decision.reason}
P&L: ${((currentSOL - position.solSpent) / position.solSpent * 100).toFixed(1)}%
SOL back: ${currentSOL.toFixed(4)}
`.trim());
          }
        }
      } catch (e) {
        console.error(`[Sniper] Monitor error for ${mint.slice(0,12)}:`, e.message);
      }
    }
  }, 30000);
  
  // Stats every 5 minutes
  const statsInterval = setInterval(async () => {
    await sendTelegram(`
📊 <b>Sniper Stats</b>
Running: ${Math.floor(process.uptime()/60)} min
Total snipe attempts: ${totalSnipes}
Open positions: ${positions.size}
`.trim());
  }, 5 * 60 * 1000);
  
  return {
    stop: () => {
      clearInterval(positionMonitor);
      clearInterval(statsInterval);
    },
    getPositions: () => positions,
    getStats: () => ({ totalSnipes, openPositions: positions.size }),
  };
}

module.exports = {
  startSniper,
  safetyCheck,
  detectNewPools,
  executeSniperBuy,
  autoSell,
};


path: scripts/src/tracker_api.js
content: /**
 * IRONMAN — Solana Tracker API
 * Free tier: 100 req/min
 * Better data than raw RPC for wallet tracking
 */

const axios = require('axios');

const BASE = 'https://data.solanatracker.io';
// Free API - no key needed for basic endpoints
const HEADERS = { 'x-api-key': 'free' };

const WHALE = [REDACTED_SECRET];

async function getWalletTrades(wallet, limit = 20) {
  try {
    const r = await axios.get(`${BASE}/wallet/${wallet}/trades`, {
      params: { limit },
      timeout: 10000,
    });
    return r.data;
  } catch (e) {
    return null;
  }
}

async function getWalletTokens(wallet) {
  try {
    const r = await axios.get(`${BASE}/wallet/${wallet}`, {
      timeout: 10000,
    });
    return r.data;
  } catch (e) {
    return null;
  }
}

async function getTokenInfo(mint) {
  try {
    const r = await axios.get(`${BASE}/tokens/${mint}`, {
      timeout: 8000,
    });
    return r.data;
  } catch (e) {
    return null;
  }
}

async function analyzeWhaleNow() {
  console.log('\n=== LIVE WHALE ANALYSIS ===');
  console.log(`Wallet: ${WHALE}\n`);

  // Get recent trades
  const trades = await getWalletTrades(WHALE, 10);
  if (trades) {
    console.log(`Recent trades: ${JSON.stringify(trades).slice(0, 500)}`);
  }

  // Get holdings
  const holdings = await getWalletTokens(WHALE);
  if (holdings) {
    console.log(`Holdings: ${JSON.stringify(holdings).slice(0, 500)}`);
  }
}

module.exports = { getWalletTrades, getWalletTokens, getTokenInfo, analyzeWhaleNow, WHALE };


path: scripts/src/wallet_tracker.js
content: /**
 * IRONMAN Solana Bot — Wallet Tracker
 * 
 * LESSON 3: Whale Wallet Tracking
 * 
 * Smart money copy trading:
 * 1. Whale ka wallet WebSocket se monitor karo
 * 2. Jab whale koi token buy kare → hum bhi buy karo
 * 3. Jab whale sell kare → hum bhi sell karo
 * 
 * Risk: Whale dump kare toh tum bhi loss mein
 * Reward: Whale ke saath profit
 * 
 * EDUCATIONAL: Is wallet ko hum analyze kar rahe hain:
 * AgmLJBMDCqWynYnQiPCuj9ewsNNsBJXyzoUhD9LJzN51
 */

const { Connection, PublicKey } = require('@solana/web3.js');
const { connection, DEX_PROGRAMS } = require('./config');
const { getTokenPrice } = require('./price_monitor');
const axios = require('axios');

// Target whale wallet
const WHALE_WALLET = [REDACTED_SECRET];

/**
 * Wallet ki recent transactions fetch karo
 * RPC call: getSignaturesForAddress
 */
async function getRecentTransactions(walletAddress, limit = 10) {
  try {
    const pubkey = new PublicKey(walletAddress);
    const sigs = await connection.getSignaturesForAddress(pubkey, { limit });
    return sigs;
  } catch (e) {
    console.error('[Tracker] Error:', e.message);
    return [];
  }
}

/**
 * Transaction detail parse karo
 * Kaunsa token buy/sell hua? Kitna?
 */
async function parseTransaction(signature) {
  try {
    const tx = await connection.getParsedTransaction(signature, {
      maxSupportedTransactionVersion: 0,
      commitment: 'confirmed',
    });
    
    if (!tx || !tx.meta) return null;
    
    // Token balance changes dhundho
    const preBalances = tx.meta.preTokenBalances || [];
    const postBalances = tx.meta.postTokenBalances || [];
    
    const changes = [];
    
    // Post balance mein kya aaya
    for (const post of postBalances) {
      const pre = preBalances.find(
        p => p.mint === post.mint && p.accountIndex === post.accountIndex
      );
      
      const preAmt = pre ? parseFloat(pre.uiTokenAmount.uiAmount || 0) : 0;
      const postAmt = parseFloat(post.uiTokenAmount.uiAmount || 0);
      const change = postAmt - preAmt;
      
      if (Math.abs(change) > 0.001) {
        changes.push({
          mint: post.mint,
          change,
          action: change > 0 ? 'BUY' : 'SELL',
          owner: post.owner,
        });
      }
    }
    
    // SOL change
    const solChange = (tx.meta.postBalances[0] - tx.meta.preBalances[0]) / 1e9;
    
    // Kaunsa DEX use hua
    const accountKeys = tx.transaction.message.accountKeys.map(
      k => typeof k === 'object' ? k.pubkey?.toString() : k.toString()
    );
    
    const dexUsed = Object.entries(DEX_PROGRAMS).find(
      ([name, addr]) => accountKeys.includes(addr)
    );
    
    return {
      signature,
      timestamp: tx.blockTime,
      solChange,
      tokenChanges: changes,
      dex: dexUsed ? dexUsed[0] : 'UNKNOWN',
      success: !tx.meta.err,
    };
    
  } catch (e) {
    return null;
  }
}

/**
 * CORE: Real-time wallet monitor via WebSocket
 * 
 * Solana WebSocket se kisi bhi wallet ki
 * activity instantly milti hai
 * Polling se 10x faster!
 */
function watchWallet(walletAddress, onTransaction) {
  console.log(`[Tracker] Watching wallet: ${walletAddress.slice(0,20)}...`);
  
  const pubkey = new PublicKey(walletAddress);
  
  // WebSocket subscription
  // Jab bhi is wallet mein koi transaction aaye
  // toh callback call hoga
  const subId = connection.onAccountChange(
    pubkey,
    async (accountInfo, context) => {
      console.log(`[Tracker] Activity detected! Slot: ${context.slot}`);
      
      // Latest transaction fetch karo
      const sigs = await getRecentTransactions(walletAddress, 3);
      if (sigs.length > 0) {
        const parsed = await parseTransaction(sigs[0].signature);
        if (parsed) {
          onTransaction(parsed);
        }
      }
    },
    'confirmed'
  );
  
  console.log(`[Tracker] WebSocket subscription ID: ${subId}`);
  return subId;
}

/**
 * Pattern Analysis
 * 
 * Whale ke last N transactions analyze karo
 * Trading pattern samjho
 */
async function analyzeWalletPattern(walletAddress, txCount = 20) {
  console.log(`\n[Analysis] Analyzing ${walletAddress.slice(0,20)}...`);
  
  const sigs = await getRecentTransactions(walletAddress, txCount);
  console.log(`[Analysis] Fetched ${sigs.length} transactions`);
  
  const parsed = [];
  for (const sig of sigs.slice(0, 10)) { // Parse first 10
    const tx = await parseTransaction(sig.signature);
    if (tx) parsed.push(tx);
    await new Promise(r => setTimeout(r, 200)); // Rate limit respect
  }
  
  // Stats
  const totalTxs = sigs.length;
  const times = sigs.map(s => s.blockTime).filter(Boolean).sort((a,b) => b-a);
  const gaps = [];
  for (let i = 0; i < times.length - 1; i++) {
    gaps.push(times[i] - times[i+1]);
  }
  
  const avgGap = gaps.length ? gaps.reduce((a,b) => a+b, 0) / gaps.length : 0;
  const minGap = gaps.length ? Math.min(...gaps) : 0;
  
  // Token frequency
  const tokenFreq = {};
  const dexFreq = {};
  
  for (const tx of parsed) {
    for (const change of tx.tokenChanges) {
      tokenFreq[change.mint] = (tokenFreq[change.mint] || 0) + 1;
    }
    dexFreq[tx.dex] = (dexFreq[tx.dex] || 0) + 1;
  }
  
  const topTokens = Object.entries(tokenFreq)
    .sort((a,b) => b[1]-a[1])
    .slice(0, 5);
  
  const report = {
    wallet: walletAddress,
    totalTxs,
    avgGapSec: avgGap.toFixed(1),
    minGapSec: minGap.toFixed(1),
    isBot: minGap < 2,
    dexUsage: dexFreq,
    topTokens,
    strategy: minGap < 2 ? 'BOT (Automated)' : 
               avgGap < 60 ? 'Active Trader' : 'Manual Trader',
  };
  
  console.log('\n=== WALLET PATTERN REPORT ===');
  console.log(`Strategy: ${report.strategy}`);
  console.log(`Avg gap between txs: ${report.avgGapSec}s`);
  console.log(`Min gap: ${report.minGapSec}s`);
  console.log(`DEX usage:`, report.dexUsage);
  console.log(`Top tokens:`, topTokens.map(([m,c]) => `${m.slice(0,8)}: ${c}x`));
  
  return report;
}

/**
 * Copy Trade Simulator (PAPER TRADING — NO REAL MONEY)
 * 
 * Whale ke trades ko simulate karo
 * Real paise mat lagao — pehle result dekho
 */
class CopyTradeSimulator {
  constructor(initialSOL = 1.0) {
    this.balance = initialSOL;
    this.initialBalance = initialSOL;
    this.holdings = new Map(); // token => amount
    this.trades = [];
    console.log(`[Simulator] Started with ${initialSOL} SOL (PAPER TRADING)`);
  }
  
  async onWhaleTrade(tx) {
    for (const change of tx.tokenChanges) {
      if (change.action === 'BUY' && this.balance > 0.01) {
        // Whale ne buy kiya — hum bhi buy karte hain (simulated)
        const tradeSOL = Math.min(this.balance * 0.1, 0.1); // 10% of balance
        const price = await getTokenPrice(change.mint);
        
        if (price) {
          const tokensReceived = (tradeSOL / price.price);
          this.balance -= tradeSOL;
          this.holdings.set(change.mint, 
            (this.holdings.get(change.mint) || 0) + tokensReceived
          );
          
          const trade = {
            action: 'BUY',
            mint: change.mint,
            solSpent: tradeSOL,
            tokensReceived,
            price: price.price,
            time: new Date().toISOString(),
            dex: tx.dex,
          };
          this.trades.push(trade);
          console.log(`[Simulator] PAPER BUY: ${tradeSOL} SOL → ${tokensReceived.toFixed(2)} tokens @ $${price.price}`);
        }
        
      } else if (change.action === 'SELL') {
        // Whale ne sell kiya — hum bhi sell karte hain
        const held = this.holdings.get(change.mint) || 0;
        if (held > 0) {
          const price = await getTokenPrice(change.mint);
          if (price) {
            const solReceived = held * price.price;
            this.balance += solReceived;
            this.holdings.delete(change.mint);
            
            const trade = {
              action: 'SELL',
              mint: change.mint,
              tokensSold: held,
              solReceived,
              price: price.price,
              time: new Date().toISOString(),
            };
            this.trades.push(trade);
            console.log(`[Simulator] PAPER SELL: ${held.toFixed(2)} tokens → ${solReceived.toFixed(4)} SOL`);
          }
        }
      }
    }
  }
  
  getPnL() {
    const pnl = this.balance - this.initialBalance;
    const pnlPct = (pnl / this.initialBalance) * 100;
    return {
      currentBalance: this.balance,
      pnl,
      pnlPct,
      trades: this.trades.length,
    };
  }
}

module.exports = {
  getRecentTransactions,
  parseTransaction,
  watchWallet,
  analyzeWalletPattern,
  CopyTradeSimulator,
  WHALE_WALLET,
};


path: references/api-setup.md
content: # Free API Keys Setup

## Helius (Required — Free)
1. Go to https://dev.helius.xyz
2. Sign up with email
3. Copy API key → paste in `.env` as `HELIUS_API_KEY`
4. Free tier: 1000 req/day (enough for learning)
5. RPC URL: `https://mainnet.helius-rpc.com/?[REDACTED_SECRET]`

## Telegram Bot (Optional — Alerts)
1. Message @BotFather on Telegram
2. `/newbot` → follow prompts → copy token
3. Message your bot once
4. Get chat ID: `https://api.telegram.org/bot<TOKEN>/getUpdates`
5. Paste both in `.env`

## Solana Wallet (Optional — Live Trading)
- Use a BURNER wallet — never your main wallet
- Export private key as base58 from Phantom: Settings → Export Private Key
- Paste in `.env` as `PRIVATE_KEY`
- Fund with small amount (0.05 SOL recommended for testing)


path: references/trading-concepts.md
content: # Solana Trading Concepts

## MEV (Maximal Extractable Value)
Bots front-run transactions by paying higher priority fees.
Example: Whale buys token → bot sees it in mempool → bot buys first → price rises → bot sells to whale at higher price.

## Copy Trading
Watch a profitable wallet → when they buy X, you buy X too → when they sell, you sell.
Risk: you're always slightly behind (latency), so you pay higher price.
Reward: ride the same price move as the whale.

## Arbitrage  
Same token, different price on two DEXes.
Buy cheap on Raydium → sell expensive on Orca = risk-free profit.
Reality: bots compete, margins are tiny (0.1-0.5%), need speed + size.

## Jupiter Aggregator
Best swap router on Solana. Finds optimal route across all DEXes.
API: quote-api.jup.ag/v6/quote → get best price
     quote-api.jup.ag/v6/swap → get transaction to execute

## Pump.fun Bonding Curve
New tokens launch on pump.fun with a bonding curve:
- Price increases as more people buy
- Token "graduates" to Raydium at ~$69K market cap
- Before graduation: only trade via pump.fun bonding curve
- After graduation: trade via Jupiter/Raydium like normal token

## Helius WebSocket
Real-time transaction monitoring:
```js
connection.onAccountChange(pubkey, callback)  // watch wallet
```
Fires within 400ms of any transaction.

## Price Impact
How much your trade moves the price.
- Low liquidity token + big trade = high price impact
- >5% impact = bad trade (you're moving the market against yourself)
- >50% impact = rug risk or extremely thin liquidity


Security scan verdicts — ClawScan: suspicious | static analysis: suspicious | VirusTotal: suspicious | SkillSpector: suspicious (CRITICAL)