From d02d734026be4568b67153618ef4997d84bb4c7b Mon Sep 17 00:00:00 2001 From: ysq Date: Mon, 18 Aug 2025 20:41:01 +0800 Subject: [PATCH] refactor: improve trading params API and fix bonk calculation bug - Bump version to 0.4.0 with breaking API changes - Standardize dex params to Box instead of Option> - Add convenient factory methods for all trading params - Fix critical bug in bonk sell calculation (virtual_base logic) - Update documentation and examples with new API usage - Improve ergonomics by removing manual params construction --- Cargo.toml | 2 +- README.md | 88 ++++++++++++------------------- README_CN.md | 115 ++++++++++++++++++++--------------------- src/main.rs | 12 ++--- src/utils/calc/bonk.rs | 2 +- 5 files changed, 96 insertions(+), 123 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9e0935d..3e54c6e 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sol-trade-sdk" -version = "0.3.5" +version = "0.4.0" edition = "2021" authors = ["William ", "sgxiang ", "wei <1415121722@qq.com>"] repository = "https://github.com/0xfnzero/sol-trade-sdk" diff --git a/README.md b/README.md index 952a00c..38b2771 100755 --- a/README.md +++ b/README.md @@ -31,14 +31,14 @@ Add the dependency to your `Cargo.toml`: ```toml # Add to your Cargo.toml -sol-trade-sdk = { path = "./sol-trade-sdk", version = "0.3.5" } +sol-trade-sdk = { path = "./sol-trade-sdk", version = "0.4.0" } ``` ### Use crates.io ```toml # Add to your Cargo.toml -sol-trade-sdk = "0.3.5" +sol-trade-sdk = "0.4.0" ``` ## Usage Examples @@ -186,6 +186,7 @@ async fn test_grpc() -> Result<(), Box> { transaction_filter, account_filter, None, + None, callback, ) .await?; @@ -247,7 +248,7 @@ async fn test_shreds() -> Result<(), Box> { 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, callback) + .shredstream_subscribe(protocols, None, None, callback) .await?; Ok(()) @@ -337,14 +338,6 @@ async fn test_pumpfun_sniper_trade_with_shreds(trade_info: PumpFunTradeEvent) -> println!("Buying tokens from PumpFun..."); - // By not using RPC to fetch the bonding curve, transaction time can be saved. - let bonding_curve = BondingCurveAccount::from_dev_trade( - &mint_pubkey, - dev_token_amount, - dev_sol_amount, - creator, - ); - // my trade cost sol amount let buy_sol_amount = 100_000; trade_client.buy( @@ -355,9 +348,13 @@ async fn test_pumpfun_sniper_trade_with_shreds(trade_info: PumpFunTradeEvent) -> slippage_basis_points, recent_blockhash, None, - Some(Box::new(PumpFunParams { - bonding_curve: Some(Arc::new(bonding_curve.clone())), - })), + Box::new(PumpFunParams::from_dev_trade( + &mint_pubkey, + dev_token_amount, + dev_sol_amount, + creator, + None, + )), None, ) .await?; @@ -381,9 +378,6 @@ async fn test_pumpfun_copy_trade_with_grpc(trade_info: PumpFunTradeEvent) -> Any // my trade cost sol amount let buy_sol_amount = 100_000; - // By not using RPC to fetch the bonding curve, transaction time can be saved. - let bonding_curve = BondingCurveAccount::from_trade(&trade_info); - trade_client.buy( DexType::PumpFun, mint_pubkey, @@ -392,9 +386,7 @@ async fn test_pumpfun_copy_trade_with_grpc(trade_info: PumpFunTradeEvent) -> Any slippage_basis_points, recent_blockhash, None, - Some(Box::new(PumpFunParams { - bonding_curve: Some(Arc::new(bonding_curve.clone())), - })), + Box::new(PumpFunParams::from_trade(&trade_info, None)), None, ) .await?; @@ -451,14 +443,8 @@ async fn test_pumpswap() -> AnyResult<()> { slippage_basis_points, recent_blockhash, None, - Some(Box::new(PumpSwapParams { - pool: Some(pool_address), - base_mint: Some(base_mint), - quote_mint: Some(quote_mint), - pool_base_token_reserves: Some(pool_base_token_reserves), - pool_quote_token_reserves: Some(pool_quote_token_reserves), - auto_handle_wsol: true, - })), + // 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?; @@ -474,14 +460,8 @@ async fn test_pumpswap() -> AnyResult<()> { recent_blockhash, None, false, - Some(Box::new(PumpSwapParams { - pool: Some(pool_address), - base_mint: Some(base_mint), - quote_mint: Some(quote_mint), - pool_base_token_reserves: Some(pool_base_token_reserves), - pool_quote_token_reserves: Some(pool_quote_token_reserves), - auto_handle_wsol: true, - })), + // 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?; @@ -525,13 +505,10 @@ async fn test_raydium_cpmm() -> Result<(), Box> { slippage_basis_points, recent_blockhash, None, - Some(Box::new(RaydiumCpmmParams { - pool_state: Some(pool_state), // If not provided, will auto-calculate - mint_token_program: Some(spl_token::ID), // Support spl_token or spl_token_2022::ID - mint_token_in_pool_state_index: Some(1), // Index of mint_token in pool_state, default is at index 1 - minimum_amount_out: Some(buy_amount_out), // If not provided, defaults to 0 - auto_handle_wsol: true, // Automatically handle wSOL wrapping/unwrapping - })), + // 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?; @@ -548,13 +525,10 @@ async fn test_raydium_cpmm() -> Result<(), Box> { recent_blockhash, None, false, - Some(Box::new(RaydiumCpmmParams { - pool_state: Some(pool_state), // If not provided, will auto-calculate - mint_token_program: Some(spl_token::ID), // Support spl_token or spl_token_2022::ID - mint_token_in_pool_state_index: Some(1), // Index of mint_token in pool_state, default is at index 1 - minimum_amount_out: Some(sell_sol_amount), // If not provided, defaults to 0 - auto_handle_wsol: true, // Automatically handle wSOL wrapping/unwrapping - })), + // 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?; @@ -591,7 +565,7 @@ async fn test_bonk_sniper_trade_with_shreds(trade_info: BonkTradeEvent) -> AnyRe slippage_basis_points, recent_blockhash, None, - Some(Box::new(BonkParams::from_dev_trade(trade_info))), + Box::new(BonkParams::from_dev_trade(trade_info.clone())), None, ).await?; @@ -606,7 +580,7 @@ async fn test_bonk_sniper_trade_with_shreds(trade_info: BonkTradeEvent) -> AnyRe recent_blockhash, None, false, - None, + Box::new(BonkParams::from_dev_trade(trade_info)), None, ).await?; @@ -634,7 +608,7 @@ async fn test_bonk_copy_trade_with_grpc(trade_info: BonkTradeEvent) -> AnyResult slippage_basis_points, recent_blockhash, None, - Some(Box::new(BonkParams::from_trade(trade_info))), + Box::new(BonkParams::from_trade(trade_info.clone())), None, ).await?; @@ -649,7 +623,7 @@ async fn test_bonk_copy_trade_with_grpc(trade_info: BonkTradeEvent) -> AnyResult recent_blockhash, None, false, - None, + Box::new(BonkParams::from_trade(trade_info)), None, ).await?; @@ -677,7 +651,8 @@ async fn test_bonk() -> Result<(), Box> { slippage_basis_points, recent_blockhash, None, - 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?; @@ -694,7 +669,8 @@ async fn test_bonk() -> Result<(), Box> { recent_blockhash, None, false, - 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?; diff --git a/README_CN.md b/README_CN.md index 9bd3abd..7178686 100755 --- a/README_CN.md +++ b/README_CN.md @@ -31,14 +31,14 @@ git clone https://github.com/0xfnzero/sol-trade-sdk ```toml # 添加到您的 Cargo.toml -sol-trade-sdk = { path = "./sol-trade-sdk", version = "0.3.5" } +sol-trade-sdk = { path = "./sol-trade-sdk", version = "0.4.0" } ``` ### 使用 crates.io ```toml # 添加到您的 Cargo.toml -sol-trade-sdk = "0.3.5" +sol-trade-sdk = "0.4.0" ``` ## 使用示例 @@ -186,6 +186,7 @@ async fn test_grpc() -> Result<(), Box> { transaction_filter, account_filter, None, + None, callback, ) .await?; @@ -247,7 +248,7 @@ async fn test_shreds() -> Result<(), Box> { println!("开始监听事件,按 Ctrl+C 停止..."); let protocols = vec![Protocol::PumpFun, Protocol::PumpSwap, Protocol::Bonk, Protocol::RaydiumCpmm]; shred_stream - .shredstream_subscribe(protocols, None, callback) + .shredstream_subscribe(protocols, None, None, callback) .await?; Ok(()) @@ -312,10 +313,11 @@ async fn test_create_solana_trade_client() -> AnyResult { ```rust use sol_trade_sdk::{ - common::bonding_curve::BondingCurveAccount, + 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; // pumpfun 狙击者交易 async fn test_pumpfun_sniper_trade_with_shreds(trade_info: PumpFunTradeEvent) -> AnyResult<()> { @@ -337,14 +339,6 @@ async fn test_pumpfun_sniper_trade_with_shreds(trade_info: PumpFunTradeEvent) -> println!("Buying tokens from PumpFun..."); - // 不使用rpc调用获取bonding_curve,可以节约交易时间 - let bonding_curve = BondingCurveAccount::from_dev_trade( - &mint_pubkey, - dev_token_amount, - dev_sol_amount, - creator, - ); - // 我本次交易所花的的sol金额 let buy_sol_amount = 100_000; trade_client.buy( @@ -355,9 +349,13 @@ async fn test_pumpfun_sniper_trade_with_shreds(trade_info: PumpFunTradeEvent) -> slippage_basis_points, recent_blockhash, None, - Some(Box::new(PumpFunParams { - bonding_curve: Some(Arc::new(bonding_curve.clone())), - })), + Box::new(PumpFunParams::from_dev_trade( + &mint_pubkey, + dev_token_amount, + dev_sol_amount, + creator, + None, + )), None, ) .await?; @@ -381,8 +379,6 @@ async fn test_pumpfun_copy_trade_with_grpc(trade_info: PumpFunTradeEvent) -> Any // 我本次交易所花的的sol金额 let buy_sol_amount = 100_000; - // 不使用rpc调用获取bonding_curve,可以节约交易时间 - let bonding_curve = BondingCurveAccount::from_trade(&trade_info); trade_client.buy( DexType::PumpFun, mint_pubkey, @@ -391,9 +387,7 @@ async fn test_pumpfun_copy_trade_with_grpc(trade_info: PumpFunTradeEvent) -> Any slippage_basis_points, recent_blockhash, None, - Some(Box::new(PumpFunParams { - bonding_curve: Some(Arc::new(bonding_curve.clone())), - })), + Box::new(PumpFunParams::from_trade(&trade_info, None)), None, ) .await?; @@ -402,7 +396,13 @@ async fn test_pumpfun_copy_trade_with_grpc(trade_info: PumpFunTradeEvent) -> Any } // pumpfun 卖出token -async fn test_pumpfun_sell() -> AnyResult<()> { +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, @@ -413,17 +413,23 @@ async fn test_pumpfun_sell() -> AnyResult<()> { recent_blockhash, None, false, - None, + Box::new(PumpFunParams::from_trade(&trade_info, None)), None, ) .await?; + + Ok(()) } ``` ### 4. PumpSwap 交易操作 ```rust -use sol_trade_sdk::trading::core::params::PumpSwapParams; +use sol_trade_sdk::{ + common::AnyResult, + trading::{core::params::PumpSwapParams, factory::DexType}, +}; +use solana_sdk::{pubkey::Pubkey, str::FromStr}; async fn test_pumpswap() -> AnyResult<()> { println!("Testing PumpSwap trading..."); @@ -450,14 +456,8 @@ async fn test_pumpswap() -> AnyResult<()> { slippage_basis_points, recent_blockhash, None, - Some(Box::new(PumpSwapParams { - pool: Some(pool_address), - base_mint: Some(base_mint), - quote_mint: Some(quote_mint), - pool_base_token_reserves: Some(pool_base_token_reserves), - pool_quote_token_reserves: Some(pool_quote_token_reserves), - auto_handle_wsol: true, - })), + // 通过 RPC 调用,会增加延迟。可以通过使用 from_buy_trade 或手动初始化 PumpSwapParams 来优化 + Box::new(PumpSwapParams::from_pool_address_by_rpc(&client.rpc, &pool_address).await?), None, ).await?; @@ -473,14 +473,8 @@ async fn test_pumpswap() -> AnyResult<()> { recent_blockhash, None, false, - Some(Box::new(PumpSwapParams { - pool: Some(pool_address), - base_mint: Some(base_mint), - quote_mint: Some(quote_mint), - pool_base_token_reserves: Some(pool_base_token_reserves), - pool_quote_token_reserves: Some(pool_quote_token_reserves), - auto_handle_wsol: true, - })), + // 通过 RPC 调用,会增加延迟。可以通过使用 from_sell_trade 或手动初始化 PumpSwapParams 来优化 + Box::new(PumpSwapParams::from_pool_address_by_rpc(&client.rpc, &pool_address).await?), None, ).await?; @@ -498,6 +492,7 @@ use sol_trade_sdk::{ 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 标准(如果需要) @@ -524,13 +519,10 @@ async fn test_raydium_cpmm() -> Result<(), Box> { slippage_basis_points, recent_blockhash, None, - Some(Box::new(RaydiumCpmmParams { - pool_state: Some(pool_state), // 如果不传,会自动计算 - mint_token_program: Some(spl_token::ID), // 支持 spl_token 或 spl_token_2022::ID - mint_token_in_pool_state_index: Some(1), // mint_token 在 pool_state 中的索引,默认在索引1 - minimum_amount_out: Some(buy_amount_out), // 如果不传,默认为0 - auto_handle_wsol: true, // 自动处理 wSOL 包装/解包装 - })), + // 通过 RPC 调用,会增加延迟,或手动初始化 RaydiumCpmmParams + Box::new( + RaydiumCpmmParams::from_pool_address_by_rpc(&trade_client.rpc, &pool_state).await?, + ), None, ).await?; @@ -547,13 +539,10 @@ async fn test_raydium_cpmm() -> Result<(), Box> { recent_blockhash, None, false, - Some(Box::new(RaydiumCpmmParams { - pool_state: Some(pool_state), // 如果不传,会自动计算 - mint_token_program: Some(spl_token::ID), // 支持 spl_token 或 spl_token_2022::ID - mint_token_in_pool_state_index: Some(1), // mint_token 在 pool_state 中的索引,默认在索引1 - minimum_amount_out: Some(sell_sol_amount), // 如果不传,默认为0 - auto_handle_wsol: true, // 自动处理 wSOL 包装/解包装 - })), + // 通过 RPC 调用,会增加延迟,或手动初始化 RaydiumCpmmParams + Box::new( + RaydiumCpmmParams::from_pool_address_by_rpc(&trade_client.rpc, &pool_state).await?, + ), None, ).await?; @@ -564,6 +553,12 @@ async fn test_raydium_cpmm() -> Result<(), Box> { ### 6. 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}; // bonk 狙击者交易 async fn test_bonk_sniper_trade_with_shreds(trade_info: BonkTradeEvent) -> AnyResult<()> { @@ -590,7 +585,7 @@ async fn test_bonk_sniper_trade_with_shreds(trade_info: BonkTradeEvent) -> AnyRe slippage_basis_points, recent_blockhash, None, - Some(Box::new(BonkParams::from_dev_trade(trade_info))), + Box::new(BonkParams::from_dev_trade(trade_info.clone())), None, ).await?; @@ -605,7 +600,7 @@ async fn test_bonk_sniper_trade_with_shreds(trade_info: BonkTradeEvent) -> AnyRe recent_blockhash, None, false, - None, + Box::new(BonkParams::from_dev_trade(trade_info)), None, ).await?; @@ -633,7 +628,7 @@ async fn test_bonk_copy_trade_with_grpc(trade_info: BonkTradeEvent) -> AnyResult slippage_basis_points, recent_blockhash, None, - Some(Box::new(BonkParams::from_trade(trade_info))), + Box::new(BonkParams::from_trade(trade_info.clone())), None, ).await?; @@ -648,7 +643,7 @@ async fn test_bonk_copy_trade_with_grpc(trade_info: BonkTradeEvent) -> AnyResult recent_blockhash, None, false, - None, + Box::new(BonkParams::from_trade(trade_info)), None, ).await?; @@ -676,7 +671,8 @@ async fn test_bonk() -> Result<(), Box> { slippage_basis_points, recent_blockhash, None, - None, + // 通过 RPC 调用,会增加延迟。可以通过使用 from_trade 或手动初始化 BonkParams 来优化 + Box::new(BonkParams::from_mint_by_rpc(&trade_client.rpc, &mint_pubkey).await?), None, ) .await?; @@ -693,7 +689,8 @@ async fn test_bonk() -> Result<(), Box> { recent_blockhash, None, false, - None, + // 通过 RPC 调用,会增加延迟。可以通过使用 from_trade 或手动初始化 BonkParams 来优化 + Box::new(BonkParams::from_mint_by_rpc(&trade_client.rpc, &mint_pubkey).await?), None, ) .await?; diff --git a/src/main.rs b/src/main.rs index ad16275..ca0977e 100755 --- a/src/main.rs +++ b/src/main.rs @@ -219,7 +219,7 @@ async fn test_pumpswap() -> AnyResult<()> { slippage_basis_points, recent_blockhash, None, - // 经过 rpc,增加耗时,可以通过from_buy_trade或者自行初始化PumpSwapParams参数来优化耗时 + // 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, ) @@ -238,7 +238,7 @@ async fn test_pumpswap() -> AnyResult<()> { recent_blockhash, None, false, - // 经过 rpc,增加耗时,可以通过from_sell_trade或者自行初始化PumpSwapParams参数来优化耗时 + // 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, ) @@ -363,7 +363,7 @@ async fn test_bonk() -> Result<(), Box> { slippage_basis_points, recent_blockhash, None, - // 经过 rpc,增加耗时,可以通过from_trade或者自行初始化BonkParams参数来优化耗时 + // Through RPC call, adds latency. Can optimize by using from_trade or manually initializing BonkParams Box::new(BonkParams::from_mint_by_rpc(&client.rpc, &mint_pubkey).await?), None, ) @@ -382,7 +382,7 @@ async fn test_bonk() -> Result<(), Box> { recent_blockhash, None, false, - // 经过 rpc,增加耗时,可以通过from_trade或者自行初始化BonkParams参数来优化耗时 + // Through RPC call, adds latency. Can optimize by using from_trade or manually initializing BonkParams Box::new(BonkParams::from_mint_by_rpc(&client.rpc, &mint_pubkey).await?), None, ) @@ -411,7 +411,7 @@ async fn test_raydium_cpmm() -> Result<(), Box> { slippage_basis_points, recent_blockhash, None, - // 经过 rpc,增加耗时,或者自行初始化RaydiumCpmmParams参数 + // Through RPC call, adds latency, or manually initialize RaydiumCpmmParams Box::new( RaydiumCpmmParams::from_pool_address_by_rpc(&client.rpc, &pool_address).await?, ), @@ -432,7 +432,7 @@ async fn test_raydium_cpmm() -> Result<(), Box> { recent_blockhash, None, false, - // 经过 rpc,增加耗时,或者自行初始化RaydiumCpmmParams参数 + // Through RPC call, adds latency, or manually initialize RaydiumCpmmParams Box::new( RaydiumCpmmParams::from_pool_address_by_rpc(&client.rpc, &pool_address).await?, ), diff --git a/src/utils/calc/bonk.rs b/src/utils/calc/bonk.rs index 5432344..21a3d73 100644 --- a/src/utils/calc/bonk.rs +++ b/src/utils/calc/bonk.rs @@ -83,7 +83,7 @@ pub fn get_sell_sol_amount_from_token_amount( let amount_in_u128 = amount_in as u128; // For sell operation, input_reserve is token reserves, output_reserve is SOL reserves - let input_reserve = virtual_base.checked_add(real_base).unwrap(); + let input_reserve = virtual_base.checked_sub(real_base).unwrap(); let output_reserve = virtual_quote.checked_add(real_quote).unwrap(); // Use constant product formula to calculate SOL amount received from selling tokens