update error

This commit is contained in:
William
2025-02-12 22:46:09 +08:00
parent 2b69bfc2a5
commit ca00680215
3 changed files with 77 additions and 68 deletions
+4 -4
View File
@@ -3,6 +3,7 @@ use std::env;
use anyhow::{Context, Result};
use reqwest::Proxy;
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
use std::convert::TryFrom;
use super::TipPercentileData;
@@ -45,17 +46,16 @@ pub async fn get_tip_accounts(block_engine_url: &str) -> Result<RpcResponse> {
println!("result: {:?}", result);
Ok(result)
}
/// tip accounts
#[derive(Debug)]
pub struct TipAccountResult {
pub accounts: Vec<String>,
}
impl TryFrom<RpcResponse> for TipAccountResult {
type Error = anyhow::Error;
fn try_from(value: RpcResponse) -> Result<Self, Self::Error> {
impl TipAccountResult {
pub fn from(value: Value) -> Result<Self> {
let accounts = value
.result
.as_array()
.context("expected 'result' to be an array")?
.iter()
+26 -16
View File
@@ -1,7 +1,7 @@
use std::{convert::TryInto, future::Future, str::FromStr, time::Duration, fmt};
use anyhow::{anyhow, Result};
use api::{get_tip_accounts, TipAccountResult};
use api::TipAccountResult;
use rand::{rng, seq::IteratorRandom};
use reqwest::Client;
use serde::Deserialize;
@@ -95,7 +95,19 @@ impl JitoClient {
}
}
async fn send_request(&self, endpoint: &str, method: &str, params: Option<Value>) -> ClientResult<Value> {
pub async fn get_tip_accounts(&self) -> Result<TipAccountResult> {
let endpoint = if let Some(uuid) = &self.uuid {
format!("/bundles?uuid={}", uuid)
} else {
"/bundles".to_string()
};
let result = self.send_request(&endpoint, "getTipAccounts", None).await?;
let tip_accounts = TipAccountResult::from(result).map_err(|e| anyhow!(e))?;
Ok(tip_accounts)
}
async fn send_request(&self, endpoint: &str, method: &str, params: Option<Value>) -> Result<Value> {
let url = format!("{}{}", self.base_url, endpoint);
let data = json!({
@@ -111,16 +123,16 @@ impl JitoClient {
.json(&data)
.send()
.await
.map_err(|e| ClientError::Other(format!("Request failed: {}", e)))?;
.map_err(|e| anyhow!(format!("Request failed: {}", e)))?;
let body = response.json::<Value>().await
.map_err(|e| ClientError::Other(format!("Failed to parse response: {}", e)))?;
.map_err(|e| anyhow!(format!("Failed to parse response: {}", e)))?;
Ok(body)
}
pub async fn init_tip_accounts(&self) -> Result<()> {
let accounts: TipAccountResult = get_tip_accounts(&self.base_url).await?.try_into()?;
let accounts = self.get_tip_accounts().await?;
let mut tip_accounts = self.tip_accounts.write().await;
accounts
@@ -186,10 +198,10 @@ impl JitoClient {
pub async fn send_transaction(
&self,
transaction: &Transaction,
) -> ClientResult<String> {
) -> Result<String, anyhow::Error> {
let wire_transaction = bincode::serialize(transaction).map_err(|e| {
ClientError::Parse(
"Transaction serialization failed".to_string(),
anyhow!(
"Transaction serialization failed: {}",
e.to_string(),
)
})?;
@@ -208,13 +220,10 @@ impl JitoClient {
response["result"]
.as_str()
.map(|s| s.to_string())
.ok_or_else(|| ClientError::Parse(
"Invalid response format".to_string(),
"Missing result field".to_string(),
))
.ok_or_else(|| anyhow!("Invalid response format: missing result field"))
}
pub async fn send_bundle(&self, params: Option<Value>, uuid: Option<String>) -> ClientResult<Value> {
pub async fn send_bundle(&self, params: Option<Value>, uuid: Option<String>) -> Result<Value> {
let mut endpoint = "/bundles".to_string();
if let Some(uuid) = uuid {
@@ -225,14 +234,14 @@ impl JitoClient {
let transactions = match params {
Some(Value::Array(transactions)) => {
if transactions.is_empty() {
return Err(ClientError::Other("Bundle must contain at least one transaction".to_string()));
return Err(anyhow!("Bundle must contain at least one transaction"));
}
if transactions.len() > 5 {
return Err(ClientError::Other("Bundle can contain at most 5 transactions".to_string()));
return Err(anyhow!("Bundle can contain at most 5 transactions"));
}
transactions
},
_ => return Err(ClientError::Other("Invalid bundle format: expected an array of transactions".to_string())),
_ => return Err(anyhow!("Invalid bundle format: expected an array of transactions")),
};
// Wrap the transactions array in another array
@@ -241,6 +250,7 @@ impl JitoClient {
// Send the wrapped transactions array
self.send_request(&endpoint, "sendBundle", Some(params))
.await
.map_err(|e| anyhow!(e))
}
pub async fn wait_for_bundle_confirmation<F, Fut>(
+47 -48
View File
@@ -32,7 +32,6 @@ use std::sync::Arc;
use std::time::Instant;
use crate::jito::JitoClient;
use crate::error::ClientError;
use borsh::BorshDeserialize;
@@ -102,10 +101,10 @@ impl PumpFun {
mint: &Keypair,
metadata: utils::CreateTokenMetadata,
priority_fee: Option<PriorityFee>,
) -> Result<Signature, ClientError> {
) -> Result<Signature, anyhow::Error> {
let ipfs = utils::create_token_metadata(metadata)
.await
.map_err(ClientError::UploadMetadataError)?;
.map_err(|_| anyhow!("Failed to upload metadata"))?;
let mut instructions = self.create_priority_fee_instructions(priority_fee);
@@ -140,10 +139,10 @@ impl PumpFun {
amount_sol: u64,
slippage_basis_points: Option<u64>,
priority_fee: Option<PriorityFee>,
) -> Result<Signature, ClientError> {
) -> Result<Signature, anyhow::Error> {
let ipfs = utils::create_token_metadata(metadata)
.await
.map_err(ClientError::UploadMetadataError)?;
.map_err(|e| anyhow!(e.to_string()))?;
let global_account = self.get_global_account()?;
let buy_amount = global_account.get_initial_buy_price(amount_sol);
@@ -202,12 +201,12 @@ impl PumpFun {
amount_sol: u64,
slippage_basis_points: Option<u64>,
priority_fee: Option<PriorityFee>,
) -> Result<Signature, ClientError> {
) -> Result<Signature, anyhow::Error> {
let global_account = self.get_global_account()?;
let bonding_curve_account = self.get_bonding_curve_account(mint)?;
let buy_amount = bonding_curve_account
.get_buy_price(amount_sol)
.map_err(ClientError::BondingCurveError)?;
.map_err(|e| anyhow!(e))?;
let buy_amount_with_slippage =
utils::calculate_with_slippage_buy(amount_sol, slippage_basis_points.unwrap_or(DEFAULT_SLIPPAGE));
@@ -253,11 +252,11 @@ impl PumpFun {
max_sol_cost: u64,
slippage_basis_points: Option<u64>,
jito_fee: Option<u64>,
) -> Result<String, ClientError> {
) -> Result<String, anyhow::Error> {
let start_time = Instant::now();
let jito_client = self.jito_client.as_ref()
.ok_or_else(|| ClientError::Other("Jito client not found".to_string()))?;
.ok_or_else(|| anyhow!("Jito client not found"))?;
let global_account = self.get_global_account()?;
let buy_amount_with_slippage =
@@ -315,22 +314,22 @@ impl PumpFun {
amount_token: Option<u64>,
slippage_basis_points: Option<u64>,
priority_fee: Option<PriorityFee>,
) -> Result<Signature, ClientError> {
) -> Result<Signature, anyhow::Error> {
let ata = get_associated_token_address(&self.payer.pubkey(), mint);
let balance = self.rpc.get_token_account_balance(&ata)?;
let balance_u64 = balance.amount.parse::<u64>()
.map_err(|_| ClientError::Other("Failed to parse token balance".to_string()))?;
.map_err(|_| anyhow!("Failed to parse token balance"))?;
let amount = amount_token.unwrap_or(balance_u64);
if amount == 0 {
return Err(ClientError::Other("Balance is 0".to_string()));
return Err(anyhow!("Balance is 0"));
}
let global_account = self.get_global_account()?;
let bonding_curve_account = self.get_bonding_curve_account(mint)?;
let min_sol_output = bonding_curve_account
.get_sell_price(amount, global_account.fee_basis_points)
.map_err(ClientError::BondingCurveError)?;
.map_err(|e| anyhow!(e))?;
let min_sol_output_with_slippage = utils::calculate_with_slippage_sell(
min_sol_output,
slippage_basis_points.unwrap_or(DEFAULT_SLIPPAGE),
@@ -376,18 +375,18 @@ impl PumpFun {
percent: u64,
slippage_basis_points: Option<u64>,
priority_fee: Option<PriorityFee>,
) -> Result<Signature, ClientError> {
) -> Result<Signature, anyhow::Error> {
if percent > 100 {
return Err(ClientError::Other("Percentage must be between 0 and 100".to_string()));
return Err(anyhow!("Percentage must be between 0 and 100"));
}
let ata = get_associated_token_address(&self.payer.pubkey(), mint);
let balance = self.rpc.get_token_account_balance(&ata)?;
let balance_u64 = balance.amount.parse::<u64>()
.map_err(|_| ClientError::Other("Failed to parse token balance".to_string()))?;
.map_err(|_| anyhow!("Failed to parse token balance"))?;
if balance_u64 == 0 {
return Err(ClientError::Other("Balance is 0".to_string()));
return Err(anyhow!("Balance is 0"));
}
let amount = balance_u64 * percent / 100;
@@ -400,18 +399,18 @@ impl PumpFun {
percent: u64,
slippage_basis_points: Option<u64>,
jito_fee: Option<u64>,
) -> Result<String, ClientError> {
) -> Result<String, anyhow::Error> {
if percent > 100 {
return Err(ClientError::Other("Percentage must be between 0 and 100".to_string()));
return Err(anyhow!("Percentage must be between 0 and 100"));
}
let ata = get_associated_token_address(&self.payer.pubkey(), mint);
let balance = self.rpc.get_token_account_balance(&ata)?;
let balance_u64 = balance.amount.parse::<u64>()
.map_err(|_| ClientError::Other("Failed to parse token balance".to_string()))?;
.map_err(|_| anyhow!("Failed to parse token balance"))?;
if balance_u64 == 0 {
return Err(ClientError::Other("Balance is 0".to_string()));
return Err(anyhow!("Balance is 0"));
}
let amount = balance_u64 * percent / 100;
@@ -425,34 +424,34 @@ impl PumpFun {
amount_token: Option<u64>,
slippage_basis_points: Option<u64>,
jito_fee: Option<u64>,
) -> Result<String, ClientError> {
) -> Result<String, anyhow::Error> {
let start_time = Instant::now();
let jito_client = self.jito_client.as_ref()
.ok_or_else(|| ClientError::Other("Jito client not found".to_string()))?;
.ok_or_else(|| anyhow!("Jito client not found"))?;
let ata = get_associated_token_address(&self.payer.pubkey(), mint);
let balance = self.rpc.get_token_account_balance(&ata)?;
let balance_u64 = balance.amount.parse::<u64>()
.map_err(|_| ClientError::Other("Failed to parse token balance".to_string()))?;
.map_err(|_| anyhow!("Failed to parse token balance"))?;
let amount = amount_token.unwrap_or(balance_u64);
if amount == 0 {
return Err(ClientError::Other("Amount cannot be zero".to_string()));
return Err(anyhow!("Amount cannot be zero"));
}
let global_account = self.get_global_account()?;
let bonding_curve_account = self.get_bonding_curve_account(mint)?;
let min_sol_output = bonding_curve_account
.get_sell_price(amount, global_account.fee_basis_points)
.map_err(ClientError::BondingCurveError)?;
.map_err(|e| anyhow!(e))?;
let min_sol_output_with_slippage = utils::calculate_with_slippage_sell(
min_sol_output,
slippage_basis_points.unwrap_or(DEFAULT_SLIPPAGE),
);
let mut instructions = vec![];
let tip_account = jito_client.get_tip_account().await.map_err(|e| ClientError::Other(e.to_string()))?;
let tip_account = jito_client.get_tip_account().await.map_err(|e| anyhow!(e))?;
instructions.push(instruction::sell(
&self.payer.clone(),
mint,
@@ -512,7 +511,7 @@ impl PumpFun {
self.payer.pubkey()
}
pub fn get_token_balance(&self, account: &Pubkey, mint: &Pubkey) -> Result<u64, ClientError> {
pub fn get_token_balance(&self, account: &Pubkey, mint: &Pubkey) -> Result<u64, anyhow::Error> {
let ata = get_associated_token_address(account, mint);
if self.rpc.get_account(&ata).is_err() {
return Ok(0);
@@ -520,18 +519,18 @@ impl PumpFun {
let balance = self.rpc.get_token_account_balance(&ata)?;
balance.amount.parse::<u64>()
.map_err(|_| ClientError::Other("Failed to parse token balance".to_string()))
.map_err(|_| anyhow!("Failed to parse token balance"))
}
pub fn get_sol_balance(&self, account: &Pubkey) -> Result<u64, ClientError> {
self.rpc.get_balance(account).map_err(ClientError::SolanaClientError)
pub fn get_sol_balance(&self, account: &Pubkey) -> Result<u64, anyhow::Error> {
self.rpc.get_balance(account).map_err(|_| anyhow!("Failed to get SOL balance"))
}
pub fn get_payer_token_balance(&self, mint: &Pubkey) -> Result<u64, ClientError> {
pub fn get_payer_token_balance(&self, mint: &Pubkey) -> Result<u64, anyhow::Error> {
self.get_token_balance(&self.payer.pubkey(), mint)
}
pub fn get_payer_sol_balance(&self) -> Result<u64, ClientError> {
pub fn get_payer_sol_balance(&self) -> Result<u64, anyhow::Error> {
self.get_sol_balance(&self.payer.pubkey())
}
@@ -563,22 +562,22 @@ impl PumpFun {
}
// Account related methods
pub fn get_global_account(&self) -> Result<accounts::GlobalAccount, ClientError> {
pub fn get_global_account(&self) -> Result<accounts::GlobalAccount, anyhow::Error> {
let global = Self::get_global_pda();
let account = self.rpc.get_account(&global)?;
accounts::GlobalAccount::try_from_slice(&account.data)
.map_err(ClientError::BorshError)
.map_err(|e| anyhow!(e))
}
pub fn get_bonding_curve_account(
&self,
mint: &Pubkey,
) -> Result<accounts::BondingCurveAccount, ClientError> {
) -> Result<accounts::BondingCurveAccount, anyhow::Error> {
let bonding_curve_pda = Self::get_bonding_curve_pda(mint)
.ok_or(ClientError::BondingCurveNotFound)?;
.ok_or(anyhow!("Bonding curve not found"))?;
let account = self.rpc.get_account(&bonding_curve_pda)?;
accounts::BondingCurveAccount::try_from_slice(&account.data)
.map_err(ClientError::BorshError)
.map_err(|e| anyhow!(e))
}
// Subscription related methods
@@ -636,7 +635,7 @@ impl PumpFun {
})
}
pub async fn get_token_price_in_usdc(&self, token_amount: f64) -> Result<f64, ClientError> {
pub async fn get_token_price_in_usdc(&self, token_amount: f64) -> Result<f64, anyhow::Error> {
if token_amount == 0.0 {
return Ok(0.0);
}
@@ -644,39 +643,39 @@ impl PumpFun {
let url = "https://api.jup.ag/price/v2?ids=So11111111111111111111111111111111111111112";
let response: serde_json::Value = reqwest::get(url)
.await
.map_err(|e: reqwest::Error| ClientError::Other(Box::new(e).to_string()))?
.map_err(|e: reqwest::Error| anyhow!(e))?
.json()
.await
.map_err(|e: reqwest::Error| ClientError::Other(Box::new(e).to_string()))?;
.map_err(|e: reqwest::Error| anyhow!(e))?;
let sol_price_str = response["data"]["So11111111111111111111111111111111111111112"]["price"]
.as_str()
.ok_or(ClientError::Other("Failed to find SOL price as a string".into()))?;
.ok_or(anyhow!("Failed to find SOL price as a string"))?;
let sol_price_in_usdc: f64 = sol_price_str
.parse()
.map_err(|e: std::num::ParseFloatError| ClientError::Other(Box::new(e).to_string()))?;
.map_err(|e: std::num::ParseFloatError| anyhow!(e))?;
let token_price_in_usdc = sol_price_in_usdc * token_amount;
Ok(token_price_in_usdc)
}
pub async fn get_sol_price_in_usdc(&self) -> Result<f64, ClientError> {
pub async fn get_sol_price_in_usdc(&self) -> Result<f64, anyhow::Error> {
let url = "https://api.jup.ag/price/v2?ids=So11111111111111111111111111111111111111112";
let response: serde_json::Value = reqwest::get(url)
.await
.map_err(|_| ClientError::Anyhow("Failed to install crypto provider"))?
.map_err(|_| anyhow!("Failed to install crypto provider"))?
.json()
.await
.map_err(|_| ClientError::Anyhow("Failed to install crypto provider"))?;
.map_err(|_| anyhow!("Failed to install crypto provider"))?;
let sol_price_str = response["data"]["So11111111111111111111111111111111111111112"]["price"]
.as_str()
.ok_or(ClientError::Anyhow("Failed to find SOL price as a string"))?;
.ok_or(anyhow!("Failed to find SOL price as a string"))?;
let sol_price_in_usdc: f64 = sol_price_str
.parse()
.map_err(|_| ClientError::Anyhow("Failed to parse SOL price as a string"))?;
.map_err(|_| anyhow!("Failed to parse SOL price as a string"))?;
Ok(sol_price_in_usdc)
}