├── Cargo.toml ├── README.md ├── key.txt └── src ├── common ├── mod.rs └── utils.rs ├── core ├── mod.rs ├── token.rs └── tx.rs ├── dex ├── mod.rs ├── pump.rs └── raydium.rs ├── engine ├── mod.rs └── swap.rs ├── lib.rs ├── log.txt ├── main.rs └── services ├── jito.rs └── mod.rs /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "temp" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | dotenv = "0.15" 8 | chrono = "0.4.38" 9 | tokio = { version = "1.38.0", features = ["full"] } 10 | solana-sdk = 11 | solana-client = 12 | solana-account-decoder = 13 | solana-transaction-status = 14 | spl-token = { version = "4.0.0", features = ["no-entrypoint"] } 15 | spl-token-2022 = { version = "0.9.0", features = ["no-entrypoint"] } 16 | spl-associated-token-account = { version = "2.2.0", features = [ 17 | "no-entrypoint", 18 | ] } 19 | spl-token-client = "=0.7.1" 20 | amm-cli = 21 | common = 22 | anyhow = "1.0.53" 23 | serde = "1.0.203" 24 | serde_json = "1.0.117" 25 | clap = { version = "4.5.7", features = ["derive"] } 26 | raydium_amm = 27 | jito-json-rpc-client = 28 | anchor-lang = 29 | bytemuck = "1.21.0" 30 | indicatif = "0.17.8" 31 | rand = "0.8.5" 32 | tracing = "0.1.40" 33 | futures-util = "0.3.30" 34 | tokio-tungstenite = 35 | tokio-stream = 36 | borsh = { version = "1.5.3" } 37 | borsh-derive = "1.5.3" 38 | solana-transaction-status-client-types = "=2.1.0" 39 | url = "2.3.1" 40 | base64 = "0.13" 41 | bincode = "1.3.3" -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🚀 **Copy trading Bot using Rust** 2 | 3 | Welcome to the ** Copy trading Bot **! This bot watches for target wallet (whale) on the Solana blockchain in real-time, copy trading like target trading. 🌟 4 | 5 | # 💬 Contact Me 6 | 7 | If you have any question or something, feel free to reach out me anytime via telegram, discord or twitter. 8 |
9 | #### 🌹 You're always welcome 🌹 10 | 11 | Telegram: [@Leo](https://t.me/shinnyleo0912)
12 | 13 | ### 🎯 Example 14 | - Source Transaction: https://solscan.io/tx/2nNc1DsGxGoYWdweZhKQqnngfEjJqDA4zxnHar2S9bsAYP2csbLRgMpUmy68xuG1RaUGV9xb9k7dGdXcjgcmtJUh 15 | - Copied Transaction: https://solscan.io/tx/n2qrk4Xg3gfBBci6CXGKFqcTC8695sgNyzvacPHVaNkiwjWecwvY5WdNKgtgJhoLJfug6QkXQuaZeB5hVazW6ev 16 | - Target Wallet: GXAtmWucJEQxuL8PtpP13atoFi78eM6c9Cuw9fK9W4na 17 | - Copy Wallet: HqbQwVM2fhdYJXqFhBE68zX6mLqCWqEqdgrtf2ePmjRz 18 | ### 🎯 **Key Features** 19 | 20 | - 🛰️ **Real-time WebSocket Streaming**: 21 | Connects to Solana's blockchain through Helius geyser RPC WebSocket and listens for new transactions, specifically Tx that target wallet is singer 22 | - 🔍 **Filter Transactions**: 23 | Filters transactions as soon as possible and fast. 24 | maybe it takes about 0.3ms totally 25 | - 📊 ** Make Copy transaction **: 26 | Using pumpfun program id and raydium module you can make copy trasaction. 27 | 28 | --- 29 | 30 | ## 🚀 **Getting Started** 31 | 32 | Follow these steps to get your **Copy trading Bot** up and running! 33 | 34 | ### Prerequisites 35 | 36 | - Cargo version 1.84.0 installed on your system 37 | - A Solana wallet with access to the Helius Geyser RPC API 38 | 39 | ### Installation 40 | 41 | 1. **Clone the Repository**: 42 | 43 | ```bash 44 | git clone https://github.com/solagent99/Copy-Trading-Bot-Rust 45 | ``` 46 | 47 | 2. **Install Dependencies**: 48 | 49 | Navigate to the project directory and run the following command: 50 | 51 | ```bash 52 | cd copy-trading-bot 53 | cargo build 54 | ``` 55 | 56 | 3. **Configure ENV**: 57 | 58 | Replace the API token in the `ENDPOINT` variable: 59 | 60 | ```ts 61 | const ENDPOINT = "https://mainnet.helius-rpc.com"; 62 | const WSS_ENDPOINT = "wss://atlas-mainnet.helius-rpc.com"; 63 | const TARGET = "YOUR_API_TOKEN"; 64 | ``` 65 | 66 | 4. **Run the Bot**: 67 | 68 | Start the bot by running: 69 | 70 | ```bash 71 | cargo run 72 | ``` 73 | 74 | --- 75 | -------------------------------------------------------------------------------- /key.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solagent99/Copy-Trading-Bot-Rust/72c28e095baa0ef709305c3e7155179342b09471/key.txt -------------------------------------------------------------------------------- /src/common/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod utils; 2 | -------------------------------------------------------------------------------- /src/common/utils.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use chrono::Local; 3 | use solana_sdk::{commitment_config::CommitmentConfig, signature::Keypair}; 4 | use std::process; 5 | use std::{env, sync::Arc}; 6 | 7 | #[derive(Clone)] 8 | pub struct AppState { 9 | pub rpc_client: Arc, 10 | pub rpc_nonblocking_client: Arc, 11 | pub wallet: Arc, 12 | } 13 | 14 | pub struct ParseTx { 15 | pub type_tx: String, 16 | pub direction: Option, 17 | pub amount_in: f64, 18 | pub amount_out: f64, 19 | pub mint: String, 20 | } 21 | 22 | use std::fs::{File, OpenOptions}; 23 | use std::io::{self, Read, Write}; 24 | use std::time::SystemTime; 25 | 26 | pub async fn log_message(message: &str) -> io::Result<()> { 27 | // Open the file in append mode. If the file doesn't exist, it will be created. 28 | let mut file = OpenOptions::new() 29 | .append(true) // Ensure the log is appended, not overwritten 30 | .create(true) // Create the file if it doesn't exist 31 | .open("./src/log.txt")?; 32 | 33 | // Get the current timestamp 34 | let now = Local::now(); 35 | 36 | // Format the time as "HH:MM:SS" 37 | now.format("%H:%M:%S").to_string(); 38 | 39 | // Write the log message with a timestamp 40 | writeln!(file, "[{:#?}] {}", now.clone(), message)?; 41 | 42 | Ok(()) 43 | } 44 | 45 | pub fn read_log() -> io::Result { 46 | // Read the entire content of the log file 47 | let mut file = File::open("log.txt")?; 48 | let mut contents = String::new(); 49 | file.read_to_string(&mut contents)?; 50 | 51 | Ok(contents) 52 | } 53 | 54 | pub fn import_env_var(key: &str) -> String { 55 | env::var(key).unwrap_or_else(|_| panic!("Environment variable {} is not set", key)) 56 | } 57 | 58 | pub fn create_rpc_client() -> Result { 59 | let rpc_https = import_env_var("RPC_ENDPOINT"); 60 | let rpc_client = solana_client::rpc_client::RpcClient::new_with_commitment( 61 | rpc_https, 62 | CommitmentConfig::processed(), 63 | ); 64 | Ok(rpc_client) 65 | } 66 | pub fn create_arc_rpc_client() -> Result> { 67 | let rpc_https = import_env_var("RPC_ENDPOINT"); 68 | let rpc_client = solana_client::rpc_client::RpcClient::new_with_commitment( 69 | rpc_https, 70 | CommitmentConfig::processed(), 71 | ); 72 | Ok(Arc::new(rpc_client)) 73 | } 74 | 75 | pub async fn create_nonblocking_rpc_client( 76 | ) -> Result> { 77 | let rpc_https = import_env_var("RPC_ENDPOINT"); 78 | let rpc_client = solana_client::nonblocking::rpc_client::RpcClient::new_with_commitment( 79 | rpc_https, 80 | CommitmentConfig::processed(), 81 | ); 82 | Ok(Arc::new(rpc_client)) 83 | } 84 | 85 | pub fn import_wallet() -> Result { 86 | let mut file = File::open("./key.txt")?; 87 | let mut contents = String::new(); 88 | file.read_to_string(&mut contents)?; 89 | if contents == "" { 90 | println!("Not set Private Key"); 91 | } 92 | let wallet: Keypair = Keypair::from_base58_string(&contents); 93 | 94 | Ok(wallet) 95 | } 96 | pub fn import_arc_wallet() -> Result> { 97 | let mut file = File::open("./key.txt")?; 98 | let mut contents = String::new(); 99 | file.read_to_string(&mut contents)?; 100 | let wallet: Keypair = Keypair::from_base58_string(contents.as_str()); 101 | 102 | Ok(Arc::new(wallet)) 103 | } 104 | -------------------------------------------------------------------------------- /src/core/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod token; 2 | pub mod tx; 3 | -------------------------------------------------------------------------------- /src/core/token.rs: -------------------------------------------------------------------------------- 1 | use solana_sdk::{pubkey::Pubkey, signature::Keypair}; 2 | use spl_token_2022::{ 3 | extension::StateWithExtensionsOwned, 4 | state::{Account, Mint}, 5 | }; 6 | use spl_token_client::{ 7 | client::{ProgramClient, ProgramRpcClient, ProgramRpcClientSendTransaction}, 8 | token::{Token, TokenError, TokenResult}, 9 | }; 10 | use std::sync::Arc; 11 | 12 | pub fn get_associated_token_address( 13 | client: Arc, 14 | keypair: Arc, 15 | address: &Pubkey, 16 | owner: &Pubkey, 17 | ) -> Pubkey { 18 | let token_client = Token::new( 19 | Arc::new(ProgramRpcClient::new( 20 | client.clone(), 21 | ProgramRpcClientSendTransaction, 22 | )), 23 | &spl_token::ID, 24 | address, 25 | None, 26 | Arc::new(Keypair::from_bytes(&keypair.to_bytes()).expect("failed to copy keypair")), 27 | ); 28 | token_client.get_associated_token_address(owner) 29 | } 30 | 31 | pub async fn get_account_info( 32 | client: Arc, 33 | address: &Pubkey, 34 | account: &Pubkey, 35 | ) -> TokenResult> { 36 | let program_client = Arc::new(ProgramRpcClient::new( 37 | client.clone(), 38 | ProgramRpcClientSendTransaction, 39 | )); 40 | let account = program_client 41 | .get_account(*account) 42 | .await 43 | .map_err(TokenError::Client)? 44 | .ok_or(TokenError::AccountNotFound) 45 | .inspect_err(|err| println!("get_account_info: {} {}: mint {}", account, err, address))?; 46 | 47 | if account.owner != spl_token::ID { 48 | return Err(TokenError::AccountInvalidOwner); 49 | } 50 | let account = StateWithExtensionsOwned::::unpack(account.data)?; 51 | if account.base.mint != *address { 52 | return Err(TokenError::AccountInvalidMint); 53 | } 54 | 55 | Ok(account) 56 | } 57 | 58 | pub async fn get_mint_info( 59 | client: Arc, 60 | _keypair: Arc, 61 | address: &Pubkey, 62 | ) -> TokenResult> { 63 | let program_client = Arc::new(ProgramRpcClient::new( 64 | client.clone(), 65 | ProgramRpcClientSendTransaction, 66 | )); 67 | let account = program_client 68 | .get_account(*address) 69 | .await 70 | .map_err(TokenError::Client)? 71 | .ok_or(TokenError::AccountNotFound) 72 | .inspect_err(|err| println!("{} {}: mint {}", address, err, address))?; 73 | 74 | if account.owner != spl_token::ID { 75 | return Err(TokenError::AccountInvalidOwner); 76 | } 77 | 78 | let mint_result = StateWithExtensionsOwned::::unpack(account.data).map_err(Into::into); 79 | let decimals: Option = None; 80 | if let (Ok(mint), Some(decimals)) = (&mint_result, decimals) { 81 | if decimals != mint.base.decimals { 82 | return Err(TokenError::InvalidDecimals); 83 | } 84 | } 85 | 86 | mint_result 87 | } 88 | -------------------------------------------------------------------------------- /src/core/tx.rs: -------------------------------------------------------------------------------- 1 | use std::{env, sync::Arc, time::Duration}; 2 | 3 | use anyhow::Result; 4 | use jito_json_rpc_client::jsonrpc_client::rpc_client::RpcClient as JitoRpcClient; 5 | use solana_client::rpc_client::RpcClient; 6 | use solana_sdk::{ 7 | hash::Hash, 8 | instruction::Instruction, 9 | signature::Keypair, 10 | signer::Signer, 11 | system_transaction, 12 | transaction::{Transaction, VersionedTransaction}, 13 | }; 14 | use spl_token::ui_amount_to_amount; 15 | 16 | use std::str::FromStr; 17 | use tokio::time::Instant; 18 | 19 | use crate::{ 20 | common::utils::log_message, 21 | services::jito::{ 22 | self, get_tip_account, get_tip_value, init_tip_accounts, wait_for_bundle_confirmation, 23 | }, 24 | }; 25 | 26 | // prioritization fee = UNIT_PRICE * UNIT_LIMIT 27 | fn get_unit_price() -> u64 { 28 | env::var("UNIT_PRICE") 29 | .ok() 30 | .and_then(|v| u64::from_str(&v).ok()) 31 | .unwrap_or(1) 32 | } 33 | 34 | fn get_unit_limit() -> u32 { 35 | env::var("UNIT_LIMIT") 36 | .ok() 37 | .and_then(|v| u32::from_str(&v).ok()) 38 | .unwrap_or(300_000) 39 | } 40 | pub async fn jito_confirm( 41 | keypair: &Keypair, 42 | version_tx: VersionedTransaction, 43 | recent_block_hash: &Hash, 44 | ) { 45 | // jito confirm 46 | } 47 | 48 | pub async fn new_signed_and_send( 49 | client: &RpcClient, 50 | keypair: &Keypair, 51 | mut instructions: Vec, 52 | jito_client: Arc, 53 | timestamp: Instant, 54 | ) -> Result> { 55 | // jito confirm 56 | } 57 | -------------------------------------------------------------------------------- /src/dex/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod pump; 2 | pub mod raydium; 3 | -------------------------------------------------------------------------------- /src/dex/pump.rs: -------------------------------------------------------------------------------- 1 | use std::{str::FromStr, sync::Arc}; 2 | 3 | use crate::{ 4 | core::{ 5 | token::{self, get_account_info}, 6 | tx, 7 | }, 8 | engine::swap::{SwapDirection, SwapInType}, 9 | }; 10 | use anyhow::{anyhow, Context, Result}; 11 | use borsh::from_slice; 12 | use borsh_derive::{BorshDeserialize, BorshSerialize}; 13 | use jito_json_rpc_client::jsonrpc_client::rpc_client::RpcClient as JitoRpcClient; 14 | use raydium_amm::math::U128; 15 | use serde::{Deserialize, Serialize}; 16 | use solana_sdk::{ 17 | instruction::{AccountMeta, Instruction}, 18 | pubkey::Pubkey, 19 | signature::Keypair, 20 | signer::Signer, 21 | system_program, 22 | }; 23 | use spl_associated_token_account::{ 24 | get_associated_token_address, instruction::create_associated_token_account_idempotent, 25 | }; 26 | use tokio::time::Instant; 27 | pub const TEN_THOUSAND: u64 = 10000; 28 | pub const TOKEN_PROGRAM: &str = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"; 29 | pub const RENT_PROGRAM: &str = "SysvarRent111111111111111111111111111111111"; 30 | pub const ASSOCIATED_TOKEN_PROGRAM: &str = "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"; 31 | pub const PUMP_GLOBAL: &str = "4wTV1YmiEkRvAtNtsSGPtUrqRYQMe5SKy2uB4Jjaxnjf"; 32 | pub const PUMP_FEE_RECIPIENT: &str = "CebN5WGQ4jvEPvsVU4EoHEpgzq1VV7AbicfhtW4xC9iM"; 33 | pub const PUMP_PROGRAM: &str = "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"; 34 | // pub const PUMP_FUN_MINT_AUTHORITY: &str = "TSLvdd1pWpHVjahSpsvCXUbgwsL3JAcvokwaKt1eokM"; 35 | pub const PUMP_ACCOUNT: &str = "Ce6TQqeHC9p8KetsN6JsjHK7UTZk7nasjjnr7XxXp9F1"; 36 | pub const PUMP_BUY_METHOD: u64 = 16927863322537952870; 37 | pub const PUMP_SELL_METHOD: u64 = 12502976635542562355; 38 | 39 | pub struct Pump { 40 | pub rpc_nonblocking_client: Arc, 41 | pub keypair: Arc, 42 | pub rpc_client: Option>, 43 | } 44 | 45 | impl Pump { 46 | pub fn new( 47 | rpc_nonblocking_client: Arc, 48 | rpc_client: Arc, 49 | keypair: Arc, 50 | ) -> Self { 51 | Self { 52 | rpc_nonblocking_client, 53 | keypair, 54 | rpc_client: Some(rpc_client), 55 | } 56 | } 57 | 58 | pub async fn swap( 59 | &self, 60 | mint: &str, 61 | amount_in: u64, 62 | swap_direction: SwapDirection, 63 | slippage: u64, 64 | jito_client: Arc, 65 | timestamp: Instant, 66 | ) -> Result> { 67 | // make instruction on pumpfun 68 | 69 | tx::new_signed_and_send( 70 | &client, 71 | &self.keypair, 72 | instructions, 73 | jito_client.clone(), 74 | timestamp.clone(), 75 | ) 76 | .await 77 | } 78 | } 79 | 80 | fn min_amount_with_slippage(input_amount: u64, slippage_bps: u64) -> u64 { 81 | input_amount 82 | .checked_mul(TEN_THOUSAND.checked_sub(slippage_bps).unwrap()) 83 | .unwrap() 84 | .checked_div(TEN_THOUSAND) 85 | .unwrap() 86 | } 87 | fn max_amount_with_slippage(input_amount: u64, slippage_bps: u64) -> u64 { 88 | input_amount 89 | .checked_mul(slippage_bps.checked_add(TEN_THOUSAND).unwrap()) 90 | .unwrap() 91 | .checked_div(TEN_THOUSAND) 92 | .unwrap() 93 | } 94 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 95 | pub struct RaydiumInfo { 96 | pub base: f64, 97 | pub quote: f64, 98 | pub price: f64, 99 | } 100 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 101 | pub struct PumpInfo { 102 | pub mint: String, 103 | pub bonding_curve: String, 104 | pub associated_bonding_curve: String, 105 | pub raydium_pool: Option, 106 | pub raydium_info: Option, 107 | pub complete: bool, 108 | pub virtual_sol_reserves: u64, 109 | pub virtual_token_reserves: u64, 110 | pub total_supply: u64, 111 | } 112 | 113 | #[derive(Debug, BorshSerialize, BorshDeserialize)] 114 | pub struct BondingCurveAccount { 115 | pub discriminator: u64, 116 | pub virtual_token_reserves: u64, 117 | pub virtual_sol_reserves: u64, 118 | pub real_token_reserves: u64, 119 | pub real_sol_reserves: u64, 120 | pub token_total_supply: u64, 121 | pub complete: bool, 122 | } 123 | 124 | pub async fn get_bonding_curve_account( 125 | rpc_client: Arc, 126 | mint: &Pubkey, 127 | program_id: &Pubkey, 128 | ) -> Result<(Pubkey, Pubkey, BondingCurveAccount)> { 129 | Ok(( 130 | bonding_curve, 131 | associated_bonding_curve, 132 | bonding_curve_account, 133 | )) 134 | } 135 | 136 | pub fn get_pda(mint: &Pubkey, program_id: &Pubkey) -> Result { 137 | let seeds = [b"bonding-curve".as_ref(), mint.as_ref()]; 138 | let (bonding_curve, _bump) = Pubkey::find_program_address(&seeds, program_id); 139 | Ok(bonding_curve) 140 | } 141 | 142 | pub async fn get_pump_info( 143 | rpc_client: Arc, 144 | mint: &str, 145 | ) -> Result { 146 | Ok(pump_info) 147 | } 148 | -------------------------------------------------------------------------------- /src/dex/raydium.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | core::{ 3 | token::{get_account_info, get_mint_info}, 4 | tx, 5 | }, 6 | engine::swap::{SwapDirection, SwapInType}, 7 | }; 8 | use amm_cli::AmmSwapInfoResult; 9 | use anyhow::{anyhow, Context, Result}; 10 | use bytemuck; 11 | use jito_json_rpc_client::jsonrpc_client::rpc_client::RpcClient as JitoRpcClient; 12 | use raydium_amm::state::{AmmInfo, Loadable}; 13 | use serde::Deserialize; 14 | use serde::Serialize; 15 | use solana_client::rpc_filter::{Memcmp, RpcFilterType}; 16 | use solana_sdk::{ 17 | instruction::Instruction, program_pack::Pack, pubkey::Pubkey, signature::Keypair, 18 | signer::Signer, system_instruction, 19 | }; 20 | use spl_associated_token_account::{ 21 | get_associated_token_address, get_associated_token_address_with_program_id, 22 | instruction::create_associated_token_account_idempotent, 23 | }; 24 | use spl_token::{amount_to_ui_amount, state::Account, ui_amount_to_amount}; 25 | use spl_token_client::token::TokenError; 26 | use std::{str::FromStr, sync::Arc, time::Duration}; 27 | use tokio::time::Instant; 28 | 29 | pub const AMM_PROGRAM: &str = "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8"; 30 | pub const RAYDIUM_AUTHORITY_V4: &str = "5Q544fKrFoe6tsEbD7S8EmxGTJYAKtTVhAW5Q5pge4j1"; 31 | 32 | #[derive(Serialize)] 33 | struct SwapRequest { 34 | quoteResponse: serde_json::Value, // You may deserialize it into a specific struct if known 35 | userPublicKey: String, 36 | wrapAndUnwrapSol: bool, 37 | dynamicComputeUnitLimit: bool, 38 | prioritizationFeeLamports: u64, 39 | } 40 | 41 | #[derive(Debug, Deserialize)] 42 | pub struct PoolInfo { 43 | pub success: bool, 44 | pub data: PoolData, 45 | } 46 | 47 | #[derive(Debug, Deserialize)] 48 | pub struct PoolData { 49 | // pub count: u32, 50 | pub data: Vec, 51 | } 52 | 53 | impl PoolData { 54 | pub fn get_pool(&self) -> Option { 55 | self.data.first().cloned() 56 | } 57 | } 58 | 59 | #[derive(Debug, Deserialize, Clone)] 60 | pub struct Pool { 61 | pub id: String, 62 | #[serde(rename = "programId")] 63 | pub program_id: String, 64 | #[serde(rename = "mintA")] 65 | pub mint_a: Mint, 66 | #[serde(rename = "mintB")] 67 | pub mint_b: Mint, 68 | #[serde(rename = "marketId")] 69 | pub market_id: String, 70 | } 71 | 72 | #[derive(Debug, Deserialize, Clone)] 73 | pub struct Mint { 74 | pub address: String, 75 | pub symbol: String, 76 | pub name: String, 77 | pub decimals: u8, 78 | } 79 | 80 | pub struct Raydium { 81 | pub rpc_nonblocking_client: Arc, 82 | pub rpc_client: Option>, 83 | pub keypair: Arc, 84 | pub pool_id: Option, 85 | } 86 | 87 | impl Raydium { 88 | pub fn new( 89 | rpc_nonblocking_client: Arc, 90 | rpc_client: Arc, 91 | keypair: Arc, 92 | ) -> Self { 93 | Self { 94 | rpc_nonblocking_client, 95 | keypair, 96 | rpc_client: Some(rpc_client), 97 | pool_id: None, 98 | } 99 | } 100 | 101 | pub async fn swap_by_mint( 102 | &self, 103 | mint_str: &str, 104 | swap_direction: SwapDirection, 105 | amount_in: u64, 106 | pool_id: String, 107 | slippage: u64, 108 | start_time: Instant, 109 | jito_client: Arc, 110 | ) -> Result> { 111 | // make instructions on raydium 112 | 113 | tx::new_signed_and_send( 114 | &self.rpc_client.clone().unwrap(), 115 | &self.keypair, 116 | instructions, 117 | jito_client.clone(), 118 | start_time.clone(), 119 | ) 120 | .await 121 | } 122 | } 123 | pub fn amm_swap( 124 | amm_program: &Pubkey, 125 | result: AmmSwapInfoResult, 126 | user_owner: &Pubkey, 127 | user_source: &Pubkey, 128 | user_destination: &Pubkey, 129 | amount_specified: u64, 130 | other_amount_threshold: u64, 131 | swap_base_in: bool, 132 | ) -> Result { 133 | Ok(swap_instruction) 134 | } 135 | 136 | pub async fn get_pool_state( 137 | rpc_client: Arc, 138 | pool_id: Option<&str>, 139 | mint: Option<&str>, 140 | ) -> Result<(Pubkey, AmmInfo)> { 141 | if let Some(pool_id) = pool_id { 142 | Ok((amm_pool_id, *pool_state)) 143 | } else { 144 | Err(anyhow!("NotFoundPool: pool state not found")) 145 | } 146 | } 147 | 148 | pub async fn get_pool_state_by_mint( 149 | rpc_client: Arc, 150 | mint: &str, 151 | ) -> Result<(Pubkey, AmmInfo)> { 152 | // logger.log(format!("[FIND POOL STATE BY mint]: {}", mint)); 153 | let pairs = vec![ 154 | // pump pool 155 | ( 156 | Some(spl_token::native_mint::ID), 157 | Pubkey::from_str(mint).ok(), 158 | ), 159 | // general pool 160 | ( 161 | Pubkey::from_str(mint).ok(), 162 | Some(spl_token::native_mint::ID), 163 | ), 164 | ]; 165 | 166 | let pool_len = core::mem::size_of::() as u64; 167 | let amm_program = Pubkey::from_str(AMM_PROGRAM)?; 168 | // Find matching AMM pool from mint pairs by filter 169 | let mut found_pools = None; 170 | for (coin_mint, pc_mint) in pairs { 171 | // logger.log(format!( 172 | // "get_pool_state_by_mint filter: coin_mint: {:?}, pc_mint: {:?}", 173 | // coin_mint, pc_mint 174 | // )); 175 | let filters = match (coin_mint, pc_mint) { 176 | (None, None) => Some(vec![RpcFilterType::DataSize(pool_len)]), 177 | (Some(coin_mint), None) => Some(vec![ 178 | RpcFilterType::Memcmp(Memcmp::new_base58_encoded(400, &coin_mint.to_bytes())), 179 | RpcFilterType::DataSize(pool_len), 180 | ]), 181 | (None, Some(pc_mint)) => Some(vec![ 182 | RpcFilterType::Memcmp(Memcmp::new_base58_encoded(432, &pc_mint.to_bytes())), 183 | RpcFilterType::DataSize(pool_len), 184 | ]), 185 | (Some(coin_mint), Some(pc_mint)) => Some(vec![ 186 | RpcFilterType::Memcmp(Memcmp::new_base58_encoded(400, &coin_mint.to_bytes())), 187 | RpcFilterType::Memcmp(Memcmp::new_base58_encoded(432, &pc_mint.to_bytes())), 188 | RpcFilterType::DataSize(pool_len), 189 | ]), 190 | }; 191 | let pools = 192 | common::rpc::get_program_accounts_with_filters(&rpc_client, amm_program, filters) 193 | .unwrap(); 194 | if !pools.is_empty() { 195 | found_pools = Some(pools); 196 | break; 197 | } 198 | } 199 | 200 | match found_pools { 201 | Some(pools) => { 202 | let pool = &pools[0]; 203 | let pool_state = AmmInfo::load_from_bytes(&pools[0].1.data)?; 204 | Ok((pool.0, *pool_state)) 205 | } 206 | None => Err(anyhow!("NotFoundPool: pool state not found")), 207 | } 208 | } 209 | 210 | // get pool info 211 | // https://api-v3.raydium.io/pools/info/mint?mint1=So11111111111111111111111111111111111111112&mint2=EzM2d8JVpzfhV7km3tUsR1U1S4xwkrPnWkM4QFeTpump&poolType=standard&poolSortField=default&sortType=desc&pageSize=10&page=1 212 | pub async fn get_pool_info(mint1: &str, mint2: &str) -> Result { 213 | let client = reqwest::Client::new(); 214 | 215 | let result = client 216 | .get("https://api-v3.raydium.io/pools/info/mint") 217 | .query(&[ 218 | ("mint1", mint1), 219 | ("mint2", mint2), 220 | ("poolType", "standard"), 221 | ("poolSortField", "default"), 222 | ("sortType", "desc"), 223 | ("pageSize", "1"), 224 | ("page", "1"), 225 | ]) 226 | .send() 227 | .await? 228 | .json::() 229 | .await 230 | .context("Failed to parse pool info JSON")?; 231 | Ok(result.data) 232 | } 233 | -------------------------------------------------------------------------------- /src/engine/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod swap; 2 | -------------------------------------------------------------------------------- /src/engine/swap.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use crate::common::utils::AppState; 4 | use crate::dex::pump::Pump; 5 | use crate::dex::raydium::Raydium; 6 | use anyhow::Result; 7 | use clap::ValueEnum; 8 | use jito_json_rpc_client::jsonrpc_client::rpc_client::RpcClient as JitoRpcClient; 9 | use raydium_amm::state::AmmInfo; 10 | use serde::Deserialize; 11 | use solana_sdk::pubkey::Pubkey; 12 | use tokio::time::Instant; 13 | 14 | #[derive(ValueEnum, Debug, Clone, Deserialize)] 15 | pub enum SwapDirection { 16 | #[serde(rename = "buy")] 17 | Buy, 18 | #[serde(rename = "sell")] 19 | Sell, 20 | } 21 | impl From for u8 { 22 | fn from(value: SwapDirection) -> Self { 23 | match value { 24 | SwapDirection::Buy => 0, 25 | SwapDirection::Sell => 1, 26 | } 27 | } 28 | } 29 | 30 | #[derive(ValueEnum, Debug, Clone, Deserialize)] 31 | pub enum SwapInType { 32 | /// Quantity 33 | #[serde(rename = "qty")] 34 | Qty, 35 | /// Percentage 36 | #[serde(rename = "pct")] 37 | Pct, 38 | } 39 | 40 | pub async fn pump_swap( 41 | state: AppState, 42 | amount_in: u64, 43 | swap_direction: &str, 44 | slippage: u64, 45 | mint: &str, 46 | jito_client: Arc, 47 | timestamp: Instant, 48 | ) -> Result> { 49 | let swap_direction = match swap_direction { 50 | "buy" => SwapDirection::Buy, 51 | "sell" => SwapDirection::Sell, 52 | _ => todo!(), 53 | }; 54 | let in_type = "qty"; 55 | let use_jito = true; 56 | let in_type = match in_type { 57 | "qty" => SwapInType::Qty, 58 | "pct" => SwapInType::Pct, 59 | _ => todo!(), 60 | }; 61 | let swapx = Pump::new(state.rpc_nonblocking_client, state.rpc_client, state.wallet); 62 | println!("2.2: {:#?}", timestamp.elapsed()); 63 | let res = match swapx 64 | .swap( 65 | mint, 66 | amount_in, 67 | swap_direction, 68 | slippage, 69 | jito_client.clone(), 70 | timestamp.clone(), 71 | ) 72 | .await 73 | { 74 | Ok(res) => res, 75 | Err(e) => { 76 | return Err(e); 77 | } 78 | }; 79 | Ok(res) 80 | } 81 | 82 | pub async fn raydium_swap( 83 | state: AppState, 84 | amount_in: u64, 85 | swap_direction: &str, 86 | pool_id: String, 87 | slippage: u64, 88 | mint: &str, 89 | jito_client: Arc, 90 | timestamp: Instant, 91 | ) -> Result> { 92 | let swap_direction = match swap_direction { 93 | "buy" => SwapDirection::Buy, 94 | "sell" => SwapDirection::Sell, 95 | _ => todo!(), 96 | }; 97 | 98 | let swapx = Raydium::new(state.rpc_nonblocking_client, state.rpc_client, state.wallet); 99 | println!("2.2: {:#?}", timestamp.elapsed()); 100 | let res = match swapx 101 | .swap_by_mint( 102 | mint, 103 | swap_direction, 104 | amount_in, 105 | pool_id, 106 | slippage, 107 | timestamp.clone(), 108 | jito_client.clone(), 109 | ) 110 | .await 111 | { 112 | Ok(res) => res, 113 | Err(e) => { 114 | return Err(e); 115 | } 116 | }; 117 | Ok(res) 118 | } 119 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod common; 2 | pub mod core; 3 | pub mod dex; 4 | pub mod engine; 5 | pub mod services; 6 | -------------------------------------------------------------------------------- /src/log.txt: -------------------------------------------------------------------------------- 1 | 2 | [2025-02-10T08:37:57.409487144+00:00] --------------------- Copy-trading-bot start!!! ------------------ 3 | 4 | [2025-02-10T08:38:55.826936184+00:00] 5 | Target Wallet: 4qjhHtM44cWL6okXcC2PrqKQYkgKxMevSZQHsbt9aq2P https://solscan.io/tx/esiXJ3gV4WZie7nhHVLdT5iwW8MAgxr5ywsTwB9TD1ZJwK1f2gUi9iFH8e8HrvbFQsoEutHiFRCkdHnUiFjNPaM 6 | [2025-02-10T08:38:57.653542901+00:00] Copy Wallet: C48dQJokWVvnLZroQix47AvKG5T1U88iGc115fXFEAzs https://solscan.io/tx/R227y9tiChBxY4dfVXcAs3aeVzMMpVNCPrvak6cZ8Fci9VFC6xwwNz2pQbXTKVyfFRvLT8oQ9pJz6NR53Zdyms7 7 | Rusult: Success 8 | 9 | 10 | 11 | [2025-02-10T08:45:57.371492763+00:00] --------------------- Copy-trading-bot start!!! ------------------ 12 | 13 | [2025-02-10T08:46:21.916862902+00:00] 14 | Target Wallet: 4qjhHtM44cWL6okXcC2PrqKQYkgKxMevSZQHsbt9aq2P https://solscan.io/tx/3qa4g857YN22tJQ1P7iVVZEkhCBpyKzmcZ3gLcJD4fRke8zRmpLSitMgMmkh4Ga3QspUgC7NsMCbLZkSMpFG3kAo 15 | [2025-02-10T08:46:24.991017282+00:00] Copy Wallet: C48dQJokWVvnLZroQix47AvKG5T1U88iGc115fXFEAzs https://solscan.io/tx/4bJHyTJrSfnnsqEyMheJCXfWBJPGZfjEDszcKtSqA6a97Dn8RwvMSNqkF4X36GWD3a8TMX9bnepyoFAK5iD9bS9a 16 | Rusult: Success 17 | 18 | [2025-02-10T08:46:54.104876102+00:00] 19 | Target Wallet: 4qjhHtM44cWL6okXcC2PrqKQYkgKxMevSZQHsbt9aq2P https://solscan.io/tx/4hp7TkucGtKd6xZmFXUyu11chez57p6LwnJgT1QbptZfRS6oPnMqJ8oojQ6tyonB9SJ4QJWJNS8n3XZLeCp2j1Hf 20 | [2025-02-10T08:46:55.948107041+00:00] Copy Wallet: C48dQJokWVvnLZroQix47AvKG5T1U88iGc115fXFEAzs https://solscan.io/tx/66ThSAJsyMU2NNsYxFJoeehYzXSUxXt8MRMVcsfPF3D6QrrsnUzHr37ht1MQQU1NYsaD5AKWozGcF5LhubyjtduY 21 | Rusult: Success 22 | 23 | [2025-02-10T08:47:42.616368108+00:00] 24 | Target Wallet: 4qjhHtM44cWL6okXcC2PrqKQYkgKxMevSZQHsbt9aq2P https://solscan.io/tx/66VXBvP7QYynbDsAHzaGCowSJdSx2BTqXfNadKxdmhcdjwPGzjYGmPQxUpEcbFxg5Q363k5so3RaQevfXDg8noyF 25 | [2025-02-10T08:47:44.635125678+00:00] Copy Wallet: C48dQJokWVvnLZroQix47AvKG5T1U88iGc115fXFEAzs https://solscan.io/tx/5SeQQSYxAiLrNEtMJk2wKPMdyk2ULPZ5kq3bwQr63EE9Jp71mxRCnCjsH1hs6rF74whcnyaodCBKNQe5NyMaBrGU 26 | Rusult: Success 27 | 28 | [2025-02-10T08:49:22.840843871+00:00] 29 | Target Wallet: 4qjhHtM44cWL6okXcC2PrqKQYkgKxMevSZQHsbt9aq2P https://solscan.io/tx/4cjbrxkfRstmLypsiyocrPVmE2KeL2xSoMNiatFjyg9JDvW2UR75qL7HH8pGwWoNyWWB3gNcAAu6mJTk9pii9QRq 30 | [2025-02-10T08:49:25.845951025+00:00] Copy Wallet: C48dQJokWVvnLZroQix47AvKG5T1U88iGc115fXFEAzs https://solscan.io/tx/4PP58EXPs7qqZTByKrtrGCNNtUV4XwXSjTEDh7on1yR4mgryWqx7NkuYgh8JFRGFWba9Umjv2u3jazNtoi8GBxAi 31 | Rusult: Success 32 | 33 | [2025-02-10T08:54:28.188323992+00:00] --------------------- Copy-trading-bot start!!! ------------------ 34 | 35 | [2025-02-10T08:56:52.983624404+00:00] 36 | Target Wallet: 4qjhHtM44cWL6okXcC2PrqKQYkgKxMevSZQHsbt9aq2P https://solscan.io/tx/3wHnM4R1EiReBeJgbc6p1omo7upbrYmTcMkqNat92sJLxH8zGKms26kPgCToHHFVoeeYvxHySRtXLv2tSEYVsJXh 37 | [2025-02-10T08:56:54.815442921+00:00] Copy Wallet: C48dQJokWVvnLZroQix47AvKG5T1U88iGc115fXFEAzs https://solscan.io/tx/3XB6rFErVSBbpQL98R6vxWErtVdTecYKoVMBMQYKszJL2YAnDqFNH7HFLMzD77C2wxqt6XBQUwEnbrT6e2j9HRW 38 | Rusult: Success 39 | 40 | [2025-02-10T08:57:57.866486303+00:00] 41 | Target Wallet: 4qjhHtM44cWL6okXcC2PrqKQYkgKxMevSZQHsbt9aq2P https://solscan.io/tx/2wNr9phuHovEuQmEgDgid3menACxm2AhXmM1v9LFunat1X9pQzN4371DCbHUxvhXmdQwx84pxePE2VqeBxoThLJb 42 | [2025-02-10T08:58:00.913166240+00:00] Copy Wallet: C48dQJokWVvnLZroQix47AvKG5T1U88iGc115fXFEAzs https://solscan.io/tx/4DAk3CSfJP6VWCD3nQ3dk1gWicEG8y2z7TFTUrMbh9AutmQDGVh8nsRzqfS9c9XZnHGGCioRdWN2cPZ2U53xEk8m 43 | Rusult: Success 44 | 45 | [2025-02-10T09:38:52.071530920+00:00] --------------------- Copy-trading-bot start!!! ------------------ 46 | 47 | [2025-02-10T09:40:23.930881075+00:00] 48 | Target Wallet: 4qjhHtM44cWL6okXcC2PrqKQYkgKxMevSZQHsbt9aq2P https://solscan.io/tx/3v1VhXqovgeWWLP5Vhng87g2c19z3JU52dz8MKRTDkBnuy3ropb2kG1UqZuYjcn7wCXDHrjis91KqsnqmNHLD2jB 49 | [2025-02-10T09:40:25.844833638+00:00] Copy Wallet: C48dQJokWVvnLZroQix47AvKG5T1U88iGc115fXFEAzs https://solscan.io/tx/2qJwnkrhYjvhk23T3v1J4tPamTdBsnpSyeBFeueJ3rJNuXwoUuADvLTjpq744jRFbJAeeJWEKbEQ8YrRYgWfmgbw 50 | Rusult: Success 51 | 52 | [2025-02-10T09:55:38.168343496+00:00] --------------------- Copy-trading-bot start!!! ------------------ 53 | 54 | [2025-02-10T09:55:57.202100605+00:00] 55 | Target Wallet: 4qjhHtM44cWL6okXcC2PrqKQYkgKxMevSZQHsbt9aq2P https://solscan.io/tx/4Smq71tnKxW9KVacUuFnhQip87AQV9H7TfTAXVoBuo5nqgoUzvqBgyyfErvMy7RKYXpG56M3oZYUy3gQZWRC4BrW 56 | [2025-02-10T09:56:00.087834088+00:00] Copy Wallet: C48dQJokWVvnLZroQix47AvKG5T1U88iGc115fXFEAzs https://solscan.io/tx/2JhNhk4YYr2BXLBR5vMpSrzx9m9VS6skTogdoHXYVDihf7E4Ft5jUfeyLHLJMFxCTVTTMg1v6cgAqn4iSrDJ8ZEh 57 | Rusult: Success 58 | 59 | [2025-02-10T09:56:46.719928076+00:00] 60 | Target Wallet: 4qjhHtM44cWL6okXcC2PrqKQYkgKxMevSZQHsbt9aq2P https://solscan.io/tx/5w1aFg6sivz35zVQVwCsNtvoNxPFRbcP5TbqwLmcYwMLgrWXqjNnPYnH6JYF1jqjdn3zAS9DdCLRag85dojx3HUf 61 | [2025-02-10T09:56:49.593760847+00:00] Copy Wallet: C48dQJokWVvnLZroQix47AvKG5T1U88iGc115fXFEAzs https://solscan.io/tx/3ZxrgFLRsCV9Sfyvncz8ncBZFE61Ur7fCgEcsUnn5Wxwd3YKXas2teiQDfANpsMqMWdkbtqunUE7tgFJmp5ch7Lu 62 | Rusult: Success 63 | 64 | [2025-02-10T09:58:05.083389943+00:00] 65 | Target Wallet: 4qjhHtM44cWL6okXcC2PrqKQYkgKxMevSZQHsbt9aq2P https://solscan.io/tx/xpbE6Kvoz8anF1YV35XGSxRYmf8aoHzE6L8J5LK7WRuHR37eUeSqjD6P7xL5DveV8kkrpf2n7niMHa5ccH2Vqxg 66 | [2025-02-10T09:58:07.994566999+00:00] Copy Wallet: C48dQJokWVvnLZroQix47AvKG5T1U88iGc115fXFEAzs https://solscan.io/tx/39QBCeu3zzfQGrdWr44cPjfUg5vDXiaoPCPqzyK6mqjogu9CdkPcDzhdvM9X4hm4giyvG4ZzbCNFQXPfpWxVCKNc 67 | Rusult: Success 68 | 69 | [2025-02-10T10:17:22.468464853+00:00] --------------------- Copy-trading-bot start!!! ------------------ 70 | 71 | [2025-02-10T11:18:10.614776621+00:00] --------------------- Copy-trading-bot start!!! ------------------ 72 | 73 | [2025-02-10T11:20:50.558764119+00:00] 74 | Target Wallet: 4qjhHtM44cWL6okXcC2PrqKQYkgKxMevSZQHsbt9aq2P https://solscan.io/tx/2asgg1tYF9yY2fxzWZwNNjdZgWCAdHarBaqRWDCTDMJEpUKiHMjstnoVRLekZLUFUiKktkmMDLuZg6gaXhH89a4B 75 | [2025-02-10T11:20:53.909480119+00:00] Copy Wallet: C48dQJokWVvnLZroQix47AvKG5T1U88iGc115fXFEAzs https://solscan.io/tx/JptLKM3nbN6JhQX6MgsoiJ4HBGyDfwPfSkD1sa29xyJHhUjPHwudLoJzyLFi6zagJ6G5Rz8RRuQe47usymLHrz9 76 | Rusult: Success 77 | 78 | [2025-02-10T11:25:37.908859373+00:00] --------------------- Copy-trading-bot start!!! ------------------ 79 | 80 | [2025-02-10T11:26:01.672606392+00:00] 81 | Target Wallet: 4qjhHtM44cWL6okXcC2PrqKQYkgKxMevSZQHsbt9aq2P https://solscan.io/tx/2Ru75FSXUwjSrYxuzfQLpLFg3X8g2y6gVrK9CMGTLpCLkVVoA2Gb3YavdbEztJNmXWg4ywpsmdqCdi3k1FZwMqUh 82 | [2025-02-10T11:26:26.383004213+00:00] 83 | Target Wallet: 4qjhHtM44cWL6okXcC2PrqKQYkgKxMevSZQHsbt9aq2P https://solscan.io/tx/z8dy61gfvZkSBL7cv618e9qWDoownbTGPDRJyjoZJReupktuvu1A8q2U3bZpTsV2JvJMWge6ueFb2vuuUCKmbRq 84 | [2025-02-10T11:33:23.931544093+00:00] 85 | Target Wallet: 4qjhHtM44cWL6okXcC2PrqKQYkgKxMevSZQHsbt9aq2P https://solscan.io/tx/4eeRYuWgYkRHE7Qnd7UzVEqsWRCQCB3GX5nPDpugGTsJFoufnyLbPiCMqKQfGsCJ3ZnkNmtwGCKXxTj5p6pfFUGb 86 | [2025-02-10T11:49:07.156021643+00:00] 87 | Target Wallet: 4qjhHtM44cWL6okXcC2PrqKQYkgKxMevSZQHsbt9aq2P https://solscan.io/tx/3iavK8ouW5v2synSiQFUAFh9bCfUg9rv6bVuE9szN8aFWWBT6bqA782KctVdG4Lc8S3X1KPpqUa3U5oGUGTvZUZe 88 | [2025-02-10T11:49:09.466351566+00:00] Copy Wallet: C48dQJokWVvnLZroQix47AvKG5T1U88iGc115fXFEAzs https://solscan.io/tx/376qcwHf3QDCKyJR2JpHd8tU21UHDcg8QVvQp9ZB8xMT4UqHMU2GHXM3DJTs1XbFBwYxC9Wd7hbcqfJxy4Mg3Xfp 89 | Rusult: Success 90 | 91 | [2025-02-10T12:03:48.371171189+00:00] --------------------- Copy-trading-bot start!!! ------------------ 92 | 93 | [2025-02-10T12:04:27.115433462+00:00] 94 | Target Wallet: 4qjhHtM44cWL6okXcC2PrqKQYkgKxMevSZQHsbt9aq2P https://solscan.io/tx/4GCXH1FwqqRs2De1fTtbWP8zqsk4EDqZUB7pwhACLYECuya4Lez2qT5hDP98m6LggUcZWBZ7iDwrpnWTN6wkmyzh 95 | [2025-02-10T12:06:37.219041155+00:00] --------------------- Copy-trading-bot start!!! ------------------ 96 | 97 | [2025-02-10T12:06:54.036514759+00:00] 98 | Target Wallet: 4qjhHtM44cWL6okXcC2PrqKQYkgKxMevSZQHsbt9aq2P https://solscan.io/tx/19haxnaSdNdGdgkmAqMMqXWivg75enXx3pJmCLWvJeo7RhdzFXQuXuqDVzS8itpPmmfjPgSVrThBhadMRxnJ2A1 99 | [2025-02-10T14:05:01.526139967+00:00] --------------------- Copy-trading-bot start!!! ------------------ 100 | 101 | [2025-02-10T14:05:24.679156772+00:00] 102 | Target Wallet: 4qjhHtM44cWL6okXcC2PrqKQYkgKxMevSZQHsbt9aq2P https://solscan.io/tx/41tapP5ggBk173DaCCsX1ZJWdCP1XUKRmsFivXjao7mJ1p4fvoasdxEo1xyGTccTFtbVTZrtJXAGDjcw69M8ErfC 103 | [2025-02-10T16:33:02.831951575+00:00] --------------------- Copy-trading-bot start!!! ------------------ 104 | 105 | [2025-02-10T16:39:07.443804766+00:00] 106 | Target Wallet: 4qjhHtM44cWL6okXcC2PrqKQYkgKxMevSZQHsbt9aq2P https://solscan.io/tx/S1CPyLL6fm2GeWoe6UPRAdgRmkP8gi4AYsmvV1HGYJfrE7jRbuoKQDiuduDYNRwvceFJTuh6gawg8ZacVHEnZKY 107 | [2025-02-10T16:41:56.280962822+00:00] --------------------- Copy-trading-bot start!!! ------------------ 108 | 109 | [2025-02-10T16:42:11.676999833+00:00] 110 | Target Wallet: 4qjhHtM44cWL6okXcC2PrqKQYkgKxMevSZQHsbt9aq2P https://solscan.io/tx/5Ecg1FnT6ZqybXLqYdNQ4SXakCJeVanoVZPVstAPsDTbzXQTHkqftXNLnyL3uT44AFY9whavabHPgrY1PbQGB93J 111 | [2025-02-10T16:49:00.902419734+00:00] 112 | Target Wallet: 4qjhHtM44cWL6okXcC2PrqKQYkgKxMevSZQHsbt9aq2P https://solscan.io/tx/28cXRKP9RxfsMFZn4bwJZxqQFtSGcFBcv4KZKzLzMECkrdso8uE84Ej5ZBsw72hYAxBMUXB3BGUFB4V19VStKcit 113 | [2025-02-10T17:01:01.832702015+00:00] --------------------- Copy-trading-bot start!!! ------------------ 114 | 115 | [2025-02-10T17:01:59.273338940+00:00] 116 | Target Wallet: 4qjhHtM44cWL6okXcC2PrqKQYkgKxMevSZQHsbt9aq2P https://solscan.io/tx/3bKshVLd1Y9dt2duqx4YhZ61nFo3PKmTLJGPd57UVo7J439ggd51kPTC6wvYehmjyANLSFLDhyQ5aUqVFDhDPUmJ 117 | [2025-02-10T17:04:48.820512135+00:00] --------------------- Copy-trading-bot start!!! ------------------ 118 | 119 | [2025-02-10T17:05:00.650302267+00:00] 120 | Target Wallet: 4qjhHtM44cWL6okXcC2PrqKQYkgKxMevSZQHsbt9aq2P https://solscan.io/tx/4moMqcCLAyooxFLzQRz9YpxMh5FNBphELqAWf9Yt9w1fkePiiD5juYAXBJSL4bgTPqJU7jwwNXAefaPUVdjGZzDT 121 | [2025-02-10T17:06:11.717214202+00:00] --------------------- Copy-trading-bot start!!! ------------------ 122 | 123 | [2025-02-10T17:06:43.592479148+00:00] --------------------- Copy-trading-bot start!!! ------------------ 124 | 125 | [2025-02-10T17:08:17.593787956+00:00] 126 | Target Wallet: 4qjhHtM44cWL6okXcC2PrqKQYkgKxMevSZQHsbt9aq2P https://solscan.io/tx/2RrR2Xe1s9EkQDuvYLubYGqqqMyGynfXyFdhfaaWHM2qENP37z3r9J3WBkUeP8C4PpiyWgeoy8w7L7rpncq5H4WD 127 | [2025-02-10T17:08:21.026814003+00:00] Copy Wallet: C48dQJokWVvnLZroQix47AvKG5T1U88iGc115fXFEAzs https://solscan.io/tx/NZ9UJQdN6VFnbnFtyjVYenZ8Anr9VU9PGVKxvwKj1p3NTNbviuGyxuEzqkaGwy5bRrnqpciN9nzjUSbmfWcojr2 128 | Rusult: Success 129 | 130 | [2025-02-10T17:30:25.961999145+00:00] --------------------- Copy-trading-bot start!!! ------------------ 131 | 132 | [2025-02-10T17:36:02.813231502+00:00] --------------------- Copy-trading-bot start!!! ------------------ 133 | 134 | [2025-02-10T17:37:46.252968615+00:00] 135 | Target Wallet: 4qjhHtM44cWL6okXcC2PrqKQYkgKxMevSZQHsbt9aq2P https://solscan.io/tx/2pKLVHvZJTewJPhkbx1PKFCEGqUKb2bK9WRaxP6McrZ1CVogbEdGKoEWjCZEsQFJcBzq4dEJdyZtm8fxrgmWbvtg 136 | [2025-02-10T17:37:49.886252528+00:00] Copy Wallet: C48dQJokWVvnLZroQix47AvKG5T1U88iGc115fXFEAzs https://solscan.io/tx/3LSeAgvML3okoj2Cf1DAQ65FP4EtMPSDjqVJmB7vxQU7mnaRvF5H5t5pCF8JeDSmfYqA8vNM5dCZze8oBL4btpMe 137 | Rusult: Success 138 | 139 | [2025-02-10T17:45:39.790817717+00:00] --------------------- Copy-trading-bot start!!! ------------------ 140 | 141 | [2025-02-10T17:46:18.165343103+00:00] 142 | Target Wallet: 4qjhHtM44cWL6okXcC2PrqKQYkgKxMevSZQHsbt9aq2P https://solscan.io/tx/4P4oi2DAPxKTjC22d2cQwkqUqBDSz7tEq26dMrAEW3yQgpDHDmNLrBF5LAV9DXFJjpNBJK7RMvtgb3Poa3QTPBYd 143 | [2025-02-10T17:54:17.990832320+00:00] --------------------- Copy-trading-bot start!!! ------------------ 144 | 145 | [2025-02-10T17:54:25.397959999+00:00] 146 | Target Wallet: 4qjhHtM44cWL6okXcC2PrqKQYkgKxMevSZQHsbt9aq2P https://solscan.io/tx/43rG3pAmaxNRELnjVr98XpqAoBV8vmTszUChH28pQRNBJvaz97RCavyhw3f7GSCMswYQbgrBmXfhUmzoR7N2Zcnw 147 | [2025-02-10T18:05:20.200337580+00:00] --------------------- Copy-trading-bot start!!! ------------------ 148 | 149 | [2025-02-10T18:09:14.504643788+00:00] 150 | Target Wallet: 4qjhHtM44cWL6okXcC2PrqKQYkgKxMevSZQHsbt9aq2P https://solscan.io/tx/2rxTFeDaWPbC5tMCqrbkX75haazwk8mLkdAziwyp1r4ZFT6rchLuNCCwKNUU4hFCzTenzifGcLxuVQwvrHCV67Ks 151 | [2025-02-10T18:09:41.699927349+00:00] 152 | Target Wallet: 4qjhHtM44cWL6okXcC2PrqKQYkgKxMevSZQHsbt9aq2P https://solscan.io/tx/5a2g79gToCVbLMBu6hjvMK7xCKn5MRfLApdacY5P2nDKPsNkpZrcU7BafrEXxkeLwh53T5hBQqnQg9WqRDZ6MbXC 153 | [2025-02-10T18:10:05.044897296+00:00] 154 | Target Wallet: 4qjhHtM44cWL6okXcC2PrqKQYkgKxMevSZQHsbt9aq2P https://solscan.io/tx/238ZELmSdRmniKUjAZFBGF73UxpheDCbfsNmoDa1rhhAoLwAhdCu45vEzFVk5qD4jFqJrhLZDEkcKcazFPcFnSw7 155 | [2025-02-10T18:10:08.369299305+00:00] Copy Wallet: C48dQJokWVvnLZroQix47AvKG5T1U88iGc115fXFEAzs https://solscan.io/tx/4SjjV19aS1t3rgkSwsWs558p98z2XG2hdVBhmnjFrvkEaNafMAGEGNdTehBwsmWok4noM7Fmx9KtHj6jUPBj631T 156 | Rusult: Success 157 | 158 | [2025-02-10T18:21:51.650560741+00:00] --------------------- Copy-trading-bot start!!! ------------------ 159 | 160 | [2025-02-10T18:23:37.516743751+00:00] 161 | Target Wallet: 4qjhHtM44cWL6okXcC2PrqKQYkgKxMevSZQHsbt9aq2P https://solscan.io/tx/LGCKFdKuGYGcfvkgeYzoDfTZpSEohYL9h7xuoGHX6RsX9GbEw24Qd6EnoNiMz3UMG1cgQp8N29pvT2NvXWDCipD 162 | [2025-02-10T18:23:40.640484909+00:00] Copy Wallet: C48dQJokWVvnLZroQix47AvKG5T1U88iGc115fXFEAzs https://solscan.io/tx/2FQ2ytKwkyxepe8oUnutigMTuqX9L8o3s34vcwKj8rPxGHJfz4Pq63VsdgEDBNeYERurthudTJhrekctBwHxZWgn 163 | Rusult: Success 164 | 165 | [2025-02-10T18:25:06.215162931+00:00] 166 | Target Wallet: 4qjhHtM44cWL6okXcC2PrqKQYkgKxMevSZQHsbt9aq2P https://solscan.io/tx/2exubiqkpxuwqYMqLvehqo9Vh6Wf3HcLvgLN3x4bwsXyD37rE2Xm1zMe3JMP8VbErNt2CM82CYzWEHraWCnWu2Rf 167 | [2025-02-10T18:25:09.205538609+00:00] Copy Wallet: C48dQJokWVvnLZroQix47AvKG5T1U88iGc115fXFEAzs https://solscan.io/tx/Fx9a8xAf8oZJz1zaoGVapqC7v9nzqL5bhkjVR5uDMFY11yBB9ZnEkd2SUAz9gn7gZad8xkNC8EV2hbospxPt7Mp 168 | Rusult: Success 169 | 170 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use bincode::Options; 2 | use jito_json_rpc_client::jsonrpc_client::rpc_client::RpcClient as JitoRpcClient; 3 | use temp::common::utils::{ 4 | create_arc_rpc_client, create_nonblocking_rpc_client, import_arc_wallet, import_env_var, 5 | import_wallet, log_message, AppState, 6 | }; 7 | use temp::core::token::get_account_info; 8 | use temp::core::tx::jito_confirm; 9 | use temp::engine::swap::{pump_swap, raydium_swap}; 10 | // use copy_trading_bot::dex::pump::pump_sdk_swap; 11 | use dotenv::dotenv; 12 | use futures_util::{SinkExt, StreamExt}; 13 | use serde::Serialize; 14 | use serde_json::Value; 15 | use solana_sdk::message::VersionedMessage; 16 | use solana_sdk::pubkey::Pubkey; 17 | use solana_sdk::signer::Signer; 18 | use solana_sdk::transaction::VersionedTransaction; 19 | use spl_associated_token_account::get_associated_token_address; 20 | use std::env; 21 | use std::str::FromStr; 22 | use std::sync::{Arc, LazyLock}; 23 | use tokio::time::Instant; 24 | use tokio_tungstenite::{connect_async, tungstenite::Message as WsMessage}; 25 | 26 | #[derive(Serialize)] 27 | struct SwapRequest { 28 | quoteResponse: serde_json::Value, // You may deserialize it into a specific struct if known 29 | userPublicKey: String, 30 | wrapAndUnwrapSol: bool, 31 | dynamicComputeUnitLimit: bool, 32 | prioritizationFeeLamports: u64, 33 | } 34 | #[tokio::main] 35 | 36 | async fn main() { 37 | dotenv().ok(); 38 | let target = env::var("TARGET_PUBKEY").expect("TARGET not set"); 39 | 40 | let rpc_client = create_arc_rpc_client().unwrap(); 41 | let rpc_nonblocking_client = create_nonblocking_rpc_client().await.unwrap(); 42 | let wallet = import_arc_wallet().unwrap(); 43 | 44 | let state = AppState { 45 | rpc_client, 46 | rpc_nonblocking_client, 47 | wallet, 48 | }; 49 | pub static BLOCK_ENGINE_URL: LazyLock = 50 | LazyLock::new(|| import_env_var("JITO_BLOCK_ENGINE_URL")); 51 | let jito_client = Arc::new(JitoRpcClient::new(format!( 52 | "{}/api/v1/bundles", 53 | *BLOCK_ENGINE_URL 54 | ))); 55 | let unwanted_key = env::var("JUP_PUBKEY").expect("JUP_PUBKEY not set"); 56 | let ws_url = env::var("RPC_WEBSOCKET_ENDPOINT").expect("RPC_WEBSOCKET_ENDPOINT not set"); 57 | 58 | let (ws_stream, _) = connect_async(ws_url) 59 | .await 60 | .expect("Failed to connect to WebSocket server"); 61 | let (mut write, mut read) = ws_stream.split(); 62 | // Subscribe to logs 63 | let subscription_message = serde_json::json!({ 64 | "jsonrpc": "2.0", 65 | "id": 1, 66 | "method": "transactionSubscribe", 67 | "params": [ 68 | 69 | { 70 | "failed": false, 71 | "accountInclude": ["675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8", "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"], 72 | "accountExclude": [unwanted_key], 73 | // Optionally specify accounts of interest 74 | }, 75 | { 76 | "commitment": "processed", 77 | "encoding": "jsonParsed", 78 | "transactionDetails": "full", 79 | "maxSupportedTransactionVersion": 0 80 | } 81 | ] 82 | }); 83 | 84 | write 85 | .send(subscription_message.to_string().into()) 86 | .await 87 | .expect("Failed to send subscription message"); 88 | 89 | let _ = log_message("--------------------- Copy-trading-bot start!!! ------------------\n") 90 | .await; 91 | 92 | // Listen for messages 93 | while let Some(Ok(msg)) = read.next().await { 94 | if let WsMessage::Text(text) = msg { 95 | let json: Value = serde_json::from_str(&text).unwrap(); 96 | 97 | let sig = json["params"]["result"]["signature"] 98 | .as_str() 99 | .unwrap_or_default(); 100 | let timestamp = Instant::now(); 101 | 102 | // filter tx raydium part 103 | tx_ray(); 104 | 105 | // filter tx pumpfun part 106 | tx_pump(); 107 | } 108 | } 109 | } 110 | 111 | pub async fn tx_ray( 112 | json: Value, 113 | target: String, 114 | timestamp: Instant, 115 | state: AppState, 116 | jito_client: Arc, 117 | ) { 118 | // parsing tx part 119 | 120 | if { 121 | dirs = "buy".to_string(); 122 | swap_to_events_on_raydium( 123 | mint, 124 | amount_in * percent / 100, 125 | dirs, 126 | pool_id, 127 | timestamp.clone(), 128 | jito_client.clone(), 129 | state.clone(), 130 | ) 131 | .await; 132 | } else { 133 | dirs = "sell".to_string(); 134 | swap_to_events_on_raydium( 135 | mint, 136 | amount_in * percent / 100, 137 | dirs, 138 | pool_id, 139 | timestamp.clone(), 140 | jito_client.clone(), 141 | state.clone(), 142 | ) 143 | .await; 144 | } 145 | } 146 | 147 | pub async fn tx_pump( 148 | json: Value, 149 | target: String, 150 | timestamp: Instant, 151 | state: AppState, 152 | jito_client: Arc, 153 | ) { 154 | // Iterate over logs and check for unwanted_key 155 | 156 | if { 157 | dirs = "buy".to_string(); 158 | swap_to_events_on_pump( 159 | mint, 160 | amount_in * percent / 100, 161 | dirs, 162 | timestamp.clone(), 163 | jito_client.clone(), 164 | state.clone(), 165 | ) 166 | .await; 167 | } else { 168 | dirs = "sell".to_string(); 169 | 170 | swap_to_events_on_pump( 171 | mint, 172 | amount_in * percent / 100, 173 | dirs, 174 | timestamp.clone(), 175 | jito_client.clone(), 176 | state.clone(), 177 | ) 178 | .await; 179 | } 180 | } 181 | 182 | pub async fn swap_on_jup(mint: String, dir: String, amount: u64) { 183 | // get tx 184 | jito_confirm() 185 | } 186 | pub async fn swap_to_events_on_pump( 187 | mint: String, 188 | amount_in: u64, 189 | dirs: String, 190 | timestamp: Instant, 191 | jito_client: Arc, 192 | state: AppState, 193 | ) { 194 | println!("2: {:#?}", timestamp.elapsed().clone()); 195 | 196 | let slippage = 10000; 197 | println!("2.1: {:#?}", timestamp.elapsed()); 198 | let res = pump_swap( 199 | state, 200 | amount_in, 201 | &dirs, 202 | slippage, 203 | &mint, 204 | jito_client, 205 | timestamp.clone(), 206 | ) 207 | .await; 208 | } 209 | 210 | pub async fn swap_to_events_on_raydium( 211 | mint: String, 212 | amount_in: u64, 213 | dirs: String, 214 | pool_id: String, 215 | timestamp: Instant, 216 | jito_client: Arc, 217 | state: AppState, 218 | ) { 219 | println!("2: {:#?}", timestamp.elapsed().clone()); 220 | 221 | let slippage = 10000; 222 | println!("2.1: {:#?}", timestamp.elapsed()); 223 | let res = raydium_swap( 224 | state, 225 | amount_in, 226 | &dirs, 227 | pool_id, 228 | slippage, 229 | &mint, 230 | jito_client, 231 | timestamp.clone(), 232 | ) 233 | .await; 234 | } 235 | -------------------------------------------------------------------------------- /src/services/jito.rs: -------------------------------------------------------------------------------- 1 | use std::{future::Future, str::FromStr, sync::LazyLock, time::Duration}; 2 | 3 | use anyhow::{anyhow, Result}; 4 | use indicatif::{ProgressBar, ProgressStyle}; 5 | use rand::{seq::IteratorRandom, thread_rng}; 6 | use serde::Deserialize; 7 | use serde_json::Value; 8 | use solana_sdk::pubkey::Pubkey; 9 | use tokio::{ 10 | sync::RwLock, 11 | time::{sleep, Instant}, 12 | }; 13 | 14 | use crate::common::utils::{import_env_var, log_message}; 15 | 16 | pub static BLOCK_ENGINE_URL: LazyLock = 17 | LazyLock::new(|| import_env_var("JITO_BLOCK_ENGINE_URL")); 18 | pub static TIP_STREAM_URL: LazyLock = 19 | LazyLock::new(|| import_env_var("JITO_TIP_STREAM_URL")); 20 | pub static TIP_PERCENTILE: LazyLock = 21 | LazyLock::new(|| import_env_var("JITO_TIP_PERCENTILE")); 22 | 23 | pub static TIP_ACCOUNTS: LazyLock>> = LazyLock::new(|| RwLock::new(vec![])); 24 | 25 | #[derive(Debug)] 26 | pub struct TipAccountResult { 27 | pub accounts: Vec, 28 | } 29 | 30 | #[derive(Deserialize, Debug)] 31 | pub struct BundleStatus { 32 | pub bundle_id: String, 33 | pub transactions: Vec, 34 | pub slot: u64, 35 | pub confirmation_status: String, 36 | pub err: ErrorStatus, 37 | } 38 | #[derive(Deserialize, Debug)] 39 | pub struct ErrorStatus { 40 | #[serde(rename = "Ok")] 41 | pub ok: Option<()>, 42 | } 43 | 44 | pub fn new_progress_bar() -> ProgressBar { 45 | let progress_bar = ProgressBar::new(42); 46 | progress_bar.set_style( 47 | ProgressStyle::default_spinner() 48 | .template("{spinner:.green} {wide_msg}") 49 | .expect("ProgressStyle::template direct input to be correct"), 50 | ); 51 | progress_bar.enable_steady_tick(Duration::from_millis(100)); 52 | progress_bar 53 | } 54 | -------------------------------------------------------------------------------- /src/services/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod jito; 2 | --------------------------------------------------------------------------------