mirror of
https://github.com/0xfnzero/solana-streamer.git
synced 2026-08-04 13:07:44 +00:00
add jito transaction
This commit is contained in:
@@ -4,15 +4,12 @@ A comprehensive Rust SDK for seamless interaction with the PumpFun Solana progra
|
||||
|
||||
|
||||
# Explanation
|
||||
This repository is forked from [https://github.com/nhuxhr/pumpfun-rs](https://github.com/nhuxhr/pumpfun-rs).
|
||||
|
||||
1. Change `PumpFun<'a>` to `PumpFun`, and `payer: &'a Keypair` to `payer: Arc<Keypair>`.
|
||||
2. Add `logs_filters` and `logs_paser` to parse the logs of the PumpFun program.
|
||||
3. Add `logs_data` to define the data structure of the logs.
|
||||
4. Add `logs_subscribe` to subscribe the logs of the PumpFun program.
|
||||
5. Add `logs_events` to define the event of the logs.
|
||||
6. Add `logs_parser` to parse the logs.
|
||||
7. Add `jito` to send transaction with Jito.
|
||||
1. Add `logs_filters` to parse the logs.
|
||||
1. Add `logs_parser` to process the logs.
|
||||
2. Add `logs_data` to define the data structure of the logs.
|
||||
4. Add `logs_events` to define the event of the logs.
|
||||
3. Add `logs_subscribe` to subscribe the logs of the PumpFun program.
|
||||
6. Add `jito` to send transaction with Jito.
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -20,7 +17,7 @@ Add the following to your `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
mai3-pumpfun-sdk = "2.4.4"
|
||||
mai3-pumpfun-sdk = "2.4.5"
|
||||
```
|
||||
|
||||
## Usage
|
||||
@@ -47,7 +44,7 @@ let callback = |event: DexEvent| {
|
||||
DexEvent::NewToken(token_info) => {
|
||||
println!("Received new token event: {:?}", token_info);
|
||||
},
|
||||
DexEvent::NewTrade(trade_info) => {
|
||||
DexEvent::NewUserTrade(trade_info) => {
|
||||
println!("Received new trade event: {:?}", trade_info);
|
||||
},
|
||||
DexEvent::NewBotTrade(trade_info) => {
|
||||
|
||||
@@ -29,5 +29,6 @@ base64 = "0.22.1"
|
||||
bs58 = "0.5.1"
|
||||
rand = "0.8.5"
|
||||
bincode = "1.3.3"
|
||||
anyhow = "1.0.90"
|
||||
reqwest = { version = "0.11.27", features = ["json"] }
|
||||
|
||||
|
||||
+179
-162
@@ -1,115 +1,130 @@
|
||||
use rand::Rng;
|
||||
use bincode;
|
||||
use bs58;
|
||||
use reqwest;
|
||||
use serde::Deserialize;
|
||||
use serde_json::{json, Value};
|
||||
use std::fmt;
|
||||
use std::str::FromStr;
|
||||
use std::time::Duration;
|
||||
use tokio::sync::Mutex;
|
||||
use rand::seq::SliceRandom;
|
||||
use reqwest::Client;
|
||||
use serde_json::{json, Value};
|
||||
|
||||
use anchor_client::solana_sdk::{
|
||||
commitment_config::CommitmentConfig,
|
||||
pubkey::Pubkey,
|
||||
signature::Signature,
|
||||
transaction::Transaction,
|
||||
};
|
||||
use crate::error::ClientError;
|
||||
|
||||
pub const MAX_RETRIES: u8 = 3;
|
||||
pub const RETRY_DELAY: Duration = Duration::from_millis(200);
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TransactionConfig {
|
||||
pub skip_preflight: bool,
|
||||
pub preflight_commitment: CommitmentConfig,
|
||||
pub encoding: String,
|
||||
pub last_n_blocks: u64,
|
||||
}
|
||||
|
||||
impl Default for TransactionConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
skip_preflight: true,
|
||||
preflight_commitment: CommitmentConfig::confirmed(),
|
||||
encoding: "base58".to_string(),
|
||||
last_n_blocks: 100,
|
||||
}
|
||||
}
|
||||
}
|
||||
use crate::error::{ClientError, ClientResult};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct JitoClient {
|
||||
endpoint: String,
|
||||
client: reqwest::Client,
|
||||
config: TransactionConfig,
|
||||
base_url: String,
|
||||
uuid: Option<String>,
|
||||
client: Client,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PrettyJsonValue(pub Value);
|
||||
|
||||
impl fmt::Display for PrettyJsonValue {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", serde_json::to_string_pretty(&self.0).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Value> for PrettyJsonValue {
|
||||
fn from(value: Value) -> Self {
|
||||
PrettyJsonValue(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl JitoClient {
|
||||
pub fn new(endpoint: &str) -> Self {
|
||||
pub fn new(base_url: &str, uuid: Option<String>) -> Self {
|
||||
Self {
|
||||
endpoint: endpoint.to_string(),
|
||||
client: reqwest::Client::new(),
|
||||
config: TransactionConfig::default(),
|
||||
base_url: base_url.to_string(),
|
||||
uuid,
|
||||
client: Client::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_tip_account(&self) -> Result<Pubkey, ClientError> {
|
||||
let response = self.send_request("getTipAccounts", json!([])).await?;
|
||||
|
||||
if let Some(accounts) = response["result"].as_array() {
|
||||
if accounts.is_empty() {
|
||||
return Err(ClientError::Other("No JITO tip accounts found".to_string()));
|
||||
}
|
||||
|
||||
let random_index = rand::rngs::OsRng.gen_range(0..accounts.len());
|
||||
if let Some(account) = accounts.get(random_index) {
|
||||
if let Some(address) = account.as_str() {
|
||||
return Pubkey::from_str(address).map_err(|e| {
|
||||
ClientError::Parse(
|
||||
"Invalid tip account address".to_string(),
|
||||
e.to_string(),
|
||||
)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(ClientError::Other("Failed to get Tip Account".to_string()))
|
||||
}
|
||||
|
||||
pub async fn estimate_priority_fees(
|
||||
&self,
|
||||
account: &Pubkey,
|
||||
) -> Result<PriorityFeeEstimate, ClientError> {
|
||||
let params = json!({
|
||||
"last_n_blocks": self.config.last_n_blocks,
|
||||
"account": account.to_string(),
|
||||
"api_version": 2
|
||||
async fn send_request(&self, endpoint: &str, method: &str, params: Option<Value>) -> ClientResult<Value> {
|
||||
let url = format!("{}{}", self.base_url, endpoint);
|
||||
|
||||
let data = json!({
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"method": method,
|
||||
"params": params.unwrap_or(json!([]))
|
||||
});
|
||||
|
||||
let response = self.send_request("qn_estimatePriorityFees", params).await?;
|
||||
// println!("Sending request to: {}", url);
|
||||
// println!("Request body: {}", serde_json::to_string_pretty(&data).unwrap());
|
||||
|
||||
if let Some(result) = response.get("result") {
|
||||
let estimate: PriorityFeeEstimate = serde_json::from_value(result.clone()).map_err(|e| {
|
||||
ClientError::Parse(
|
||||
"Failed to parse priority fee estimate".to_string(),
|
||||
e.to_string(),
|
||||
)
|
||||
})?;
|
||||
let response = self.client
|
||||
.post(&url)
|
||||
.header("Content-Type", "application/json")
|
||||
.json(&data)
|
||||
.send()
|
||||
.await
|
||||
.map_err(|e| ClientError::Other(format!("Request failed: {}", e)))?;
|
||||
|
||||
Ok(estimate)
|
||||
let status = response.status();
|
||||
// println!("Response status: {}", status);
|
||||
|
||||
let body = response.json::<Value>().await
|
||||
.map_err(|e| ClientError::Other(format!("Failed to parse response: {}", e)))?;
|
||||
// println!("Response body: {}", serde_json::to_string_pretty(&body).unwrap());
|
||||
|
||||
Ok(body)
|
||||
}
|
||||
|
||||
pub async fn get_tip_accounts(&self) -> ClientResult<Value> {
|
||||
let endpoint = if let Some(uuid) = &self.uuid {
|
||||
format!("/bundles?uuid={}", uuid)
|
||||
} else {
|
||||
Err(ClientError::Parse(
|
||||
"Invalid response format".to_string(),
|
||||
"Missing result field".to_string(),
|
||||
))
|
||||
"/bundles".to_string()
|
||||
};
|
||||
|
||||
self.send_request(&endpoint, "getTipAccounts", None).await
|
||||
}
|
||||
|
||||
// Get a random tip account
|
||||
pub async fn get_tip_account(&self) -> ClientResult<Pubkey> {
|
||||
let tip_accounts_response = self.get_tip_accounts().await?;
|
||||
|
||||
let tip_accounts = tip_accounts_response["result"]
|
||||
.as_array()
|
||||
.ok_or_else(|| ClientError::Other("Failed to parse tip accounts as array".to_string()))?;
|
||||
|
||||
if tip_accounts.is_empty() {
|
||||
return Err(ClientError::Other("No tip accounts available".to_string()));
|
||||
}
|
||||
|
||||
let random_account = tip_accounts
|
||||
.choose(&mut rand::thread_rng())
|
||||
.ok_or_else(|| ClientError::Other("Failed to choose random tip account".to_string()))?;
|
||||
|
||||
let address = random_account
|
||||
.as_str()
|
||||
.ok_or_else(|| ClientError::Other("Failed to parse tip account as string".to_string()))?;
|
||||
|
||||
Pubkey::from_str(address)
|
||||
.map_err(|e| ClientError::Other(format!("Failed to parse pubkey: {}", e)))
|
||||
}
|
||||
|
||||
pub async fn get_bundle_statuses(&self, bundle_uuids: Vec<String>) -> ClientResult<Value> {
|
||||
let endpoint = if let Some(uuid) = &self.uuid {
|
||||
format!("/bundles?uuid={}", uuid)
|
||||
} else {
|
||||
"/bundles".to_string()
|
||||
};
|
||||
|
||||
// Construct the params as a list within a list
|
||||
let params = json!([bundle_uuids]);
|
||||
|
||||
self.send_request(&endpoint, "getBundleStatuses", Some(params))
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn send_transaction(
|
||||
&self,
|
||||
transaction: &Transaction,
|
||||
) -> Result<Signature, ClientError> {
|
||||
) -> ClientResult<String> {
|
||||
let wire_transaction = bincode::serialize(transaction).map_err(|e| {
|
||||
ClientError::Parse(
|
||||
"Transaction serialization failed".to_string(),
|
||||
@@ -117,44 +132,17 @@ impl JitoClient {
|
||||
)
|
||||
})?;
|
||||
|
||||
let encoded_tx = bs58::encode(&wire_transaction).into_string();
|
||||
let serialized_tx = bs58::encode(&wire_transaction).into_string();
|
||||
|
||||
for retry in 0..MAX_RETRIES {
|
||||
match self.try_send_transaction(&encoded_tx).await {
|
||||
Ok(signature) => {
|
||||
return Ok(Signature::from_str(&signature).map_err(|e| {
|
||||
ClientError::Parse(
|
||||
"Invalid signature".to_string(),
|
||||
e.to_string(),
|
||||
)
|
||||
})?);
|
||||
}
|
||||
Err(e) => {
|
||||
println!("Retry {} failed: {:?}", retry, e);
|
||||
if retry == MAX_RETRIES - 1 {
|
||||
return Err(e);
|
||||
}
|
||||
tokio::time::sleep(RETRY_DELAY).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Prepare bundle for submission (array of transactions)
|
||||
let bundle = json!([serialized_tx]);
|
||||
|
||||
Err(ClientError::Other("Max retries exceeded".to_string()))
|
||||
}
|
||||
// UUID for the bundle
|
||||
let uuid = None;
|
||||
|
||||
async fn try_send_transaction(&self, encoded_tx: &str) -> Result<String, ClientError> {
|
||||
let params = json!([
|
||||
encoded_tx,
|
||||
{
|
||||
"skipPreflight": self.config.skip_preflight,
|
||||
"preflightCommitment": self.config.preflight_commitment.commitment,
|
||||
"encoding": self.config.encoding,
|
||||
"maxRetries": MAX_RETRIES,
|
||||
"minContextSlot": null
|
||||
}
|
||||
]);
|
||||
|
||||
let response = self.send_request("sendTransaction", params).await?;
|
||||
// Send bundle using Jito SDK
|
||||
// println!("Sending bundle with 1 transaction...");
|
||||
let response = self.send_bundle(Some(bundle), uuid).await?;
|
||||
|
||||
response["result"]
|
||||
.as_str()
|
||||
@@ -165,54 +153,83 @@ impl JitoClient {
|
||||
))
|
||||
}
|
||||
|
||||
async fn send_request(&self, method: &str, params: Value) -> Result<Value, ClientError> {
|
||||
let request_body = json!({
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"method": method,
|
||||
"params": params
|
||||
});
|
||||
|
||||
let response = self.client
|
||||
.post(&self.endpoint)
|
||||
.header("Content-Type", "application/json")
|
||||
.json(&request_body)
|
||||
.send()
|
||||
pub async fn send_bundle(&self, params: Option<Value>, uuid: Option<&str>) -> ClientResult<Value> {
|
||||
let mut endpoint = "/bundles".to_string();
|
||||
|
||||
if let Some(uuid) = uuid {
|
||||
endpoint = format!("{}?uuid={}", endpoint, uuid);
|
||||
}
|
||||
|
||||
// Ensure params is an array of transactions
|
||||
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()));
|
||||
}
|
||||
if transactions.len() > 5 {
|
||||
return Err(ClientError::Other("Bundle can contain at most 5 transactions".to_string()));
|
||||
}
|
||||
transactions
|
||||
},
|
||||
_ => return Err(ClientError::Other("Invalid bundle format: expected an array of transactions".to_string())),
|
||||
};
|
||||
|
||||
// Wrap the transactions array in another array
|
||||
let params = json!([transactions]);
|
||||
|
||||
// Send the wrapped transactions array
|
||||
self.send_request(&endpoint, "sendBundle", Some(params))
|
||||
.await
|
||||
.map_err(|e| ClientError::Solana(
|
||||
"Request failed".to_string(),
|
||||
e.to_string(),
|
||||
))?;
|
||||
}
|
||||
|
||||
let response_data: Value = response.json().await.map_err(|e| {
|
||||
ClientError::Parse(
|
||||
"Invalid JSON response".to_string(),
|
||||
e.to_string(),
|
||||
)
|
||||
})?;
|
||||
pub async fn send_txn(&self, params: Option<Value>, bundle_only: bool) -> ClientResult<Value> {
|
||||
let mut query_params = Vec::new();
|
||||
|
||||
if let Some(error) = response_data.get("error") {
|
||||
return Err(ClientError::Solana(
|
||||
"RPC error".to_string(),
|
||||
error.to_string(),
|
||||
));
|
||||
if bundle_only {
|
||||
query_params.push("bundleOnly=true".to_string());
|
||||
}
|
||||
|
||||
Ok(response_data)
|
||||
let endpoint = if query_params.is_empty() {
|
||||
"/transactions".to_string()
|
||||
} else {
|
||||
format!("/transactions?{}", query_params.join("&"))
|
||||
};
|
||||
|
||||
// Construct params as an array instead of an object
|
||||
let params = match params {
|
||||
Some(Value::Object(map)) => {
|
||||
let tx = map.get("tx").and_then(Value::as_str).unwrap_or_default();
|
||||
let skip_preflight = map.get("skipPreflight").and_then(Value::as_bool).unwrap_or(false);
|
||||
json!([
|
||||
tx,
|
||||
{
|
||||
"encoding": "base64",
|
||||
"skipPreflight": skip_preflight
|
||||
}
|
||||
])
|
||||
},
|
||||
_ => json!([]),
|
||||
};
|
||||
|
||||
self.send_request(&endpoint, "sendTransaction", Some(params)).await
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct PriorityFeeEstimate {
|
||||
pub recommended: u64,
|
||||
pub per_compute_unit: PriorityFeeLevel,
|
||||
pub per_transaction: PriorityFeeLevel,
|
||||
}
|
||||
pub async fn get_in_flight_bundle_statuses(&self, bundle_uuids: Vec<String>) -> ClientResult<Value> {
|
||||
let endpoint = if let Some(uuid) = &self.uuid {
|
||||
format!("/bundles?uuid={}", uuid)
|
||||
} else {
|
||||
"/bundles".to_string()
|
||||
};
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct PriorityFeeLevel {
|
||||
pub extreme: u64, // 95th percentile
|
||||
pub high: u64, // 80th percentile
|
||||
pub medium: u64, // 60th percentile
|
||||
pub low: u64, // 40th percentile
|
||||
// Construct the params as a list within a list
|
||||
let params = json!([bundle_uuids]);
|
||||
|
||||
self.send_request(&endpoint, "getInflightBundleStatuses", Some(params))
|
||||
.await
|
||||
}
|
||||
|
||||
// Helper method to convert Value to PrettyJsonValue
|
||||
pub fn prettify(value: Value) -> PrettyJsonValue {
|
||||
PrettyJsonValue(value)
|
||||
}
|
||||
}
|
||||
+36
-36
@@ -25,6 +25,7 @@ use anchor_spl::associated_token::{
|
||||
get_associated_token_address,
|
||||
spl_associated_token_account::instruction::create_associated_token_account,
|
||||
};
|
||||
|
||||
use instruction::logs_subscribe;
|
||||
use instruction::logs_subscribe::SubscriptionHandle;
|
||||
use instruction::logs_events::DexEvent;
|
||||
@@ -41,6 +42,7 @@ use crate::error::ClientError;
|
||||
const DEFAULT_SLIPPAGE: u64 = 1000; // 10%
|
||||
const DEFAULT_COMPUTE_UNIT_LIMIT: u32 = 10_000_000;
|
||||
const DEFAULT_COMPUTE_UNIT_PRICE: u64 = 500_000;
|
||||
const JITO_TIP_AMOUNT: u64 = 1_000; // 0.000001 SOL
|
||||
|
||||
/// Priority fee configuration
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
@@ -87,7 +89,7 @@ impl PumpFun {
|
||||
commitment.unwrap_or(CommitmentConfig::confirmed())
|
||||
);
|
||||
|
||||
let jito_client = jito_url.map(|url| JitoClient::new(&url));
|
||||
let jito_client = jito_url.map(|url| JitoClient::new(&url, None));
|
||||
|
||||
Self {
|
||||
rpc,
|
||||
@@ -251,16 +253,13 @@ impl PumpFun {
|
||||
mint: &Pubkey,
|
||||
amount_sol: u64,
|
||||
slippage_basis_points: Option<u64>,
|
||||
priority_fee: Option<PriorityFee>,
|
||||
) -> Result<Signature, ClientError> {
|
||||
) -> Result<String, ClientError> {
|
||||
let start_time = Instant::now();
|
||||
|
||||
let jito_client = self.jito_client.as_ref()
|
||||
.ok_or_else(|| ClientError::Other("Jito client not found".to_string()))?;
|
||||
|
||||
let global_account = self.get_global_account()?;
|
||||
let bonding_curve_pda = Self::get_bonding_curve_pda(mint)
|
||||
.ok_or(ClientError::BondingCurveNotFound)?;
|
||||
let bonding_curve_account = self.get_bonding_curve_account(mint)?;
|
||||
let buy_amount = bonding_curve_account
|
||||
.get_buy_price(amount_sol)
|
||||
@@ -268,12 +267,9 @@ impl PumpFun {
|
||||
let buy_amount_with_slippage =
|
||||
utils::calculate_with_slippage_buy(amount_sol, slippage_basis_points.unwrap_or(DEFAULT_SLIPPAGE));
|
||||
|
||||
let (unit_limit, _unit_price) = self.get_compute_units(priority_fee);
|
||||
let mut instructions = self.create_priority_fee_instructions(priority_fee);
|
||||
let mut instructions = vec![];
|
||||
|
||||
let priority_fees = jito_client.estimate_priority_fees(&bonding_curve_pda).await?;
|
||||
let tip_account = jito_client.get_tip_account().await?;
|
||||
|
||||
let ata = get_associated_token_address(&self.payer.pubkey(), mint);
|
||||
if self.rpc.get_account(&ata).is_err() {
|
||||
instructions.push(create_associated_token_account(
|
||||
@@ -293,14 +289,12 @@ impl PumpFun {
|
||||
_max_sol_cost: buy_amount_with_slippage,
|
||||
},
|
||||
));
|
||||
|
||||
let total_priority_fee = self.calculate_priority_fee(priority_fees.per_compute_unit.extreme, unit_limit);
|
||||
|
||||
instructions.push(
|
||||
system_instruction::transfer(
|
||||
&self.payer.pubkey(),
|
||||
&tip_account,
|
||||
total_priority_fee,
|
||||
JITO_TIP_AMOUNT,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -396,14 +390,36 @@ impl PumpFun {
|
||||
self.sell(mint, Some(amount), slippage_basis_points, priority_fee).await
|
||||
}
|
||||
|
||||
pub async fn sell_by_percent_with_jito(
|
||||
&self,
|
||||
mint: &Pubkey,
|
||||
percent: u64,
|
||||
slippage_basis_points: Option<u64>,
|
||||
) -> Result<String, ClientError> {
|
||||
if percent > 100 {
|
||||
return Err(ClientError::Other("Percentage must be between 0 and 100".to_string()));
|
||||
}
|
||||
|
||||
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()))?;
|
||||
|
||||
if balance_u64 == 0 {
|
||||
return Err(ClientError::Other("Balance is 0".to_string()));
|
||||
}
|
||||
|
||||
let amount = balance_u64 * percent / 100;
|
||||
self.sell_with_jito(mint, Some(amount), slippage_basis_points).await
|
||||
}
|
||||
|
||||
/// Sell tokens using Jito
|
||||
pub async fn sell_with_jito(
|
||||
&self,
|
||||
mint: &Pubkey,
|
||||
amount_token: Option<u64>,
|
||||
slippage_basis_points: Option<u64>,
|
||||
priority_fee: Option<PriorityFee>,
|
||||
) -> Result<Signature, ClientError> {
|
||||
) -> Result<String, ClientError> {
|
||||
let start_time = Instant::now();
|
||||
|
||||
let jito_client = self.jito_client.as_ref()
|
||||
@@ -420,8 +436,6 @@ impl PumpFun {
|
||||
}
|
||||
|
||||
let global_account = self.get_global_account()?;
|
||||
let bonding_curve_pda = Self::get_bonding_curve_pda(mint)
|
||||
.ok_or(ClientError::BondingCurveNotFound)?;
|
||||
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)
|
||||
@@ -431,12 +445,8 @@ impl PumpFun {
|
||||
slippage_basis_points.unwrap_or(DEFAULT_SLIPPAGE),
|
||||
);
|
||||
|
||||
let (unit_limit, _unit_price) = self.get_compute_units(priority_fee);
|
||||
let mut instructions = self.create_priority_fee_instructions(priority_fee);
|
||||
|
||||
let priority_fees = jito_client.estimate_priority_fees(&bonding_curve_pda).await?;
|
||||
let mut instructions = vec![];
|
||||
let tip_account = jito_client.get_tip_account().await?;
|
||||
|
||||
instructions.push(instruction::sell(
|
||||
&self.payer.clone(),
|
||||
mint,
|
||||
@@ -447,13 +457,11 @@ impl PumpFun {
|
||||
},
|
||||
));
|
||||
|
||||
let total_priority_fee = self.calculate_priority_fee(priority_fees.per_compute_unit.extreme, unit_limit);
|
||||
|
||||
instructions.push(
|
||||
system_instruction::transfer(
|
||||
&self.payer.pubkey(),
|
||||
&tip_account,
|
||||
total_priority_fee,
|
||||
JITO_TIP_AMOUNT,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -485,18 +493,6 @@ impl PumpFun {
|
||||
instructions
|
||||
}
|
||||
|
||||
fn get_compute_units(&self, priority_fee: Option<PriorityFee>) -> (u32, u64) {
|
||||
let fee = priority_fee.unwrap_or(PriorityFee::default());
|
||||
let unit_limit = fee.limit.unwrap_or(DEFAULT_COMPUTE_UNIT_LIMIT);
|
||||
let unit_price = fee.price.unwrap_or(DEFAULT_COMPUTE_UNIT_PRICE);
|
||||
(unit_limit, unit_price)
|
||||
}
|
||||
|
||||
fn calculate_priority_fee(&self, priority_fee_per_cu: u64, unit_limit: u32) -> u64 {
|
||||
let total_priority_fee_microlamports = priority_fee_per_cu as u128 * unit_limit as u128;
|
||||
(total_priority_fee_microlamports / 1_000_000) as u64
|
||||
}
|
||||
|
||||
// Public interface methods
|
||||
pub fn get_payer_pubkey(&self) -> Pubkey {
|
||||
self.payer.pubkey()
|
||||
@@ -504,6 +500,10 @@ impl PumpFun {
|
||||
|
||||
pub fn get_token_balance(&self, account: &Pubkey, mint: &Pubkey) -> Result<u64, ClientError> {
|
||||
let ata = get_associated_token_address(account, mint);
|
||||
if self.rpc.get_account(&ata).is_err() {
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
let balance = self.rpc.get_token_account_balance(&ata)?;
|
||||
balance.amount.parse::<u64>()
|
||||
.map_err(|_| ClientError::Other("Failed to parse token balance".to_string()))
|
||||
|
||||
Reference in New Issue
Block a user