├── .gitignore ├── WalletScanData ├── 20240816021817.json └── 20240926105415.json ├── .prettierignore ├── src ├── config │ ├── index.ts │ ├── logger.ts │ ├── constants.ts │ └── utils.ts ├── wallets.ts ├── scan.ts ├── send.ts ├── jito-bundle │ └── index.ts ├── buy.ts ├── gather.ts ├── sell.ts ├── amm │ └── Ammswap.ts └── main.ts ├── .prettierrc ├── wallets.json ├── tsconfig.json ├── .env.example ├── package.json └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .env -------------------------------------------------------------------------------- /WalletScanData/20240816021817.json: -------------------------------------------------------------------------------- 1 | [] 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .next 3 | yarn.lock 4 | package-lock.json 5 | public 6 | -------------------------------------------------------------------------------- /src/config/index.ts: -------------------------------------------------------------------------------- 1 | export * from './constants'; 2 | export * from './logger'; 3 | export * from './utils'; 4 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 2, 3 | "useTabs": false, 4 | "singleQuote": true, 5 | "semi": true, 6 | "bracketSpacing": true, 7 | "printWidth": 120, 8 | "arrowParens": "always", 9 | "eslintIntegration": true 10 | } 11 | -------------------------------------------------------------------------------- /src/config/logger.ts: -------------------------------------------------------------------------------- 1 | import pino from 'pino/pino'; 2 | 3 | const transport = pino.transport({ 4 | target: 'pino-pretty', 5 | }); 6 | 7 | export const logger = pino( 8 | { 9 | level: 'info', 10 | base: undefined, 11 | }, 12 | transport, 13 | ); 14 | -------------------------------------------------------------------------------- /WalletScanData/20240926105415.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "publicKey": "J8rE2LgPezWi9NFryaZ4DsoadMffeVmiJ3UWHNKA6fEX", 4 | "secretKey": "2UrXq4MCLA8AW3AbxFJ9XHXBBX3s8Wbp5rWDoGux144tv56MK4ttD3a1pqTH1yBiqxEkbK6eEqgP6oyF8UhPe199", 5 | "tokenBalance": 0, 6 | "solBalance": 0 7 | } 8 | ] -------------------------------------------------------------------------------- /wallets.json: -------------------------------------------------------------------------------- 1 | [{"publicKey":"ER4EEZwuLbSDoiCbnt5GU6kQwvXvWrtxHuBjgwCoCypk","secretKey":"3UHbEFeXNJp7piuTXan87uvBknLamXPe3TyNLP7b1h9szjBC2LbVynSDJvgVbnY2JiECSsVW6AjvKkRnG9R9MXya"},{"publicKey":"3s66P6GGRtQ5zTmnemkZWdDrdRPZULGvfyg38A7t4kr4","secretKey":"4tF9i88UmNzDP1vXf8Rfm5Xa1GzuUtnJoYgEYN4s4vg4xGp2axnHDxAiwEsu784WCyNWviB4ikgMNXTT5vKLq6o2"},{"publicKey":"CWDXH99nSwfG6gXoKNPJm4RWZiDc6uZxWyAatF4uQEgv","secretKey":"S13h4cAxdogBb9uta2tykK1ELTtVXAXpa83WgFT8781WgdPer1ew5eqFPPxv36jYnfphovo3fJzYfVvbYZcVHcS"}] -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "commonjs", 5 | "strict": true, 6 | "esModuleInterop": true, 7 | "forceConsistentCasingInFileNames": true, 8 | "outDir": "dist", 9 | "rootDir": "src", 10 | "resolveJsonModule": true, 11 | "skipLibCheck": true, 12 | "lib": ["es6", "dom"], 13 | "sourceMap": true, 14 | "declaration": true 15 | // "typeRoots": ["./types", "./node_modules/@types"] 16 | }, 17 | "include": ["src/**/*.ts", "types/**/*.d.ts"], 18 | "exclude": ["node_modules"] 19 | } 20 | -------------------------------------------------------------------------------- /src/wallets.ts: -------------------------------------------------------------------------------- 1 | import { Keypair } from '@solana/web3.js'; 2 | import fs from 'fs'; 3 | import bs58 from 'bs58'; 4 | import { NUMBER_OF_WALLETS } from './config'; 5 | 6 | let newWallets = []; 7 | for (let i = 0; i < NUMBER_OF_WALLETS; i++) { 8 | // Generating a new random Solana keypair 9 | const keypair = Keypair.generate(); 10 | 11 | newWallets.push({ 12 | publicKey: keypair.publicKey.toBase58(), 13 | secretKey: bs58.encode(keypair.secretKey), 14 | }); 15 | } 16 | fs.writeFileSync('./wallets.json', JSON.stringify(newWallets)); 17 | console.log('Successfully Created!'); 18 | console.log("Available wallets:\n", newWallets); 19 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | RPC_ENDPOINT = https://proud-long-dawn.solana-mainnet.quiknode.pro/974eacf63ac2797810e72a192b24b0f409d2ddc4/ 2 | 3 | PROVIDER_PRIVATE_KEY = 3hpo4Mi4jCwVtKHwr6Vy1XmVzkkLeLLpaZtJFJ8S5bVbLW2vhv5kPip3AJh5s7z5kp8QgEx1LYQFE6af6MsQijGS 4 | 5 | JITO_FEE_PAYER_PRIVATE_KEY = 6 | 7 | GATHER_WALLET_ADDRESS = 8 | 9 | NUMBER_OF_WALLETS = 3 10 | 11 | SEND_SOL_AMOUNT = 0.01 12 | 13 | POOL_ADDRESS = 3B5vXBEYAmV8y13pgvzSi7eLDFS5tRd4pZZZPNuA4Ao2 14 | TOKEN_ADDRESS = HRw8mqK8N3ASKFKJGMJpy4FodwR3GKvCFKPDQNqUNuEP 15 | #Process Run Time Varriable 16 | MAX_TIME = 110000 17 | MIN_TIME = 100000 18 | 19 | #Breaktime between trading 20 | MAX_TRADE_WAIT = 2000 21 | MIN_TRADE_WAIT = 1000 22 | 23 | #SELL Quantity Range 24 | MIN_SELL_QUANTITY = 10 25 | MAX_SELL_QUANTITY = 15 26 | 27 | #BUY Quantity Range-Must be approx 3% Higher than Sell Quantity Range 28 | MIN_BUY_QUANTITY = 12 29 | MAX_BUY_QUANTITY = 17 30 | 31 | #SLIPPAGE 32 | SLIPPAGE = 10 33 | 34 | #[1, 4] 35 | TRANSACTION_COUNT_PER_BUNDLE = 4 36 | 37 | 38 | COMPUTE_UNIT_LIMIT = 101337 39 | COMPUTE_UNIT_PRICE = 41111 40 | 41 | #1000000 /* 500000 /* 300000 */ /* 200000 */ /* 100000 */ /* 50000 */ /* 10000 */ /* 5000 */ /* 1000 */ 42 | 43 | JITO_FEE = 5000 44 | 45 | BUFFER = 0.005 46 | 47 | IS_TOKEN_2022 = false -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tradingbot", 3 | "version": "1.0.0", 4 | "description": "", 5 | "scripts": { 6 | "dev": "node dist/main.js", 7 | "start": "ts-node src/main.ts", 8 | "send": "ts-node src/send.ts", 9 | "sell": "ts-node src/sell.ts", 10 | "build": "tsc", 11 | "gather": "ts-node src/gather.ts", 12 | "wallet": "ts-node src/wallets.ts", 13 | "scan": "ts-node src/scan.ts", 14 | "buy": "ts-node src/buy.ts", 15 | "format": "prettier --write src/**/*.ts" 16 | }, 17 | "keywords": [ 18 | "solana", 19 | "trading", 20 | "bot" 21 | ], 22 | "author": "jsphantom", 23 | "license": "ISC", 24 | "dependencies": { 25 | "@ixjb94/indicators": "^1.2.2", 26 | "@project-serum/anchor": "^0.26.0", 27 | "@raydium-io/raydium-sdk": "^1.3.1-beta.58", 28 | "@raydium-io/raydium-sdk-v2": "0.1.35-alpha", 29 | "@solana/spl-token": "^0.4.6", 30 | "@solana/spl-token-registry": "^0.2.7", 31 | "@solana/web3.js": "^1.89.1", 32 | "@types/jsonfile": "^6.1.4", 33 | "@types/node": "^14.11.2", 34 | "bigint-buffer": "^1.1.5", 35 | "bip39": "^3.1.0", 36 | "bluebird": "^3.7.2", 37 | "bs58": "^5.0.0", 38 | "decimal.js": "^10.4.3", 39 | "dotenv": "^16.4.5", 40 | "ed25519-hd-key": "^1.3.0", 41 | "jsonfile": "^6.1.0", 42 | "node-cron": "^3.0.3", 43 | "pino": "^9.2.0", 44 | "pino-pretty": "^11.2.1", 45 | "prettier": "^3.2.5", 46 | "ts-node": "^10.9.1", 47 | "typescript": "^5.3.3" 48 | }, 49 | "devDependencies": { 50 | "@types/bluebird": "^3.5.42", 51 | "@types/bn.js": "^5.1.5", 52 | "npm-check-updates": "^16.14.11" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PumpFun AMM(pumpswap) Trading Bot 2 | 3 | ## About 4 | 5 | PumpFun AMM(pumpswap) Trading Bot is a specialized sniping bot, volume bot and bundler designed to work with the PumpFun AMM decentralized exchange (DEX). It enables users to execute high-frequency trading strategies by automating buy/sell operations during token launches, pump events, or other fast-paced trading scenarios. Written in **TypeScript**, this bot aims to provide a seamless trading experience while allowing customization of trading strategies. 6 | 7 | --- 8 | 9 | ## Contact 10 | 11 | If you wanna build this trading bot, contact here: [Telegram](https://t.me/shiny0103) [Twitter](https://x.com/0xTan1319) 12 | 13 | ## Features 14 | 15 | - **Sniping Functionality**: Quickly execute trades to take advantage of token launches or pump events. 16 | - **Wallet Scanning**: Includes functionality to scan specific wallets for trading activity. 17 | - **Customizable Configurations**: Adjust the bot's settings through environment variables (`.env` file) and configuration files. 18 | - **Fast Execution**: Optimized for low latency and rapid transaction execution. 19 | - **Secure Wallet Integration**: Uses secure methods to manage multiple wallets (`wallets.json`). 20 | 21 | --- 22 | 23 | ## File Structure 24 | 25 | ```plaintext 26 | pumpfun-amm-trading-bot/ 27 | ├── WalletScanData/ # Contains wallet scanning data 28 | ├── src/ # Main source code for the bot 29 | ├── .env.example # Example environment configuration file 30 | ├── .gitignore # Files/folders ignored by Git 31 | ├── .prettierignore # Files/folders ignored by Prettier 32 | ├── .prettierrc # Prettier configuration 33 | ├── README.md # Project documentation (this file) 34 | ├── package-lock.json # Lockfile for dependencies 35 | ├── package.json # Node.js project configuration 36 | ├── tsconfig.json # TypeScript configuration 37 | ├── wallets.json # File for managing wallet information 38 | ├── yarn.lock # Yarn lockfile for dependencies 39 | -------------------------------------------------------------------------------- /src/scan.ts: -------------------------------------------------------------------------------- 1 | import { 2 | TOKEN_ADDRESS, 3 | GATHER_WALLET_ADDRESS, 4 | COMPUTE_UNIT_LIMIT, 5 | COMPUTE_UNIT_PRICE, 6 | RPC_ENDPOINT, 7 | getTokenAccount, 8 | } from './config'; 9 | import wallets from '../wallets.json'; 10 | import { getWallet, getTokenAccountBalance, getCoinBalance, fetchWithTimeout } from './config'; 11 | import { logger } from './config'; 12 | import { Connection, Keypair } from '@solana/web3.js'; 13 | import { executeAndConfirm } from './amm/Ammswap'; 14 | import { unpackMint, getOrCreateAssociatedTokenAccount, createTransferInstruction, Account } from '@solana/spl-token'; 15 | import bs58 from 'bs58'; 16 | import * as fs from 'fs'; 17 | const connection: Connection = new Connection(RPC_ENDPOINT, { 18 | fetch: fetchWithTimeout, 19 | commitment: 'confirmed', 20 | }); 21 | interface WALLET_STATUS { 22 | secretKey: string; 23 | publicKey: string; 24 | tokenBalance: number; 25 | solBalance: number; 26 | } 27 | function getFormattedDate(): string { 28 | const now = new Date(); 29 | 30 | const year: string = String(now.getFullYear()); // Get last two digits of the year 31 | const month: string = String(now.getMonth() + 1).padStart(2, '0'); // Get month and pad with zero 32 | const day: string = String(now.getDate()).padStart(2, '0'); // Get day and pad with zero 33 | const hours: string = String(now.getHours()).padStart(2, '0'); // Get hours and pad with zero 34 | const minutes: string = String(now.getMinutes()).padStart(2, '0'); // Get minutes and pad with zero 35 | const seconds: string = String(now.getSeconds()).padStart(2, '0'); // Get seconds and pad with zero 36 | 37 | // Combine into the specified format 38 | return `${year}${month}${day}${hours}${minutes}${seconds}`; 39 | } 40 | let walletArray: WALLET_STATUS[] = []; 41 | let filename = './WalletScanData/'; 42 | async function scan() { 43 | for (let i = 0; i < wallets.length; i++) { 44 | try { 45 | let keypair: Keypair = getWallet(wallets[i].secretKey); 46 | let token_in_wallet = await getTokenAccountBalance(connection, wallets[i].publicKey, TOKEN_ADDRESS); 47 | const walletBalance = await getCoinBalance(connection, keypair.publicKey); 48 | walletArray.push({ 49 | publicKey: keypair.publicKey.toBase58(), 50 | secretKey: bs58.encode(keypair.secretKey), 51 | tokenBalance: token_in_wallet.uiAmount, 52 | solBalance: walletBalance / 10 ** 9, 53 | }); 54 | } catch (e: unknown) { 55 | logger.info(`[SWAP - SELL - ERROR] ${e}`); 56 | } 57 | } 58 | filename = filename + getFormattedDate() + '.json'; 59 | fs.writeFileSync(filename, JSON.stringify(walletArray)); 60 | logger.info('Wallet scanning is finished!!'); 61 | } 62 | scan(); 63 | -------------------------------------------------------------------------------- /src/config/constants.ts: -------------------------------------------------------------------------------- 1 | import { Logger } from 'pino/pino'; 2 | import dotenv from 'dotenv'; 3 | import { logger } from './logger'; 4 | import { TxVersion } from '@raydium-io/raydium-sdk-v2'; 5 | 6 | dotenv.config(); 7 | 8 | const retrieveEnvVariable = (variableName: string, logger: Logger) => { 9 | const variable = process.env[variableName] || ''; 10 | if (!variable) { 11 | logger.error(`${variableName} is not set`); 12 | process.exit(1); 13 | } 14 | return variable; 15 | }; 16 | 17 | export const PROVIDER_PRIVATE_KEY = retrieveEnvVariable('PROVIDER_PRIVATE_KEY', logger); 18 | export const JITO_FEE_PAYER_PRIVATE_KEY = retrieveEnvVariable('JITO_FEE_PAYER_PRIVATE_KEY', logger); 19 | export const GATHER_WALLET_ADDRESS = retrieveEnvVariable('GATHER_WALLET_ADDRESS', logger); 20 | 21 | // Connection 22 | export const NETWORK = 'mainnet-beta'; 23 | export const RPC_ENDPOINT: string = retrieveEnvVariable('RPC_ENDPOINT', logger); 24 | export const YOUR_WALLET_SECRET_KEY: string = retrieveEnvVariable('YOUR_WALLET_SECRET_KEY', logger); 25 | export const POOL_ADDRESS: string = retrieveEnvVariable('POOL_ADDRESS', logger); 26 | export const TOKEN_ADDRESS: string = retrieveEnvVariable('TOKEN_ADDRESS', logger); 27 | export const MAX_TIME: number = Number(retrieveEnvVariable('MAX_TIME', logger)); 28 | export const MIN_TIME: number = Number(retrieveEnvVariable('MIN_TIME', logger)); 29 | export const MAX_TRADE_WAIT: number = Number(retrieveEnvVariable('MAX_TRADE_WAIT', logger)); 30 | export const MIN_TRADE_WAIT: number = Number(retrieveEnvVariable('MIN_TRADE_WAIT', logger)); 31 | export const MIN_SELL_QUANTITY: number = Number(retrieveEnvVariable('MIN_SELL_QUANTITY', logger)); 32 | export const MAX_SELL_QUANTITY: number = Number(retrieveEnvVariable('MAX_SELL_QUANTITY', logger)); 33 | export const MIN_BUY_QUANTITY: number = Number(retrieveEnvVariable('MIN_BUY_QUANTITY', logger)); 34 | export const MAX_BUY_QUANTITY: number = Number(retrieveEnvVariable('MAX_BUY_QUANTITY', logger)); 35 | export const NUMBER_OF_WALLETS: number = Number(retrieveEnvVariable('NUMBER_OF_WALLETS', logger)); 36 | export const SEND_SOL_AMOUNT: number = Number(retrieveEnvVariable('SEND_SOL_AMOUNT', logger)); 37 | export const TRANSACTION_COUNT_PER_BUNDLE: number = Number(retrieveEnvVariable('TRANSACTION_COUNT_PER_BUNDLE', logger)); 38 | export const BUFFER: number = Number(retrieveEnvVariable('BUFFER', logger)); 39 | export const IS_TOKEN_2022: boolean = Boolean(retrieveEnvVariable('IS_TOKEN_2022', logger) === 'true'); 40 | export const JITO_FEE = retrieveEnvVariable('JITO_FEE', logger); 41 | export const SLIPPAGE: number = Number(retrieveEnvVariable('SLIPPAGE', logger)); 42 | export const COMPUTE_UNIT_LIMIT: number = Number(retrieveEnvVariable('COMPUTE_UNIT_LIMIT', logger)); 43 | export const COMPUTE_UNIT_PRICE: number = Number(retrieveEnvVariable('COMPUTE_UNIT_PRICE', logger)); 44 | export const txVersion = TxVersion.V0; 45 | -------------------------------------------------------------------------------- /src/send.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Connection, 3 | Keypair, 4 | PublicKey, 5 | LAMPORTS_PER_SOL, 6 | ComputeBudgetProgram, 7 | SystemProgram, 8 | VersionedTransaction, 9 | TransactionMessage, 10 | } from '@solana/web3.js'; 11 | import fs from 'fs'; 12 | import { logger, getWallet, getCoinBalance, getTokenDecimal, fetchWithTimeout } from './config'; 13 | import { 14 | RPC_ENDPOINT, 15 | PROVIDER_PRIVATE_KEY, 16 | TOKEN_ADDRESS, 17 | SEND_SOL_AMOUNT, 18 | NUMBER_OF_WALLETS, 19 | COMPUTE_UNIT_PRICE, 20 | COMPUTE_UNIT_LIMIT, 21 | } from './config'; 22 | import { executeAndConfirm } from './amm/Ammswap'; 23 | 24 | import bs58 from 'bs58'; 25 | import wallets from '../wallets.json'; 26 | 27 | const connection: Connection = new Connection(RPC_ENDPOINT); 28 | const providerWallet: Keypair = getWallet(PROVIDER_PRIVATE_KEY); 29 | let tokenDecimal: number; 30 | 31 | interface WALLET_STATUS { 32 | wallet: Keypair; 33 | id: number; 34 | } 35 | 36 | let walletArray: WALLET_STATUS[] = []; 37 | 38 | const createWalletsAndSendSol = async (connection: Connection) => { 39 | tokenDecimal = await getTokenDecimal(connection, new PublicKey(TOKEN_ADDRESS)); 40 | const walletBalance = await getCoinBalance(connection, providerWallet.publicKey); 41 | 42 | if (walletBalance / LAMPORTS_PER_SOL < SEND_SOL_AMOUNT * NUMBER_OF_WALLETS) { 43 | logger.error('Deposite sol into the provider wallet'); 44 | process.exit(1); 45 | } 46 | let diffWalletCount = NUMBER_OF_WALLETS - wallets.length; 47 | if (diffWalletCount > 0) { 48 | let newWallets = [...wallets]; 49 | for (diffWalletCount; diffWalletCount > 0; diffWalletCount--) { 50 | // Generating a new random Solana keypair 51 | const keypair = Keypair.generate(); 52 | 53 | newWallets.push({ 54 | publicKey: keypair.publicKey.toBase58(), 55 | secretKey: bs58.encode(keypair.secretKey), 56 | }); 57 | } 58 | fs.writeFileSync('../wallets.json', JSON.stringify(newWallets, null, 1)); 59 | } 60 | for (let i = 0; i < NUMBER_OF_WALLETS; i++) { 61 | const keypair: Keypair = getWallet(wallets[i].secretKey); 62 | walletArray = [...walletArray, { wallet: keypair, id: i }]; 63 | } 64 | logger.info('Wallet Checking Now...'); 65 | for (let i = 0; i < NUMBER_OF_WALLETS; i++) { 66 | logger.info(`${i + 1}. Checking ${walletArray[i].wallet.publicKey.toBase58()}`); 67 | let walletBalance = await getCoinBalance(connection, walletArray[i].wallet.publicKey); 68 | if (walletBalance < SEND_SOL_AMOUNT * LAMPORTS_PER_SOL) { 69 | let diffBalance = SEND_SOL_AMOUNT * LAMPORTS_PER_SOL - walletBalance; 70 | const latestBlockhash = await connection.getLatestBlockhash(); 71 | const instructions = [ 72 | SystemProgram.transfer({ 73 | fromPubkey: providerWallet.publicKey, 74 | toPubkey: walletArray[i].wallet.publicKey, 75 | lamports: diffBalance, 76 | }), 77 | ComputeBudgetProgram.setComputeUnitPrice({ microLamports: COMPUTE_UNIT_PRICE }), 78 | ComputeBudgetProgram.setComputeUnitLimit({ units: COMPUTE_UNIT_LIMIT }), 79 | ]; 80 | const messageV0 = new TransactionMessage({ 81 | payerKey: providerWallet.publicKey, 82 | recentBlockhash: latestBlockhash.blockhash, 83 | instructions, 84 | }).compileToV0Message(); 85 | const transaction = new VersionedTransaction(messageV0); 86 | transaction.sign([providerWallet]); 87 | let result; 88 | 89 | try { 90 | result = await executeAndConfirm(connection, transaction, latestBlockhash); 91 | if (result.confirmed) { 92 | logger.info(`transaction Sent: https://solscan.io/tx/${result.signature}`); 93 | } else { 94 | logger.error('Transaction sending is failed, retrying now'); 95 | i--; 96 | continue; 97 | } 98 | } catch (error) { 99 | logger.error('Transaction sending is failed, retrying now'); 100 | i--; 101 | continue; 102 | } 103 | } 104 | logger.info('This wallet has enough sol balance'); 105 | } 106 | }; 107 | createWalletsAndSendSol(connection); 108 | -------------------------------------------------------------------------------- /src/jito-bundle/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Connection, 3 | Keypair, 4 | PublicKey, 5 | TransactionMessage, 6 | SystemProgram, 7 | VersionedTransaction, 8 | BlockhashWithExpiryBlockHeight, 9 | Transaction, 10 | } from '@solana/web3.js'; 11 | import axios, { AxiosError, AxiosResponse } from 'axios'; 12 | 13 | import { logger, sleep } from '../config'; 14 | import { confirm } from '../amm/Ammswap'; 15 | import { Currency, CurrencyAmount } from '@raydium-io/raydium-sdk-v2'; 16 | import bs58 from 'bs58'; 17 | 18 | const jitoTipAccounts = [ 19 | 'Cw8CFyM9FkoMi7K7Crf6HNQqf4uEMzpKw6QNghXLvLkY', 20 | 'DttWaMuVvTiduZRnguLF7jNxTgiMBZ1hyAumKUiL2KRL', 21 | '96gYZGLnJYVFmbjzopPSU6QiEV5fGqZNyN9nmNhvrZU5', 22 | '3AVi9Tg9Uo68tJfuvoKvqKNWKkC5wPdSSdeBnizKZ6jT', 23 | 'HFqU5x63VTqvQss8hp11i4wVV8bD44PvwucfZ2bU7gRe', 24 | 'ADaUMid9yfUytqMBgopwjb2DTLSokTSzL1zt6iGPaS49', 25 | 'ADuUkR4vqLUMWXxW9gh6D6L8pMSawimctcNZ5pGwDcEt', 26 | 'DfXygSm4jCyNCybVYYK6DwvWqjKee8pbDmJGcLWNDXjh', 27 | ]; 28 | 29 | export const getRandomValidatorKey = (): PublicKey => { 30 | const randomValidator = jitoTipAccounts[Math.floor(Math.random() * jitoTipAccounts.length)]; 31 | return new PublicKey(randomValidator); 32 | }; 33 | 34 | export const executeAndConfirmByJito = async ( 35 | connection: Connection, 36 | payer: Keypair, 37 | jitoFee: string, 38 | bundleTransactionLimit: number, 39 | transactions: Array, 40 | latestBlockhash: BlockhashWithExpiryBlockHeight, 41 | ): Promise<{ confirmed: boolean; signature: string }> => { 42 | 43 | if (transactions.length > bundleTransactionLimit) { 44 | console.error('Exceeded bundleTransactionLimit'); 45 | return { confirmed: false, signature: '' }; 46 | } 47 | logger.info('Starting Kitty transaction execution...'); 48 | const JitoFeeWallet = getRandomValidatorKey(); 49 | 50 | logger.trace(`Selected Kitty fee wallet: ${JitoFeeWallet.toBase58()}`); 51 | try { 52 | const fee = new CurrencyAmount(Currency.SOL, jitoFee, true).raw.toNumber(); 53 | logger.info(`Calculated fee: ${fee} lamports`); 54 | 55 | const jitTipTxFeeMessage = new TransactionMessage({ 56 | payerKey: payer.publicKey, 57 | recentBlockhash: latestBlockhash.blockhash, 58 | instructions: [ 59 | SystemProgram.transfer({ 60 | fromPubkey: payer.publicKey, 61 | toPubkey: JitoFeeWallet, 62 | lamports: fee, 63 | }), 64 | ], 65 | }).compileToV0Message(); 66 | 67 | const jitoFeeTx = new VersionedTransaction(jitTipTxFeeMessage); 68 | jitoFeeTx.sign([payer]); 69 | const jitoTxsignature = bs58.encode(jitoFeeTx.signatures[0]); 70 | // Serialize the transactions once here 71 | const serializedjitoFeeTx = bs58.encode(jitoFeeTx.serialize()); 72 | let serializedTransaction: string[] = []; 73 | transactions.map((transaction) => { 74 | // transaction.message.recentBlockhash = blockhash; 75 | serializedTransaction.push(bs58.encode(transaction.serialize())); 76 | }); 77 | 78 | const serializedTransactions = [serializedjitoFeeTx, ...serializedTransaction]; 79 | // const serializedTransactions = [serializedjitoFeeTx]; 80 | // https://jito-labs.gitbook.io/mev/searcher-resources/json-rpc-api-reference/url 81 | const endpoint = 'https://ny.mainnet.block-engine.jito.wtf/api/v1/bundles'; 82 | let flag = false; 83 | 84 | try { 85 | let result = await axios.post(endpoint, { 86 | jsonrpc: '2.0', 87 | id: 1, 88 | method: 'sendBundle', 89 | params: [serializedTransactions], 90 | }); 91 | if (result) { 92 | flag = true; 93 | } 94 | } catch (error) { 95 | logger.error((error as AxiosError).code); 96 | } 97 | 98 | logger.info('Sending transactions to endpoints...'); 99 | 100 | if (flag) { 101 | logger.info(`At least one successful response`); 102 | logger.info(`Confirming jito transaction...`); 103 | 104 | return await confirm(connection, jitoTxsignature, latestBlockhash); 105 | } else { 106 | logger.info(`No successful responses received for jito`); 107 | } 108 | 109 | return { confirmed: false, signature: jitoTxsignature }; 110 | } catch (error) { 111 | if (error instanceof AxiosError) { 112 | logger.info({ error: error.response?.data }, 'Failed to execute jito transaction'); 113 | } 114 | console.error('Error during transaction execution', error); 115 | return { confirmed: false, signature: '' }; 116 | } 117 | }; 118 | -------------------------------------------------------------------------------- /src/buy.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Connection, 3 | Keypair, 4 | PublicKey, 5 | BlockhashWithExpiryBlockHeight, 6 | VersionedTransaction, 7 | } from '@solana/web3.js'; 8 | import BN from 'bn.js'; 9 | import { 10 | initSdk, 11 | getRandomRunTime, 12 | getRandomNumber, 13 | logger, 14 | getWallet, 15 | getCoinBalance, 16 | getTokenDecimal, 17 | JITO_FEE, 18 | fetchWithTimeout, 19 | } from './config'; 20 | import { 21 | MIN_BUY_QUANTITY, 22 | MAX_BUY_QUANTITY, 23 | MIN_TIME, 24 | MAX_TIME, 25 | MIN_TRADE_WAIT, 26 | MAX_TRADE_WAIT, 27 | RPC_ENDPOINT, 28 | PROVIDER_PRIVATE_KEY, 29 | TOKEN_ADDRESS, 30 | SLIPPAGE, 31 | YOUR_WALLET_SECRET_KEY, 32 | NUMBER_OF_WALLETS, 33 | TRANSACTION_COUNT_PER_BUNDLE, 34 | JITO_FEE_PAYER_PRIVATE_KEY, 35 | BUFFER, 36 | } from './config'; 37 | 38 | import { getPoolInfo, getAmountOut, makeSwapTransaction, executeAndConfirm } from './amm/Ammswap'; 39 | import { executeAndConfirmByJito } from './jito-bundle'; 40 | import { Raydium } from '@raydium-io/raydium-sdk-v2'; 41 | import bs58 from 'bs58'; 42 | import wallets from '../wallets.json'; 43 | 44 | let cpmmPoolInfomation: any; 45 | const connection = new Connection(RPC_ENDPOINT, { 46 | fetch: fetchWithTimeout, 47 | commitment: 'confirmed', 48 | }) 49 | const providerWallet: Keypair = getWallet(PROVIDER_PRIVATE_KEY); 50 | const jitoFeeWallet: Keypair = getWallet(JITO_FEE_PAYER_PRIVATE_KEY); 51 | let tokenDecimal: number; 52 | let transactionCountPerBundle: number = TRANSACTION_COUNT_PER_BUNDLE; 53 | 54 | interface WALLET_STATUS { 55 | wallet: Keypair; 56 | id: number; 57 | } 58 | let walletArray: WALLET_STATUS[] = []; 59 | 60 | let timeout = getRandomRunTime(MIN_TIME, MAX_TIME); 61 | const main = async () => { 62 | logger.info(`Keep Buying...`); 63 | logger.info(`We will exit this process after ${timeout} miliseconds...`); 64 | for (let i = 0; i < NUMBER_OF_WALLETS; i++) { 65 | const keypair: Keypair = getWallet(wallets[i].secretKey); 66 | walletArray = [...walletArray, { wallet: keypair, id: i }]; 67 | } 68 | await buy(); 69 | }; 70 | setInterval(() => { 71 | if (timeout === 0) { 72 | logger.info('process is exited\n\t Times up!'); 73 | process.exit(1); 74 | } 75 | timeout--; 76 | }, 1000); 77 | const shuffle = (arr: Array) => { 78 | return arr.sort(() => { 79 | return Math.random() - 0.5; 80 | }); 81 | }; 82 | const buy = async () => { 83 | try { 84 | let bundleTransactions: VersionedTransaction[] = []; 85 | if (!tokenDecimal) { 86 | tokenDecimal = await getTokenDecimal(connection, new PublicKey(TOKEN_ADDRESS)); 87 | } 88 | let walletAmount = walletArray.length; 89 | if (walletAmount === 0) { 90 | logger.info('Please send sol to child wallets.'); 91 | process.exit(1); 92 | } 93 | walletArray = [...shuffle(walletArray)]; 94 | 95 | for (let i = 0; i < transactionCountPerBundle; i++) { 96 | const signers: Keypair = Keypair.fromSecretKey(new Uint8Array(bs58.decode(YOUR_WALLET_SECRET_KEY))); 97 | if (transactionCountPerBundle > walletAmount) { 98 | transactionCountPerBundle = walletAmount; 99 | i--; 100 | continue; 101 | } 102 | 103 | if (!cpmmPoolInfomation) { 104 | cpmmPoolInfomation = await getPoolInfo(connection, signers); 105 | } 106 | const inputMint = cpmmPoolInfomation.poolInfo.mintA.address; 107 | const baseIn = inputMint === cpmmPoolInfomation.poolInfo.mintA.address; 108 | const raydium: Raydium = await initSdk(connection, walletArray[i].wallet); 109 | let tokenAmount = getRandomNumber(MIN_BUY_QUANTITY, MAX_BUY_QUANTITY); 110 | const lampAmount: number = await getCoinBalance(connection, walletArray[i].wallet.publicKey); 111 | let tokenUnitAmount = Number(tokenAmount) * 10 ** tokenDecimal; 112 | const swapResult = await getAmountOut(new BN(tokenUnitAmount), false, cpmmPoolInfomation.rpcData); 113 | const solAmount = Number(swapResult.destinationAmountSwapped) * (1 + SLIPPAGE / 100); 114 | console.log(">>>>>>>>>>>>>>>>>>>",lampAmount, solAmount); 115 | 116 | if (new BN(lampAmount).lt(new BN(solAmount + BUFFER * 10 ** 9))) { 117 | walletArray = [...walletArray.filter((item, index) => index !== i)]; 118 | walletAmount--; 119 | i--; 120 | continue; 121 | } else { 122 | const swapResult = await getAmountOut(new BN(solAmount), baseIn, cpmmPoolInfomation.rpcData); 123 | const transaction = await makeSwapTransaction( 124 | raydium, 125 | cpmmPoolInfomation.poolInfo, 126 | cpmmPoolInfomation.poolKeys, 127 | providerWallet.publicKey, 128 | baseIn, 129 | 0.1, 130 | swapResult 131 | ) 132 | bundleTransactions = [...bundleTransactions, transaction]; 133 | } 134 | } 135 | if (transactionCountPerBundle !== TRANSACTION_COUNT_PER_BUNDLE) transactionCountPerBundle++; 136 | if (bundleTransactions.length) { 137 | let latestBlockhash: BlockhashWithExpiryBlockHeight = await connection.getLatestBlockhash(); 138 | const result = await executeAndConfirmByJito( 139 | connection, 140 | jitoFeeWallet, 141 | JITO_FEE, 142 | transactionCountPerBundle, 143 | bundleTransactions, 144 | latestBlockhash, 145 | ); 146 | if (result.confirmed) { 147 | logger.info(`https://explorer.jito.wtf/bundle/${result.signature}`); 148 | } else { 149 | logger.info(`BlockheightError`); 150 | } 151 | } else { 152 | logger.info('Not found available wallets'); 153 | } 154 | const wtime = getRandomRunTime(MIN_TRADE_WAIT, MAX_TRADE_WAIT); 155 | logger.info(`waiting ${wtime} miliseconds...`); 156 | setTimeout(buy, wtime); 157 | } catch (error: any) { 158 | console.log(error); 159 | } 160 | }; 161 | 162 | main(); 163 | -------------------------------------------------------------------------------- /src/gather.ts: -------------------------------------------------------------------------------- 1 | import { 2 | TOKEN_ADDRESS, 3 | GATHER_WALLET_ADDRESS, 4 | COMPUTE_UNIT_LIMIT, 5 | COMPUTE_UNIT_PRICE, 6 | RPC_ENDPOINT, 7 | getTokenAccount, 8 | } from './config'; 9 | import wallets from '../wallets.json'; 10 | import { getWallet, getTokenAccountBalance, getCoinBalance } from './config'; 11 | import { logger, fetchWithTimeout, IS_TOKEN_2022 } from './config'; 12 | import { 13 | Connection, 14 | PublicKey, 15 | ComputeBudgetProgram, 16 | Transaction, 17 | sendAndConfirmTransaction, 18 | SystemProgram, 19 | Keypair, 20 | VersionedTransaction, 21 | TransactionMessage, 22 | } from '@solana/web3.js'; 23 | import { executeAndConfirm } from './amm/Ammswap'; 24 | import { 25 | unpackMint, 26 | getOrCreateAssociatedTokenAccount, 27 | createTransferInstruction, 28 | Account, 29 | TOKEN_2022_PROGRAM_ID, 30 | TOKEN_PROGRAM_ID, 31 | } from '@solana/spl-token'; 32 | import bs58 from 'bs58'; 33 | export const wallet_2_gather_keypair = Keypair.fromSecretKey(new Uint8Array(bs58.decode(GATHER_WALLET_ADDRESS))); 34 | 35 | const connection: Connection = new Connection(RPC_ENDPOINT, { 36 | fetch: fetchWithTimeout, 37 | commitment: 'confirmed', 38 | }); 39 | 40 | interface WALLET_STATUS { 41 | wallet: Keypair; 42 | id: number; 43 | origin: number; 44 | } 45 | 46 | let walletArray: WALLET_STATUS[] = []; 47 | const getWalletAndgather = async () => { 48 | const toTokenAccount = await getTokenAccount( 49 | connection, 50 | wallet_2_gather_keypair, 51 | new PublicKey(TOKEN_ADDRESS), 52 | IS_TOKEN_2022, 53 | ); 54 | let numberOfWallets = wallets.length; 55 | for (let i = 0; i < numberOfWallets; i++) { 56 | const keypair: Keypair = getWallet(wallets[i].secretKey); 57 | walletArray = [...walletArray, { wallet: keypair, id: i, origin: i % 2 }]; 58 | } 59 | 60 | const gather = async (origin: number) => { 61 | let mwalletArray = walletArray.filter((item) => item.origin === origin); 62 | for (let i = 0; i < mwalletArray.length; i++) { 63 | try { 64 | let selWallet: Keypair = mwalletArray[i].wallet; 65 | try { 66 | let fromTokenAccount!: Account; 67 | try { 68 | fromTokenAccount = await getTokenAccount( 69 | connection, 70 | selWallet, 71 | new PublicKey(TOKEN_ADDRESS), 72 | IS_TOKEN_2022, 73 | ); 74 | } catch (error) { 75 | throw new Error(`${selWallet.publicKey.toBase58()} - Can't find Token Account`); 76 | } 77 | let tokenAmount = await getTokenAccountBalance(connection, selWallet.publicKey.toBase58(), TOKEN_ADDRESS); 78 | if (tokenAmount.amount != 0) { 79 | const latestBlockhash = await connection.getLatestBlockhash(); 80 | let programId = IS_TOKEN_2022 ? TOKEN_2022_PROGRAM_ID : TOKEN_PROGRAM_ID; 81 | const instructions = [ 82 | createTransferInstruction( 83 | fromTokenAccount.address, 84 | toTokenAccount.address, 85 | selWallet.publicKey, 86 | tokenAmount.amount, 87 | [], 88 | programId, 89 | ), 90 | ComputeBudgetProgram.setComputeUnitPrice({ microLamports: COMPUTE_UNIT_PRICE }), 91 | ComputeBudgetProgram.setComputeUnitLimit({ units: COMPUTE_UNIT_LIMIT }), 92 | ]; 93 | const messageV0 = new TransactionMessage({ 94 | payerKey: selWallet.publicKey, 95 | recentBlockhash: latestBlockhash.blockhash, 96 | instructions, 97 | }).compileToV0Message(); 98 | 99 | const transaction = new VersionedTransaction(messageV0); 100 | transaction.sign([selWallet]); 101 | let result1 = await executeAndConfirm(connection, transaction, latestBlockhash); 102 | if (result1.confirmed) { 103 | logger.info( 104 | `Token Sent! =>', 105 | ${selWallet.publicKey.toBase58()}, 106 | https://solscan.io/tx/${result1.signature}`, 107 | ); 108 | } 109 | } 110 | } catch (error) { 111 | logger.info(`Can't find Token Account! - ${selWallet.publicKey.toBase58()}`); 112 | } 113 | 114 | const walletBalance = await getCoinBalance(connection, selWallet.publicKey); 115 | 116 | if (walletBalance < 1000000) { 117 | console.log(`This wallet(${selWallet.publicKey.toBase58()}) don't have enough coin balance!!`); 118 | } else { 119 | const latestBlockhash = await connection.getLatestBlockhash(); 120 | const instructions = [ 121 | SystemProgram.transfer({ 122 | fromPubkey: selWallet.publicKey, 123 | toPubkey: wallet_2_gather_keypair.publicKey, 124 | lamports: walletBalance - 1000000, 125 | }), 126 | ComputeBudgetProgram.setComputeUnitPrice({ microLamports: COMPUTE_UNIT_PRICE }), 127 | ComputeBudgetProgram.setComputeUnitLimit({ units: COMPUTE_UNIT_LIMIT }), 128 | ]; 129 | const messageV0 = new TransactionMessage({ 130 | payerKey: selWallet.publicKey, 131 | recentBlockhash: latestBlockhash.blockhash, 132 | instructions, 133 | }).compileToV0Message(); 134 | 135 | const transaction = new VersionedTransaction(messageV0); 136 | transaction.sign([selWallet]); 137 | let result2 = await executeAndConfirm(connection, transaction, latestBlockhash); 138 | if (result2.confirmed) { 139 | logger.info(`Sol Sent! =>, ${selWallet.publicKey.toBase58()}, https://solscan.io/tx/${result2.signature}`); 140 | } 141 | } 142 | } catch (e: unknown) { 143 | logger.info(`[SWAP - SELL - ERROR] ${e}`); 144 | } 145 | } 146 | }; 147 | const callGather = async () => { 148 | if (numberOfWallets === 1) { 149 | await gather(0); 150 | } else { 151 | gather(0); 152 | gather(1); 153 | } 154 | }; 155 | await callGather(); 156 | }; 157 | 158 | getWalletAndgather(); 159 | -------------------------------------------------------------------------------- /src/sell.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Connection, 3 | Keypair, 4 | PublicKey, 5 | LAMPORTS_PER_SOL, 6 | ComputeBudgetProgram, 7 | BlockhashWithExpiryBlockHeight, 8 | Transaction, 9 | SystemProgram, 10 | VersionedTransaction, 11 | TransactionMessage, 12 | } from '@solana/web3.js'; 13 | import BN from 'bn.js'; 14 | import fs from 'fs'; 15 | import { 16 | initSdk, 17 | getRandomRunTime, 18 | getRandomNumber, 19 | logger, 20 | getWallet, 21 | getCoinBalance, 22 | getTokenAccount, 23 | getTokenBalance, 24 | getTokenDecimal, 25 | JITO_FEE, 26 | fetchWithTimeout, 27 | getTokenAccountBalance, 28 | sleep, 29 | } from './config'; 30 | import { 31 | MIN_BUY_QUANTITY, 32 | MAX_BUY_QUANTITY, 33 | MIN_SELL_QUANTITY, 34 | MAX_SELL_QUANTITY, 35 | MIN_TIME, 36 | YOUR_WALLET_SECRET_KEY, 37 | MAX_TIME, 38 | MIN_TRADE_WAIT, 39 | MAX_TRADE_WAIT, 40 | RPC_ENDPOINT, 41 | PROVIDER_PRIVATE_KEY, 42 | TOKEN_ADDRESS, 43 | SLIPPAGE, 44 | SEND_SOL_AMOUNT, 45 | NUMBER_OF_WALLETS, 46 | COMPUTE_UNIT_PRICE, 47 | COMPUTE_UNIT_LIMIT, 48 | TRANSACTION_COUNT_PER_BUNDLE, 49 | JITO_FEE_PAYER_PRIVATE_KEY, 50 | BUFFER, 51 | } from './config'; 52 | 53 | import { getPoolInfo, getAmountOut, makeSwapTransaction, executeAndConfirm } from './amm/Ammswap'; 54 | import { Account, NATIVE_MINT } from '@solana/spl-token'; 55 | import { executeAndConfirmByJito } from './jito-bundle'; 56 | import { Raydium } from '@raydium-io/raydium-sdk-v2'; 57 | import bs58 from 'bs58'; 58 | import wallets from '../wallets.json'; 59 | 60 | let cpmmPoolInfomation: any; 61 | const connection: Connection = new Connection(RPC_ENDPOINT, { 62 | fetch: fetchWithTimeout, 63 | commitment: 'confirmed', 64 | }); 65 | const providerWallet: Keypair = getWallet(PROVIDER_PRIVATE_KEY); 66 | const jitoFeeWallet: Keypair = getWallet(JITO_FEE_PAYER_PRIVATE_KEY); 67 | let tokenDecimal: number; 68 | let transactionCountPerBundle: number = TRANSACTION_COUNT_PER_BUNDLE; 69 | 70 | interface WALLET_STATUS { 71 | wallet: Keypair; 72 | id: number; 73 | } 74 | 75 | let walletArray: WALLET_STATUS[] = []; 76 | 77 | let timeout = getRandomRunTime(MIN_TIME, MAX_TIME); 78 | 79 | const main = async () => { 80 | logger.info(`Keep Selling`); 81 | logger.info(`We will exit this process after ${timeout} miliseconds...`); 82 | for (let i = 0; i < NUMBER_OF_WALLETS; i++) { 83 | const keypair: Keypair = getWallet(wallets[i].secretKey); 84 | walletArray = [...walletArray, { wallet: keypair, id: i }]; 85 | } 86 | await sell(); 87 | }; 88 | setInterval(() => { 89 | if (timeout === 0) { 90 | logger.info('process is exited\n\t Times up!'); 91 | process.exit(1); 92 | } 93 | timeout--; 94 | }, 1000); 95 | 96 | const shuffle = (arr: Array) => { 97 | return arr.sort((a, b) => { 98 | return Math.random() - 0.5; 99 | }); 100 | }; 101 | const sell = async () => { 102 | try { 103 | let bundleTransactions : VersionedTransaction[] = []; 104 | if (!tokenDecimal) { 105 | tokenDecimal = await getTokenDecimal(connection, new PublicKey(TOKEN_ADDRESS)); 106 | } 107 | let walletAmount = walletArray.length; 108 | if (walletAmount === 0) { 109 | logger.info('Please send sol to child wallets.'); 110 | process.exit(1); 111 | } 112 | walletArray = [...shuffle(walletArray)]; 113 | 114 | for (let i = 0; i < transactionCountPerBundle; i++) { 115 | //Reconfig transaction number per bundle 116 | const signers = Keypair.fromSecretKey(new Uint8Array(bs58.decode(YOUR_WALLET_SECRET_KEY))); 117 | 118 | if (transactionCountPerBundle > walletAmount) { 119 | transactionCountPerBundle = walletAmount; 120 | i--; 121 | continue; 122 | } 123 | 124 | if (!cpmmPoolInfomation) { 125 | cpmmPoolInfomation = await getPoolInfo(connection, signers); 126 | 127 | } 128 | const inputMint = cpmmPoolInfomation.poolInfo.mintA.address 129 | const baseIn = inputMint === cpmmPoolInfomation.poolInfo.mintA.address 130 | const raydium: Raydium = await initSdk(connection, walletArray[i].wallet); 131 | let tokenAmount = getRandomNumber(MIN_SELL_QUANTITY, MAX_SELL_QUANTITY); 132 | let lampAmount = await getCoinBalance(connection, walletArray[i].wallet.publicKey); 133 | let tokenUnitAmount = Number(tokenAmount) * 10 ** tokenDecimal; 134 | let token_in_wallet = await getTokenAccountBalance( 135 | connection, 136 | walletArray[i].wallet.publicKey.toBase58(), 137 | TOKEN_ADDRESS, 138 | ); 139 | if (lampAmount / LAMPORTS_PER_SOL < 0.0015) { 140 | walletArray = [...walletArray.filter((item, index) => index !== i)]; 141 | walletAmount--; 142 | i--; 143 | } else { 144 | if (token_in_wallet.uiAmount < +tokenAmount) { 145 | walletArray = [...walletArray.filter((item, index) => index !== i)]; 146 | walletAmount--; 147 | i--; 148 | continue; 149 | } else { 150 | let latestBlockhash: BlockhashWithExpiryBlockHeight = await connection.getLatestBlockhash(); 151 | 152 | const swapResult = await getAmountOut(new BN(tokenUnitAmount), false, cpmmPoolInfomation.rpcData); 153 | const transaction = await makeSwapTransaction( 154 | raydium, 155 | cpmmPoolInfomation.poolInfo, 156 | cpmmPoolInfomation.poolKeys, 157 | providerWallet.publicKey, 158 | false, 159 | 0.1, 160 | swapResult 161 | ) 162 | 163 | bundleTransactions = [...bundleTransactions, transaction ]; 164 | } 165 | } 166 | } 167 | 168 | if (transactionCountPerBundle !== TRANSACTION_COUNT_PER_BUNDLE) transactionCountPerBundle++; 169 | 170 | if (bundleTransactions.length) { 171 | let latestBlockhash: BlockhashWithExpiryBlockHeight = await connection.getLatestBlockhash(); 172 | 173 | const result = await executeAndConfirmByJito( 174 | connection, 175 | jitoFeeWallet, 176 | JITO_FEE, 177 | transactionCountPerBundle, 178 | bundleTransactions, 179 | latestBlockhash, 180 | ); 181 | if (result.confirmed) { 182 | logger.info(`https://explorer.jito.wtf/bundle/${result.signature}`); 183 | } else { 184 | logger.info(`BlockheightError`); 185 | } 186 | } else { 187 | logger.info('Not found available wallets'); 188 | } 189 | 190 | const wtime = getRandomRunTime(MIN_TRADE_WAIT, MAX_TRADE_WAIT); 191 | logger.info(`waiting ${wtime} miliseconds...`); 192 | setTimeout(sell, wtime); 193 | } catch (error: any) { 194 | console.log(error); 195 | } 196 | }; 197 | 198 | main(); 199 | -------------------------------------------------------------------------------- /src/amm/Ammswap.ts: -------------------------------------------------------------------------------- 1 | import { 2 | VersionedTransaction, 3 | BlockhashWithExpiryBlockHeight, 4 | Transaction, 5 | Connection, 6 | PublicKey, 7 | Signer, 8 | Keypair, 9 | ComputeBudgetInstruction, 10 | TransactionMessage, 11 | SystemProgram, 12 | sendAndConfirmTransaction, 13 | } from '@solana/web3.js'; 14 | 15 | import BN from 'bn.js'; 16 | import { 17 | initSdk, 18 | logger, 19 | isValidAmm, 20 | sleep, 21 | getWallet, 22 | txVersion, 23 | RPC_ENDPOINT, 24 | POOL_ADDRESS, 25 | COMPUTE_UNIT_LIMIT, 26 | COMPUTE_UNIT_PRICE, 27 | BUFFER, 28 | } from '../config'; 29 | import { isReturnStatement } from 'typescript'; 30 | import { TransportMultiOptions } from 'pino'; 31 | import { publicKey } from '@project-serum/anchor/dist/cjs/utils'; 32 | import { MINT_SIZE, TOKEN_PROGRAM_ID } from '@solana/spl-token'; 33 | import InstructionNamespaceFactory from '@project-serum/anchor/dist/cjs/program/namespace/instruction'; 34 | import { sign } from 'crypto'; 35 | import { promises } from 'fs'; 36 | const poolId = POOL_ADDRESS; 37 | 38 | 39 | const jitpTipAccounts = [ 40 | 'Cw8CFyM9FkoMi7K7Crf6HNQqf4uEMzpKw6QNghXLvLkY', 41 | 'DttWaMuVvTiduZRnguLF7jNxTgiMBZ1hyAumKUiL2KRL', 42 | '96gYZGLnJYVFmbjzopPSU6QiEV5fGqZNyN9nmNhvrZU5', 43 | '3AVi9Tg9Uo68tJfuvoKvqKNWKkC5wPdSSdeBnizKZ6jT', 44 | 'HFqU5x63VTqvQss8hp11i4wVV8bD44PvwucfZ2bU7gRe', 45 | 'ADaUMid9yfUytqMBgopwjb2DTLSokTSzL1zt6iGPaS49', 46 | 'ADuUkR4vqLUMWXxW9gh6D6L8pMSawimctcNZ5pGwDcEt', 47 | 'DfXygSm4jCyNCybVYYK6DwvWqjKee8pbDmJGcLWNDXjh', 48 | ]; 49 | 50 | export const getRandomValidatorKey = (): PublicKey => { 51 | const randomValidator = jitpTipAccounts[Math.floor(Math.random() * jitpTipAccounts.length)]; 52 | return new PublicKey(randomValidator); 53 | }; 54 | 55 | export const getPoolInfo = async (connection: Connection, wallet: Keypair) => { 56 | 57 | const pumpfun = await initSdk(connection, wallet); 58 | let poolInfo: ApiV3PoolInfoStandardItemAmm; 59 | let poolKeys: AmmKeys | undefined; 60 | let tickCache: ReturnTypeFetchMultiplePoolTickArrays; 61 | let AmmPoolInfo: AmmPoolInfoInterface; 62 | let rpcData: AmmRpcData; 63 | 64 | if (pumpfun.cluster === 'mainnet') { 65 | const data = await pumpfun.api.fetchPoolById({ ids: poolId }); 66 | poolInfo = data[0] as ApiV3PoolInfoStandardItemAmm; 67 | if (!isValidAmm(poolInfo.programId)) throw new Error('target pool is not Amm pool'); 68 | rpcData = await pumpfun.Amm.getRpcPoolInfo(poolInfo.id, true); 69 | } else { 70 | const data = await pumpfun.Amm.getPoolInfoFromRpc(poolId); 71 | poolInfo = data.poolInfo; 72 | poolKeys = data.poolKeys; 73 | rpcData = data.rpcData; 74 | } 75 | 76 | return { 77 | pumpfun: pumpfun, 78 | poolInfo: poolInfo, 79 | poolKeys: poolKeys, 80 | rpcData: rpcData, 81 | }; 82 | }; 83 | 84 | export const getAmountOut = async (inputAmount: BN, baseIn: boolean, rpcData: AmmRpcData) => { 85 | return CurveCalculator.swap( 86 | inputAmount, 87 | baseIn ? rpcData.baseReserve : rpcData.quoteReserve, 88 | baseIn ? rpcData.quoteReserve : rpcData.baseReserve, 89 | rpcData.configInfo!.tradeFeeRate, 90 | ) 91 | }; 92 | 93 | // export const makeSwapTransaction = async ( 94 | // poolInfo: any, 95 | // poolKeys: any, 96 | // baseIn: any, 97 | // slippage: any, 98 | // swapResult: any, 99 | // pumpfun: pumpfun, 100 | // latestBlockhash: BlockhashWithExpiryBlockHeight, 101 | // signers: Array, // Array: the correct datatype 102 | // connection: Connection, 103 | // payer: Keypair, 104 | // jitoFee: string, 105 | // ) => { 106 | // const provider = payer.publicKey; 107 | // const { transaction } = await pumpfun.Amm.swap({ 108 | // poolInfo, 109 | // poolKeys, 110 | // payer: provider, 111 | // baseIn, 112 | // slippage, 113 | // swapResult, 114 | // // txVersion: TxVersion.LEGACY 115 | // }); 116 | 117 | // // transaction.feePayer = provider; 118 | // transaction.recentBlockhash = latestBlockhash.blockhash; 119 | // if (signers.length > 0) { 120 | // // transaction.add( 121 | // // SystemProgram.createAccount({ 122 | // // fromPubkey: payer.publicKey, 123 | // // newAccountPubkey: payer.publicKey, 124 | // // space: MINT_SIZE, 125 | // // lamports: 1, 126 | // // programId: TOKEN_PROGRAM_ID 127 | // // }) 128 | // // ) 129 | 130 | // // transaction.sign(...signers); 131 | // // transaction.addSignature(provider, Buffer.from(payer.secretKey)); 132 | // } else { 133 | // throw new Error("No signers provided for the transaction."); 134 | // } 135 | // console.log("===========TRNASACTION SIGNATURE==============", transaction.signatures); 136 | // return transaction; 137 | // }; 138 | 139 | 140 | export const makeSwapTransaction = async ( 141 | pumpfun: pumpfun, 142 | poolInfo: ApiV3PoolInfoStandardItemAmm, 143 | poolKeys: AmmKeys, 144 | payer: PublicKey, 145 | baseIn: boolean, 146 | slippage: number, 147 | swapResult: SwapResult, 148 | ): Promise => { 149 | const { transaction, execute } = await pumpfun.Amm.swap({ 150 | poolInfo, 151 | poolKeys, 152 | payer, 153 | baseIn, 154 | slippage, 155 | swapResult, 156 | // config.bypassAssociatedCheck, 157 | // config.checkCreateATAOwner, 158 | // config.associatedOnly, 159 | // computeBudgetConfig, 160 | }); 161 | // transaction.serialize() 162 | const { signedTx } = await execute(); 163 | let transaction1 = signedTx; 164 | return transaction as VersionedTransaction 165 | } 166 | 167 | 168 | export const confirm = async ( 169 | connection: Connection, 170 | signature: string, 171 | latestBlockhash: BlockhashWithExpiryBlockHeight, 172 | ) => { 173 | 174 | const confirmation = await connection.confirmTransaction( 175 | { 176 | signature, 177 | lastValidBlockHeight: latestBlockhash.lastValidBlockHeight, 178 | blockhash: latestBlockhash.blockhash, 179 | }, 180 | 'confirmed', 181 | ); 182 | 183 | return { confirmed: !confirmation.value.err, signature }; 184 | }; 185 | 186 | 187 | export const executeAndConfirm = async ( 188 | connection: Connection, 189 | transaction: VersionedTransaction, 190 | latestBlockhash: BlockhashWithExpiryBlockHeight, 191 | ): Promise<{ confirmed: boolean; signature?: string; error?: string }> => { 192 | logger.debug('Executing transaction...'); 193 | 194 | const signature = await connection.sendRawTransaction(transaction.serialize(), { 195 | preflightCommitment: connection.commitment, 196 | }); 197 | 198 | return await confirm(connection, signature, latestBlockhash); 199 | }; 200 | function tyepof(secretKey: any): any { 201 | throw new Error('Function not implemented.'); 202 | } 203 | 204 | -------------------------------------------------------------------------------- /src/config/utils.ts: -------------------------------------------------------------------------------- 1 | import { 2 | CREATE_CPMM_POOL_PROGRAM, 3 | Cluster, 4 | DEV_CREATE_CPMM_POOL_PROGRAM, 5 | Raydium, 6 | parseTokenAccountResp, 7 | } from '@raydium-io/raydium-sdk-v2'; 8 | import { 9 | Connection, 10 | PublicKey, 11 | Signer, 12 | Keypair, 13 | GetProgramAccountsFilter, 14 | sendAndConfirmTransaction, 15 | Transaction, 16 | ComputeBudgetProgram, 17 | TransactionMessage, 18 | VersionedTransaction, 19 | } from '@solana/web3.js'; 20 | import { 21 | getOrCreateAssociatedTokenAccount, 22 | createAssociatedTokenAccountInstruction, 23 | Account, 24 | TOKEN_PROGRAM_ID, 25 | TOKEN_2022_PROGRAM_ID, 26 | getAssociatedTokenAddressSync, 27 | getAccount, 28 | TokenAccountNotFoundError, 29 | TokenInvalidAccountOwnerError, 30 | TokenInvalidMintError, 31 | TokenInvalidOwnerError, 32 | } from '@solana/spl-token'; 33 | import bs58 from 'bs58'; 34 | import { mnemonicToSeedSync } from 'bip39'; 35 | import { derivePath } from 'ed25519-hd-key'; 36 | import BN from 'bn.js'; 37 | import { logger } from './logger'; 38 | import { COMPUTE_UNIT_PRICE, COMPUTE_UNIT_LIMIT, RPC_ENDPOINT, YOUR_WALLET_SECRET_KEY } from './constants'; 39 | import wallets from './../../wallets.json'; 40 | import { executeAndConfirm } from '../amm/Ammswap'; 41 | import { log } from 'console'; 42 | 43 | // export const connection = new Connection(RPC_ENDPOINT); // 44 | // export const owner: Keypair = Keypair.fromSecretKey(bs58.decode(YOUR_WALLET_SECRET_KEY)); 45 | 46 | // const VALID_PROGRAM_ID = new Set([CREATE_CPMM_POOL_PROGRAM.toBase58(), DEV_CREATE_CPMM_POOL_PROGRAM.toBase58()]) 47 | 48 | 49 | export const initSdk = async (connection: Connection, owner: Keypair) => { 50 | 51 | const cluster = 'mainnet'; // 'mainnet' | 'devnet' 52 | const raydium = await Raydium.load({ 53 | owner, 54 | connection, 55 | cluster, 56 | disableFeatureCheck: true, 57 | disableLoadToken: true, 58 | blockhashCommitment: 'finalized', 59 | }); 60 | 61 | return raydium; 62 | }; 63 | 64 | export async function sleep(ms: number) { 65 | return await new Promise((resolve) => setTimeout(resolve, ms)); 66 | } 67 | export const isValidCpmm = (id: string) => new Set([CREATE_CPMM_POOL_PROGRAM.toBase58(), DEV_CREATE_CPMM_POOL_PROGRAM.toBase58()]).has(id) 68 | 69 | export function getWallet(wallet: string): Keypair { 70 | // most likely someone pasted the private key in binary format 71 | if (wallet.startsWith('[')) { 72 | const raw = new Uint8Array(JSON.parse(wallet)); 73 | return Keypair.fromSecretKey(raw); 74 | } 75 | 76 | // most likely someone pasted mnemonic 77 | if (wallet.split(' ').length > 1) { 78 | const seed = mnemonicToSeedSync(wallet, ''); 79 | const path = `m/44'/501'/0'/0'`; // we assume it's first path 80 | return Keypair.fromSeed(derivePath(path, seed.toString('hex')).key); 81 | } 82 | 83 | // most likely someone pasted base58 encoded private key 84 | return Keypair.fromSecretKey(bs58.decode(wallet)); 85 | } 86 | export async function getTokenAccountBalance(connection: Connection, wallet: string, mint_token: string) { 87 | const token_account = await connection.getParsedTokenAccountsByOwner( 88 | new PublicKey(wallet), 89 | { programId: TOKEN_PROGRAM_ID }, 90 | 'confirmed', 91 | ); 92 | const token_2022_accounts = await connection.getParsedTokenAccountsByOwner( 93 | new PublicKey(wallet), 94 | { programId: TOKEN_PROGRAM_ID }, 95 | 'confirmed', 96 | ); 97 | let token_accounts = [...token_account.value, ...token_2022_accounts.value]; 98 | 99 | for (const account of token_accounts) { 100 | const parsedAccountInfo: any = account.account.data; 101 | if (parsedAccountInfo.parsed.info.mint === mint_token) { 102 | return { 103 | uiAmount: parsedAccountInfo.parsed.info.tokenAmount.uiAmount, 104 | amount: parsedAccountInfo.parsed.info.tokenAmount.amount, 105 | }; 106 | } 107 | } 108 | return { 109 | uiAmount: 0, 110 | amount: 0, 111 | }; 112 | } 113 | export const fetchWithTimeout = async (input: RequestInfo | URL, init?: RequestInit): Promise => { 114 | const maxRetries = 300; // Number of retry attempts 115 | 116 | for (let attempt = 1; attempt <= maxRetries; attempt++) { 117 | try { 118 | await sleep(200); 119 | let res = await Promise.race([ 120 | fetch(input, init), // This fetch call returns a Promise 121 | // Timeout Promise that rejects after 5 seconds 122 | new Promise((_, reject) => setTimeout(() => reject(new Error('Request Timeout')), 5000)), 123 | ]); 124 | if (res.status === 429 /* Too many requests */) { 125 | throw Error(); 126 | } 127 | return res; 128 | } catch (error) { 129 | if (attempt === maxRetries) { 130 | // If it's the last attempt, reject the promise 131 | break; 132 | } 133 | // Wait for a brief moment before retrying (optional) 134 | await new Promise((resolve) => setTimeout(resolve, 1000)); // Optionally wait for 1 second before retrying 135 | } 136 | } 137 | 138 | // If we exit the loop without returning, throw an error (this should not happen) 139 | throw new Error('Request Timeout'); 140 | }; 141 | export const getTokenDecimal = async (connection: Connection, tokenAddress: PublicKey): Promise => { 142 | try { 143 | await sleep(200); 144 | const tokenSupply = await connection.getTokenSupply(tokenAddress); 145 | return tokenSupply.value.decimals; 146 | } catch (error) { 147 | logger.error('getTokenDecimal'); 148 | throw error; 149 | } 150 | }; 151 | 152 | export const getRandomNumber = (min: number, max: number): string => { 153 | const result = Math.random() * (max - min) + min; 154 | return result.toFixed(6); 155 | }; 156 | 157 | export const getRandomRunTime = (min: number, max: number): number => { 158 | return Math.floor(Math.random() * (max - min + 1) + min); 159 | }; 160 | 161 | export const getCoinBalance = async (connection: Connection, pubKey: PublicKey): Promise => { 162 | try { 163 | await sleep(200); 164 | return await connection.getBalance(pubKey); 165 | } catch (error) { 166 | logger.error('getCoinBalance'); 167 | throw error; 168 | } 169 | }; 170 | 171 | export const getTokenBalance = async (connection: Connection, tokenAccount: Account): Promise => { 172 | try { 173 | await sleep(200); 174 | const balance = await connection.getTokenAccountBalance(tokenAccount.address); 175 | return balance?.value?.uiAmount ? balance?.value?.uiAmount : 0; 176 | } catch (error) { 177 | throw error; 178 | } 179 | }; 180 | 181 | export const getTokenAccount = async ( 182 | connection: Connection, 183 | wallet: Signer, 184 | tokenAddress: PublicKey, 185 | is_token_2022: boolean = false, 186 | ): Promise => { 187 | try { 188 | await sleep(200); 189 | let programId = is_token_2022 ? TOKEN_2022_PROGRAM_ID : TOKEN_PROGRAM_ID; 190 | const associatedToken = getAssociatedTokenAddressSync( 191 | new PublicKey(tokenAddress), 192 | wallet.publicKey, 193 | false, 194 | programId, 195 | ); 196 | 197 | // This is the optimal logic, considering TX fee, client-side computation, RPC roundtrips and guaranteed idempotent. 198 | // Sadly we can't do this atomically. 199 | let account: Account; 200 | try { 201 | account = await getAccount(connection, associatedToken, 'confirmed', programId); 202 | } catch (error: unknown) { 203 | // TokenAccountNotFoundError can be possible if the associated address has already received some lamports, 204 | // becoming a system account. Assuming program derived addressing is safe, this is the only case for the 205 | // TokenInvalidAccountOwnerError in this code path. 206 | if (error instanceof TokenAccountNotFoundError || error instanceof TokenInvalidAccountOwnerError) { 207 | // As this isn't atomic, it's possible others can create associated accounts meanwhile. 208 | 209 | try { 210 | const instructions = [ 211 | createAssociatedTokenAccountInstruction( 212 | wallet.publicKey, 213 | associatedToken, 214 | wallet.publicKey, 215 | tokenAddress, 216 | programId, 217 | ), 218 | ComputeBudgetProgram.setComputeUnitPrice({ microLamports: COMPUTE_UNIT_PRICE }), 219 | ComputeBudgetProgram.setComputeUnitLimit({ units: COMPUTE_UNIT_LIMIT }), 220 | ]; 221 | const latestBlockhash = await connection.getLatestBlockhash(); 222 | const messageV0 = new TransactionMessage({ 223 | payerKey: wallet.publicKey, 224 | recentBlockhash: latestBlockhash.blockhash, 225 | instructions, 226 | }).compileToV0Message(); 227 | 228 | const transaction = new VersionedTransaction(messageV0); 229 | transaction.sign([wallet]); 230 | 231 | let result1 = await executeAndConfirm(connection, transaction, latestBlockhash); 232 | if (!result1.confirmed) { 233 | console.log(result1.error); 234 | process.exit(1); 235 | } 236 | } catch (error: unknown) { 237 | // Ignore all errors; for now there is no API-compatible way to selectively ignore the expected 238 | // instruction error if the associated account exists already. 239 | } 240 | 241 | // Now this should always succeed 242 | account = await getAccount(connection, associatedToken, 'confirmed', programId); 243 | } else { 244 | throw error; 245 | } 246 | } 247 | 248 | if (!account.mint.equals(tokenAddress)) throw new TokenInvalidMintError(); 249 | if (!account.owner.equals(wallet.publicKey)) throw new TokenInvalidOwnerError(); 250 | 251 | return account; 252 | } catch (error) { 253 | throw error; 254 | } 255 | }; 256 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Connection, 3 | Keypair, 4 | PublicKey, 5 | LAMPORTS_PER_SOL, 6 | BlockhashWithExpiryBlockHeight, 7 | VersionedTransaction, 8 | } from '@solana/web3.js'; 9 | import BN from 'bn.js'; 10 | import { 11 | initSdk, 12 | getRandomRunTime, 13 | getRandomNumber, 14 | logger, 15 | getWallet, 16 | getCoinBalance, 17 | getTokenDecimal, 18 | getTokenAccountBalance, 19 | JITO_FEE, 20 | fetchWithTimeout, 21 | sleep, 22 | } from './config'; 23 | import { 24 | MIN_BUY_QUANTITY, 25 | MAX_BUY_QUANTITY, 26 | MIN_SELL_QUANTITY, 27 | MAX_SELL_QUANTITY, 28 | MIN_TIME, 29 | MAX_TIME, 30 | MIN_TRADE_WAIT, 31 | MAX_TRADE_WAIT, 32 | RPC_ENDPOINT, 33 | PROVIDER_PRIVATE_KEY, 34 | TOKEN_ADDRESS, 35 | SLIPPAGE, 36 | NUMBER_OF_WALLETS, 37 | YOUR_WALLET_SECRET_KEY, 38 | TRANSACTION_COUNT_PER_BUNDLE, 39 | JITO_FEE_PAYER_PRIVATE_KEY, 40 | BUFFER, 41 | } from './config'; 42 | 43 | import { getPoolInfo, getAmountOut, makeSwapTransaction, executeAndConfirm } from './amm/Ammswap'; 44 | import { executeAndConfirmByJito } from './jito-bundle'; 45 | import { Raydium } from '@raydium-io/raydium-sdk-v2'; 46 | import bs58 from 'bs58'; 47 | import wallets from '../wallets.json'; 48 | 49 | let cpmmPoolInfomation: any; 50 | const connection: Connection = new Connection(RPC_ENDPOINT, { 51 | fetch: fetchWithTimeout, 52 | commitment: 'confirmed', 53 | }); 54 | const providerWallet: Keypair = getWallet(PROVIDER_PRIVATE_KEY); 55 | const jitoFeeWallet: Keypair = getWallet(JITO_FEE_PAYER_PRIVATE_KEY); 56 | let tokenDecimal: number; 57 | let transactionCountPerBundle: number = TRANSACTION_COUNT_PER_BUNDLE; 58 | 59 | interface WALLET_STATUS { 60 | wallet: Keypair; 61 | id: number; 62 | } 63 | 64 | let walletArray: WALLET_STATUS[] = []; 65 | 66 | let timeout = getRandomRunTime(MIN_TIME, MAX_TIME); 67 | const main = async () => { 68 | logger.info(`Randomly Buying & Selling`); 69 | logger.info(`We will exit this process after ${timeout} miliseconds...`); 70 | for (let i = 0; i < NUMBER_OF_WALLETS; i++) { 71 | const keypair: Keypair = getWallet(wallets[i].secretKey); 72 | walletArray = [...walletArray, { wallet: keypair, id: i }]; 73 | } 74 | 75 | await balance(); 76 | }; 77 | setInterval(() => { 78 | if (timeout === 0) { 79 | logger.info('process is exited\n\t Times up!'); 80 | process.exit(1); 81 | } 82 | timeout--; 83 | }, 1000); 84 | 85 | const shuffle = (arr: Array) => { 86 | return arr.sort((a, b) => { 87 | return Math.random() - 0.5; 88 | }); 89 | }; 90 | 91 | const balance = async () => { 92 | try { 93 | let bundleTransactions: VersionedTransaction[] = []; 94 | 95 | if (!tokenDecimal) { 96 | tokenDecimal = await getTokenDecimal(connection, new PublicKey(TOKEN_ADDRESS)); 97 | } 98 | let walletAmount = walletArray.length; 99 | if (walletAmount === 0) { 100 | logger.info('Please send sol to child wallets.'); 101 | process.exit(1); 102 | } 103 | walletArray = [...shuffle(walletArray)]; 104 | 105 | for (let i = 0; i < transactionCountPerBundle; i++) { 106 | const signers = Keypair.fromSecretKey(new Uint8Array(bs58.decode(YOUR_WALLET_SECRET_KEY))); 107 | //Reconfig transaction number per bundle 108 | if (transactionCountPerBundle > walletAmount) { 109 | transactionCountPerBundle = walletAmount; 110 | i--; 111 | continue; 112 | } 113 | let method = getRandomRunTime(1, 2); 114 | if (!cpmmPoolInfomation) { 115 | cpmmPoolInfomation = await getPoolInfo(connection, signers); 116 | } 117 | const inputMint = cpmmPoolInfomation.poolInfo.mintA.address 118 | const baseIn = inputMint === cpmmPoolInfomation.poolInfo.mintA.address 119 | const raydium: Raydium = await initSdk(connection, walletArray[i].wallet); 120 | console.log(method===1?"Will buy" : "Will sell"); 121 | 122 | // 1: buy 2: sell 123 | if (method === 1) { 124 | let tokenAmount = getRandomNumber(MIN_BUY_QUANTITY, MAX_BUY_QUANTITY); 125 | const lampAmount: number = await getCoinBalance(connection, walletArray[i].wallet.publicKey); 126 | let tokenUnitAmount = Number(tokenAmount) * 10 ** tokenDecimal; 127 | const swapResult = await getAmountOut( 128 | new BN(tokenUnitAmount), 129 | false, 130 | cpmmPoolInfomation.rpcData 131 | ); 132 | 133 | const solAmount = Number(swapResult.destinationAmountSwapped) * (1 + SLIPPAGE / 100); 134 | 135 | if (new BN(lampAmount).lt(new BN(solAmount + BUFFER * 10 ** 9))) { 136 | //Check if it could sell 137 | let tokenAmount = getRandomNumber(MIN_SELL_QUANTITY, MAX_SELL_QUANTITY); 138 | let tokenUnitAmount = Number(tokenAmount) * 10 ** tokenDecimal; 139 | let token_in_wallet = await getTokenAccountBalance( 140 | connection, 141 | walletArray[i].wallet.publicKey.toBase58(), 142 | TOKEN_ADDRESS, 143 | ); 144 | 145 | if (lampAmount / LAMPORTS_PER_SOL < 0.0015) { 146 | walletArray = [...walletArray.filter((item, index) => index !== i)]; 147 | walletAmount--; 148 | i--; 149 | continue; 150 | } else { 151 | if (token_in_wallet.uiAmount > +tokenAmount) { 152 | const swapResult = await getAmountOut( 153 | new BN(tokenUnitAmount), 154 | false, 155 | cpmmPoolInfomation.rpcData, 156 | ); 157 | 158 | const transaction = await makeSwapTransaction( 159 | raydium, 160 | cpmmPoolInfomation.poolInfo, 161 | cpmmPoolInfomation.poolKeys, 162 | providerWallet.publicKey, 163 | false, 164 | 0.1, 165 | swapResult 166 | ) 167 | 168 | bundleTransactions = [...bundleTransactions, transaction]; 169 | } else { 170 | walletArray = [...walletArray.filter((item, index) => index !== i)]; 171 | 172 | walletAmount--; 173 | i--; 174 | continue; 175 | } 176 | } 177 | } else { 178 | const swapResult = await getAmountOut( 179 | new BN(solAmount), 180 | baseIn, 181 | cpmmPoolInfomation.rpcData, 182 | ); 183 | const transaction = await makeSwapTransaction( 184 | raydium, 185 | cpmmPoolInfomation.poolInfo, 186 | cpmmPoolInfomation.poolKeys, 187 | providerWallet.publicKey, 188 | baseIn, 189 | 0.1, 190 | swapResult 191 | ) 192 | bundleTransactions = [...bundleTransactions, transaction]; 193 | } 194 | } else { 195 | let tokenAmount = getRandomNumber(MIN_SELL_QUANTITY, MAX_SELL_QUANTITY); 196 | let lampAmount = await getCoinBalance(connection, walletArray[i].wallet.publicKey); 197 | let tokenUnitAmount = Number(tokenAmount) * 10 ** tokenDecimal; 198 | let token_in_wallet = await getTokenAccountBalance( 199 | connection, 200 | walletArray[i].wallet.publicKey.toBase58(), 201 | TOKEN_ADDRESS, 202 | ); 203 | if (lampAmount / LAMPORTS_PER_SOL < 0.0015) { 204 | walletArray = [...walletArray.filter((item, index) => index !== i)]; 205 | walletAmount--; 206 | i--; 207 | } else { 208 | if (token_in_wallet.uiAmount < +tokenAmount) { 209 | const swapResult = await getAmountOut( 210 | new BN(tokenUnitAmount), 211 | false, 212 | cpmmPoolInfomation.rpcData, 213 | ); 214 | 215 | const solAmount = Number(swapResult.destinationAmountSwapped) * (1 + SLIPPAGE / 100); 216 | if (new BN(lampAmount).lt(new BN(solAmount + BUFFER * 10 ** 9))) { 217 | walletArray = [...walletArray.filter((item, index) => index !== i)]; 218 | walletAmount--; 219 | i--; 220 | continue; 221 | } else { 222 | const swapResult = await getAmountOut( 223 | new BN(solAmount), 224 | baseIn, 225 | cpmmPoolInfomation.rpcData, 226 | ); 227 | const transaction = await makeSwapTransaction( 228 | raydium, 229 | cpmmPoolInfomation.poolInfo, 230 | cpmmPoolInfomation.poolKeys, 231 | providerWallet.publicKey, 232 | baseIn, 233 | 0.1, 234 | swapResult 235 | ) 236 | 237 | bundleTransactions = [...bundleTransactions, transaction]; 238 | } 239 | } else { 240 | const swapResult = await getAmountOut( 241 | new BN(tokenUnitAmount), 242 | false, 243 | cpmmPoolInfomation.rpcData, 244 | ); 245 | const transaction = await makeSwapTransaction( 246 | raydium, 247 | cpmmPoolInfomation.poolInfo, 248 | cpmmPoolInfomation.poolKeys, 249 | providerWallet.publicKey, 250 | false, 251 | 0.1, 252 | swapResult 253 | ) 254 | 255 | bundleTransactions = [...bundleTransactions, transaction]; 256 | } 257 | } 258 | } 259 | } 260 | 261 | if (transactionCountPerBundle !== TRANSACTION_COUNT_PER_BUNDLE) transactionCountPerBundle++; 262 | 263 | if (bundleTransactions.length) { 264 | let latestBlockhash: BlockhashWithExpiryBlockHeight = await connection.getLatestBlockhash(); 265 | 266 | const result = await executeAndConfirmByJito( 267 | connection, 268 | jitoFeeWallet, 269 | JITO_FEE, 270 | transactionCountPerBundle, 271 | bundleTransactions, 272 | latestBlockhash, 273 | ); 274 | console.log("==================== Multi =============", result); 275 | 276 | if (result.confirmed) { 277 | logger.info(`https://explorer.jito.wtf/bundle/${result.signature}`); 278 | } else { 279 | logger.info(`BlockheightError`); 280 | } 281 | } else { 282 | logger.info('Not found available wallets'); 283 | } 284 | 285 | const wtime = getRandomRunTime(MIN_TRADE_WAIT, MAX_TRADE_WAIT); 286 | logger.info(`waiting ${wtime} miliseconds...`); 287 | setTimeout(balance, wtime); 288 | } catch (error: any) { 289 | console.log(error); 290 | } 291 | 292 | }; 293 | 294 | main(); 295 | --------------------------------------------------------------------------------