├── 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 |
--------------------------------------------------------------------------------