feat: refactor trade API to return execution status with signature

- Bump version from 1.2.0 to 1.2.1
- Change trade methods return type from Signature to (bool, Signature)
- Improve parallel execution error handling and status tracking
- Update CLI examples to match new API interface
- Enhance transaction success/failure reporting capabilities

Breaking Change: Trade API now returns tuple (success_status, signature)
This commit is contained in:
ysq
2025-09-25 21:39:14 +08:00
parent ce5243702c
commit 3962a278b9
8 changed files with 61 additions and 45 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "sol-trade-sdk"
version = "1.2.0"
version = "1.2.1"
edition = "2021"
authors = [
"William <byteblock6@gmail.com>",
+2 -2
View File
@@ -87,14 +87,14 @@ Add the dependency to your `Cargo.toml`:
```toml
# Add to your Cargo.toml
sol-trade-sdk = { path = "./sol-trade-sdk", version = "1.2.0" }
sol-trade-sdk = { path = "./sol-trade-sdk", version = "1.2.1" }
```
### Use crates.io
```toml
# Add to your Cargo.toml
sol-trade-sdk = "1.2.0"
sol-trade-sdk = "1.2.1"
```
## 🛠️ Usage Examples
+2 -2
View File
@@ -87,14 +87,14 @@ git clone https://github.com/0xfnzero/sol-trade-sdk
```toml
# 添加到您的 Cargo.toml
sol-trade-sdk = { path = "./sol-trade-sdk", version = "1.2.0" }
sol-trade-sdk = { path = "./sol-trade-sdk", version = "1.2.1" }
```
### 使用 crates.io
```toml
# 添加到您的 Cargo.toml
sol-trade-sdk = "1.2.0"
sol-trade-sdk = "1.2.1"
```
## 🛠️ 使用示例
+19 -19
View File
@@ -620,9 +620,9 @@ async fn handle_buy_pumpfun(
durable_nonce: None,
};
match client.buy(buy_params).await {
Ok(signature) => {
Ok((_, signature)) => {
println!(" ✅ Successfully bought tokens from PumpFun!");
println!(" ✅ Transaction Signature: {}", signature);
println!(" ✅ Transaction Signature: {:?}", signature);
}
Err(e) => {
println!(" ❌ Failed to buy tokens from PumpFun: {}", e);
@@ -669,9 +669,9 @@ async fn handle_buy_pumpswap(
durable_nonce: None,
};
match client.buy(buy_params).await {
Ok(signature) => {
Ok((_, signature)) => {
println!(" ✅ Successfully bought tokens from PumpSwap!");
println!(" ✅ Transaction Signature: {}", signature);
println!(" ✅ Transaction Signature: {:?}", signature);
}
Err(e) => {
println!(" ❌ Failed to buy tokens from PumpSwap: {}", e);
@@ -717,9 +717,9 @@ async fn handle_buy_bonk(
durable_nonce: None,
};
match client.buy(buy_params).await {
Ok(signature) => {
Ok((_, signature)) => {
println!(" ✅ Successfully bought tokens from Bonk!");
println!(" ✅ Transaction Signature: {}", signature);
println!(" ✅ Transaction Signature: {:?}", signature);
}
Err(e) => {
println!(" ❌ Failed to buy tokens from Bonk: {}", e);
@@ -769,9 +769,9 @@ async fn handle_buy_raydium_v4(
durable_nonce: None,
};
match client.buy(buy_params).await {
Ok(signature) => {
Ok((_, signature)) => {
println!(" ✅ Successfully bought tokens from Raydium V4!");
println!(" ✅ Transaction Signature: {}", signature);
println!(" ✅ Transaction Signature: {:?}", signature);
}
Err(e) => {
println!(" ❌ Failed to buy tokens from Raydium V4: {}", e);
@@ -821,9 +821,9 @@ async fn handle_buy_raydium_cpmm(
durable_nonce: None,
};
match client.buy(buy_params).await {
Ok(signature) => {
Ok((_, signature)) => {
println!(" ✅ Successfully bought tokens from Raydium CPMM!");
println!(" ✅ Transaction Signature: {}", signature);
println!(" ✅ Transaction Signature: {:?}", signature);
}
Err(e) => {
println!(" ❌ Failed to buy tokens from Raydium CPMM: {}", e);
@@ -984,9 +984,9 @@ async fn handle_sell_pumpfun(
};
match client.sell(sell_params).await {
Ok(signature) => {
Ok((_, signature)) => {
println!(" ✅ Successfully sold tokens from PumpFun!");
println!(" ✅ Transaction Signature: {}", signature);
println!(" ✅ Transaction Signature: {:?}", signature);
}
Err(e) => {
println!(" ❌ Failed to sell tokens from PumpFun: {}", e);
@@ -1035,7 +1035,7 @@ async fn handle_sell_pumpswap(
durable_nonce: None,
};
match client.sell(sell_params).await {
Ok(signature) => {
Ok((_, signature)) => {
println!(" ✅ Successfully sold tokens from PumpSwap!");
println!(" ✅ Transaction Signature: {}", signature);
}
@@ -1086,9 +1086,9 @@ async fn handle_sell_bonk(
durable_nonce: None,
};
match client.sell(sell_params).await {
Ok(signature) => {
Ok((_, signature)) => {
println!(" ✅ Successfully sold tokens from Bonk!");
println!(" ✅ Transaction Signature: {}", signature);
println!(" ✅ Transaction Signature: {:?}", signature);
}
Err(e) => {
println!(" ❌ Failed to sell tokens from Bonk: {}", e);
@@ -1140,9 +1140,9 @@ async fn handle_sell_raydium_v4(
durable_nonce: None,
};
match client.sell(sell_params).await {
Ok(signature) => {
Ok((_, signature)) => {
println!(" ✅ Successfully sold tokens from Raydium V4!");
println!(" ✅ Transaction Signature: {}", signature);
println!(" ✅ Transaction Signature: {:?}", signature);
}
Err(e) => {
println!(" ❌ Failed to sell tokens from Raydium V4: {}", e);
@@ -1194,9 +1194,9 @@ async fn handle_sell_raydium_cpmm(
durable_nonce: None,
};
match client.sell(sell_params).await {
Ok(signature) => {
Ok((_, signature)) => {
println!(" ✅ Successfully sold tokens from Raydium CPMM!");
println!(" ✅ Transaction Signature: {}", signature);
println!(" ✅ Transaction Signature: {:?}", signature);
}
Err(e) => {
println!(" ❌ Failed to sell tokens from Raydium CPMM: {}", e);
+3 -3
View File
@@ -257,7 +257,7 @@ impl SolanaTrade {
/// - Network or RPC errors occur
/// - Insufficient SOL balance for the purchase
/// - Required accounts cannot be created or accessed
pub async fn buy(&self, params: TradeBuyParams) -> Result<Signature, anyhow::Error> {
pub async fn buy(&self, params: TradeBuyParams) -> Result<(bool, Signature), anyhow::Error> {
if params.slippage_basis_points.is_none() {
println!(
"slippage_basis_points is none, use default slippage basis points: {}",
@@ -346,7 +346,7 @@ impl SolanaTrade {
/// - Insufficient token balance for the sale
/// - Token account doesn't exist or is not properly initialized
/// - Required accounts cannot be created or accessed
pub async fn sell(&self, params: TradeSellParams) -> Result<Signature, anyhow::Error> {
pub async fn sell(&self, params: TradeSellParams) -> Result<(bool, Signature), anyhow::Error> {
if params.slippage_basis_points.is_none() {
println!(
"slippage_basis_points is none, use default slippage basis points: {}",
@@ -447,7 +447,7 @@ impl SolanaTrade {
mut params: TradeSellParams,
amount_token: u64,
percent: u64,
) -> Result<Signature, anyhow::Error> {
) -> Result<(bool, Signature), anyhow::Error> {
if percent == 0 || percent > 100 {
return Err(anyhow::anyhow!("Percentage must be between 1 and 100"));
}
+1 -1
View File
@@ -26,7 +26,7 @@ impl GenericTradeExecutor {
#[async_trait::async_trait]
impl TradeExecutor for GenericTradeExecutor {
async fn swap(&self, params: SwapParams) -> Result<Signature> {
async fn swap(&self, params: SwapParams) -> Result<(bool, Signature)> {
let start = Instant::now();
// 暂时支持这三种。后续重构扩展builder 支持所有的 swap
let is_buy = params.input_mint == crate::constants::SOL_TOKEN_ACCOUNT
+32 -16
View File
@@ -18,7 +18,7 @@ pub async fn buy_parallel_execute(
params: SwapParams,
instructions: Vec<Instruction>,
protocol_name: &'static str,
) -> Result<Signature> {
) -> Result<(bool, Signature)> {
parallel_execute(
params.swqos_clients,
params.payer,
@@ -41,7 +41,7 @@ pub async fn sell_parallel_execute(
params: SwapParams,
instructions: Vec<Instruction>,
protocol_name: &'static str,
) -> Result<Signature> {
) -> Result<(bool, Signature)> {
parallel_execute(
params.swqos_clients,
params.payer,
@@ -75,7 +75,7 @@ async fn parallel_execute(
is_buy: bool,
wait_transaction_confirmed: bool,
with_tip: bool,
) -> Result<Signature> {
) -> Result<(bool, Signature)> {
if swqos_clients.is_empty() {
return Err(anyhow!("swqos_clients is empty"));
}
@@ -88,7 +88,8 @@ async fn parallel_execute(
return Err(anyhow!("No Rpc Default Swqos configured."));
}
let cores = core_affinity::get_core_ids().unwrap();
let mut handles: Vec<JoinHandle<Result<Signature>>> = Vec::with_capacity(swqos_clients.len());
let mut handles: Vec<JoinHandle<Result<(bool, Signature, anyhow::Error)>>> =
Vec::with_capacity(swqos_clients.len());
let instructions = Arc::new(instructions);
@@ -169,12 +170,21 @@ async fn parallel_execute(
start = Instant::now();
swqos_client
let mut err = None;
let success = match swqos_client
.send_transaction(
if is_buy { TradeType::Buy } else { TradeType::Sell },
&transaction,
)
.await?;
.await
{
Ok(()) => true,
Err(e) => {
err = Some(e);
false
}
};
println!(
"[{:?}] - [{:?}] - Submitting transaction instructions: {:?}",
@@ -183,11 +193,11 @@ async fn parallel_execute(
start.elapsed()
);
transaction
.signatures
.first()
.ok_or_else(|| anyhow!("Transaction has no signatures"))
.cloned()
if let Some(signature) = transaction.signatures.first() {
return Ok((success, signature.clone(), err.unwrap()));
} else {
return Err(anyhow!("Transaction has no signatures"));
}
});
handles.push(handle);
@@ -211,7 +221,7 @@ async fn parallel_execute(
if !wait_transaction_confirmed {
if let Some(result) = rx.recv().await {
match result {
Ok(Ok(sig)) => return Ok(sig),
Ok(Ok((success, sig, _))) => return Ok((success, sig)),
Ok(Err(e)) => errors.push(format!("Task error: {}", e)),
Err(e) => errors.push(format!("Join error: {}", e)),
}
@@ -219,16 +229,22 @@ async fn parallel_execute(
return Err(anyhow!("No transaction signature available"));
}
let mut last_signature = None;
while let Some(result) = rx.recv().await {
match result {
Ok(Ok(sig)) => {
return Ok(sig);
Ok(Ok((success, sig, err))) => {
if success {
return Ok((success, sig));
}
errors.push(format!("Task error: {}", err));
last_signature = Some(sig);
}
Ok(Err(e)) => errors.push(format!("Task error: {}", e)),
Err(e) => errors.push(format!("Join error: {}", e)),
}
}
// If no success, return error
return Err(anyhow!("All transactions failed: {:?}", errors));
println!("All transactions failed: {:?}", errors);
return Ok((false, last_signature.unwrap()));
}
+1 -1
View File
@@ -5,7 +5,7 @@ use solana_sdk::{instruction::Instruction, signature::Signature};
/// 交易执行器trait - 定义了所有交易协议都需要实现的核心方法
#[async_trait::async_trait]
pub trait TradeExecutor: Send + Sync {
async fn swap(&self, params: SwapParams) -> Result<Signature>;
async fn swap(&self, params: SwapParams) -> Result<(bool, Signature)>;
/// 获取协议名称
fn protocol_name(&self) -> &'static str;
}