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<T> instead of Option<Box<T>>
- 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
This commit is contained in:
ysq
2025-08-18 20:41:01 +08:00
parent 57474bfa6e
commit d02d734026
5 changed files with 96 additions and 123 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "sol-trade-sdk"
version = "0.3.5"
version = "0.4.0"
edition = "2021"
authors = ["William <byteblock6@gmail.com>", "sgxiang <sgxiang@gmail.com>", "wei <1415121722@qq.com>"]
repository = "https://github.com/0xfnzero/sol-trade-sdk"
+32 -56
View File
@@ -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<dyn std::error::Error>> {
transaction_filter,
account_filter,
None,
None,
callback,
)
.await?;
@@ -247,7 +248,7 @@ async fn test_shreds() -> Result<(), Box<dyn std::error::Error>> {
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<dyn std::error::Error>> {
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<dyn std::error::Error>> {
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<dyn std::error::Error>> {
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<dyn std::error::Error>> {
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?;
+56 -59
View File
@@ -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<dyn std::error::Error>> {
transaction_filter,
account_filter,
None,
None,
callback,
)
.await?;
@@ -247,7 +248,7 @@ async fn test_shreds() -> Result<(), Box<dyn std::error::Error>> {
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<SolanaTrade> {
```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<dyn std::error::Error>> {
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<dyn std::error::Error>> {
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<dyn std::error::Error>> {
### 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<dyn std::error::Error>> {
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<dyn std::error::Error>> {
recent_blockhash,
None,
false,
None,
// 通过 RPC 调用,会增加延迟。可以通过使用 from_trade 或手动初始化 BonkParams 来优化
Box::new(BonkParams::from_mint_by_rpc(&trade_client.rpc, &mint_pubkey).await?),
None,
)
.await?;
+6 -6
View File
@@ -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<dyn std::error::Error>> {
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<dyn std::error::Error>> {
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<dyn std::error::Error>> {
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<dyn std::error::Error>> {
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?,
),
+1 -1
View File
@@ -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