From c10406915c0625adf362bc9b0e6c0bfa4722cf66 Mon Sep 17 00:00:00 2001 From: ysq Date: Mon, 25 Aug 2025 00:21:15 +0800 Subject: [PATCH] feat: add examples and enhance MEV protection services This commit adds comprehensive examples for various trading operations including: - PumpFun copy and sniper trading - Bonk copy and sniper trading - PumpSwap trading - Raydium CPMM and AMM V4 trading - Middleware system demonstration - Event subscription Enhanced MEV protection services with FlashBlock and Node1 integration. Updated instruction modules for bonk, pumpswap, and raydium_cpmm. Improved SWQOS modules for better transaction handling. --- .gitignore | 1 - Cargo.toml | 19 +- README.md | 797 ++----------------- README_CN.md | 814 ++------------------ examples/bonk_copy_trading/Cargo.toml | 10 + examples/bonk_copy_trading/src/main.rs | 186 +++++ examples/bonk_sniper_trading/Cargo.toml | 10 + examples/bonk_sniper_trading/src/main.rs | 160 ++++ examples/event_subscription/Cargo.toml | 8 + examples/event_subscription/src/main.rs | 181 +++++ examples/middleware_system/Cargo.toml | 10 + examples/middleware_system/src/main.rs | 110 +++ examples/pumpfun_copy_trading/Cargo.toml | 10 + examples/pumpfun_copy_trading/src/main.rs | 183 +++++ examples/pumpfun_sniper_trading/Cargo.toml | 10 + examples/pumpfun_sniper_trading/src/main.rs | 151 ++++ examples/pumpswap_trading/Cargo.toml | 11 + examples/pumpswap_trading/src/main.rs | 224 ++++++ examples/raydium_amm_v4_trading/Cargo.toml | 10 + examples/raydium_amm_v4_trading/src/main.rs | 196 +++++ examples/raydium_cpmm_trading/Cargo.toml | 10 + examples/raydium_cpmm_trading/src/main.rs | 199 +++++ examples/trading_client/Cargo.toml | 9 + examples/trading_client/src/main.rs | 59 ++ src/instruction/bonk.rs | 42 +- src/instruction/pumpswap.rs | 74 +- src/instruction/raydium_cpmm.rs | 45 +- src/swqos/bloxroute.rs | 5 +- src/swqos/flashblock.rs | 5 +- src/swqos/jito.rs | 5 +- src/swqos/nextblock.rs | 5 +- src/swqos/node1.rs | 5 +- src/swqos/solana_rpc.rs | 5 +- src/swqos/temporal.rs | 5 +- src/swqos/zeroslot.rs | 5 +- src/trading/core/params.rs | 32 + 36 files changed, 2049 insertions(+), 1562 deletions(-) mode change 100755 => 100644 README.md create mode 100644 examples/bonk_copy_trading/Cargo.toml create mode 100644 examples/bonk_copy_trading/src/main.rs create mode 100644 examples/bonk_sniper_trading/Cargo.toml create mode 100644 examples/bonk_sniper_trading/src/main.rs create mode 100644 examples/event_subscription/Cargo.toml create mode 100644 examples/event_subscription/src/main.rs create mode 100644 examples/middleware_system/Cargo.toml create mode 100644 examples/middleware_system/src/main.rs create mode 100644 examples/pumpfun_copy_trading/Cargo.toml create mode 100644 examples/pumpfun_copy_trading/src/main.rs create mode 100644 examples/pumpfun_sniper_trading/Cargo.toml create mode 100644 examples/pumpfun_sniper_trading/src/main.rs create mode 100644 examples/pumpswap_trading/Cargo.toml create mode 100644 examples/pumpswap_trading/src/main.rs create mode 100644 examples/raydium_amm_v4_trading/Cargo.toml create mode 100644 examples/raydium_amm_v4_trading/src/main.rs create mode 100644 examples/raydium_cpmm_trading/Cargo.toml create mode 100644 examples/raydium_cpmm_trading/src/main.rs create mode 100644 examples/trading_client/Cargo.toml create mode 100644 examples/trading_client/src/main.rs diff --git a/.gitignore b/.gitignore index 758bff4..d66b15d 100755 --- a/.gitignore +++ b/.gitignore @@ -24,4 +24,3 @@ Cargo.lock tmp_*.rs tmp_*.log -examples/ \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 0f53f3d..b5d200d 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sol-trade-sdk" -version = "0.5.0" +version = "0.5.1" edition = "2021" authors = [ "William ", @@ -13,11 +13,26 @@ license = "MIT" keywords = ["solana", "memecoins", "pumpfun", "pumpswap", "raydium"] readme = "README.md" + +[workspace] +members = [ + "examples/trading_client", + "examples/event_subscription", + "examples/middleware_system", + "examples/pumpfun_copy_trading", + "examples/pumpfun_sniper_trading", + "examples/pumpswap_trading", + "examples/bonk_sniper_trading", + "examples/bonk_copy_trading", + "examples/raydium_cpmm_trading", + "examples/raydium_amm_v4_trading", +] + [lib] crate-type = ["cdylib", "rlib"] [dependencies] -solana-streamer-sdk = "0.3.9" +solana-streamer-sdk = "0.3.10" solana-sdk = "2.3.0" solana-client = "2.3.6" solana-program = "2.3.0" diff --git a/README.md b/README.md old mode 100755 new mode 100644 index 3583f14..39478e2 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Sol Trade SDK [中文](https://github.com/0xfnzero/sol-trade-sdk/blob/main/README_CN.md) | [English](https://github.com/0xfnzero/sol-trade-sdk/blob/main/README.md) | [Telegram](https://t.me/fnzero_group) -A comprehensive Rust SDK for seamless interaction with Solana DEX trading programs. This SDK provides a robust set of tools and interfaces to integrate PumpFun, PumpSwap, Bonk, and Raydium CPMM functionality into your applications. +A comprehensive Rust SDK for seamless interaction with Solana DEX trading programs. This SDK provides a robust set of tools and interfaces to integrate PumpFun, PumpSwap, and Bonk functionality into your applications. ## Project Features @@ -33,14 +33,14 @@ Add the dependency to your `Cargo.toml`: ```toml # Add to your Cargo.toml -sol-trade-sdk = { path = "./sol-trade-sdk", version = "0.5.0" } +sol-trade-sdk = { path = "./sol-trade-sdk", version = "0.5.1" } ``` ### Use crates.io ```toml # Add to your Cargo.toml -sol-trade-sdk = "0.5.0" +sol-trade-sdk = "0.5.1" ``` ## Usage Examples @@ -71,190 +71,18 @@ The `lookup_table_key` parameter is an optional `Pubkey` that specifies an addre - Improves transaction success rate and speed - Particularly useful for complex transactions with many account references +#### About ShredStream + +When using shred to subscribe to events, due to the nature of shreds, you cannot get complete information about transaction events. +Please ensure that the parameters your trading logic depends on are available in shreds when using them. + ### 1. Event Subscription - Monitor Token Trading -#### 1.1 Subscribe to Events Using Yellowstone gRPC +See the example code in [examples/event_subscription](https://github.com/0xfnzero/sol-trade-sdk/tree/main/examples/event_subscription/src/main.rs). -```rust -use sol_trade_sdk::solana_streamer_sdk::{ - streaming::{ - event_parser::{ - protocols::{ - bonk::{BonkPoolCreateEvent, BonkTradeEvent}, - pumpfun::{PumpFunCreateTokenEvent, PumpFunTradeEvent}, - pumpswap::{ - PumpSwapBuyEvent, PumpSwapCreatePoolEvent, PumpSwapDepositEvent, - PumpSwapSellEvent, PumpSwapWithdrawEvent, - }, - raydium_cpmm::RaydiumCpmmSwapEvent, - }, - Protocol, UnifiedEvent, - }, - yellowstone_grpc::{AccountFilter, TransactionFilter}, - YellowstoneGrpc, - }, - match_event, -}; - -use solana_streamer_sdk::streaming::event_parser::protocols::{ - bonk::parser::BONK_PROGRAM_ID, - pumpfun::parser::PUMPFUN_PROGRAM_ID, - pumpswap::parser::PUMPSWAP_PROGRAM_ID, - raydium_amm_v4::parser::RAYDIUM_AMM_V4_PROGRAM_ID, - raydium_clmm::parser::RAYDIUM_CLMM_PROGRAM_ID, - raydium_cpmm::parser::RAYDIUM_CPMM_PROGRAM_ID -}; - -async fn test_grpc() -> Result<(), Box> { - // Subscribe to events using GRPC client - println!("Subscribing to GRPC events..."); - - let grpc = YellowstoneGrpc::new( - "https://solana-yellowstone-grpc.publicnode.com:443".to_string(), - None, - )?; - - // Define callback function to handle events - let callback = |event: Box| { - match_event!(event, { - BonkPoolCreateEvent => |e: BonkPoolCreateEvent| { - println!("BonkPoolCreateEvent: {:?}", e.base_mint_param.symbol); - }, - BonkTradeEvent => |e: BonkTradeEvent| { - println!("BonkTradeEvent: {:?}", e); - }, - PumpFunTradeEvent => |e: PumpFunTradeEvent| { - println!("PumpFunTradeEvent: {:?}", e); - }, - PumpFunCreateTokenEvent => |e: PumpFunCreateTokenEvent| { - println!("PumpFunCreateTokenEvent: {:?}", e); - }, - PumpSwapBuyEvent => |e: PumpSwapBuyEvent| { - println!("Buy event: {:?}", e); - }, - PumpSwapSellEvent => |e: PumpSwapSellEvent| { - println!("Sell event: {:?}", e); - }, - PumpSwapCreatePoolEvent => |e: PumpSwapCreatePoolEvent| { - println!("CreatePool event: {:?}", e); - }, - PumpSwapDepositEvent => |e: PumpSwapDepositEvent| { - println!("Deposit event: {:?}", e); - }, - PumpSwapWithdrawEvent => |e: PumpSwapWithdrawEvent| { - println!("Withdraw event: {:?}", e); - }, - RaydiumCpmmSwapEvent => |e: RaydiumCpmmSwapEvent| { - println!("Raydium CPMM Swap event: {:?}", e); - }, - // ..... - // For more events and documentation, please refer to https://github.com/0xfnzero/solana-streamer - }); - }; - - // Subscribe to events from multiple protocols - println!("Starting to listen for events, press Ctrl+C to stop..."); - let protocols = vec![Protocol::PumpFun, Protocol::PumpSwap, Protocol::Bonk, Protocol::RaydiumCpmm]; - - // Filter accounts - let account_include = vec![ - PUMPFUN_PROGRAM_ID.to_string(), // Listen to pumpfun program ID - PUMPSWAP_PROGRAM_ID.to_string(), // Listen to pumpswap program ID - BONK_PROGRAM_ID.to_string(), // Listen to bonk program ID - RAYDIUM_CPMM_PROGRAM_ID.to_string(), // Listen to raydium_cpmm program ID - RAYDIUM_CLMM_PROGRAM_ID.to_string(), // Listen to raydium_clmm program ID - RAYDIUM_AMM_V4_PROGRAM_ID.to_string(), // Listen to raydium_amm_v4 program ID - "xxxxxxxx".to_string(), // Listen to xxxxx account - ]; - let account_exclude = vec![]; - let account_required = vec![]; - - // Transaction filter for monitoring transaction data - let transaction_filter = TransactionFilter { - account_include: account_include.clone(), - account_exclude, - account_required, - }; - - // Account filter for monitoring account data owned by programs - let account_filter = AccountFilter { - account: vec![], - owner: account_include.clone() - }; - - grpc.subscribe_events_immediate( - protocols, - None, - transaction_filter, - account_filter, - None, - None, - callback, - ) - .await?; - - Ok(()) -} -``` - -#### 1.2 Subscribe to Events Using ShredStream - -```rust -use sol_trade_sdk::solana_streamer_sdk::streaming::ShredStreamGrpc; - -async fn test_shreds() -> Result<(), Box> { - // Subscribe to events using ShredStream client - println!("Subscribing to ShredStream events..."); - - let shred_stream = ShredStreamGrpc::new("http://127.0.0.1:10800".to_string()).await?; - - // Define callback function to handle events (same as above) - let callback = |event: Box| { - match_event!(event, { - BonkPoolCreateEvent => |e: BonkPoolCreateEvent| { - println!("BonkPoolCreateEvent: {:?}", e.base_mint_param.symbol); - }, - BonkTradeEvent => |e: BonkTradeEvent| { - println!("BonkTradeEvent: {:?}", e); - }, - PumpFunTradeEvent => |e: PumpFunTradeEvent| { - println!("PumpFunTradeEvent: {:?}", e); - }, - PumpFunCreateTokenEvent => |e: PumpFunCreateTokenEvent| { - println!("PumpFunCreateTokenEvent: {:?}", e); - }, - PumpSwapBuyEvent => |e: PumpSwapBuyEvent| { - println!("Buy event: {:?}", e); - }, - PumpSwapSellEvent => |e: PumpSwapSellEvent| { - println!("Sell event: {:?}", e); - }, - PumpSwapCreatePoolEvent => |e: PumpSwapCreatePoolEvent| { - println!("CreatePool event: {:?}", e); - }, - PumpSwapDepositEvent => |e: PumpSwapDepositEvent| { - println!("Deposit event: {:?}", e); - }, - PumpSwapWithdrawEvent => |e: PumpSwapWithdrawEvent| { - println!("Withdraw event: {:?}", e); - }, - RaydiumCpmmSwapEvent => |e: RaydiumCpmmSwapEvent| { - println!("Raydium CPMM Swap event: {:?}", e); - }, - // ..... - // For more events and documentation, please refer to https://github.com/0xfnzero/solana-streamer - }); - }; - - // Subscribe to events - println!("Starting to listen for events, press Ctrl+C to stop..."); - let protocols = vec![Protocol::PumpFun, Protocol::PumpSwap, Protocol::Bonk, Protocol::RaydiumCpmm]; - shred_stream - .shredstream_subscribe(protocols, None, None, callback) - .await?; - - Ok(()) -} +Run the example code: +```bash +cargo run --package event_subscription ``` ### 2. Initialize SolanaTrade Instance @@ -269,603 +97,97 @@ When configuring SWQOS services, note the different parameter requirements for e - **ZeroSlot**: The first parameter is API Token - **Temporal**: The first parameter is API Token - **FlashBlock**: The first parameter is API Token, Add the official TG support at https://t.me/FlashBlock_Official to get a free key and instantly accelerate your trades! Official docs: https://doc.flashblock.trade/ -- **Node1**: The first parameter is API Token, Add the official TG support at https://t.me/node1_me - to get a free key and instantly accelerate your trades! Official docs: https://node1.me/docs.html +- **Node1**: The first parameter is API Token, Add the official TG support at https://t.me/node1_me to get a free key and instantly accelerate your trades! Official docs: https://node1.me/docs.html -```rust -use std::{str::FromStr, sync::Arc}; -use sol_trade_sdk::{ - common::{AnyResult, PriorityFee, TradeConfig}, - swqos::{SwqosConfig, SwqosRegion}, - SolanaTrade -}; -use solana_sdk::{commitment_config::CommitmentConfig, pubkey::Pubkey, signature::Keypair}; +When using multiple MEV services, you need to use `Durable Nonce`. You need to initialize a `NonceCache` class (or write your own nonce management class), get the latest `nonce` value, and use it as the `blockhash` when trading. -/// Example of creating a SolanaTrade client -async fn test_create_solana_trade_client() -> AnyResult { - println!("Creating SolanaTrade client..."); +#### 2.2 Creating SolanaTrade Instance - let payer = Keypair::new(); - let rpc_url = "https://mainnet.helius-rpc.com/?api-key=xxxxxx".to_string(); +See the example code in [examples/trading_client](https://github.com/0xfnzero/sol-trade-sdk/tree/main/examples/trading_client/src/main.rs). - // Configure various SWQOS services - let swqos_configs = vec![ - // First parameter is UUID, pass empty string if no UUID - SwqosConfig::Jito("your uuid".to_string(), SwqosRegion::Frankfurt), - SwqosConfig::NextBlock("your api_token".to_string(), SwqosRegion::Frankfurt), - SwqosConfig::Bloxroute("your api_token".to_string(), SwqosRegion::Frankfurt), - SwqosConfig::ZeroSlot("your api_token".to_string(), SwqosRegion::Frankfurt), - SwqosConfig::Temporal("your api_token".to_string(), SwqosRegion::Frankfurt), - // Add tg official customer https://t.me/FlashBlock_Official to get free FlashBlock key - SwqosConfig::FlashBlock("your api_token".to_string(), SwqosRegion::Frankfurt), - // Add tg official customer https://t.me/node1_me to get free Node1 key - SwqosConfig::Node1("your api_token".to_string(), SwqosRegion::Frankfurt), - SwqosConfig::Default(rpc_url.clone()), - ]; - - // Define trading configuration - let trade_config = TradeConfig { - rpc_url: rpc_url.clone(), - commitment: CommitmentConfig::confirmed(), - priority_fee: PriorityFee::default(), - swqos_configs, - lookup_table_key: None, - }; - - let solana_trade_client = SolanaTrade::new(Arc::new(payer), trade_config).await; - println!("SolanaTrade client created successfully!"); - - Ok(solana_trade_client) -} +Run the example code: +```bash +cargo run --package trading_client ``` ### 3. PumpFun Trading Operations -```rust -use sol_trade_sdk::{ - common::bonding_curve::BondingCurveAccount, - constants::pumpfun::global_constants::TOKEN_TOTAL_SUPPLY, - trading::{core::params::PumpFunParams, factory::DexType}, -}; +#### 3.1 Sniping -// pumpfun sniper trade -async fn test_pumpfun_sniper_trade_with_shreds(trade_info: PumpFunTradeEvent) -> AnyResult<()> { - println!("Testing PumpFun trading..."); +See the example code in [examples/pumpfun_sniper_trading](https://github.com/0xfnzero/sol-trade-sdk/tree/main/examples/pumpfun_sniper_trading/src/main.rs). - // if not dev trade, return - if !trade_info.is_dev_create_token_trade { - return Ok(()); - } +Run the example code: +```bash +cargo run --package pumpfun_sniper_trading +``` - let trade_client = test_create_solana_trade_client().await?; +#### 3.2 Copy Trading - let mint_pubkey = trade_info.mint; - let creator = trade_info.creator; - let dev_sol_amount = trade_info.max_sol_cost; - let dev_token_amount = trade_info.token_amount; - let slippage_basis_points = Some(100); - let recent_blockhash = trade_client.rpc.get_latest_blockhash().await?; - - println!("Buying tokens from PumpFun..."); - - // my trade cost sol amount - let buy_sol_amount = 100_000; - trade_client.buy( - DexType::PumpFun, - mint_pubkey, - Some(creator), - buy_sol_amount, - slippage_basis_points, - recent_blockhash, - None, - Box::new(PumpFunParams::from_dev_trade( - &mint_pubkey, - dev_token_amount, - dev_sol_amount, - creator, - None, - )), - None, - ) - .await?; +See the example code in [examples/pumpfun_copy_trading](https://github.com/0xfnzero/sol-trade-sdk/tree/main/examples/pumpfun_copy_trading/src/main.rs). - Ok(()) -} - -// pumpfun copy trade -async fn test_pumpfun_copy_trade_with_grpc(trade_info: PumpFunTradeEvent) -> AnyResult<()> { - println!("Testing PumpFun trading..."); - - let trade_client = test_create_solana_trade_client().await?; - - let mint_pubkey = trade_info.mint; - let creator = trade_info.creator; - let slippage_basis_points = Some(100); - let recent_blockhash = trade_client.rpc.get_latest_blockhash().await?; - - println!("Buying tokens from PumpFun..."); - - // my trade cost sol amount - let buy_sol_amount = 100_000; - - trade_client.buy( - DexType::PumpFun, - mint_pubkey, - Some(creator), - buy_sol_amount, - slippage_basis_points, - recent_blockhash, - None, - Box::new(PumpFunParams::from_trade(&trade_info, None)), - None, - ) - .await?; - - Ok(()) -} - -// pumpfun sell token -async fn test_pumpfun_sell() -> AnyResult<()> { - let amount_token = 100_000_000; - trade_client.sell( - DexType::PumpFun, - mint_pubkey, - Some(creator), - amount_token, - slippage_basis_points, - recent_blockhash, - None, - false, - None, - None, - ) - .await?; -} +Run the example code: +```bash +cargo run --package pumpfun_copy_trading ``` ### 4. PumpSwap Trading Operations -```rust -use sol_trade_sdk::trading::core::params::PumpSwapParams; +See the example code in [examples/pumpswap_trading](https://github.com/0xfnzero/sol-trade-sdk/tree/main/examples/pumpswap_trading/src/main.rs). -async fn test_pumpswap() -> AnyResult<()> { - println!("Testing PumpSwap trading..."); - - let client = test_create_solana_trade_client().await?; - let creator = Pubkey::from_str("11111111111111111111111111111111")?; - let mint_pubkey = Pubkey::from_str("2zMMhcVQEXDtdE6vsFS7S7D5oUodfJHE8vd1gnBouauv")?; - let buy_sol_cost = 100_000; - let slippage_basis_points = Some(100); - let recent_blockhash = client.rpc.get_latest_blockhash().await?; - let pool_address = Pubkey::from_str("xxxxxxx")?; - let base_mint = Pubkey::from_str("2zMMhcVQEXDtdE6vsFS7S7D5oUodfJHE8vd1gnBouauv")?; - let quote_mint = Pubkey::from_str("So11111111111111111111111111111111111111112")?; - let pool_base_token_reserves = 0; // Input the correct value - let pool_quote_token_reserves = 0; // Input the correct value - - // Buy tokens - println!("Buying tokens from PumpSwap..."); - client.buy( - DexType::PumpSwap, - mint_pubkey, - Some(creator), - buy_sol_cost, - slippage_basis_points, - recent_blockhash, - None, - // Through RPC call, adds latency. Can optimize by using from_buy_trade or manually initializing PumpSwapParams - Box::new(PumpSwapParams::from_pool_address_by_rpc(&client.rpc, &pool_address).await?), - None, - ).await?; - - // Sell tokens - println!("Selling tokens from PumpSwap..."); - let amount_token = 0; - client.sell( - DexType::PumpSwap, - mint_pubkey, - Some(creator), - amount_token, - slippage_basis_points, - recent_blockhash, - None, - false, - // Through RPC call, adds latency. Can optimize by using from_sell_trade or manually initializing PumpSwapParams - Box::new(PumpSwapParams::from_pool_address_by_rpc(&client.rpc, &pool_address).await?), - None, - ).await?; - - Ok(()) -} +Run the example code: +```bash +cargo run --package pumpswap_trading ``` ### 5. Raydium CPMM Trading Operations -```rust -use sol_trade_sdk::{ - trading::{ - core::params::RaydiumCpmmParams, - factory::DexType, - raydium_cpmm::common::{get_buy_token_amount, get_sell_sol_amount} - }, -}; -use spl_token; // For standard SPL Token -// use spl_token_2022; // For Token 2022 standard (if needed) +See the example code in [examples/raydium_cpmm_trading](https://github.com/0xfnzero/sol-trade-sdk/tree/main/examples/raydium_cpmm_trading/src/main.rs). -async fn test_raydium_cpmm() -> Result<(), Box> { - println!("Testing Raydium CPMM trading..."); - - let trade_client = test_create_solana_trade_client().await?; - - let mint_pubkey = Pubkey::from_str("xxxxxxxx")?; // Token address - let buy_sol_cost = 100_000; // 0.0001 SOL (in lamports) - let slippage_basis_points = Some(100); // 1% slippage - let recent_blockhash = trade_client.rpc.get_latest_blockhash().await?; - let pool_state = Pubkey::from_str("xxxxxxx")?; // Pool state address - - // Calculate expected token amount when buying - let buy_amount_out = get_buy_token_amount(&trade_client.rpc, &pool_state, buy_sol_cost).await?; - - println!("Buying tokens from Raydium CPMM..."); - trade_client.buy( - DexType::RaydiumCpmm, - mint_pubkey, - None, - buy_sol_cost, - slippage_basis_points, - recent_blockhash, - None, - // Through RPC call, adds latency, or manually initialize RaydiumCpmmParams - Box::new( - RaydiumCpmmParams::from_pool_address_by_rpc(&trade_client.rpc, &pool_state).await?, - ), - None, - ).await?; - - println!("Selling tokens from Raydium CPMM..."); - let amount_token = 100_000_000; // Token amount to sell - let sell_sol_amount = get_sell_sol_amount(&trade_client.rpc, &pool_state, amount_token).await?; - - trade_client.sell( - DexType::RaydiumCpmm, - mint_pubkey, - None, - amount_token, - slippage_basis_points, - recent_blockhash, - None, - false, - // Through RPC call, adds latency, or manually initialize RaydiumCpmmParams - Box::new( - RaydiumCpmmParams::from_pool_address_by_rpc(&trade_client.rpc, &pool_state).await?, - ), - None, - ).await?; - - Ok(()) -} +Run the example code: +```bash +cargo run --package raydium_cpmm_trading ``` ### 6. Raydium AMM V4 Trading Operations -```rust -use sol_trade_sdk::trading::core::params::RaydiumAmmV4Params; +See the example code in [examples/raydium_amm_v4_trading](https://github.com/0xfnzero/sol-trade-sdk/tree/main/examples/raydium_amm_v4_trading/src/main.rs). -async fn test_raydium_amm_v4() -> Result<(), Box> { - println!("Testing Raydium AMM V4 trading..."); - - let trade_client = test_create_solana_trade_client().await?; - - let mint_pubkey = Pubkey::from_str("xxxxxxx")?; // Token address - let buy_sol_cost = 100_000; // 0.0001 SOL (in lamports) - let slippage_basis_points = Some(100); // 1% slippage - let recent_blockhash = trade_client.rpc.get_latest_blockhash().await?; - let amm_address = Pubkey::from_str("xxxxxx")?; // AMM pool address - - println!("Buying tokens from Raydium AMM V4..."); - trade_client.buy( - DexType::RaydiumAmmV4, - mint_pubkey, - None, - buy_sol_cost, - slippage_basis_points, - recent_blockhash, - None, - // Through RPC call, adds latency, or from_amm_info_and_reserves or manually initialize RaydiumAmmV4Params - Box::new( - RaydiumAmmV4Params::from_amm_address_by_rpc(&trade_client.rpc, amm_address).await?, - ), - None, - ).await?; - - println!("Selling tokens from Raydium AMM V4..."); - let amount_token = 100_000_000; // Token amount to sell - - trade_client.sell( - DexType::RaydiumAmmV4, - mint_pubkey, - None, - amount_token, - slippage_basis_points, - recent_blockhash, - None, - false, - // Through RPC call, adds latency, or from_amm_info_and_reserves or manually initialize RaydiumAmmV4Params - Box::new( - RaydiumAmmV4Params::from_amm_address_by_rpc(&trade_client.rpc, amm_address).await?, - ), - None, - ).await?; - - Ok(()) -} +Run the example code: +```bash +cargo run --package raydium_amm_v4_trading ``` ### 7. Bonk Trading Operations -```rust +#### 7.1 Sniping -// bonk sniper trade -async fn test_bonk_sniper_trade_with_shreds(trade_info: BonkTradeEvent) -> AnyResult<()> { - println!("Testing Bonk trading..."); +See the example code in [examples/bonk_sniper_trading](https://github.com/0xfnzero/sol-trade-sdk/tree/main/examples/bonk_sniper_trading/src/main.rs). - if !trade_info.is_dev_create_token_trade { - return Ok(()); - } +Run the example code: +```bash +cargo run --package bonk_sniper_trading +``` - let trade_client = test_create_solana_trade_client().await?; - let mint_pubkey = Pubkey::from_str("xxxxxxx")?; - let buy_sol_cost = 100_000; - let slippage_basis_points = Some(100); - let recent_blockhash = trade_client.rpc.get_latest_blockhash().await?; +#### 7.2 Copy Trading - println!("Buying tokens from letsbonk.fun..."); - - // Use dev trade info to build BonkParams, can save transaction time - trade_client.buy( - DexType::Bonk, - mint_pubkey, - None, - buy_sol_cost, - slippage_basis_points, - recent_blockhash, - None, - Box::new(BonkParams::from_dev_trade(trade_info.clone())), - None, - ).await?; +See the example code in [examples/bonk_copy_trading](https://github.com/0xfnzero/sol-trade-sdk/tree/main/examples/bonk_copy_trading/src/main.rs). - println!("Selling tokens from letsbonk.fun..."); - let amount_token = 0; - trade_client.sell( - DexType::Bonk, - mint_pubkey, - None, - amount_token, - slippage_basis_points, - recent_blockhash, - None, - false, - Box::new(BonkParams::from_dev_trade(trade_info)), - None, - ).await?; - - Ok(()) -} - -// bonk copy trade -async fn test_bonk_copy_trade_with_grpc(trade_info: BonkTradeEvent) -> AnyResult<()> { - println!("Testing Bonk trading..."); - - let trade_client = test_create_solana_trade_client().await?; - let mint_pubkey = Pubkey::from_str("xxxxxxx")?; - let buy_sol_cost = 100_000; - let slippage_basis_points = Some(100); - let recent_blockhash = trade_client.rpc.get_latest_blockhash().await?; - - println!("Buying tokens from letsbonk.fun..."); - - // Use trade event info to build BonkParams, can save transaction time - trade_client.buy( - DexType::Bonk, - mint_pubkey, - None, - buy_sol_cost, - slippage_basis_points, - recent_blockhash, - None, - Box::new(BonkParams::from_trade(trade_info.clone())), - None, - ).await?; - - println!("Selling tokens from letsbonk.fun..."); - let amount_token = 0; - trade_client.sell( - DexType::Bonk, - mint_pubkey, - None, - amount_token, - slippage_basis_points, - recent_blockhash, - None, - false, - Box::new(BonkParams::from_trade(trade_info)), - None, - ).await?; - - Ok(()) -} - -// bonk regular trade -async fn test_bonk() -> Result<(), Box> { - println!("Testing Bonk trading..."); - - let trade_client = test_create_solana_trade_client().await?; - - let mint_pubkey = Pubkey::from_str("xxxxxxx")?; - let buy_sol_amount = 100_000; - let slippage_basis_points = Some(100); // 1% - let recent_blockhash = trade_client.rpc.get_latest_blockhash().await?; - - println!("Buying tokens from letsbonk.fun..."); - - trade_client.buy( - DexType::Bonk, - mint_pubkey, - None, - buy_sol_amount, - slippage_basis_points, - recent_blockhash, - None, - // Through RPC call, adds latency. Can optimize by using from_trade or manually initializing BonkParams - Box::new(BonkParams::from_mint_by_rpc(&trade_client.rpc, &mint_pubkey).await?), - None, - ) - .await?; - - println!("Selling tokens from letsbonk.fun..."); - - let amount_token = 100_000; - trade_client.sell( - DexType::Bonk, - mint_pubkey, - None, - amount_token, - slippage_basis_points, - recent_blockhash, - None, - false, - // Through RPC call, adds latency. Can optimize by using from_trade or manually initializing BonkParams - Box::new(BonkParams::from_mint_by_rpc(&trade_client.rpc, &mint_pubkey).await?), - None, - ) - .await?; - - Ok(()) -} +Run the example code: +```bash +cargo run --package bonk_copy_trading ``` ### 8. Middleware System The SDK provides a powerful middleware system that allows you to modify, add, or remove instructions before transaction execution. This gives you tremendous flexibility to customize trading behavior. -#### 8.1 Using Built-in Logging Middleware +See the example code in [examples/middleware_system](https://github.com/0xfnzero/sol-trade-sdk/tree/main/examples/middleware_system/src/main.rs). -```rust -use sol_trade_sdk::{ - trading::{ - factory::DexType, - middleware::builtin::LoggingMiddleware, - MiddlewareManager, - }, -}; - -async fn test_middleware() -> AnyResult<()> { - let mut client = test_create_solana_trade_client().await?; - - // SDK example middleware that prints instruction information - // You can reference LoggingMiddleware to implement the InstructionMiddleware trait for your own middleware - let middleware_manager = MiddlewareManager::new() - .add_middleware(Box::new(LoggingMiddleware)); - - client = client.with_middleware_manager(middleware_manager); - - let creator = Pubkey::from_str("11111111111111111111111111111111")?; - let mint_pubkey = Pubkey::from_str("xxxxx")?; - let buy_sol_cost = 100_000; - let slippage_basis_points = Some(100); - let recent_blockhash = client.rpc.get_latest_blockhash().await?; - let pool_address = Pubkey::from_str("xxxx")?; - - // Buy tokens - println!("Buying tokens from PumpSwap..."); - client - .buy( - DexType::PumpSwap, - mint_pubkey, - Some(creator), - buy_sol_cost, - slippage_basis_points, - recent_blockhash, - None, - Box::new(PumpSwapParams::from_pool_address_by_rpc(&client.rpc, &pool_address).await?), - None, - ) - .await?; - Ok(()) -} +Run the example code: +```bash +cargo run --package middleware_system ``` -#### 8.2 Creating Custom Middleware - -You can create custom middleware by implementing the `InstructionMiddleware` trait: - -```rust -use sol_trade_sdk::trading::middleware::traits::InstructionMiddleware; -use anyhow::Result; -use solana_sdk::instruction::Instruction; - -/// Custom middleware example - Add additional instructions -#[derive(Clone)] -pub struct CustomMiddleware; - -impl InstructionMiddleware for CustomMiddleware { - fn name(&self) -> &'static str { - "CustomMiddleware" - } - - fn process_protocol_instructions( - &self, - protocol_instructions: Vec, - protocol_name: String, - is_buy: bool, - ) -> Result> { - println!("Custom middleware processing, protocol: {}", protocol_name); - - // Here you can: - // 1. Modify existing instructions - // 2. Add new instructions - // 3. Remove specific instructions - - // Example: Add a custom instruction at the beginning - // let custom_instruction = create_your_custom_instruction(); - // instructions.insert(0, custom_instruction); - - Ok(protocol_instructions) - } - - fn process_full_instructions( - &self, - full_instructions: Vec, - protocol_name: String, - is_buy: bool, - ) -> Result> { - println!("Custom middleware processing, instruction count: {}", full_instructions.len()); - Ok(full_instructions) - } - - fn clone_box(&self) -> Box { - Box::new(self.clone()) - } -} - -// Using custom middleware -async fn test_custom_middleware() -> AnyResult<()> { - let mut client = test_create_solana_trade_client().await?; - - let middleware_manager = MiddlewareManager::new() - .add_middleware(Box::new(LoggingMiddleware)) // Logging middleware - .add_middleware(Box::new(CustomMiddleware)); - - client = client.with_middleware_manager(middleware_manager); - - // Now all transactions will be processed through your middleware - // ... - Ok(()) -} -``` - -#### 8.3 Middleware Execution Order - Middleware executes in the order they are added: ```rust @@ -882,8 +204,8 @@ use sol_trade_sdk::common::PriorityFee; // Custom priority fee configuration let priority_fee = PriorityFee { - unit_limit: 190000, - unit_price: 1000000, + tip_unit_limit: 190000, + tip_unit_price: 1000000, rpc_unit_limit: 500000, rpc_unit_price: 500000, buy_tip_fee: 0.001, @@ -1023,4 +345,3 @@ MIT License - [English](README.md) - [中文](README_CN.md) - diff --git a/README_CN.md b/README_CN.md index 3f751b1..dd362f3 100755 --- a/README_CN.md +++ b/README_CN.md @@ -33,19 +33,19 @@ git clone https://github.com/0xfnzero/sol-trade-sdk ```toml # 添加到您的 Cargo.toml -sol-trade-sdk = { path = "./sol-trade-sdk", version = "0.5.0" } +sol-trade-sdk = { path = "./sol-trade-sdk", version = "0.5.1" } ``` ### 使用 crates.io ```toml # 添加到您的 Cargo.toml -sol-trade-sdk = "0.5.0" +sol-trade-sdk = "0.5.1" ``` ## 使用示例 -### 重要参数说明 +### 重要说明 #### auto_handle_wsol 参数 @@ -71,190 +71,18 @@ sol-trade-sdk = "0.5.0" - 提高交易成功率和速度 - 特别适用于具有许多账户引用的复杂交易 +#### 关于shredstream + +当你使用 shred 订阅事件时,由于 shred 的特性,你无法获取到交易事件的完整信息。 +请你在使用时,确保你的交易逻辑依赖的参数,在shred中都能获取到。 + ### 1. 事件订阅 - 监听代币交易 -#### 1.1 使用 Yellowstone gRPC 订阅事件 +查看[examples/event_subscription](https://github.com/0xfnzero/sol-trade-sdk/tree/main/examples/event_subscription/src/main.rs) 中的示例代码。 -```rust -use sol_trade_sdk::solana_streamer_sdk::{ - streaming::{ - event_parser::{ - protocols::{ - bonk::{BonkPoolCreateEvent, BonkTradeEvent}, - pumpfun::{PumpFunCreateTokenEvent, PumpFunTradeEvent}, - pumpswap::{ - PumpSwapBuyEvent, PumpSwapCreatePoolEvent, PumpSwapDepositEvent, - PumpSwapSellEvent, PumpSwapWithdrawEvent, - }, - raydium_cpmm::RaydiumCpmmSwapEvent, - }, - Protocol, UnifiedEvent, - }, - yellowstone_grpc::{AccountFilter, TransactionFilter}, - YellowstoneGrpc, - }, - match_event, -}; - -use solana_streamer_sdk::streaming::event_parser::protocols::{ - bonk::parser::BONK_PROGRAM_ID, - pumpfun::parser::PUMPFUN_PROGRAM_ID, - pumpswap::parser::PUMPSWAP_PROGRAM_ID, - raydium_amm_v4::parser::RAYDIUM_AMM_V4_PROGRAM_ID, - raydium_clmm::parser::RAYDIUM_CLMM_PROGRAM_ID, - raydium_cpmm::parser::RAYDIUM_CPMM_PROGRAM_ID -}; - -async fn test_grpc() -> Result<(), Box> { - // 使用 GRPC 客户端订阅事件 - println!("正在订阅 GRPC 事件..."); - - let grpc = YellowstoneGrpc::new( - "https://solana-yellowstone-grpc.publicnode.com:443".to_string(), - None, - )?; - - // 定义回调函数处理事件 - let callback = |event: Box| { - match_event!(event, { - BonkPoolCreateEvent => |e: BonkPoolCreateEvent| { - println!("BonkPoolCreateEvent: {:?}", e.base_mint_param.symbol); - }, - BonkTradeEvent => |e: BonkTradeEvent| { - println!("BonkTradeEvent: {:?}", e); - }, - PumpFunTradeEvent => |e: PumpFunTradeEvent| { - println!("PumpFunTradeEvent: {:?}", e); - }, - PumpFunCreateTokenEvent => |e: PumpFunCreateTokenEvent| { - println!("PumpFunCreateTokenEvent: {:?}", e); - }, - PumpSwapBuyEvent => |e: PumpSwapBuyEvent| { - println!("Buy event: {:?}", e); - }, - PumpSwapSellEvent => |e: PumpSwapSellEvent| { - println!("Sell event: {:?}", e); - }, - PumpSwapCreatePoolEvent => |e: PumpSwapCreatePoolEvent| { - println!("CreatePool event: {:?}", e); - }, - PumpSwapDepositEvent => |e: PumpSwapDepositEvent| { - println!("Deposit event: {:?}", e); - }, - PumpSwapWithdrawEvent => |e: PumpSwapWithdrawEvent| { - println!("Withdraw event: {:?}", e); - }, - RaydiumCpmmSwapEvent => |e: RaydiumCpmmSwapEvent| { - println!("Raydium CPMM Swap event: {:?}", e); - }, - // ..... - // 更多的事件和说明请参考 https://github.com/0xfnzero/solana-streamer - }); - }; - - // 订阅多个协议的事件 - println!("开始监听事件,按 Ctrl+C 停止..."); - let protocols = vec![Protocol::PumpFun, Protocol::PumpSwap, Protocol::Bonk, Protocol::RaydiumCpmm]; - - // 过滤账户 - let account_include = vec![ - PUMPFUN_PROGRAM_ID.to_string(), // 监听 pumpfun 程序 ID - PUMPSWAP_PROGRAM_ID.to_string(), // 监听 pumpswap 程序 ID - BONK_PROGRAM_ID.to_string(), // 监听 bonk 程序 ID - RAYDIUM_CPMM_PROGRAM_ID.to_string(), // 监听 raydium_cpmm 程序 ID - RAYDIUM_CLMM_PROGRAM_ID.to_string(), // 监听 raydium_clmm 程序 ID - RAYDIUM_AMM_V4_PROGRAM_ID.to_string(), // 监听 raydium_amm_v4 程序 ID - "xxxxxxxx".to_string(), // 监听特定账户 - ]; - let account_exclude = vec![]; - let account_required = vec![]; - - // 监听交易数据 - let transaction_filter = TransactionFilter { - account_include: account_include.clone(), - account_exclude, - account_required, - }; - - // 监听属于owner程序的账号数据 -> 账号事件监听 - let account_filter = AccountFilter { - account: vec![], - owner: account_include.clone() - }; - - grpc.subscribe_events_immediate( - protocols, - None, - transaction_filter, - account_filter, - None, - None, - callback, - ) - .await?; - - Ok(()) -} -``` - -#### 1.2 使用 ShredStream 订阅事件 - -```rust -use sol_trade_sdk::solana_streamer_sdk::streaming::ShredStreamGrpc; - -async fn test_shreds() -> Result<(), Box> { - // 使用 ShredStream 客户端订阅事件 - println!("正在订阅 ShredStream 事件..."); - - let shred_stream = ShredStreamGrpc::new("http://127.0.0.1:10800".to_string()).await?; - - // 定义回调函数处理事件(与上面相同) - let callback = |event: Box| { - match_event!(event, { - BonkPoolCreateEvent => |e: BonkPoolCreateEvent| { - println!("BonkPoolCreateEvent: {:?}", e.base_mint_param.symbol); - }, - BonkTradeEvent => |e: BonkTradeEvent| { - println!("BonkTradeEvent: {:?}", e); - }, - PumpFunTradeEvent => |e: PumpFunTradeEvent| { - println!("PumpFunTradeEvent: {:?}", e); - }, - PumpFunCreateTokenEvent => |e: PumpFunCreateTokenEvent| { - println!("PumpFunCreateTokenEvent: {:?}", e); - }, - PumpSwapBuyEvent => |e: PumpSwapBuyEvent| { - println!("Buy event: {:?}", e); - }, - PumpSwapSellEvent => |e: PumpSwapSellEvent| { - println!("Sell event: {:?}", e); - }, - PumpSwapCreatePoolEvent => |e: PumpSwapCreatePoolEvent| { - println!("CreatePool event: {:?}", e); - }, - PumpSwapDepositEvent => |e: PumpSwapDepositEvent| { - println!("Deposit event: {:?}", e); - }, - PumpSwapWithdrawEvent => |e: PumpSwapWithdrawEvent| { - println!("Withdraw event: {:?}", e); - }, - RaydiumCpmmSwapEvent => |e: RaydiumCpmmSwapEvent| { - println!("Raydium CPMM Swap event: {:?}", e); - }, - // ..... - // 更多的事件和说明请参考 https://github.com/0xfnzero/solana-streamer - }); - }; - - // 订阅事件 - println!("开始监听事件,按 Ctrl+C 停止..."); - let protocols = vec![Protocol::PumpFun, Protocol::PumpSwap, Protocol::Bonk, Protocol::RaydiumCpmm]; - shred_stream - .shredstream_subscribe(protocols, None, None, callback) - .await?; - - Ok(()) -} +运行示例代码: +```bash +cargo run --package event_subscription ``` ### 2. 初始化 SolanaTrade 实例 @@ -271,620 +99,96 @@ async fn test_shreds() -> Result<(), Box> { - **FlashBlock**: 第一个参数是 API Token, 添加tg官方客服https://t.me/FlashBlock_Official 获取免费key立即加速你的交易!官方文档: https://doc.flashblock.trade/ - **Node1**: 第一个参数是 API Token, 添加tg官方客服https://t.me/node1_me 获取免费key立即加速你的交易!官方文档: https://node1.me/docs.html -```rust -use std::{str::FromStr, sync::Arc}; -use sol_trade_sdk::{ - common::{AnyResult, PriorityFee, TradeConfig}, - swqos::{SwqosConfig, SwqosRegion}, - SolanaTrade -}; -use solana_sdk::{commitment_config::CommitmentConfig, pubkey::Pubkey, signature::Keypair}; +当使用多个MEV服务时,需要使用`Durable Nonce`。你需要初始化`NonceCache`类(或者自行写一个管理nonce的类),获取最新的`nonce`值,并在交易的时候作为`blockhash`使用。 -/// 创建 SolanaTrade 客户端的示例 -async fn test_create_solana_trade_client() -> AnyResult { - println!("Creating SolanaTrade client..."); +#### 2.2 创建 SolanaTrade 实例 - let payer = Keypair::new(); - let rpc_url = "https://mainnet.helius-rpc.com/?api-key=xxxxxx".to_string(); +查看[examples/trading_client](https://github.com/0xfnzero/sol-trade-sdk/tree/main/examples/trading_client/src/main.rs) 中的示例代码。 - // 配置各种 SWQOS 服务 - let swqos_configs = vec![ - // 第一个参数是 uuid,如果没有 uuid 则传空字符串 - SwqosConfig::Jito("your uuid".to_string(), SwqosRegion::Frankfurt), - SwqosConfig::NextBlock("your api_token".to_string(), SwqosRegion::Frankfurt), - SwqosConfig::Bloxroute("your api_token".to_string(), SwqosRegion::Frankfurt), - SwqosConfig::ZeroSlot("your api_token".to_string(), SwqosRegion::Frankfurt), - SwqosConfig::Temporal("your api_token".to_string(), SwqosRegion::Frankfurt), - // 添加tg官方客服 https://t.me/FlashBlock_Official 获取免费 FlashBlock key - SwqosConfig::FlashBlock("your api_token".to_string(), SwqosRegion::Frankfurt), - // 添加tg官方客服 https://t.me/node1_me 获取免费 Node1 key - SwqosConfig::Node1("your api_token".to_string(), SwqosRegion::Frankfurt), - SwqosConfig::Default(rpc_url.clone()), - ]; - - // 定义交易配置 - let trade_config = TradeConfig { - rpc_url: rpc_url.clone(), - commitment: CommitmentConfig::confirmed(), - priority_fee: PriorityFee::default(), - swqos_configs, - lookup_table_key: None, - }; - - let solana_trade_client = SolanaTrade::new(Arc::new(payer), trade_config).await; - println!("SolanaTrade client created successfully!"); - - Ok(solana_trade_client) -} +运行示例代码: +```bash +cargo run --package trading_client ``` ### 3. PumpFun 交易操作 -```rust -use sol_trade_sdk::{ - common::{bonding_curve::BondingCurveAccount, AnyResult}, - constants::pumpfun::global_constants::TOKEN_TOTAL_SUPPLY, - trading::{core::params::PumpFunParams, factory::DexType}, -}; -use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::protocols::pumpfun::PumpFunTradeEvent; +#### 3.1 狙击 -// pumpfun 狙击者交易 -async fn test_pumpfun_sniper_trade_with_shreds(trade_info: PumpFunTradeEvent) -> AnyResult<()> { - println!("Testing PumpFun trading..."); +查看[examples/pumpfun_sniper_trading](https://github.com/0xfnzero/sol-trade-sdk/tree/main/examples/pumpfun_sniper_trading/src/main.rs) 中的示例代码。 - // 如果不是开发者购买,则返回 - if !trade_info.is_dev_create_token_trade { - return Ok(()); - } +运行示例代码: +```bash +cargo run --package pumpfun_sniper_trading +``` - let trade_client = test_create_solana_trade_client().await?; +#### 3.2 跟单 - let mint_pubkey = trade_info.mint; - let creator = trade_info.creator; - let dev_sol_amount = trade_info.max_sol_cost; - let dev_token_amount = trade_info.token_amount; - let slippage_basis_points = Some(100); - let recent_blockhash = trade_client.rpc.get_latest_blockhash().await?; - - println!("Buying tokens from PumpFun..."); - - // 我本次交易所花的的sol金额 - let buy_sol_amount = 100_000; - trade_client.buy( - DexType::PumpFun, - mint_pubkey, - Some(creator), - buy_sol_amount, - slippage_basis_points, - recent_blockhash, - None, - Box::new(PumpFunParams::from_dev_trade( - &mint_pubkey, - dev_token_amount, - dev_sol_amount, - creator, - None, - )), - None, - ) - .await?; +查看[examples/pumpfun_copy_trading](https://github.com/0xfnzero/sol-trade-sdk/tree/main/examples/pumpfun_copy_trading/src/main.rs) 中的示例代码。 - Ok(()) -} - -// pumpfun 跟单交易 -async fn test_pumpfun_copy_trade_with_grpc(trade_info: PumpFunTradeEvent) -> AnyResult<()> { - println!("Testing PumpFun trading..."); - - let trade_client = test_create_solana_trade_client().await?; - - let mint_pubkey = trade_info.mint; - let creator = trade_info.creator; - let slippage_basis_points = Some(100); - let recent_blockhash = trade_client.rpc.get_latest_blockhash().await?; - - println!("Buying tokens from PumpFun..."); - - // 我本次交易所花的的sol金额 - let buy_sol_amount = 100_000; - - trade_client.buy( - DexType::PumpFun, - mint_pubkey, - Some(creator), - buy_sol_amount, - slippage_basis_points, - recent_blockhash, - None, - Box::new(PumpFunParams::from_trade(&trade_info, None)), - None, - ) - .await?; - - Ok(()) -} - -// pumpfun 卖出token -async fn test_pumpfun_sell(trade_info: PumpFunTradeEvent) -> AnyResult<()> { - let trade_client = test_create_solana_trade_client().await?; - let mint_pubkey = trade_info.mint; - let creator = trade_info.creator; - let slippage_basis_points = Some(100); - let recent_blockhash = trade_client.rpc.get_latest_blockhash().await?; - - let amount_token = 100_000_000; - trade_client.sell( - DexType::PumpFun, - mint_pubkey, - Some(creator), - amount_token, - slippage_basis_points, - recent_blockhash, - None, - false, - Box::new(PumpFunParams::from_trade(&trade_info, None)), - None, - ) - .await?; - - Ok(()) -} +运行示例代码: +```bash +cargo run --package pumpfun_copy_trading ``` ### 4. PumpSwap 交易操作 -```rust -use sol_trade_sdk::{ - common::AnyResult, - trading::{core::params::PumpSwapParams, factory::DexType}, -}; -use solana_sdk::{pubkey::Pubkey, str::FromStr}; +查看[examples/pumpswap_trading](https://github.com/0xfnzero/sol-trade-sdk/tree/main/examples/pumpswap_trading/src/main.rs) 中的示例代码。 -async fn test_pumpswap() -> AnyResult<()> { - println!("Testing PumpSwap trading..."); - - let client = test_create_solana_trade_client().await?; - let creator = Pubkey::from_str("11111111111111111111111111111111")?; - let mint_pubkey = Pubkey::from_str("2zMMhcVQEXDtdE6vsFS7S7D5oUodfJHE8vd1gnBouauv")?; - let buy_sol_cost = 100_000; - let slippage_basis_points = Some(100); - let recent_blockhash = client.rpc.get_latest_blockhash().await?; - let pool_address = Pubkey::from_str("xxxxxxx")?; - let base_mint = Pubkey::from_str("2zMMhcVQEXDtdE6vsFS7S7D5oUodfJHE8vd1gnBouauv")?; - let quote_mint = Pubkey::from_str("So11111111111111111111111111111111111111112")?; - let pool_base_token_reserves = 0; // 输入正确的值 - let pool_quote_token_reserves = 0; // 输入正确的值 - - // 买入代币 - println!("Buying tokens from PumpSwap..."); - client.buy( - DexType::PumpSwap, - mint_pubkey, - Some(creator), - buy_sol_cost, - slippage_basis_points, - recent_blockhash, - None, - // 通过 RPC 调用,会增加延迟。可以通过使用 from_buy_trade 或手动初始化 PumpSwapParams 来优化 - Box::new(PumpSwapParams::from_pool_address_by_rpc(&client.rpc, &pool_address).await?), - None, - ).await?; - - // 卖出代币 - println!("Selling tokens from PumpSwap..."); - let amount_token = 0; - client.sell( - DexType::PumpSwap, - mint_pubkey, - Some(creator), - amount_token, - slippage_basis_points, - recent_blockhash, - None, - false, - // 通过 RPC 调用,会增加延迟。可以通过使用 from_sell_trade 或手动初始化 PumpSwapParams 来优化 - Box::new(PumpSwapParams::from_pool_address_by_rpc(&client.rpc, &pool_address).await?), - None, - ).await?; - - Ok(()) -} +运行示例代码: +```bash +cargo run --package pumpswap_trading ``` ### 5. Raydium CPMM 交易操作 -```rust -use sol_trade_sdk::{ - trading::{ - core::params::RaydiumCpmmParams, - factory::DexType, - raydium_cpmm::common::{get_buy_token_amount, get_sell_sol_amount} - }, -}; -use solana_sdk::{pubkey::Pubkey, str::FromStr}; -use spl_token; // 用于标准 SPL Token -// use spl_token_2022; // 用于 Token 2022 标准(如果需要) +查看[examples/raydium_cpmm_trading](https://github.com/0xfnzero/sol-trade-sdk/tree/main/examples/raydium_cpmm_trading/src/main.rs) 中的示例代码。 -async fn test_raydium_cpmm() -> Result<(), Box> { - println!("Testing Raydium CPMM trading..."); - - let trade_client = test_create_solana_trade_client().await?; - - let mint_pubkey = Pubkey::from_str("xxxxxxxx")?; // 代币地址 - let buy_sol_cost = 100_000; // 0.0001 SOL(以lamports为单位) - let slippage_basis_points = Some(100); // 1% 滑点 - let recent_blockhash = trade_client.rpc.get_latest_blockhash().await?; - let pool_state = Pubkey::from_str("xxxxxxx")?; // 池状态地址 - - // 计算买入时预期获得的代币数量 - let buy_amount_out = get_buy_token_amount(&trade_client.rpc, &pool_state, buy_sol_cost).await?; - - println!("Buying tokens from Raydium CPMM..."); - trade_client.buy( - DexType::RaydiumCpmm, - mint_pubkey, - None, - buy_sol_cost, - slippage_basis_points, - recent_blockhash, - None, - // 通过 RPC 调用,会增加延迟,或手动初始化 RaydiumCpmmParams - Box::new( - RaydiumCpmmParams::from_pool_address_by_rpc(&trade_client.rpc, &pool_state).await?, - ), - None, - ).await?; - - println!("Selling tokens from Raydium CPMM..."); - let amount_token = 100_000_000; // 卖出代币数量 - let sell_sol_amount = get_sell_sol_amount(&trade_client.rpc, &pool_state, amount_token).await?; - - trade_client.sell( - DexType::RaydiumCpmm, - mint_pubkey, - None, - amount_token, - slippage_basis_points, - recent_blockhash, - None, - false, - // 通过 RPC 调用,会增加延迟,或手动初始化 RaydiumCpmmParams - Box::new( - RaydiumCpmmParams::from_pool_address_by_rpc(&trade_client.rpc, &pool_state).await?, - ), - None, - ).await?; - - Ok(()) -} +运行示例代码: +```bash +cargo run --package raydium_cpmm_trading ``` + ### 6. Raydium AMM V4 交易操作 -```rust -use sol_trade_sdk::trading::core::params::RaydiumAmmV4Params; +查看[examples/raydium_amm_v4_trading](https://github.com/0xfnzero/sol-trade-sdk/tree/main/examples/raydium_amm_v4_trading/src/main.rs) 中的示例代码。 -async fn test_raydium_amm_v4() -> Result<(), Box> { - println!("Testing Raydium AMM V4 trading..."); - - let trade_client = test_create_solana_trade_client().await?; - - let mint_pubkey = Pubkey::from_str("xxxxxxx")?; // 代币地址 - let buy_sol_cost = 100_000; // 0.0001 SOL(以lamports为单位) - let slippage_basis_points = Some(100); // 1% 滑点 - let recent_blockhash = trade_client.rpc.get_latest_blockhash().await?; - let amm_address = Pubkey::from_str("xxxxxx")?; // AMM 池地址 - - println!("Buying tokens from Raydium AMM V4..."); - trade_client.buy( - DexType::RaydiumAmmV4, - mint_pubkey, - None, - buy_sol_cost, - slippage_basis_points, - recent_blockhash, - None, - // 通过 RPC 调用,会增加延迟,或使用 from_amm_info_and_reserves 或手动初始化 RaydiumAmmV4Params - Box::new( - RaydiumAmmV4Params::from_amm_address_by_rpc(&trade_client.rpc, amm_address).await?, - ), - None, - ).await?; - - println!("Selling tokens from Raydium AMM V4..."); - let amount_token = 100_000_000; // 卖出代币数量 - - trade_client.sell( - DexType::RaydiumAmmV4, - mint_pubkey, - None, - amount_token, - slippage_basis_points, - recent_blockhash, - None, - false, - // 通过 RPC 调用,会增加延迟,或使用 from_amm_info_and_reserves 或手动初始化 RaydiumAmmV4Params - Box::new( - RaydiumAmmV4Params::from_amm_address_by_rpc(&trade_client.rpc, amm_address).await?, - ), - None, - ).await?; - - Ok(()) -} +运行示例代码: +```bash +cargo run --package raydium_amm_v4_trading ``` ### 7. Bonk 交易操作 -```rust -use sol_trade_sdk::{ - common::AnyResult, - trading::{core::params::BonkParams, factory::DexType}, -}; -use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::protocols::bonk::BonkTradeEvent; -use solana_sdk::{pubkey::Pubkey, str::FromStr}; +#### 7.1 狙击 -// bonk 狙击者交易 -async fn test_bonk_sniper_trade_with_shreds(trade_info: BonkTradeEvent) -> AnyResult<()> { - println!("Testing Bonk trading..."); +查看[examples/bonk_sniper_trading](https://github.com/0xfnzero/sol-trade-sdk/tree/main/examples/bonk_sniper_trading/src/main.rs) 中的示例代码。 - if !trade_info.is_dev_create_token_trade { - return Ok(()); - } +运行示例代码: +```bash +cargo run --package bonk_sniper_trading +``` - let trade_client = test_create_solana_trade_client().await?; - let mint_pubkey = Pubkey::from_str("xxxxxxx")?; - let buy_sol_cost = 100_000; - let slippage_basis_points = Some(100); - let recent_blockhash = trade_client.rpc.get_latest_blockhash().await?; +#### 7.2 跟单 - println!("Buying tokens from letsbonk.fun..."); - - // 使用开发者交易信息构建 BonkParams,可以节约交易时间 - trade_client.buy( - DexType::Bonk, - mint_pubkey, - None, - buy_sol_cost, - slippage_basis_points, - recent_blockhash, - None, - Box::new(BonkParams::from_dev_trade(trade_info.clone())), - None, - ).await?; +查看[examples/bonk_copy_trading](https://github.com/0xfnzero/sol-trade-sdk/tree/main/examples/bonk_copy_trading/src/main.rs) 中的示例代码。 - println!("Selling tokens from letsbonk.fun..."); - let amount_token = 0; - trade_client.sell( - DexType::Bonk, - mint_pubkey, - None, - amount_token, - slippage_basis_points, - recent_blockhash, - None, - false, - Box::new(BonkParams::from_dev_trade(trade_info)), - None, - ).await?; - - Ok(()) -} - -// bonk 跟单交易 -async fn test_bonk_copy_trade_with_grpc(trade_info: BonkTradeEvent) -> AnyResult<()> { - println!("Testing Bonk trading..."); - - let trade_client = test_create_solana_trade_client().await?; - let mint_pubkey = Pubkey::from_str("xxxxxxx")?; - let buy_sol_cost = 100_000; - let slippage_basis_points = Some(100); - let recent_blockhash = trade_client.rpc.get_latest_blockhash().await?; - - println!("Buying tokens from letsbonk.fun..."); - - // 使用交易事件信息构建 BonkParams,可以节约交易时间 - trade_client.buy( - DexType::Bonk, - mint_pubkey, - None, - buy_sol_cost, - slippage_basis_points, - recent_blockhash, - None, - Box::new(BonkParams::from_trade(trade_info.clone())), - None, - ).await?; - - println!("Selling tokens from letsbonk.fun..."); - let amount_token = 0; - trade_client.sell( - DexType::Bonk, - mint_pubkey, - None, - amount_token, - slippage_basis_points, - recent_blockhash, - None, - false, - Box::new(BonkParams::from_trade(trade_info)), - None, - ).await?; - - Ok(()) -} - -// bonk 普通交易 -async fn test_bonk() -> Result<(), Box> { - println!("Testing Bonk trading..."); - - let trade_client = test_create_solana_trade_client().await?; - - let mint_pubkey = Pubkey::from_str("xxxxxxx")?; - let buy_sol_amount = 100_000; - let slippage_basis_points = Some(100); // 1% - let recent_blockhash = trade_client.rpc.get_latest_blockhash().await?; - - println!("Buying tokens from letsbonk.fun..."); - - trade_client.buy( - DexType::Bonk, - mint_pubkey, - None, - buy_sol_amount, - slippage_basis_points, - recent_blockhash, - None, - // 通过 RPC 调用,会增加延迟。可以通过使用 from_trade 或手动初始化 BonkParams 来优化 - Box::new(BonkParams::from_mint_by_rpc(&trade_client.rpc, &mint_pubkey).await?), - None, - ) - .await?; - - println!("Selling tokens from letsbonk.fun..."); - - let amount_token = 100_000; - trade_client.sell( - DexType::Bonk, - mint_pubkey, - None, - amount_token, - slippage_basis_points, - recent_blockhash, - None, - false, - // 通过 RPC 调用,会增加延迟。可以通过使用 from_trade 或手动初始化 BonkParams 来优化 - Box::new(BonkParams::from_mint_by_rpc(&trade_client.rpc, &mint_pubkey).await?), - None, - ) - .await?; - - Ok(()) -} +运行示例代码: +```bash +cargo run --package bonk_copy_trading ``` ### 8. 中间件系统 SDK 提供了强大的中间件系统,允许您在交易执行前对指令进行修改、添加或移除。这为您提供了极大的灵活性来自定义交易行为。 -#### 8.1 使用内置的日志中间件 +查看[examples/middleware_system](https://github.com/0xfnzero/sol-trade-sdk/tree/main/examples/middleware_system/src/main.rs) 中的示例代码。 -```rust -use sol_trade_sdk::{ - trading::{ - factory::DexType, - middleware::builtin::LoggingMiddleware, - MiddlewareManager, - }, -}; - -async fn test_middleware() -> AnyResult<()> { - let mut client = test_create_solana_trade_client().await?; - - // SDK 内置的示例中间件,打印指令信息 - // 您可以参考 LoggingMiddleware 来实现 InstructionMiddleware trait 来实现自己的中间件 - let middleware_manager = MiddlewareManager::new() - .add_middleware(Box::new(LoggingMiddleware)); - - client = client.with_middleware_manager(middleware_manager); - - let creator = Pubkey::from_str("11111111111111111111111111111111")?; - let mint_pubkey = Pubkey::from_str("xxxxx")?; - let buy_sol_cost = 100_000; - let slippage_basis_points = Some(100); - let recent_blockhash = client.rpc.get_latest_blockhash().await?; - let pool_address = Pubkey::from_str("xxxx")?; - - // 购买代币 - println!("Buying tokens from PumpSwap..."); - client - .buy( - DexType::PumpSwap, - mint_pubkey, - Some(creator), - buy_sol_cost, - slippage_basis_points, - recent_blockhash, - None, - Box::new(PumpSwapParams::from_pool_address_by_rpc(&client.rpc, &pool_address).await?), - None, - ) - .await?; - Ok(()) -} +运行示例代码: +```bash +cargo run --package middleware_system ``` -#### 8.2 创建自定义中间件 - -您可以通过实现 `InstructionMiddleware` trait 来创建自定义中间件: - -```rust -use sol_trade_sdk::trading::middleware::traits::InstructionMiddleware; -use anyhow::Result; -use solana_sdk::instruction::Instruction; - -/// 自定义中间件示例 - 添加额外指令 -#[derive(Clone)] -pub struct CustomMiddleware; - -impl InstructionMiddleware for CustomMiddleware { - fn name(&self) -> &'static str { - "CustomMiddleware" - } - - fn process_protocol_instructions( - &self, - protocol_instructions: Vec, - protocol_name: String, - is_buy: bool, - ) -> Result> { - println!("自定义中间件处理中,协议: {}", protocol_name); - - // 在这里您可以: - // 1. 修改现有指令 - // 2. 添加新指令 - // 3. 移除特定指令 - - // 示例:在指令开始前添加一个自定义指令 - // let custom_instruction = create_your_custom_instruction(); - // instructions.insert(0, custom_instruction); - - Ok(protocol_instructions) - } - - fn process_full_instructions( - &self, - full_instructions: Vec, - protocol_name: String, - is_buy: bool, - ) -> Result> { - println!("自定义中间件处理中,指令数量: {}", full_instructions.len()); - Ok(full_instructions) - } - - fn clone_box(&self) -> Box { - Box::new(self.clone()) - } -} - -// 使用自定义中间件 -async fn test_custom_middleware() -> AnyResult<()> { - let mut client = test_create_solana_trade_client().await?; - - let middleware_manager = MiddlewareManager::new() - .add_middleware(Box::new(LoggingMiddleware)) // 日志中间件 - .add_middleware(Box::new(CustomMiddleware)); - - client = client.with_middleware_manager(middleware_manager); - - // 现在所有交易都会通过您的中间件处理 - // ... - Ok(()) -} -``` - -#### 8.3 中间件执行顺序 - 中间件按照添加顺序依次执行: ```rust @@ -901,8 +205,8 @@ use sol_trade_sdk::common::PriorityFee; // 自定义优先费用配置 let priority_fee = PriorityFee { - unit_limit: 190000, - unit_price: 1000000, + tip_unit_limit: 190000, + tip_unit_price: 1000000, rpc_unit_limit: 500000, rpc_unit_price: 500000, buy_tip_fee: 0.001, diff --git a/examples/bonk_copy_trading/Cargo.toml b/examples/bonk_copy_trading/Cargo.toml new file mode 100644 index 0000000..c61c9b1 --- /dev/null +++ b/examples/bonk_copy_trading/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "bonk_copy_trading" +version = "0.1.0" +edition = "2021" + +[dependencies] +sol-trade-sdk = { path = "../.." } +solana-sdk = "2.3.0" +spl-associated-token-account = "7.0.0" +tokio = { version = "1", features = ["full"] } diff --git a/examples/bonk_copy_trading/src/main.rs b/examples/bonk_copy_trading/src/main.rs new file mode 100644 index 0000000..210b999 --- /dev/null +++ b/examples/bonk_copy_trading/src/main.rs @@ -0,0 +1,186 @@ +use std::sync::{ + atomic::{AtomicBool, Ordering}, + Arc, +}; + +use sol_trade_sdk::solana_streamer_sdk::match_event; +use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::common::filter::EventTypeFilter; +use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::common::EventType; +use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::protocols::bonk::parser::BONK_PROGRAM_ID; +use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::protocols::bonk::BonkTradeEvent; +use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::{Protocol, UnifiedEvent}; +use sol_trade_sdk::solana_streamer_sdk::streaming::yellowstone_grpc::{ + AccountFilter, TransactionFilter, +}; +use sol_trade_sdk::solana_streamer_sdk::streaming::YellowstoneGrpc; +use sol_trade_sdk::{ + common::{AnyResult, PriorityFee, TradeConfig}, + swqos::SwqosConfig, + trading::{core::params::BonkParams, factory::DexType}, + SolanaTrade, +}; +use solana_sdk::signer::Signer; +use solana_sdk::{commitment_config::CommitmentConfig, signature::Keypair}; +use spl_associated_token_account::get_associated_token_address; + +// Global static flag to ensure transaction is executed only once +static ALREADY_EXECUTED: AtomicBool = AtomicBool::new(false); + +#[tokio::main] +async fn main() -> Result<(), Box> { + println!("Subscribing to GRPC events..."); + + let grpc = YellowstoneGrpc::new( + "https://solana-yellowstone-grpc.publicnode.com:443".to_string(), + None, + )?; + + let callback = create_event_callback(); + let protocols = vec![Protocol::Bonk]; + // Filter accounts + let account_include = vec![ + BONK_PROGRAM_ID.to_string(), // Listen to bonk program ID + ]; + let account_exclude = vec![]; + let account_required = vec![]; + + // Listen to transaction data + let transaction_filter = TransactionFilter { + account_include: account_include.clone(), + account_exclude, + account_required, + }; + + // Listen to account data belonging to owner programs -> account event monitoring + let account_filter = AccountFilter { account: vec![], owner: vec![] }; + + // listen to specific event type + let event_type_filter = EventTypeFilter { + include: vec![ + EventType::BonkBuyExactIn, + EventType::BonkSellExactIn, + EventType::BonkBuyExactOut, + EventType::BonkSellExactOut, + ], + }; + + grpc.subscribe_events_immediate( + protocols, + None, + transaction_filter, + account_filter, + Some(event_type_filter), + None, + callback, + ) + .await?; + + tokio::signal::ctrl_c().await?; + + Ok(()) +} + +/// Create an event callback function that handles different types of events +fn create_event_callback() -> impl Fn(Box) { + |event: Box| { + match_event!(event, { + BonkTradeEvent => |e: BonkTradeEvent| { + // Test code, only test one transaction + if !ALREADY_EXECUTED.swap(true, Ordering::SeqCst) { + let event_clone = e.clone(); + tokio::spawn(async move { + if let Err(err) = bonk_copy_trade_with_grpc(event_clone).await { + eprintln!("Error in copy trade: {:?}", err); + std::process::exit(0); + } + }); + } + }, + }); + } +} + +/// Create SolanaTrade client +/// Initializes a new SolanaTrade client with configuration +async fn create_solana_trade_client() -> AnyResult { + println!("Creating SolanaTrade client..."); + + let payer = Keypair::from_base58_string("use_your_payer_keypair_here"); + let rpc_url = "https://api.mainnet-beta.solana.com".to_string(); + + let swqos_configs = vec![SwqosConfig::Default(rpc_url.clone())]; + + let mut priority_fee = PriorityFee::default(); + // Configure according to your needs + priority_fee.rpc_unit_limit = 150000; + + let trade_config = TradeConfig { + rpc_url, + commitment: CommitmentConfig::confirmed(), + priority_fee: priority_fee, + swqos_configs, + lookup_table_key: None, + }; + + let solana_trade_client = SolanaTrade::new(Arc::new(payer), trade_config).await; + println!("SolanaTrade client created successfully!"); + + Ok(solana_trade_client) +} + +/// Bonk sniper trade +/// This function demonstrates how to snipe a new token from a Bonk trade event +async fn bonk_copy_trade_with_grpc(trade_info: BonkTradeEvent) -> AnyResult<()> { + println!("Testing Bonk trading..."); + + let client = create_solana_trade_client().await?; + let mint_pubkey = trade_info.base_token_mint; + let slippage_basis_points = Some(100); + let recent_blockhash = client.rpc.get_latest_blockhash().await?; + + // Buy tokens + println!("Buying tokens from Bonk..."); + let buy_sol_amount = 100_000; + client + .buy( + DexType::Bonk, + mint_pubkey, + buy_sol_amount, + slippage_basis_points, + recent_blockhash, + None, + Box::new(BonkParams::from_trade(trade_info.clone())), + None, + true, + ) + .await?; + + // Sell tokens + println!("Selling tokens from Bonk..."); + + let rpc = client.rpc.clone(); + let payer = client.payer.pubkey(); + let account = get_associated_token_address(&payer, &mint_pubkey); + let balance = rpc.get_token_account_balance(&account).await?; + println!("Balance: {:?}", balance); + let amount_token = balance.amount.parse::().unwrap(); + + println!("Selling {} tokens", amount_token); + client + .sell( + DexType::Bonk, + mint_pubkey, + amount_token, + slippage_basis_points, + recent_blockhash, + None, + false, + Box::new(BonkParams::from_trade(trade_info.clone())), + None, + true, + ) + .await?; + + // Exit program + std::process::exit(0); +} diff --git a/examples/bonk_sniper_trading/Cargo.toml b/examples/bonk_sniper_trading/Cargo.toml new file mode 100644 index 0000000..b317b98 --- /dev/null +++ b/examples/bonk_sniper_trading/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "bonk_sniper_trading" +version = "0.1.0" +edition = "2021" + +[dependencies] +sol-trade-sdk = { path = "../.." } +solana-sdk = "2.3.0" +spl-associated-token-account = "7.0.0" +tokio = { version = "1", features = ["full"] } diff --git a/examples/bonk_sniper_trading/src/main.rs b/examples/bonk_sniper_trading/src/main.rs new file mode 100644 index 0000000..d31328f --- /dev/null +++ b/examples/bonk_sniper_trading/src/main.rs @@ -0,0 +1,160 @@ +use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::common::filter::EventTypeFilter; +use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::common::EventType; +use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::protocols::bonk::BonkTradeEvent; +use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::{Protocol, UnifiedEvent}; +use sol_trade_sdk::solana_streamer_sdk::streaming::grpc::ClientConfig; +use sol_trade_sdk::solana_streamer_sdk::{match_event, streaming::ShredStreamGrpc}; +use sol_trade_sdk::{ + common::{AnyResult, PriorityFee, TradeConfig}, + swqos::SwqosConfig, + trading::{core::params::BonkParams, factory::DexType}, + SolanaTrade, +}; +use solana_sdk::signer::Signer; +use solana_sdk::{commitment_config::CommitmentConfig, signature::Keypair}; +use spl_associated_token_account::get_associated_token_address; +use std::sync::{ + atomic::{AtomicBool, Ordering}, + Arc, +}; + +/// Atomic flag to ensure the sniper trade is executed only once +static ALREADY_EXECUTED: AtomicBool = AtomicBool::new(false); + +/// Main entry point - subscribes to Bonk events and executes sniper trades on token creation +#[tokio::main] +async fn main() -> Result<(), Box> { + println!("Subscribing to ShredStream events..."); + let shred_stream = ShredStreamGrpc::new("use_your_shred_stream_url_here".to_string()).await?; + let callback = create_event_callback(); + let protocols = vec![Protocol::Bonk]; + let event_type_filter = EventTypeFilter { + include: vec![ + EventType::BonkBuyExactIn, + EventType::BonkBuyExactOut, + EventType::BonkSellExactIn, + EventType::BonkSellExactOut, + EventType::BonkInitialize, + EventType::BonkInitializeV2, + ], + }; + println!("Starting to listen for events, press Ctrl+C to stop..."); + shred_stream.shredstream_subscribe(protocols, None, Some(event_type_filter), callback).await?; + tokio::signal::ctrl_c().await?; + Ok(()) +} + +/// Create an event callback function that handles different types of events +fn create_event_callback() -> impl Fn(Box) { + |event: Box| { + match_event!(event, { + BonkTradeEvent => |e: BonkTradeEvent| { + // Only process developer token creation events + if !e.is_dev_create_token_trade { + return; + } + // Ensure we only execute the trade once using atomic compare-and-swap + if !ALREADY_EXECUTED.swap(true, Ordering::SeqCst) { + let event_clone = e.clone(); + // Spawn a new task to handle the trading operation + tokio::spawn(async move { + if let Err(err) = bonk_sniper_trade_with_shreds(event_clone).await { + eprintln!("Error in sniper trade: {:?}", err); + std::process::exit(0); + } + }); + } + }, + }); + } +} + +/// Create SolanaTrade client +/// Initializes a new SolanaTrade client with configuration +async fn create_solana_trade_client() -> AnyResult { + println!("Creating SolanaTrade client..."); + + let payer = Keypair::from_base58_string("use_your_payer_keypair_here"); + let rpc_url = "https://api.mainnet-beta.solana.com".to_string(); + + let swqos_configs = vec![SwqosConfig::Default(rpc_url.clone())]; + + let mut priority_fee = PriorityFee::default(); + // Set RPC unit limit based on your requirements + priority_fee.rpc_unit_limit = 150000; + + let trade_config = TradeConfig { + rpc_url, + commitment: CommitmentConfig::confirmed(), + priority_fee: priority_fee, + swqos_configs, + lookup_table_key: None, + }; + + let solana_trade_client = SolanaTrade::new(Arc::new(payer), trade_config).await; + println!("SolanaTrade client created successfully!"); + + Ok(solana_trade_client) +} + +/// Execute Bonk sniper trading strategy based on received token creation event +/// This function buys tokens immediately after creation and then sells all tokens +async fn bonk_sniper_trade_with_shreds(trade_info: BonkTradeEvent) -> AnyResult<()> { + println!("Testing Bonk trading..."); + + let client = create_solana_trade_client().await?; + let mint_pubkey = trade_info.base_token_mint; + let slippage_basis_points = Some(300); + let recent_blockhash = client.rpc.get_latest_blockhash().await?; + + // Buy tokens + println!("Buying tokens from Bonk..."); + let buy_sol_amount = 100_000; + client + .buy( + DexType::Bonk, + mint_pubkey, + buy_sol_amount, + slippage_basis_points, + recent_blockhash, + None, + Box::new(BonkParams::from_dev_trade(trade_info.clone())), + None, + true, + ) + .await?; + + // Sell tokens + println!("Selling tokens from Bonk..."); + + let rpc = client.rpc.clone(); + let payer = client.payer.pubkey(); + let account = get_associated_token_address(&payer, &mint_pubkey); + let balance = rpc.get_token_account_balance(&account).await?; + println!("Balance: {:?}", balance); + let amount_token = balance.amount.parse::().unwrap(); + + println!("Selling {} tokens", amount_token); + client + .sell( + DexType::Bonk, + mint_pubkey, + amount_token, + slippage_basis_points, + recent_blockhash, + None, + false, + Box::new(BonkParams::immediate_sell( + trade_info.base_token_program, + trade_info.platform_config, + trade_info.platform_associated_account, + trade_info.creator_associated_account, + )), + None, + true, + ) + .await?; + + // Exit program after completing the trade + std::process::exit(0); +} diff --git a/examples/event_subscription/Cargo.toml b/examples/event_subscription/Cargo.toml new file mode 100644 index 0000000..28eefcf --- /dev/null +++ b/examples/event_subscription/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "event_subscription" +version = "0.1.0" +edition = "2021" + +[dependencies] +sol-trade-sdk = { path = "../.." } +tokio = { version = "1", features = ["full"] } diff --git a/examples/event_subscription/src/main.rs b/examples/event_subscription/src/main.rs new file mode 100644 index 0000000..a949909 --- /dev/null +++ b/examples/event_subscription/src/main.rs @@ -0,0 +1,181 @@ +use sol_trade_sdk::solana_streamer_sdk::{ + match_event, + streaming::{ + event_parser::{ + common::{filter::EventTypeFilter, EventType}, + protocols::{ + bonk::{parser::BONK_PROGRAM_ID, BonkPoolCreateEvent, BonkTradeEvent}, + pumpfun::{parser::PUMPFUN_PROGRAM_ID, PumpFunCreateTokenEvent, PumpFunTradeEvent}, + pumpswap::{ + parser::PUMPSWAP_PROGRAM_ID, PumpSwapBuyEvent, PumpSwapCreatePoolEvent, + PumpSwapDepositEvent, PumpSwapSellEvent, PumpSwapWithdrawEvent, + }, + raydium_amm_v4::parser::RAYDIUM_AMM_V4_PROGRAM_ID, + raydium_clmm::parser::RAYDIUM_CLMM_PROGRAM_ID, + raydium_cpmm::{parser::RAYDIUM_CPMM_PROGRAM_ID, RaydiumCpmmSwapEvent}, + }, + Protocol, UnifiedEvent, + }, + yellowstone_grpc::{AccountFilter, TransactionFilter}, + ShredStreamGrpc, YellowstoneGrpc, + }, +}; + +#[tokio::main] +async fn main() -> Result<(), Box> { + println!("This example demonstrates how to subscribe to events using Yellowstone gRPC and ShredStream."); + println!("You can choose which example to run by uncommenting the relevant function call."); + + // Uncomment one of these to run the example: + test_grpc().await?; // Use public Yellowstone gRPC endpoint + // test_shreds().await?; // Use local ShredStream endpoint + + Ok(()) +} + +/// Subscribe to events using Yellowstone gRPC +async fn test_grpc() -> Result<(), Box> { + println!("Subscribing to GRPC events..."); + + // Initialize gRPC client with public endpoint + let grpc = YellowstoneGrpc::new( + "https://solana-yellowstone-grpc.publicnode.com:443".to_string(), + None, // No auth token needed + )?; + + let callback = create_event_callback(); + + // Define protocols to monitor + let protocols = + vec![Protocol::PumpFun, Protocol::PumpSwap, Protocol::Bonk, Protocol::RaydiumCpmm]; + + // Define program IDs to monitor + let account_include = vec![ + PUMPFUN_PROGRAM_ID.to_string(), // Listen to pumpfun program ID + PUMPSWAP_PROGRAM_ID.to_string(), // Listen to pumpswap program ID + BONK_PROGRAM_ID.to_string(), // Listen to bonk program ID + RAYDIUM_CPMM_PROGRAM_ID.to_string(), // Listen to raydium_cpmm program ID + RAYDIUM_CLMM_PROGRAM_ID.to_string(), // Listen to raydium_clmm program ID + RAYDIUM_AMM_V4_PROGRAM_ID.to_string(), // Listen to raydium_amm_v4 program ID + ]; + let account_exclude = vec![]; + let account_required = vec![]; + + // Configure transaction filter + let transaction_filter = TransactionFilter { + account_include: account_include.clone(), + account_exclude, + account_required, + }; + + // Configure account filter for program-owned accounts + let account_filter = AccountFilter { account: vec![], owner: account_include.clone() }; + + // Configure event type filter (all events) + let event_type_filter = None; + // For specific events only: + // let event_type_filter = + // EventTypeFilter { include: vec![EventType::PumpSwapBuy, EventType::PumpSwapSell] }; + + println!("Starting to listen for events, press Ctrl+C to stop..."); + + // Start subscription + grpc.subscribe_events_immediate( + protocols, + None, + transaction_filter, + account_filter, + event_type_filter, + None, + callback, + ) + .await?; + + // Wait for termination signal + tokio::signal::ctrl_c().await?; + + Ok(()) +} + +/// Subscribe to events using ShredStream +async fn test_shreds() -> Result<(), Box> { + println!("Subscribing to ShredStream events..."); + + // Initialize ShredStream client with local endpoint + let shred_stream = ShredStreamGrpc::new("http://127.0.0.1:10800".to_string()).await?; + + let callback = create_event_callback(); + + // Define protocols to monitor + let protocols = vec![Protocol::PumpFun, Protocol::PumpSwap, Protocol::Bonk]; + + // Configure event type filter (all events) + let event_type_filter = None; + // For specific events only: + // let event_type_filter = + // EventTypeFilter { include: vec![EventType::PumpSwapBuy, EventType::PumpSwapSell] }; + + println!("Starting to listen for events, press Ctrl+C to stop..."); + + // Start subscription + shred_stream + .shredstream_subscribe( + protocols, + None, // No slot range specified + event_type_filter, + callback, + ) + .await?; + + // Wait for termination signal + tokio::signal::ctrl_c().await?; + + Ok(()) +} + +/// Create an event callback function that handles different types of events +fn create_event_callback() -> impl Fn(Box) { + |event: Box| { + // Process events using match_event! macro + match_event!(event, { + // Bonk protocol events + BonkPoolCreateEvent => |e: BonkPoolCreateEvent| { + println!("BonkPoolCreateEvent: {:?}", e.base_mint_param.symbol); + }, + BonkTradeEvent => |e: BonkTradeEvent| { + println!("BonkTradeEvent: {:?}", e); + }, + + // PumpFun protocol events + PumpFunTradeEvent => |e: PumpFunTradeEvent| { + println!("PumpFunTradeEvent: {:?}", e); + }, + PumpFunCreateTokenEvent => |e: PumpFunCreateTokenEvent| { + println!("PumpFunCreateTokenEvent: {:?}", e); + }, + + // PumpSwap protocol events + PumpSwapBuyEvent => |e: PumpSwapBuyEvent| { + println!("Buy event: {:?}", e); + }, + PumpSwapSellEvent => |e: PumpSwapSellEvent| { + println!("Sell event: {:?}", e); + }, + PumpSwapCreatePoolEvent => |e: PumpSwapCreatePoolEvent| { + println!("CreatePool event: {:?}", e); + }, + PumpSwapDepositEvent => |e: PumpSwapDepositEvent| { + println!("Deposit event: {:?}", e); + }, + PumpSwapWithdrawEvent => |e: PumpSwapWithdrawEvent| { + println!("Withdraw event: {:?}", e); + }, + + // Raydium protocol events + RaydiumCpmmSwapEvent => |e: RaydiumCpmmSwapEvent| { + println!("RaydiumCpmmSwapEvent: {:?}", e); + }, + // For more events and documentation, please refer to https://github.com/0xfnzero/solana-streamer + }); + } +} diff --git a/examples/middleware_system/Cargo.toml b/examples/middleware_system/Cargo.toml new file mode 100644 index 0000000..d270db3 --- /dev/null +++ b/examples/middleware_system/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "middleware_system" +version = "0.1.0" +edition = "2021" + +[dependencies] +sol-trade-sdk = { path = "../.." } +solana-sdk = "2.3.0" +tokio = { version = "1", features = ["full"] } +anyhow = "1.0.79" \ No newline at end of file diff --git a/examples/middleware_system/src/main.rs b/examples/middleware_system/src/main.rs new file mode 100644 index 0000000..3bc3914 --- /dev/null +++ b/examples/middleware_system/src/main.rs @@ -0,0 +1,110 @@ +use anyhow::Result; +use sol_trade_sdk::{ + common::{AnyResult, PriorityFee, TradeConfig}, + swqos::{SwqosConfig, SwqosRegion}, + trading::{ + core::params::PumpSwapParams, factory::DexType, middleware::builtin::LoggingMiddleware, + InstructionMiddleware, MiddlewareManager, + }, + SolanaTrade, +}; +use solana_sdk::{ + commitment_config::CommitmentConfig, instruction::Instruction, pubkey::Pubkey, + signature::Keypair, +}; +use std::{str::FromStr, sync::Arc}; + +#[tokio::main] +async fn main() -> Result<(), Box> { + test_middleware().await?; + Ok(()) +} + +/// Custom middleware +#[derive(Clone)] +pub struct CustomMiddleware; + +impl InstructionMiddleware for CustomMiddleware { + fn name(&self) -> &'static str { + "CustomMiddleware" + } + + fn process_protocol_instructions( + &self, + protocol_instructions: Vec, + protocol_name: String, + is_buy: bool, + ) -> Result> { + // do anything you want here + // you can modify the instructions here + Ok(protocol_instructions) + } + + fn process_full_instructions( + &self, + full_instructions: Vec, + protocol_name: String, + is_buy: bool, + ) -> Result> { + // do anything you want here + // you can modify the instructions here + Ok(full_instructions) + } + + fn clone_box(&self) -> Box { + Box::new(self.clone()) + } +} + +/// Create SolanaTrade client +/// Initializes a new SolanaTrade client with configuration +async fn create_solana_trade_client() -> AnyResult { + println!("Creating SolanaTrade client..."); + + // In real transactions, use your own private key to initialize the payer + let payer = Keypair::new(); + let rpc_url = "https://api.mainnet-beta.solana.com".to_string(); + + let swqos_configs = vec![SwqosConfig::Default(rpc_url.clone())]; + + let trade_config = TradeConfig { + rpc_url, + commitment: CommitmentConfig::confirmed(), + priority_fee: PriorityFee::default(), + swqos_configs, + lookup_table_key: None, + }; + + let solana_trade_client = SolanaTrade::new(Arc::new(payer), trade_config).await; + println!("SolanaTrade client created successfully!"); + + Ok(solana_trade_client) +} + +async fn test_middleware() -> AnyResult<()> { + let mut client = create_solana_trade_client().await?; + // SDK example middleware that prints instruction information + // You can reference LoggingMiddleware to implement the InstructionMiddleware trait for your own middleware + let middleware_manager = MiddlewareManager::new().add_middleware(Box::new(CustomMiddleware)); + client = client.with_middleware_manager(middleware_manager); + let mint_pubkey = Pubkey::from_str("pumpCmXqMfrsAkQ5r49WcJnRayYRqmXz6ae8H7H9Dfn")?; + let buy_sol_cost = 100_000; + let slippage_basis_points = Some(100); + let recent_blockhash = client.rpc.get_latest_blockhash().await?; + let pool_address = Pubkey::from_str("539m4mVWt6iduB6W8rDGPMarzNCMesuqY5eUTiiYHAgR")?; + client + .buy( + DexType::PumpSwap, + mint_pubkey, + buy_sol_cost, + slippage_basis_points, + recent_blockhash, + None, + Box::new(PumpSwapParams::from_pool_address_by_rpc(&client.rpc, &pool_address).await?), + None, + true, + ) + .await?; + println!("tip: This transaction will not succeed because we're using a test account. You can modify the code to initialize the payer with your own private key"); + Ok(()) +} diff --git a/examples/pumpfun_copy_trading/Cargo.toml b/examples/pumpfun_copy_trading/Cargo.toml new file mode 100644 index 0000000..e1a431c --- /dev/null +++ b/examples/pumpfun_copy_trading/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "pumpfun_copy_trading" +version = "0.1.0" +edition = "2021" + +[dependencies] +sol-trade-sdk = { path = "../.." } +solana-sdk = "2.3.0" +spl-associated-token-account = "7.0.0" +tokio = { version = "1", features = ["full"] } diff --git a/examples/pumpfun_copy_trading/src/main.rs b/examples/pumpfun_copy_trading/src/main.rs new file mode 100644 index 0000000..60aed62 --- /dev/null +++ b/examples/pumpfun_copy_trading/src/main.rs @@ -0,0 +1,183 @@ +use std::sync::{ + atomic::{AtomicBool, Ordering}, + Arc, +}; + +use sol_trade_sdk::solana_streamer_sdk::match_event; +use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::common::filter::EventTypeFilter; +use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::common::EventType; +use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::protocols::pumpfun::parser::PUMPFUN_PROGRAM_ID; +use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::protocols::pumpfun::PumpFunTradeEvent; +use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::{Protocol, UnifiedEvent}; +use sol_trade_sdk::solana_streamer_sdk::streaming::yellowstone_grpc::{ + AccountFilter, TransactionFilter, +}; +use sol_trade_sdk::solana_streamer_sdk::streaming::YellowstoneGrpc; +use sol_trade_sdk::{ + common::{AnyResult, PriorityFee, TradeConfig}, + swqos::SwqosConfig, + trading::{core::params::PumpFunParams, factory::DexType}, + SolanaTrade, +}; +use solana_sdk::signer::Signer; +use solana_sdk::{commitment_config::CommitmentConfig, signature::Keypair}; +use spl_associated_token_account::get_associated_token_address; + +// Global static flag to ensure transaction is executed only once +static ALREADY_EXECUTED: AtomicBool = AtomicBool::new(false); + +#[tokio::main] +async fn main() -> Result<(), Box> { + println!("Subscribing to GRPC events..."); + + let grpc = YellowstoneGrpc::new( + "https://solana-yellowstone-grpc.publicnode.com:443".to_string(), + None, + )?; + + let callback = create_event_callback(); + let protocols = vec![Protocol::PumpFun]; + // Filter accounts + let account_include = vec![ + PUMPFUN_PROGRAM_ID.to_string(), // Listen to pumpfun program ID + ]; + let account_exclude = vec![]; + let account_required = vec![]; + + // Listen to transaction data + let transaction_filter = TransactionFilter { + account_include: account_include.clone(), + account_exclude, + account_required, + }; + + // Listen to account data belonging to owner programs -> account event monitoring + let account_filter = AccountFilter { account: vec![], owner: vec![] }; + + // listen to specific event type + let event_type_filter = + EventTypeFilter { include: vec![EventType::PumpFunBuy, EventType::PumpFunSell] }; + + grpc.subscribe_events_immediate( + protocols, + None, + transaction_filter, + account_filter, + Some(event_type_filter), + None, + callback, + ) + .await?; + + tokio::signal::ctrl_c().await?; + + Ok(()) +} + +/// Create an event callback function that handles different types of events +fn create_event_callback() -> impl Fn(Box) { + |event: Box| { + match_event!(event, { + PumpFunTradeEvent => |e: PumpFunTradeEvent| { + // Test code, only test one transaction + if !ALREADY_EXECUTED.swap(true, Ordering::SeqCst) { + let event_clone = e.clone(); + tokio::spawn(async move { + if let Err(err) = pumpfun_copy_trade_with_grpc(event_clone).await { + eprintln!("Error in copy trade: {:?}", err); + std::process::exit(0); + } + }); + } + }, + }); + } +} + +/// Create SolanaTrade client +/// Initializes a new SolanaTrade client with configuration +async fn create_solana_trade_client() -> AnyResult { + println!("Creating SolanaTrade client..."); + + let payer = Keypair::from_base58_string("use_your_payer_keypair_here"); + let rpc_url = "https://api.mainnet-beta.solana.com".to_string(); + + let swqos_configs = vec![SwqosConfig::Default(rpc_url.clone())]; + + let mut priority_fee = PriorityFee::default(); + // Configure according to your needs + priority_fee.rpc_unit_limit = 100000; + + let trade_config = TradeConfig { + rpc_url, + commitment: CommitmentConfig::confirmed(), + priority_fee: priority_fee, + swqos_configs, + lookup_table_key: None, + }; + + let solana_trade_client = SolanaTrade::new(Arc::new(payer), trade_config).await; + println!("SolanaTrade client created successfully!"); + + Ok(solana_trade_client) +} + +/// PumpFun sniper trade +/// This function demonstrates how to snipe a new token from a PumpFun trade event +async fn pumpfun_copy_trade_with_grpc(trade_info: PumpFunTradeEvent) -> AnyResult<()> { + println!("Testing PumpFun trading..."); + + let client = create_solana_trade_client().await?; + let mint_pubkey = trade_info.mint; + let slippage_basis_points = Some(100); + let recent_blockhash = client.rpc.get_latest_blockhash().await?; + + // Buy tokens + println!("Buying tokens from PumpFun..."); + let buy_sol_amount = 100_000; + client + .buy( + DexType::PumpFun, + mint_pubkey, + buy_sol_amount, + slippage_basis_points, + recent_blockhash, + None, + Box::new(PumpFunParams::from_trade(&trade_info, None)), + None, + true, + ) + .await?; + + // Sell tokens + println!("Selling tokens from PumpFun..."); + + let rpc = client.rpc.clone(); + let payer = client.payer.pubkey(); + let account = get_associated_token_address(&payer, &mint_pubkey); + let balance = rpc.get_token_account_balance(&account).await?; + println!("Balance: {:?}", balance); + let amount_token = balance.amount.parse::().unwrap(); + + println!("Selling {} tokens", amount_token); + client + .sell( + DexType::PumpFun, + mint_pubkey, + amount_token, + slippage_basis_points, + recent_blockhash, + None, + false, + Box::new(PumpFunParams::from_trade(&trade_info, Some(true))), + None, + true, + ) + .await?; + + // PumpFunParams can also be set as PumpFunParams::immediate_sell(creator_vault, close_token_account_when_sell) + // creator_vault can be obtained from the trade event + + // Exit program + std::process::exit(0); +} diff --git a/examples/pumpfun_sniper_trading/Cargo.toml b/examples/pumpfun_sniper_trading/Cargo.toml new file mode 100644 index 0000000..48db355 --- /dev/null +++ b/examples/pumpfun_sniper_trading/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "pumpfun_sniper_trading" +version = "0.1.0" +edition = "2021" + +[dependencies] +sol-trade-sdk = { path = "../.." } +solana-sdk = "2.3.0" +spl-associated-token-account = "7.0.0" +tokio = { version = "1", features = ["full"] } diff --git a/examples/pumpfun_sniper_trading/src/main.rs b/examples/pumpfun_sniper_trading/src/main.rs new file mode 100644 index 0000000..c64d9c8 --- /dev/null +++ b/examples/pumpfun_sniper_trading/src/main.rs @@ -0,0 +1,151 @@ +use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::common::filter::EventTypeFilter; +use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::common::EventType; +use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::protocols::pumpfun::PumpFunTradeEvent; +use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::{Protocol, UnifiedEvent}; +use sol_trade_sdk::solana_streamer_sdk::{match_event, streaming::ShredStreamGrpc}; +use sol_trade_sdk::{ + common::{AnyResult, PriorityFee, TradeConfig}, + swqos::SwqosConfig, + trading::{core::params::PumpFunParams, factory::DexType}, + SolanaTrade, +}; +use solana_sdk::signer::Signer; +use solana_sdk::{commitment_config::CommitmentConfig, signature::Keypair}; +use spl_associated_token_account::get_associated_token_address; +use std::mem::take; +use std::sync::{ + atomic::{AtomicBool, Ordering}, + Arc, +}; + +/// Atomic flag to ensure the sniper trade is executed only once +static ALREADY_EXECUTED: AtomicBool = AtomicBool::new(false); + +/// Main entry point - subscribes to PumpFun events and executes sniper trades on token creation +#[tokio::main] +async fn main() -> Result<(), Box> { + println!("Subscribing to ShredStream events..."); + let shred_stream = ShredStreamGrpc::new("use_your_shred_stream_url_here".to_string()).await?; + let callback = create_event_callback(); + let protocols = vec![Protocol::PumpFun]; + let event_type_filter = EventTypeFilter { + include: vec![EventType::PumpFunBuy, EventType::PumpFunSell, EventType::PumpFunCreateToken], + }; + println!("Starting to listen for events, press Ctrl+C to stop..."); + shred_stream.shredstream_subscribe(protocols, None, Some(event_type_filter), callback).await?; + tokio::signal::ctrl_c().await?; + Ok(()) +} + +/// Create an event callback function that handles different types of events +fn create_event_callback() -> impl Fn(Box) { + |event: Box| { + match_event!(event, { + PumpFunTradeEvent => |e: PumpFunTradeEvent| { + // Only process developer token creation events + if !e.is_dev_create_token_trade { + return; + } + // Ensure we only execute the trade once using atomic compare-and-swap + if !ALREADY_EXECUTED.swap(true, Ordering::SeqCst) { + let event_clone = e.clone(); + // Spawn a new task to handle the trading operation + tokio::spawn(async move { + if let Err(err) = pumpfun_sniper_trade_with_shreds(event_clone).await { + eprintln!("Error in copy trade: {:?}", err); + std::process::exit(0); + } + }); + } + }, + }); + } +} + +/// Create SolanaTrade client +/// Initializes a new SolanaTrade client with configuration +async fn create_solana_trade_client() -> AnyResult { + println!("Creating SolanaTrade client..."); + + let payer = Keypair::from_base58_string("use_your_payer_keypair_here"); + let rpc_url = "https://api.mainnet-beta.solana.com".to_string(); + + let swqos_configs = vec![SwqosConfig::Default(rpc_url.clone())]; + + let mut priority_fee = PriorityFee::default(); + // Set RPC unit limit based on your requirements + priority_fee.rpc_unit_limit = 100000; + + let trade_config = TradeConfig { + rpc_url, + commitment: CommitmentConfig::confirmed(), + priority_fee: priority_fee, + swqos_configs, + lookup_table_key: None, + }; + + let solana_trade_client = SolanaTrade::new(Arc::new(payer), trade_config).await; + println!("SolanaTrade client created successfully!"); + + Ok(solana_trade_client) +} + +/// Execute PumpFun sniper trading strategy based on received token creation event +/// This function buys tokens immediately after creation and then sells all tokens +async fn pumpfun_sniper_trade_with_shreds(trade_info: PumpFunTradeEvent) -> AnyResult<()> { + println!("Testing PumpFun trading..."); + + let client = create_solana_trade_client().await?; + let mint_pubkey = trade_info.mint; + let slippage_basis_points = Some(300); + let recent_blockhash = client.rpc.get_latest_blockhash().await?; + + // Buy tokens + println!("Buying tokens from PumpFun..."); + let buy_sol_amount = 100_000; + client + .buy( + DexType::PumpFun, + mint_pubkey, + buy_sol_amount, + slippage_basis_points, + recent_blockhash, + None, + Box::new(PumpFunParams::from_dev_trade(&trade_info, None)), + None, + true, + ) + .await?; + + // Sell tokens + println!("Selling tokens from PumpFun..."); + + let rpc = client.rpc.clone(); + let payer = client.payer.pubkey(); + let account = get_associated_token_address(&payer, &mint_pubkey); + let balance = rpc.get_token_account_balance(&account).await?; + println!("Balance: {:?}", balance); + let amount_token = balance.amount.parse::().unwrap(); + + println!("Selling {} tokens", amount_token); + client + .sell( + DexType::PumpFun, + mint_pubkey, + amount_token, + slippage_basis_points, + recent_blockhash, + None, + false, + Box::new(PumpFunParams::immediate_sell(trade_info.creator_vault, true)), + None, + true, + ) + .await?; + + // PumpFunParams can also be set as PumpFunParams::immediate_sell(creator_vault, close_token_account_when_sell) + // creator_vault can be obtained from the trade event + + // Exit program after completing the trade + std::process::exit(0); +} diff --git a/examples/pumpswap_trading/Cargo.toml b/examples/pumpswap_trading/Cargo.toml new file mode 100644 index 0000000..3ee9cfe --- /dev/null +++ b/examples/pumpswap_trading/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "pumpswap_trading" +version = "0.1.0" +edition = "2021" + +[dependencies] +sol-trade-sdk = { path = "../.." } +solana-sdk = "2.3.0" +spl-associated-token-account = "7.0.0" +tokio = { version = "1", features = ["full"] } +spl-token-2022 = { version = "8.0.0", features = ["no-entrypoint"] } \ No newline at end of file diff --git a/examples/pumpswap_trading/src/main.rs b/examples/pumpswap_trading/src/main.rs new file mode 100644 index 0000000..77938a4 --- /dev/null +++ b/examples/pumpswap_trading/src/main.rs @@ -0,0 +1,224 @@ +use std::sync::{ + atomic::{AtomicBool, Ordering}, + Arc, +}; + +use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::{ + common::EventType, protocols::pumpswap::PumpSwapSellEvent, +}; +use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::{Protocol, UnifiedEvent}; +use sol_trade_sdk::solana_streamer_sdk::streaming::yellowstone_grpc::{ + AccountFilter, TransactionFilter, +}; +use sol_trade_sdk::solana_streamer_sdk::streaming::YellowstoneGrpc; +use sol_trade_sdk::solana_streamer_sdk::{ + match_event, streaming::event_parser::protocols::pumpswap::parser::PUMPSWAP_PROGRAM_ID, +}; +use sol_trade_sdk::{ + common::{AnyResult, PriorityFee, TradeConfig}, + swqos::SwqosConfig, + trading::{core::params::PumpSwapParams, factory::DexType}, + SolanaTrade, +}; +use sol_trade_sdk::{ + constants::pumpswap::accounts, + solana_streamer_sdk::streaming::event_parser::{ + common::filter::EventTypeFilter, protocols::pumpswap::PumpSwapBuyEvent, + }, +}; +use solana_sdk::{commitment_config::CommitmentConfig, signature::Keypair}; +use solana_sdk::{pubkey::Pubkey, signer::Signer}; +use spl_associated_token_account::get_associated_token_address_with_program_id; + +// Global static flag to ensure transaction is executed only once +static ALREADY_EXECUTED: AtomicBool = AtomicBool::new(false); + +#[tokio::main] +async fn main() -> Result<(), Box> { + println!("Subscribing to GRPC events..."); + + let grpc = YellowstoneGrpc::new( + "https://solana-yellowstone-grpc.publicnode.com:443".to_string(), + None, + )?; + + let callback = create_event_callback(); + let protocols = vec![Protocol::PumpSwap]; + // Filter accounts + let account_include = vec![ + PUMPSWAP_PROGRAM_ID.to_string(), // Listen to PumpSwap program ID + ]; + let account_exclude = vec![]; + let account_required = vec![]; + + // Listen to transaction data + let transaction_filter = TransactionFilter { + account_include: account_include.clone(), + account_exclude, + account_required, + }; + + // Listen to account data belonging to owner programs -> account event monitoring + let account_filter = AccountFilter { account: vec![], owner: vec![] }; + + // listen to specific event type + let event_type_filter = + EventTypeFilter { include: vec![EventType::PumpSwapBuy, EventType::PumpSwapSell] }; + + grpc.subscribe_events_immediate( + protocols, + None, + transaction_filter, + account_filter, + Some(event_type_filter), + None, + callback, + ) + .await?; + + tokio::signal::ctrl_c().await?; + + Ok(()) +} + +/// Create an event callback function that handles different types of events +fn create_event_callback() -> impl Fn(Box) { + |event: Box| { + match_event!(event, { + PumpSwapBuyEvent => |e: PumpSwapBuyEvent| { + if e.base_mint == accounts::WSOL_TOKEN_ACCOUNT || e.quote_mint == accounts::WSOL_TOKEN_ACCOUNT { + // Test code, only test one transaction + if !ALREADY_EXECUTED.swap(true, Ordering::SeqCst) { + let event_clone = e.clone(); + tokio::spawn(async move { + if let Err(err) = pumpswap_trade_with_grpc_buy_event(event_clone).await { + eprintln!("Error in trade: {:?}", err); + std::process::exit(0); + } + }); + } + } + }, + PumpSwapSellEvent => |e: PumpSwapSellEvent| { + if e.base_mint == accounts::WSOL_TOKEN_ACCOUNT || e.quote_mint == accounts::WSOL_TOKEN_ACCOUNT { + // Test code, only test one transaction + if !ALREADY_EXECUTED.swap(true, Ordering::SeqCst) { + let event_clone = e.clone(); + tokio::spawn(async move { + if let Err(err) = pumpswap_trade_with_grpc_sell_event(event_clone).await { + eprintln!("Error in trade: {:?}", err); + std::process::exit(0); + } + }); + } + } + } + }); + } +} + +/// Create SolanaTrade client +/// Initializes a new SolanaTrade client with configuration +async fn create_solana_trade_client() -> AnyResult { + println!("Creating SolanaTrade client..."); + + let payer = Keypair::from_base58_string("use_your_payer_keypair_here"); + let rpc_url = "https://api.mainnet-beta.solana.com".to_string(); + + let swqos_configs = vec![SwqosConfig::Default(rpc_url.clone())]; + + let mut priority_fee = PriorityFee::default(); + // Configure according to your needs + priority_fee.rpc_unit_limit = 150000; + + let trade_config = TradeConfig { + rpc_url, + commitment: CommitmentConfig::confirmed(), + priority_fee: priority_fee, + swqos_configs, + lookup_table_key: None, + }; + + let solana_trade_client = SolanaTrade::new(Arc::new(payer), trade_config).await; + println!("SolanaTrade client created successfully!"); + + Ok(solana_trade_client) +} + +async fn pumpswap_trade_with_grpc_buy_event(trade_info: PumpSwapBuyEvent) -> AnyResult<()> { + let params = PumpSwapParams::from_buy_trade(&trade_info); + let mint = if trade_info.base_mint == accounts::WSOL_TOKEN_ACCOUNT { + trade_info.quote_mint + } else { + trade_info.base_mint + }; + pumpswap_trade_with_grpc(mint, params).await?; + Ok(()) +} + +async fn pumpswap_trade_with_grpc_sell_event(trade_info: PumpSwapSellEvent) -> AnyResult<()> { + let params = PumpSwapParams::from_sell_trade(&trade_info); + let mint = if trade_info.base_mint == accounts::WSOL_TOKEN_ACCOUNT { + trade_info.quote_mint + } else { + trade_info.base_mint + }; + pumpswap_trade_with_grpc(mint, params).await?; + Ok(()) +} + +async fn pumpswap_trade_with_grpc(mint_pubkey: Pubkey, params: PumpSwapParams) -> AnyResult<()> { + println!("Testing PumpSwap trading..."); + + let client = create_solana_trade_client().await?; + let slippage_basis_points = Some(500); + let recent_blockhash = client.rpc.get_latest_blockhash().await?; + + // Buy tokens + println!("Buying tokens from PumpSwap..."); + let buy_sol_amount = 100_000; + client + .buy( + DexType::PumpSwap, + mint_pubkey, + buy_sol_amount, + slippage_basis_points, + recent_blockhash, + None, + Box::new(params.clone()), + None, + true, + ) + .await?; + + // Sell tokens + println!("Selling tokens from PumpSwap..."); + + let rpc = client.rpc.clone(); + let payer = client.payer.pubkey(); + let program_id = if params.base_mint == mint_pubkey { + params.base_token_program + } else { + params.quote_token_program + }; + let account = get_associated_token_address_with_program_id(&payer, &mint_pubkey, &program_id); + let balance = rpc.get_token_account_balance(&account).await?; + let amount_token = balance.amount.parse::().unwrap(); + client + .sell( + DexType::PumpSwap, + mint_pubkey, + amount_token, + slippage_basis_points, + recent_blockhash, + None, + false, + Box::new(params.clone()), + None, + true, + ) + .await?; + + // Exit program + std::process::exit(0); +} diff --git a/examples/raydium_amm_v4_trading/Cargo.toml b/examples/raydium_amm_v4_trading/Cargo.toml new file mode 100644 index 0000000..c1f9ecf --- /dev/null +++ b/examples/raydium_amm_v4_trading/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "raydium_amm_v4_trading" +version = "0.1.0" +edition = "2021" + +[dependencies] +sol-trade-sdk = { path = "../.." } +solana-sdk = "2.3.0" +spl-associated-token-account = "7.0.0" +tokio = { version = "1", features = ["full"] } diff --git a/examples/raydium_amm_v4_trading/src/main.rs b/examples/raydium_amm_v4_trading/src/main.rs new file mode 100644 index 0000000..0bd1c54 --- /dev/null +++ b/examples/raydium_amm_v4_trading/src/main.rs @@ -0,0 +1,196 @@ +use std::sync::{ + atomic::{AtomicBool, Ordering}, + Arc, +}; + +use sol_trade_sdk::{constants::raydium_amm_v4::accounts, solana_streamer_sdk::{match_event, streaming::event_parser::protocols::raydium_amm_v4::RaydiumAmmV4SwapEvent}, trading::common::get_multi_token_balances}; +use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::common::filter::EventTypeFilter; +use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::common::EventType; +use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::protocols::raydium_amm_v4::parser::RAYDIUM_AMM_V4_PROGRAM_ID; +use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::{Protocol, UnifiedEvent}; +use sol_trade_sdk::solana_streamer_sdk::streaming::yellowstone_grpc::{ + AccountFilter, TransactionFilter, +}; +use sol_trade_sdk::solana_streamer_sdk::streaming::YellowstoneGrpc; +use sol_trade_sdk::{ + common::{AnyResult, PriorityFee, TradeConfig}, + swqos::SwqosConfig, + trading::{core::params::RaydiumAmmV4Params, factory::DexType}, + SolanaTrade, +}; +use solana_sdk::signer::Signer; +use solana_sdk::{commitment_config::CommitmentConfig, signature::Keypair}; +use spl_associated_token_account::get_associated_token_address; + +// Global static flag to ensure transaction is executed only once +static ALREADY_EXECUTED: AtomicBool = AtomicBool::new(false); + +#[tokio::main] +async fn main() -> Result<(), Box> { + println!("Subscribing to GRPC events..."); + + let grpc = YellowstoneGrpc::new( + "https://solana-yellowstone-grpc.publicnode.com:443".to_string(), + None, + )?; + + let callback = create_event_callback(); + let protocols = vec![Protocol::RaydiumAmmV4]; + // Filter accounts + let account_include = vec![ + RAYDIUM_AMM_V4_PROGRAM_ID.to_string(), // Listen to raydium_amm_v4 program ID + ]; + let account_exclude = vec![]; + let account_required = vec![]; + + // Listen to transaction data + let transaction_filter = TransactionFilter { + account_include: account_include.clone(), + account_exclude, + account_required, + }; + + // Listen to account data belonging to owner programs -> account event monitoring + let account_filter = AccountFilter { account: vec![], owner: vec![] }; + + // listen to specific event type + let event_type_filter = EventTypeFilter { + include: vec![EventType::RaydiumAmmV4SwapBaseIn, EventType::RaydiumAmmV4SwapBaseOut], + }; + + grpc.subscribe_events_immediate( + protocols, + None, + transaction_filter, + account_filter, + Some(event_type_filter), + None, + callback, + ) + .await?; + + tokio::signal::ctrl_c().await?; + + Ok(()) +} + +/// Create an event callback function that handles different types of events +fn create_event_callback() -> impl Fn(Box) { + |event: Box| { + match_event!(event, { + RaydiumAmmV4SwapEvent => |e: RaydiumAmmV4SwapEvent| { + // Test code, only test one transaction + if !ALREADY_EXECUTED.swap(true, Ordering::SeqCst) { + let event_clone = e.clone(); + tokio::spawn(async move { + if let Err(err) = raydium_amm_v4_copy_trade_with_grpc(event_clone).await { + eprintln!("Error in copy trade: {:?}", err); + std::process::exit(0); + } + }); + } + }, + }); + } +} + +/// Create SolanaTrade client +/// Initializes a new SolanaTrade client with configuration +async fn create_solana_trade_client() -> AnyResult { + println!("Creating SolanaTrade client..."); + + let payer = Keypair::from_base58_string("use_your_payer_keypair_here"); + let rpc_url = "https://api.mainnet-beta.solana.com".to_string(); + + let swqos_configs = vec![SwqosConfig::Default(rpc_url.clone())]; + + let mut priority_fee = PriorityFee::default(); + // Configure according to your needs + priority_fee.rpc_unit_limit = 150000; + + let trade_config = TradeConfig { + rpc_url, + commitment: CommitmentConfig::confirmed(), + priority_fee: priority_fee, + swqos_configs, + lookup_table_key: None, + }; + + let solana_trade_client = SolanaTrade::new(Arc::new(payer), trade_config).await; + println!("SolanaTrade client created successfully!"); + + Ok(solana_trade_client) +} + +/// Raydium_amm_v4 sniper trade +/// This function demonstrates how to snipe a new token from a Raydium_amm_v4 trade event +async fn raydium_amm_v4_copy_trade_with_grpc(trade_info: RaydiumAmmV4SwapEvent) -> AnyResult<()> { + println!("Testing Raydium_amm_v4 trading..."); + + let client = create_solana_trade_client().await?; + let slippage_basis_points = Some(100); + let recent_blockhash = client.rpc.get_latest_blockhash().await?; + + let amm_info = + sol_trade_sdk::trading::raydium_amm_v4::common::fetch_amm_info(&client.rpc, trade_info.amm) + .await?; + let (coin_reserve, pc_reserve) = + get_multi_token_balances(&client.rpc, &amm_info.token_coin, &amm_info.token_pc).await?; + let mint_pubkey = if amm_info.pc_mint == accounts::WSOL_TOKEN_ACCOUNT { + amm_info.coin_mint + } else { + amm_info.pc_mint + }; + let params = RaydiumAmmV4Params::from_amm_info_and_reserves( + trade_info.amm, + amm_info, + coin_reserve, + pc_reserve, + ); + // Buy tokens + println!("Buying tokens from Raydium_amm_v4..."); + let buy_sol_amount = 100_000; + client + .buy( + DexType::RaydiumAmmV4, + mint_pubkey, + buy_sol_amount, + slippage_basis_points, + recent_blockhash, + None, + Box::new(params), + None, + true, + ) + .await?; + + // Sell tokens + println!("Selling tokens from Raydium_amm_v4..."); + + let rpc = client.rpc.clone(); + let payer = client.payer.pubkey(); + let account = get_associated_token_address(&payer, &mint_pubkey); + let balance = rpc.get_token_account_balance(&account).await?; + println!("Balance: {:?}", balance); + let amount_token = balance.amount.parse::().unwrap(); + + println!("Selling {} tokens", amount_token); + let params = RaydiumAmmV4Params::from_amm_address_by_rpc(&client.rpc, trade_info.amm).await?; + client + .sell( + DexType::RaydiumAmmV4, + mint_pubkey, + amount_token, + slippage_basis_points, + recent_blockhash, + None, + false, + Box::new(params), + None, + true, + ) + .await?; + + // Exit program + std::process::exit(0); +} diff --git a/examples/raydium_cpmm_trading/Cargo.toml b/examples/raydium_cpmm_trading/Cargo.toml new file mode 100644 index 0000000..e372de3 --- /dev/null +++ b/examples/raydium_cpmm_trading/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "raydium_cpmm_trading" +version = "0.1.0" +edition = "2021" + +[dependencies] +sol-trade-sdk = { path = "../.." } +solana-sdk = "2.3.0" +spl-associated-token-account = "7.0.0" +tokio = { version = "1", features = ["full"] } diff --git a/examples/raydium_cpmm_trading/src/main.rs b/examples/raydium_cpmm_trading/src/main.rs new file mode 100644 index 0000000..a7d4738 --- /dev/null +++ b/examples/raydium_cpmm_trading/src/main.rs @@ -0,0 +1,199 @@ +use std::sync::{ + atomic::{AtomicBool, Ordering}, + Arc, +}; + +use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::{Protocol, UnifiedEvent}; +use sol_trade_sdk::solana_streamer_sdk::streaming::yellowstone_grpc::{ + AccountFilter, TransactionFilter, +}; +use sol_trade_sdk::solana_streamer_sdk::streaming::YellowstoneGrpc; +use sol_trade_sdk::solana_streamer_sdk::{ + match_event, streaming::event_parser::protocols::raydium_cpmm::RaydiumCpmmSwapEvent, +}; +use sol_trade_sdk::{ + common::{AnyResult, PriorityFee, TradeConfig}, + swqos::SwqosConfig, + SolanaTrade, +}; +use sol_trade_sdk::{ + constants::raydium_cpmm::accounts, + solana_streamer_sdk::streaming::event_parser::protocols::raydium_cpmm::parser::RAYDIUM_CPMM_PROGRAM_ID, +}; +use sol_trade_sdk::{ + solana_streamer_sdk::streaming::event_parser::common::filter::EventTypeFilter, + trading::factory::DexType, +}; +use sol_trade_sdk::{ + solana_streamer_sdk::streaming::event_parser::common::EventType, + trading::core::params::RaydiumCpmmParams, +}; +use solana_sdk::signer::Signer; +use solana_sdk::{commitment_config::CommitmentConfig, signature::Keypair}; +use spl_associated_token_account::get_associated_token_address; + +// Global static flag to ensure transaction is executed only once +static ALREADY_EXECUTED: AtomicBool = AtomicBool::new(false); + +#[tokio::main] +async fn main() -> Result<(), Box> { + println!("Subscribing to GRPC events..."); + + let grpc = YellowstoneGrpc::new( + "https://solana-yellowstone-grpc.publicnode.com:443".to_string(), + None, + )?; + + let callback = create_event_callback(); + let protocols = vec![Protocol::RaydiumCpmm]; + // Filter accounts + let account_include = vec![ + RAYDIUM_CPMM_PROGRAM_ID.to_string(), // Listen to raydium_cpmm program ID + ]; + let account_exclude = vec![]; + let account_required = vec![]; + + // Listen to transaction data + let transaction_filter = TransactionFilter { + account_include: account_include.clone(), + account_exclude, + account_required, + }; + + // Listen to account data belonging to owner programs -> account event monitoring + let account_filter = AccountFilter { account: vec![], owner: vec![] }; + + // listen to specific event type + let event_type_filter = EventTypeFilter { + include: vec![EventType::RaydiumCpmmSwapBaseInput, EventType::RaydiumCpmmSwapBaseOutput], + }; + + grpc.subscribe_events_immediate( + protocols, + None, + transaction_filter, + account_filter, + Some(event_type_filter), + None, + callback, + ) + .await?; + + tokio::signal::ctrl_c().await?; + + Ok(()) +} + +/// Create an event callback function that handles different types of events +fn create_event_callback() -> impl Fn(Box) { + |event: Box| { + match_event!(event, { + RaydiumCpmmSwapEvent => |e: RaydiumCpmmSwapEvent| { + // Test code, only test one transaction + if !ALREADY_EXECUTED.swap(true, Ordering::SeqCst) { + let event_clone = e.clone(); + tokio::spawn(async move { + if let Err(err) = raydium_cpmm_copy_trade_with_grpc(event_clone).await { + eprintln!("Error in copy trade: {:?}", err); + std::process::exit(0); + } + }); + } + }, + }); + } +} + +/// Create SolanaTrade client +/// Initializes a new SolanaTrade client with configuration +async fn create_solana_trade_client() -> AnyResult { + println!("Creating SolanaTrade client..."); + + let payer = Keypair::from_base58_string("use_your_payer_keypair_here"); + let rpc_url = "https://api.mainnet-beta.solana.com".to_string(); + + let swqos_configs = vec![SwqosConfig::Default(rpc_url.clone())]; + + let mut priority_fee = PriorityFee::default(); + // Configure according to your needs + priority_fee.rpc_unit_limit = 150000; + + let trade_config = TradeConfig { + rpc_url, + commitment: CommitmentConfig::confirmed(), + priority_fee: priority_fee, + swqos_configs, + lookup_table_key: None, + }; + + let solana_trade_client = SolanaTrade::new(Arc::new(payer), trade_config).await; + println!("SolanaTrade client created successfully!"); + + Ok(solana_trade_client) +} + +/// Raydium_cpmm sniper trade +/// This function demonstrates how to snipe a new token from a Raydium_cpmm trade event +async fn raydium_cpmm_copy_trade_with_grpc(trade_info: RaydiumCpmmSwapEvent) -> AnyResult<()> { + println!("Testing Raydium_cpmm trading..."); + + let client = create_solana_trade_client().await?; + let mint_pubkey = if trade_info.input_token_mint == accounts::WSOL_TOKEN_ACCOUNT { + trade_info.output_token_mint + } else { + trade_info.input_token_mint + }; + let slippage_basis_points = Some(100); + let recent_blockhash = client.rpc.get_latest_blockhash().await?; + + let buy_params = + RaydiumCpmmParams::from_pool_address_by_rpc(&client.rpc, &trade_info.pool_state).await?; + // Buy tokens + println!("Buying tokens from Raydium_cpmm..."); + let buy_sol_amount = 100_000; + client + .buy( + DexType::RaydiumCpmm, + mint_pubkey, + buy_sol_amount, + slippage_basis_points, + recent_blockhash, + None, + Box::new(buy_params), + None, + true, + ) + .await?; + + // Sell tokens + println!("Selling tokens from Raydium_cpmm..."); + + let rpc = client.rpc.clone(); + let payer = client.payer.pubkey(); + let account = get_associated_token_address(&payer, &mint_pubkey); + let balance = rpc.get_token_account_balance(&account).await?; + println!("Balance: {:?}", balance); + let amount_token = balance.amount.parse::().unwrap(); + + let sell_params = + RaydiumCpmmParams::from_pool_address_by_rpc(&client.rpc, &trade_info.pool_state).await?; + + println!("Selling {} tokens", amount_token); + client + .sell( + DexType::RaydiumCpmm, + mint_pubkey, + amount_token, + slippage_basis_points, + recent_blockhash, + None, + false, + Box::new(sell_params), + None, + true, + ) + .await?; + + // Exit program + std::process::exit(0); +} diff --git a/examples/trading_client/Cargo.toml b/examples/trading_client/Cargo.toml new file mode 100644 index 0000000..d8df48e --- /dev/null +++ b/examples/trading_client/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "trading_client" +version = "0.1.0" +edition = "2021" + +[dependencies] +sol-trade-sdk = { path = "../.." } +solana-sdk = "2.3.0" +tokio = { version = "1", features = ["full"] } diff --git a/examples/trading_client/src/main.rs b/examples/trading_client/src/main.rs new file mode 100644 index 0000000..3892e38 --- /dev/null +++ b/examples/trading_client/src/main.rs @@ -0,0 +1,59 @@ +use sol_trade_sdk::{ + common::{AnyResult, PriorityFee, TradeConfig}, + swqos::{SwqosConfig, SwqosRegion}, + SolanaTrade, +}; +use solana_sdk::{commitment_config::CommitmentConfig, signature::Keypair}; +use std::sync::Arc; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let _ = test_create_solana_trade_client().await?; + println!("Successfully created SolanaTrade client!"); + Ok(()) +} + +/// Create SolanaTrade client +/// Initializes a new SolanaTrade client with configuration +async fn test_create_solana_trade_client() -> AnyResult { + println!("Creating SolanaTrade client..."); + + let payer = Keypair::new(); + let rpc_url = "https://mainnet.helius-rpc.com/?api-key=xxxxxx".to_string(); + + println!("rpc_url: {}", rpc_url); + + let swqos_configs = create_swqos_configs(&rpc_url); + let trade_config = create_trade_config(rpc_url, swqos_configs); + + let solana_trade_client = SolanaTrade::new(Arc::new(payer), trade_config).await; + println!("SolanaTrade client created successfully!"); + + Ok(solana_trade_client) +} + +fn create_swqos_configs(rpc_url: &str) -> Vec { + vec![ + // First parameter is UUID, pass empty string if no UUID + SwqosConfig::Jito("your uuid".to_string(), SwqosRegion::Frankfurt), + SwqosConfig::NextBlock("your api_token".to_string(), SwqosRegion::Frankfurt), + SwqosConfig::Bloxroute("your api_token".to_string(), SwqosRegion::Frankfurt), + SwqosConfig::ZeroSlot("your api_token".to_string(), SwqosRegion::Frankfurt), + SwqosConfig::Temporal("your api_token".to_string(), SwqosRegion::Frankfurt), + // Add tg official customer https://t.me/FlashBlock_Official to get free FlashBlock key + SwqosConfig::FlashBlock("your api_token".to_string(), SwqosRegion::Frankfurt), + // Add tg official customer https://t.me/node1_me to get free Node1 key + SwqosConfig::Node1("your api_token".to_string(), SwqosRegion::Frankfurt), + SwqosConfig::Default(rpc_url.to_string()), + ] +} + +fn create_trade_config(rpc_url: String, swqos_configs: Vec) -> TradeConfig { + TradeConfig { + rpc_url, + commitment: CommitmentConfig::confirmed(), + priority_fee: PriorityFee::default(), + swqos_configs, + lookup_table_key: None, + } +} diff --git a/src/instruction/bonk.rs b/src/instruction/bonk.rs index 39358ce..14d6790 100755 --- a/src/instruction/bonk.rs +++ b/src/instruction/bonk.rs @@ -117,7 +117,7 @@ impl BonkInstructionBuilder { ¶ms.payer.pubkey(), ¶ms.payer.pubkey(), ¶ms.mint, - &accounts::TOKEN_PROGRAM, + &protocol_params.mint_token_program, )); // Create buy instruction @@ -125,7 +125,10 @@ impl BonkInstructionBuilder { solana_sdk::instruction::AccountMeta::new(params.payer.pubkey(), true), // Payer (signer) solana_sdk::instruction::AccountMeta::new_readonly(accounts::AUTHORITY, false), // Authority (readonly) solana_sdk::instruction::AccountMeta::new_readonly(accounts::GLOBAL_CONFIG, false), // Global Config (readonly) - solana_sdk::instruction::AccountMeta::new_readonly(protocol_params.platform_onfig, false), // Platform Config (readonly) + solana_sdk::instruction::AccountMeta::new_readonly( + protocol_params.platform_onfig, + false, + ), // Platform Config (readonly) solana_sdk::instruction::AccountMeta::new(pool_state, false), // Pool State solana_sdk::instruction::AccountMeta::new(user_base_token_account, false), // User Base Token solana_sdk::instruction::AccountMeta::new(user_quote_token_account, false), // User Quote Token @@ -141,8 +144,14 @@ impl BonkInstructionBuilder { solana_sdk::instruction::AccountMeta::new_readonly(accounts::EVENT_AUTHORITY, false), // Event Authority (readonly) solana_sdk::instruction::AccountMeta::new_readonly(accounts::BONK, false), // Program (readonly) solana_sdk::instruction::AccountMeta::new_readonly(accounts::SYSTEM_PROGRAM, false), // System Program (readonly) - solana_sdk::instruction::AccountMeta::new(protocol_params.platform_associated_account, false), // Platform Associated Account - solana_sdk::instruction::AccountMeta::new(protocol_params.creator_associated_account, false), // Creator Associated Account + solana_sdk::instruction::AccountMeta::new( + protocol_params.platform_associated_account, + false, + ), // Platform Associated Account + solana_sdk::instruction::AccountMeta::new( + protocol_params.creator_associated_account, + false, + ), // Creator Associated Account ]; // Create instruction data let mut data = vec![]; @@ -218,10 +227,12 @@ impl BonkInstructionBuilder { ); // Create user token accounts - let user_base_token_account = spl_associated_token_account::get_associated_token_address( - ¶ms.payer.pubkey(), - ¶ms.mint, - ); + let user_base_token_account = + spl_associated_token_account::get_associated_token_address_with_program_id( + ¶ms.payer.pubkey(), + ¶ms.mint, + &protocol_params.mint_token_program, + ); let user_quote_token_account = spl_associated_token_account::get_associated_token_address( ¶ms.payer.pubkey(), &accounts::WSOL_TOKEN_ACCOUNT, @@ -252,7 +263,10 @@ impl BonkInstructionBuilder { solana_sdk::instruction::AccountMeta::new(params.payer.pubkey(), true), // Payer (signer) solana_sdk::instruction::AccountMeta::new_readonly(accounts::AUTHORITY, false), // Authority (readonly) solana_sdk::instruction::AccountMeta::new_readonly(accounts::GLOBAL_CONFIG, false), // Global Config (readonly) - solana_sdk::instruction::AccountMeta::new_readonly(protocol_params.platform_onfig, false), // Platform Config (readonly) + solana_sdk::instruction::AccountMeta::new_readonly( + protocol_params.platform_onfig, + false, + ), // Platform Config (readonly) solana_sdk::instruction::AccountMeta::new(pool_state, false), // Pool State solana_sdk::instruction::AccountMeta::new(user_base_token_account, false), // User Base Token solana_sdk::instruction::AccountMeta::new(user_quote_token_account, false), // User Quote Token @@ -268,8 +282,14 @@ impl BonkInstructionBuilder { solana_sdk::instruction::AccountMeta::new_readonly(accounts::EVENT_AUTHORITY, false), // Event Authority (readonly) solana_sdk::instruction::AccountMeta::new_readonly(accounts::BONK, false), // Program (readonly) solana_sdk::instruction::AccountMeta::new_readonly(accounts::SYSTEM_PROGRAM, false), // System Program (readonly) - solana_sdk::instruction::AccountMeta::new(protocol_params.platform_associated_account, false), // Platform Associated Account - solana_sdk::instruction::AccountMeta::new(protocol_params.creator_associated_account, false), // Creator Associated Account + solana_sdk::instruction::AccountMeta::new( + protocol_params.platform_associated_account, + false, + ), // Platform Associated Account + solana_sdk::instruction::AccountMeta::new( + protocol_params.creator_associated_account, + false, + ), // Creator Associated Account ]; // Create instruction data diff --git a/src/instruction/pumpswap.rs b/src/instruction/pumpswap.rs index 4bb899d..bdc86fb 100755 --- a/src/instruction/pumpswap.rs +++ b/src/instruction/pumpswap.rs @@ -57,6 +57,8 @@ impl InstructionBuilder for PumpSwapInstructionBuilder { coin_creator_vault_ata, coin_creator_vault_authority, protocol_params.auto_handle_wsol, + protocol_params.base_token_program, + protocol_params.quote_token_program, ) .await } @@ -86,6 +88,8 @@ impl InstructionBuilder for PumpSwapInstructionBuilder { coin_creator_vault_ata, coin_creator_vault_authority, protocol_params.auto_handle_wsol, + protocol_params.base_token_program, + protocol_params.quote_token_program, ) .await } @@ -104,7 +108,12 @@ impl PumpSwapInstructionBuilder { params_coin_creator_vault_ata: Pubkey, params_coin_creator_vault_authority: Pubkey, auto_handle_wsol: bool, + base_token_program: Pubkey, + quote_token_program: Pubkey, ) -> Result> { + if base_mint != accounts::WSOL_TOKEN_ACCOUNT && quote_mint != accounts::WSOL_TOKEN_ACCOUNT { + return Err(anyhow!("Invalid base mint and quote mint")); + } if params.rpc.is_none() { return Err(anyhow!("RPC is not set")); } @@ -147,28 +156,32 @@ impl PumpSwapInstructionBuilder { } // Create user token accounts - let user_base_token_account = spl_associated_token_account::get_associated_token_address( - ¶ms.payer.pubkey(), - &base_mint, - ); - let user_quote_token_account = spl_associated_token_account::get_associated_token_address( - ¶ms.payer.pubkey(), - "e_mint, - ); + let user_base_token_account = + spl_associated_token_account::get_associated_token_address_with_program_id( + ¶ms.payer.pubkey(), + &base_mint, + &base_token_program, + ); + let user_quote_token_account = + spl_associated_token_account::get_associated_token_address_with_program_id( + ¶ms.payer.pubkey(), + "e_mint, + "e_token_program, + ); // Get pool token accounts let pool_base_token_account = spl_associated_token_account::get_associated_token_address_with_program_id( &pool, &base_mint, - &accounts::TOKEN_PROGRAM, + &base_token_program, ); let pool_quote_token_account = spl_associated_token_account::get_associated_token_address_with_program_id( &pool, "e_mint, - &accounts::TOKEN_PROGRAM, + "e_token_program, ); let mut instructions = vec![]; @@ -216,7 +229,7 @@ impl PumpSwapInstructionBuilder { ¶ms.payer.pubkey(), ¶ms.payer.pubkey(), if quote_mint_is_wsol { &base_mint } else { "e_mint }, - &accounts::TOKEN_PROGRAM, + if quote_mint_is_wsol { &base_token_program } else { "e_token_program }, )); let fee_recipient_ata = fee_recipient_ata(accounts::FEE_RECIPIENT, quote_mint); @@ -234,8 +247,8 @@ impl PumpSwapInstructionBuilder { solana_sdk::instruction::AccountMeta::new(pool_quote_token_account, false), // pool_quote_token_account solana_sdk::instruction::AccountMeta::new_readonly(accounts::FEE_RECIPIENT, false), // fee_recipient (readonly) solana_sdk::instruction::AccountMeta::new(fee_recipient_ata, false), // fee_recipient_ata - solana_sdk::instruction::AccountMeta::new_readonly(accounts::TOKEN_PROGRAM, false), // TOKEN_PROGRAM_ID (readonly) - solana_sdk::instruction::AccountMeta::new_readonly(accounts::TOKEN_PROGRAM, false), // TOKEN_PROGRAM_ID (readonly, duplicated as in JS) + solana_sdk::instruction::AccountMeta::new_readonly(base_token_program, false), // TOKEN_PROGRAM_ID (readonly) + solana_sdk::instruction::AccountMeta::new_readonly(quote_token_program, false), // TOKEN_PROGRAM_ID (readonly, duplicated as in JS) solana_sdk::instruction::AccountMeta::new_readonly(accounts::SYSTEM_PROGRAM, false), // System Program (readonly) solana_sdk::instruction::AccountMeta::new_readonly( accounts::ASSOCIATED_TOKEN_PROGRAM, @@ -309,7 +322,12 @@ impl PumpSwapInstructionBuilder { params_coin_creator_vault_ata: Pubkey, params_coin_creator_vault_authority: Pubkey, auto_handle_wsol: bool, + base_token_program: Pubkey, + quote_token_program: Pubkey, ) -> Result> { + if base_mint != accounts::WSOL_TOKEN_ACCOUNT && quote_mint != accounts::WSOL_TOKEN_ACCOUNT { + return Err(anyhow!("Invalid base mint and quote mint")); + } if params.rpc.is_none() { return Err(anyhow!("RPC is not set")); } @@ -358,25 +376,29 @@ impl PumpSwapInstructionBuilder { let fee_recipient_ata = fee_recipient_ata(accounts::FEE_RECIPIENT, quote_mint); - let user_base_token_account = spl_associated_token_account::get_associated_token_address( - ¶ms.payer.pubkey(), - &base_mint, - ); - let user_quote_token_account = spl_associated_token_account::get_associated_token_address( - ¶ms.payer.pubkey(), - "e_mint, - ); + let user_base_token_account = + spl_associated_token_account::get_associated_token_address_with_program_id( + ¶ms.payer.pubkey(), + &base_mint, + &base_token_program, + ); + let user_quote_token_account = + spl_associated_token_account::get_associated_token_address_with_program_id( + ¶ms.payer.pubkey(), + "e_mint, + "e_token_program, + ); let pool_base_token_account = spl_associated_token_account::get_associated_token_address_with_program_id( &pool, &base_mint, - &accounts::TOKEN_PROGRAM, + &base_token_program, ); let pool_quote_token_account = spl_associated_token_account::get_associated_token_address_with_program_id( &pool, "e_mint, - &accounts::TOKEN_PROGRAM, + "e_token_program, ); let mut instructions = vec![]; @@ -397,7 +419,7 @@ impl PumpSwapInstructionBuilder { ¶ms.payer.pubkey(), ¶ms.payer.pubkey(), if quote_mint_is_wsol { &base_mint } else { "e_mint }, - &accounts::TOKEN_PROGRAM, + if quote_mint_is_wsol { &base_token_program } else { "e_token_program }, )); // Create sell instruction @@ -413,8 +435,8 @@ impl PumpSwapInstructionBuilder { solana_sdk::instruction::AccountMeta::new(pool_quote_token_account, false), // pool_quote_token_account solana_sdk::instruction::AccountMeta::new_readonly(accounts::FEE_RECIPIENT, false), // fee_recipient (readonly) solana_sdk::instruction::AccountMeta::new(fee_recipient_ata, false), // fee_recipient_ata - solana_sdk::instruction::AccountMeta::new_readonly(accounts::TOKEN_PROGRAM, false), // TOKEN_PROGRAM_ID (readonly) - solana_sdk::instruction::AccountMeta::new_readonly(accounts::TOKEN_PROGRAM, false), // TOKEN_PROGRAM_ID (readonly, duplicated as in JS) + solana_sdk::instruction::AccountMeta::new_readonly(base_token_program, false), // TOKEN_PROGRAM_ID (readonly) + solana_sdk::instruction::AccountMeta::new_readonly(quote_token_program, false), // TOKEN_PROGRAM_ID (readonly, duplicated as in JS) solana_sdk::instruction::AccountMeta::new_readonly(accounts::SYSTEM_PROGRAM, false), // System Program (readonly) solana_sdk::instruction::AccountMeta::new_readonly( accounts::ASSOCIATED_TOKEN_PROGRAM, diff --git a/src/instruction/raydium_cpmm.rs b/src/instruction/raydium_cpmm.rs index 29b1edc..7c211e9 100755 --- a/src/instruction/raydium_cpmm.rs +++ b/src/instruction/raydium_cpmm.rs @@ -55,21 +55,29 @@ impl RaydiumCpmmInstructionBuilder { ) .unwrap(); + let is_base_in = protocol_params.base_mint == accounts::WSOL_TOKEN_ACCOUNT; + let mint_token_program = if is_base_in { + protocol_params.quote_token_program + } else { + protocol_params.base_token_program + }; + let wsol_token_account = spl_associated_token_account::get_associated_token_address( ¶ms.payer.pubkey(), &accounts::WSOL_TOKEN_ACCOUNT, ); - let mint_token_account = spl_associated_token_account::get_associated_token_address( - ¶ms.payer.pubkey(), - ¶ms.mint, - ); + let mint_token_account = + spl_associated_token_account::get_associated_token_address_with_program_id( + ¶ms.payer.pubkey(), + ¶ms.mint, + &mint_token_program, + ); // Get pool token accounts let wsol_vault_account = get_vault_pda(&pool_state, &accounts::WSOL_TOKEN_ACCOUNT).unwrap(); let mint_vault_account = get_vault_pda(&pool_state, ¶ms.mint).unwrap(); let observation_state_account = get_observation_state_pda(&pool_state).unwrap(); - let is_base_in = protocol_params.base_mint == accounts::WSOL_TOKEN_ACCOUNT; let amount_in: u64 = params.sol_amount; let result = compute_swap_amount( @@ -106,12 +114,6 @@ impl RaydiumCpmmInstructionBuilder { ); } - let mint_token_program = if is_base_in { - protocol_params.quote_token_program - } else { - protocol_params.base_token_program - }; - instructions.push(create_associated_token_account_idempotent( ¶ms.payer.pubkey(), ¶ms.payer.pubkey(), @@ -176,6 +178,11 @@ impl RaydiumCpmmInstructionBuilder { } let is_base_in = protocol_params.base_mint == params.mint; + let mint_token_program = if is_base_in { + protocol_params.base_token_program + } else { + protocol_params.quote_token_program + }; let minimum_amount_out: u64 = compute_swap_amount( protocol_params.base_reserve, @@ -197,10 +204,12 @@ impl RaydiumCpmmInstructionBuilder { ¶ms.payer.pubkey(), &accounts::WSOL_TOKEN_ACCOUNT, ); - let mint_token_account = spl_associated_token_account::get_associated_token_address( - ¶ms.payer.pubkey(), - ¶ms.mint, - ); + let mint_token_account = + spl_associated_token_account::get_associated_token_address_with_program_id( + ¶ms.payer.pubkey(), + ¶ms.mint, + &mint_token_program, + ); // Get pool token accounts let wsol_vault_account = get_vault_pda(&pool_state, &accounts::WSOL_TOKEN_ACCOUNT).unwrap(); @@ -221,12 +230,6 @@ impl RaydiumCpmmInstructionBuilder { ), ); - let mint_token_program = if is_base_in { - protocol_params.base_token_program - } else { - protocol_params.quote_token_program - }; - // Create sell instruction let accounts = vec![ solana_sdk::instruction::AccountMeta::new(params.payer.pubkey(), true), // Payer (signer) diff --git a/src/swqos/bloxroute.rs b/src/swqos/bloxroute.rs index d03ca5b..9a7f78e 100755 --- a/src/swqos/bloxroute.rs +++ b/src/swqos/bloxroute.rs @@ -92,7 +92,10 @@ impl BloxrouteClient { let start_time: Instant = Instant::now(); match poll_transaction_confirmation(&self.rpc_client, signature).await { Ok(_) => (), - Err(_) => (), + Err(e) => { + println!(" bloxroute{}确认失败: {:?}", trade_type, start_time.elapsed()); + return Err(e); + }, } println!(" bloxroute{}确认: {:?}", trade_type, start_time.elapsed()); diff --git a/src/swqos/flashblock.rs b/src/swqos/flashblock.rs index 24dae54..0eb2b56 100644 --- a/src/swqos/flashblock.rs +++ b/src/swqos/flashblock.rs @@ -92,7 +92,10 @@ impl FlashBlockClient { let start_time: Instant = Instant::now(); match poll_transaction_confirmation(&self.rpc_client, signature).await { Ok(_) => (), - Err(_) => (), + Err(e) => { + println!(" FlashBlock{}确认失败: {:?}", trade_type, start_time.elapsed()); + return Err(e); + }, } println!(" FlashBlock{}确认: {:?}", trade_type, start_time.elapsed()); diff --git a/src/swqos/jito.rs b/src/swqos/jito.rs index be73870..bbe4d0b 100755 --- a/src/swqos/jito.rs +++ b/src/swqos/jito.rs @@ -108,7 +108,10 @@ impl JitoClient { let start_time: Instant = Instant::now(); match poll_transaction_confirmation(&self.rpc_client, signature).await { Ok(_) => (), - Err(_) => (), + Err(e) => { + println!(" jito{}确认失败: {:?}", trade_type, start_time.elapsed()); + return Err(e); + }, } println!(" jito{}确认: {:?}", trade_type, start_time.elapsed()); diff --git a/src/swqos/nextblock.rs b/src/swqos/nextblock.rs index 7abaec8..67ca398 100755 --- a/src/swqos/nextblock.rs +++ b/src/swqos/nextblock.rs @@ -89,7 +89,10 @@ impl NextBlockClient { let start_time: Instant = Instant::now(); match poll_transaction_confirmation(&self.rpc_client, signature).await { Ok(_) => (), - Err(_) => (), + Err(e) => { + println!(" nextblock{}确认失败: {:?}", trade_type, start_time.elapsed()); + return Err(e); + }, } println!(" nextblock{}确认: {:?}", trade_type, start_time.elapsed()); diff --git a/src/swqos/node1.rs b/src/swqos/node1.rs index 3ff5cf4..94dfe21 100644 --- a/src/swqos/node1.rs +++ b/src/swqos/node1.rs @@ -177,7 +177,10 @@ impl Node1Client { let start_time: Instant = Instant::now(); match poll_transaction_confirmation(&self.rpc_client, signature).await { Ok(_) => (), - Err(_) => (), + Err(e) => { + println!(" node1{}确认失败: {:?}", trade_type, start_time.elapsed()); + return Err(e); + }, } println!(" node1{}确认: {:?}", trade_type, start_time.elapsed()); diff --git a/src/swqos/solana_rpc.rs b/src/swqos/solana_rpc.rs index 5307e05..44398c4 100755 --- a/src/swqos/solana_rpc.rs +++ b/src/swqos/solana_rpc.rs @@ -30,7 +30,10 @@ impl SwqosClientTrait for SolRpcClient { let start_time = Instant::now(); match poll_transaction_confirmation(&self.rpc_client, signature).await { Ok(_) => (), - Err(_) => (), + Err(e) => { + println!(" rpc{}确认失败: {:?}", trade_type, start_time.elapsed()); + return Err(e); + }, } println!(" signature: {:?}", signature); println!(" rpc{}确认: {:?}", trade_type, start_time.elapsed()); diff --git a/src/swqos/temporal.rs b/src/swqos/temporal.rs index 2cb741e..c190c45 100755 --- a/src/swqos/temporal.rs +++ b/src/swqos/temporal.rs @@ -98,7 +98,10 @@ impl TemporalClient { let start_time: Instant = Instant::now(); match poll_transaction_confirmation(&self.rpc_client, signature).await { Ok(_) => (), - Err(_) => (), + Err(e) => { + println!(" nozomi{}确认失败: {:?}", trade_type, start_time.elapsed()); + return Err(e); + }, } println!(" nozomi{}确认: {:?}", trade_type, start_time.elapsed()); diff --git a/src/swqos/zeroslot.rs b/src/swqos/zeroslot.rs index dc4466d..8b11b3f 100755 --- a/src/swqos/zeroslot.rs +++ b/src/swqos/zeroslot.rs @@ -99,7 +99,10 @@ impl ZeroSlotClient { let start_time: Instant = Instant::now(); match poll_transaction_confirmation(&self.rpc_client, signature).await { Ok(_) => (), - Err(_) => (), + Err(e) => { + println!(" 0slot{}确认失败: {:?}", trade_type, start_time.elapsed()); + return Err(e); + }, } println!(" 0slot{}确认: {:?}", trade_type, start_time.elapsed()); diff --git a/src/trading/core/params.rs b/src/trading/core/params.rs index 5804b86..06a2ba9 100755 --- a/src/trading/core/params.rs +++ b/src/trading/core/params.rs @@ -179,6 +179,10 @@ pub struct PumpSwapParams { pub coin_creator_vault_ata: Pubkey, /// Coin creator vault authority pub coin_creator_vault_authority: Pubkey, + /// Token program ID + pub base_token_program: Pubkey, + /// Quote token program ID + pub quote_token_program: Pubkey, /// Automatically handle WSOL wrapping /// When true, automatically handles wrapping and unwrapping operations between SOL and WSOL pub auto_handle_wsol: bool, @@ -194,6 +198,8 @@ impl PumpSwapParams { pool_quote_token_reserves: event.pool_quote_token_reserves, coin_creator_vault_ata: event.coin_creator_vault_ata, coin_creator_vault_authority: event.coin_creator_vault_authority, + base_token_program: event.base_token_program, + quote_token_program: event.quote_token_program, auto_handle_wsol: true, } } @@ -207,6 +213,8 @@ impl PumpSwapParams { pool_quote_token_reserves: event.pool_quote_token_reserves, coin_creator_vault_ata: event.coin_creator_vault_ata, coin_creator_vault_authority: event.coin_creator_vault_authority, + base_token_program: event.base_token_program, + quote_token_program: event.quote_token_program, auto_handle_wsol: true, } } @@ -221,6 +229,20 @@ impl PumpSwapParams { let creator = pool_data.creator; let coin_creator_vault_ata = coin_creator_vault_ata(creator, pool_data.quote_mint); let coin_creator_vault_authority = coin_creator_vault_authority(creator); + + let base_token_program_ata = + spl_associated_token_account::get_associated_token_address_with_program_id( + &pool_address, + &pool_data.base_mint, + &accounts::TOKEN_PROGRAM, + ); + let quote_token_program_ata = + spl_associated_token_account::get_associated_token_address_with_program_id( + &pool_address, + &pool_data.quote_mint, + &accounts::TOKEN_PROGRAM, + ); + Ok(Self { pool: pool_address.clone(), base_mint: pool_data.base_mint, @@ -229,6 +251,16 @@ impl PumpSwapParams { pool_quote_token_reserves: pool_quote_token_reserves, coin_creator_vault_ata: coin_creator_vault_ata, coin_creator_vault_authority: coin_creator_vault_authority, + base_token_program: if pool_data.pool_base_token_account == base_token_program_ata { + accounts::TOKEN_PROGRAM + } else { + spl_token_2022::ID + }, + quote_token_program: if pool_data.pool_quote_token_account == quote_token_program_ata { + accounts::TOKEN_PROGRAM + } else { + spl_token_2022::ID + }, auto_handle_wsol: true, }) }