rebuild code
This commit is contained in:
@@ -25,6 +25,7 @@ solana-security-txt = "1.1.1"
|
|||||||
solana-entry = "2.1.16"
|
solana-entry = "2.1.16"
|
||||||
solana-rpc-client-nonce-utils = "2.1.16"
|
solana-rpc-client-nonce-utils = "2.1.16"
|
||||||
solana-perf = "2.1.16"
|
solana-perf = "2.1.16"
|
||||||
|
solana-metrics = "2.1.16"
|
||||||
|
|
||||||
spl-token = "8.0.0"
|
spl-token = "8.0.0"
|
||||||
spl-token-2022 = { version = "8.0.0", features = ["no-entrypoint"] }
|
spl-token-2022 = { version = "8.0.0", features = ["no-entrypoint"] }
|
||||||
|
|||||||
+7
-4
@@ -3,12 +3,15 @@ use std::sync::Arc;
|
|||||||
use solana_client::rpc_client::RpcClient;
|
use solana_client::rpc_client::RpcClient;
|
||||||
use solana_sdk::{commitment_config::CommitmentConfig, pubkey::Pubkey, signature::Keypair};
|
use solana_sdk::{commitment_config::CommitmentConfig, pubkey::Pubkey, signature::Keypair};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use crate::{constants::pumpfun::trade::{DEFAULT_BUY_TIP_FEE, DEFAULT_COMPUTE_UNIT_LIMIT, DEFAULT_COMPUTE_UNIT_PRICE, DEFAULT_RPC_UNIT_LIMIT, DEFAULT_RPC_UNIT_PRICE, DEFAULT_SELL_TIP_FEE}, swqos::FeeClient};
|
use crate::{constants::pumpfun::trade::{DEFAULT_BUY_TIP_FEE, DEFAULT_COMPUTE_UNIT_LIMIT, DEFAULT_COMPUTE_UNIT_PRICE, DEFAULT_RPC_UNIT_LIMIT, DEFAULT_RPC_UNIT_PRICE, DEFAULT_SELL_TIP_FEE}, swqos::SwqosClient};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum FeeType {
|
pub enum SwqosType {
|
||||||
Jito,
|
Jito,
|
||||||
NextBlock,
|
NextBlock,
|
||||||
|
ZeroSlot,
|
||||||
|
Nozomi,
|
||||||
|
Rpc,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@@ -104,11 +107,11 @@ pub struct MethodArgs {
|
|||||||
pub payer: Arc<Keypair>,
|
pub payer: Arc<Keypair>,
|
||||||
pub rpc: Arc<RpcClient>,
|
pub rpc: Arc<RpcClient>,
|
||||||
pub nonblocking_rpc: Arc<SolanaRpcClient>,
|
pub nonblocking_rpc: Arc<SolanaRpcClient>,
|
||||||
pub jito_client: Arc<FeeClient>,
|
pub jito_client: Arc<SwqosClient>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MethodArgs {
|
impl MethodArgs {
|
||||||
pub fn new(payer: Arc<Keypair>, rpc: Arc<RpcClient>, nonblocking_rpc: Arc<SolanaRpcClient>, jito_client: Arc<FeeClient>) -> Self {
|
pub fn new(payer: Arc<Keypair>, rpc: Arc<RpcClient>, nonblocking_rpc: Arc<SolanaRpcClient>, jito_client: Arc<SwqosClient>) -> Self {
|
||||||
Self { payer, rpc, nonblocking_rpc, jito_client }
|
Self { payer, rpc, nonblocking_rpc, jito_client }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ use crate::common::pumpswap::logs_events::PumpSwapEvent;
|
|||||||
use crate::common::pumpfun::logs_filters::LogFilter;
|
use crate::common::pumpfun::logs_filters::LogFilter;
|
||||||
use crate::common::pumpswap::logs_filters::LogFilter as PumpswapLogFilter;
|
use crate::common::pumpswap::logs_filters::LogFilter as PumpswapLogFilter;
|
||||||
use crate::common::raydium::logs_filters::LogFilter as RaydiumLogFilter;
|
use crate::common::raydium::logs_filters::LogFilter as RaydiumLogFilter;
|
||||||
use crate::swqos::jito_grpc::shredstream::shredstream_proxy_client::ShredstreamProxyClient;
|
use crate::protos::shredstream::shredstream_proxy_client::ShredstreamProxyClient;
|
||||||
use crate::swqos::jito_grpc::shredstream::SubscribeEntriesRequest;
|
use crate::protos::shredstream::SubscribeEntriesRequest;
|
||||||
|
|
||||||
const CHANNEL_SIZE: usize = 1000;
|
const CHANNEL_SIZE: usize = 1000;
|
||||||
|
|
||||||
|
|||||||
+31
-25
@@ -9,11 +9,12 @@ pub mod swqos;
|
|||||||
pub mod pumpfun;
|
pub mod pumpfun;
|
||||||
pub mod pumpswap;
|
pub mod pumpswap;
|
||||||
pub mod trading;
|
pub mod trading;
|
||||||
|
pub mod protos;
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
|
||||||
use swqos::{FeeClient, JitoClient, NextBlockClient, NozomiClient, SolRpcClient, ZeroSlotClient};
|
use swqos::SwqosClient;
|
||||||
use rustls::crypto::{ring::default_provider, CryptoProvider};
|
use rustls::crypto::{ring::default_provider, CryptoProvider};
|
||||||
use solana_hash::Hash;
|
use solana_hash::Hash;
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
@@ -30,6 +31,11 @@ use constants::trade_type::{COPY_BUY, SNIPER_BUY};
|
|||||||
use constants::trade_platform::{PUMPFUN, PUMPFUN_SWAP, RAYDIUM};
|
use constants::trade_platform::{PUMPFUN, PUMPFUN_SWAP, RAYDIUM};
|
||||||
use accounts::BondingCurveAccount;
|
use accounts::BondingCurveAccount;
|
||||||
|
|
||||||
|
use crate::swqos::jito::JitoClient;
|
||||||
|
use crate::swqos::nextblock::NextBlockClient;
|
||||||
|
use crate::swqos::nozomi::NozomiClient;
|
||||||
|
use crate::swqos::solana_rpc::SolRpcClient;
|
||||||
|
use crate::swqos::zeroslot::ZeroSlotClient;
|
||||||
use crate::trading::core::params::PumpFunParams;
|
use crate::trading::core::params::PumpFunParams;
|
||||||
use crate::trading::core::params::PumpFunSellParams;
|
use crate::trading::core::params::PumpFunSellParams;
|
||||||
use crate::trading::core::params::PumpSwapParams;
|
use crate::trading::core::params::PumpSwapParams;
|
||||||
@@ -40,7 +46,7 @@ use crate::trading::SellWithTipParams;
|
|||||||
pub struct SolanaTrade {
|
pub struct SolanaTrade {
|
||||||
pub payer: Arc<Keypair>,
|
pub payer: Arc<Keypair>,
|
||||||
pub rpc: Arc<SolanaRpcClient>,
|
pub rpc: Arc<SolanaRpcClient>,
|
||||||
pub fee_clients: Vec<Arc<FeeClient>>,
|
pub swqos_clients: Vec<Arc<SwqosClient>>,
|
||||||
pub priority_fee: PriorityFee,
|
pub priority_fee: PriorityFee,
|
||||||
pub cluster: Cluster,
|
pub cluster: Cluster,
|
||||||
}
|
}
|
||||||
@@ -52,7 +58,7 @@ impl Clone for SolanaTrade {
|
|||||||
Self {
|
Self {
|
||||||
payer: self.payer.clone(),
|
payer: self.payer.clone(),
|
||||||
rpc: self.rpc.clone(),
|
rpc: self.rpc.clone(),
|
||||||
fee_clients: self.fee_clients.clone(),
|
swqos_clients: self.swqos_clients.clone(),
|
||||||
priority_fee: self.priority_fee.clone(),
|
priority_fee: self.priority_fee.clone(),
|
||||||
cluster: self.cluster.clone(),
|
cluster: self.cluster.clone(),
|
||||||
}
|
}
|
||||||
@@ -76,14 +82,14 @@ impl SolanaTrade {
|
|||||||
cluster.clone().commitment
|
cluster.clone().commitment
|
||||||
);
|
);
|
||||||
let rpc = Arc::new(rpc);
|
let rpc = Arc::new(rpc);
|
||||||
let mut fee_clients: Vec<Arc<FeeClient>> = vec![];
|
let mut swqos_clients: Vec<Arc<SwqosClient>> = vec![];
|
||||||
if cluster.clone().use_jito {
|
if cluster.clone().use_jito {
|
||||||
let jito_client = JitoClient::new(
|
let jito_client = JitoClient::new(
|
||||||
cluster.clone().rpc_url,
|
cluster.clone().rpc_url,
|
||||||
cluster.clone().block_engine_url
|
cluster.clone().block_engine_url
|
||||||
).await.expect("Failed to create Jito client");
|
).await.expect("Failed to create Jito client");
|
||||||
|
|
||||||
fee_clients.push(Arc::new(jito_client));
|
swqos_clients.push(Arc::new(jito_client));
|
||||||
}
|
}
|
||||||
|
|
||||||
if cluster.clone().use_zeroslot {
|
if cluster.clone().use_zeroslot {
|
||||||
@@ -93,7 +99,7 @@ impl SolanaTrade {
|
|||||||
cluster.clone().zeroslot_auth_token
|
cluster.clone().zeroslot_auth_token
|
||||||
);
|
);
|
||||||
|
|
||||||
fee_clients.push(Arc::new(zeroslot_client));
|
swqos_clients.push(Arc::new(zeroslot_client));
|
||||||
}
|
}
|
||||||
|
|
||||||
if cluster.clone().use_nozomi {
|
if cluster.clone().use_nozomi {
|
||||||
@@ -103,7 +109,7 @@ impl SolanaTrade {
|
|||||||
cluster.clone().nozomi_auth_token
|
cluster.clone().nozomi_auth_token
|
||||||
);
|
);
|
||||||
|
|
||||||
fee_clients.push(Arc::new(nozomi_client));
|
swqos_clients.push(Arc::new(nozomi_client));
|
||||||
}
|
}
|
||||||
|
|
||||||
if cluster.clone().use_nextblock {
|
if cluster.clone().use_nextblock {
|
||||||
@@ -113,18 +119,18 @@ impl SolanaTrade {
|
|||||||
cluster.clone().nextblock_auth_token
|
cluster.clone().nextblock_auth_token
|
||||||
);
|
);
|
||||||
|
|
||||||
fee_clients.push(Arc::new(nextblock_client));
|
swqos_clients.push(Arc::new(nextblock_client));
|
||||||
}
|
}
|
||||||
|
|
||||||
if cluster.clone().use_rpc {
|
if cluster.clone().use_rpc {
|
||||||
let rpc_client = SolRpcClient::new(rpc.clone());
|
let rpc_client = SolRpcClient::new(rpc.clone());
|
||||||
fee_clients.push(Arc::new(rpc_client));
|
swqos_clients.push(Arc::new(rpc_client));
|
||||||
}
|
}
|
||||||
|
|
||||||
let instance = Self {
|
let instance = Self {
|
||||||
payer,
|
payer,
|
||||||
rpc,
|
rpc,
|
||||||
fee_clients,
|
swqos_clients,
|
||||||
priority_fee: cluster.clone().priority_fee,
|
priority_fee: cluster.clone().priority_fee,
|
||||||
cluster: cluster.clone(),
|
cluster: cluster.clone(),
|
||||||
};
|
};
|
||||||
@@ -192,7 +198,7 @@ impl SolanaTrade {
|
|||||||
) -> Result<(), anyhow::Error> {
|
) -> Result<(), anyhow::Error> {
|
||||||
pumpfun::create::create_and_buy_with_tip(
|
pumpfun::create::create_and_buy_with_tip(
|
||||||
self.rpc.clone(),
|
self.rpc.clone(),
|
||||||
self.fee_clients.clone(),
|
self.swqos_clients.clone(),
|
||||||
payer,
|
payer,
|
||||||
mint,
|
mint,
|
||||||
ipfs,
|
ipfs,
|
||||||
@@ -350,7 +356,7 @@ impl SolanaTrade {
|
|||||||
priority_fee.buy_tip_fees = vec![custom_buy_tip_fee.unwrap(),custom_buy_tip_fee.unwrap(),custom_buy_tip_fee.unwrap(),custom_buy_tip_fee.unwrap()];
|
priority_fee.buy_tip_fees = vec![custom_buy_tip_fee.unwrap(),custom_buy_tip_fee.unwrap(),custom_buy_tip_fee.unwrap(),custom_buy_tip_fee.unwrap()];
|
||||||
}
|
}
|
||||||
pumpfun::buy::buy_with_tip(
|
pumpfun::buy::buy_with_tip(
|
||||||
self.fee_clients.clone(),
|
self.swqos_clients.clone(),
|
||||||
self.payer.clone(),
|
self.payer.clone(),
|
||||||
mint,
|
mint,
|
||||||
creator,
|
creator,
|
||||||
@@ -384,7 +390,7 @@ impl SolanaTrade {
|
|||||||
.as_any()
|
.as_any()
|
||||||
.downcast_ref::<PumpFunParams>() {
|
.downcast_ref::<PumpFunParams>() {
|
||||||
pumpfun::buy::buy_with_tip(
|
pumpfun::buy::buy_with_tip(
|
||||||
self.fee_clients.clone(),
|
self.swqos_clients.clone(),
|
||||||
self.payer.clone(),
|
self.payer.clone(),
|
||||||
mint,
|
mint,
|
||||||
creator,
|
creator,
|
||||||
@@ -402,7 +408,7 @@ impl SolanaTrade {
|
|||||||
.downcast_ref::<PumpSwapParams>() {
|
.downcast_ref::<PumpSwapParams>() {
|
||||||
pumpswap::buy::buy_with_tip(
|
pumpswap::buy::buy_with_tip(
|
||||||
self.rpc.clone(),
|
self.rpc.clone(),
|
||||||
self.fee_clients.clone(),
|
self.swqos_clients.clone(),
|
||||||
self.payer.clone(),
|
self.payer.clone(),
|
||||||
mint,
|
mint,
|
||||||
creator,
|
creator,
|
||||||
@@ -441,7 +447,7 @@ impl SolanaTrade {
|
|||||||
}
|
}
|
||||||
if trade_platform == PUMPFUN {
|
if trade_platform == PUMPFUN {
|
||||||
pumpfun::buy::buy_with_tip(
|
pumpfun::buy::buy_with_tip(
|
||||||
self.fee_clients.clone(),
|
self.swqos_clients.clone(),
|
||||||
self.payer.clone(),
|
self.payer.clone(),
|
||||||
mint,
|
mint,
|
||||||
creator,
|
creator,
|
||||||
@@ -456,7 +462,7 @@ impl SolanaTrade {
|
|||||||
} else if trade_platform == PUMPFUN_SWAP {
|
} else if trade_platform == PUMPFUN_SWAP {
|
||||||
pumpswap::buy::buy_with_tip(
|
pumpswap::buy::buy_with_tip(
|
||||||
self.rpc.clone(),
|
self.rpc.clone(),
|
||||||
self.fee_clients.clone(),
|
self.swqos_clients.clone(),
|
||||||
self.payer.clone(),
|
self.payer.clone(),
|
||||||
mint,
|
mint,
|
||||||
creator,
|
creator,
|
||||||
@@ -595,7 +601,7 @@ impl SolanaTrade {
|
|||||||
if trade_platform == PUMPFUN {
|
if trade_platform == PUMPFUN {
|
||||||
pumpfun::sell::sell_by_percent_with_tip(
|
pumpfun::sell::sell_by_percent_with_tip(
|
||||||
self.rpc.clone(),
|
self.rpc.clone(),
|
||||||
self.fee_clients.clone(),
|
self.swqos_clients.clone(),
|
||||||
self.payer.clone(),
|
self.payer.clone(),
|
||||||
mint,
|
mint,
|
||||||
creator,
|
creator,
|
||||||
@@ -608,7 +614,7 @@ impl SolanaTrade {
|
|||||||
} else if trade_platform == PUMPFUN_SWAP {
|
} else if trade_platform == PUMPFUN_SWAP {
|
||||||
pumpswap::sell::sell_by_percent_with_tip(
|
pumpswap::sell::sell_by_percent_with_tip(
|
||||||
self.rpc.clone(),
|
self.rpc.clone(),
|
||||||
self.fee_clients.clone(),
|
self.swqos_clients.clone(),
|
||||||
self.payer.clone(),
|
self.payer.clone(),
|
||||||
mint,
|
mint,
|
||||||
creator,
|
creator,
|
||||||
@@ -639,7 +645,7 @@ impl SolanaTrade {
|
|||||||
if trade_platform == PUMPFUN {
|
if trade_platform == PUMPFUN {
|
||||||
pumpfun::sell::sell_by_amount_with_tip(
|
pumpfun::sell::sell_by_amount_with_tip(
|
||||||
self.rpc.clone(),
|
self.rpc.clone(),
|
||||||
self.fee_clients.clone(),
|
self.swqos_clients.clone(),
|
||||||
self.payer.clone(),
|
self.payer.clone(),
|
||||||
mint,
|
mint,
|
||||||
creator,
|
creator,
|
||||||
@@ -651,7 +657,7 @@ impl SolanaTrade {
|
|||||||
} else if trade_platform == PUMPFUN_SWAP {
|
} else if trade_platform == PUMPFUN_SWAP {
|
||||||
pumpswap::sell::sell_by_amount_with_tip(
|
pumpswap::sell::sell_by_amount_with_tip(
|
||||||
self.rpc.clone(),
|
self.rpc.clone(),
|
||||||
self.fee_clients.clone(),
|
self.swqos_clients.clone(),
|
||||||
self.payer.clone(),
|
self.payer.clone(),
|
||||||
mint,
|
mint,
|
||||||
creator,
|
creator,
|
||||||
@@ -681,7 +687,7 @@ impl SolanaTrade {
|
|||||||
) -> Result<(), anyhow::Error> {
|
) -> Result<(), anyhow::Error> {
|
||||||
pumpfun::sell::sell_with_tip(
|
pumpfun::sell::sell_with_tip(
|
||||||
self.rpc.clone(),
|
self.rpc.clone(),
|
||||||
self.fee_clients.clone(),
|
self.swqos_clients.clone(),
|
||||||
self.payer.clone(),
|
self.payer.clone(),
|
||||||
mint,
|
mint,
|
||||||
creator,
|
creator,
|
||||||
@@ -808,7 +814,7 @@ impl SolanaTrade {
|
|||||||
.downcast_ref::<PumpFunSellParams>() {
|
.downcast_ref::<PumpFunSellParams>() {
|
||||||
pumpfun::sell::sell_by_percent_with_tip(
|
pumpfun::sell::sell_by_percent_with_tip(
|
||||||
self.rpc.clone(),
|
self.rpc.clone(),
|
||||||
self.fee_clients.clone(),
|
self.swqos_clients.clone(),
|
||||||
self.payer.clone(),
|
self.payer.clone(),
|
||||||
mint,
|
mint,
|
||||||
creator,
|
creator,
|
||||||
@@ -824,7 +830,7 @@ impl SolanaTrade {
|
|||||||
.downcast_ref::<PumpSwapParams>() {
|
.downcast_ref::<PumpSwapParams>() {
|
||||||
pumpswap::sell::sell_by_percent_with_tip(
|
pumpswap::sell::sell_by_percent_with_tip(
|
||||||
self.rpc.clone(),
|
self.rpc.clone(),
|
||||||
self.fee_clients.clone(),
|
self.swqos_clients.clone(),
|
||||||
self.payer.clone(),
|
self.payer.clone(),
|
||||||
mint,
|
mint,
|
||||||
creator,
|
creator,
|
||||||
@@ -858,7 +864,7 @@ impl SolanaTrade {
|
|||||||
.downcast_ref::<PumpFunSellParams>() {
|
.downcast_ref::<PumpFunSellParams>() {
|
||||||
pumpfun::sell::sell_by_amount_with_tip(
|
pumpfun::sell::sell_by_amount_with_tip(
|
||||||
self.rpc.clone(),
|
self.rpc.clone(),
|
||||||
self.fee_clients.clone(),
|
self.swqos_clients.clone(),
|
||||||
self.payer.clone(),
|
self.payer.clone(),
|
||||||
mint,
|
mint,
|
||||||
creator,
|
creator,
|
||||||
@@ -873,7 +879,7 @@ impl SolanaTrade {
|
|||||||
.downcast_ref::<PumpSwapParams>() {
|
.downcast_ref::<PumpSwapParams>() {
|
||||||
pumpswap::sell::sell_by_amount_with_tip(
|
pumpswap::sell::sell_by_amount_with_tip(
|
||||||
self.rpc.clone(),
|
self.rpc.clone(),
|
||||||
self.fee_clients.clone(),
|
self.swqos_clients.clone(),
|
||||||
self.payer.clone(),
|
self.payer.clone(),
|
||||||
mint,
|
mint,
|
||||||
creator,
|
creator,
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ use solana_sdk::{
|
|||||||
transaction::VersionedTransaction,
|
transaction::VersionedTransaction,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::swqos::jito_grpc::{
|
use crate::protos::{
|
||||||
packet::{
|
packet::{
|
||||||
Meta as ProtoMeta, Packet as ProtoPacket, PacketBatch as ProtoPacketBatch,
|
Meta as ProtoMeta, Packet as ProtoPacket, PacketBatch as ProtoPacketBatch,
|
||||||
PacketFlags as ProtoPacketFlags,
|
PacketFlags as ProtoPacketFlags,
|
||||||
@@ -9,3 +9,6 @@ pub mod shared;
|
|||||||
pub mod shredstream;
|
pub mod shredstream;
|
||||||
pub mod trace_shred;
|
pub mod trace_shred;
|
||||||
pub mod convert;
|
pub mod convert;
|
||||||
|
pub mod nextblock_grpc;
|
||||||
|
pub mod searcher_client;
|
||||||
|
pub mod token_authenticator;
|
||||||
@@ -3,7 +3,7 @@ use std::{
|
|||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::swqos::jito_grpc::{
|
use crate::protos::{
|
||||||
bundle::{
|
bundle::{
|
||||||
Bundle, BundleResult,
|
Bundle, BundleResult,
|
||||||
},
|
},
|
||||||
@@ -25,8 +25,7 @@ use yellowstone_grpc_client::ClientTlsConfig;
|
|||||||
|
|
||||||
use crate::swqos::common::poll_transaction_confirmation;
|
use crate::swqos::common::poll_transaction_confirmation;
|
||||||
use crate::common::SolanaRpcClient;
|
use crate::common::SolanaRpcClient;
|
||||||
|
use crate::swqos::TradeType;
|
||||||
use super::TradeType;
|
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum BlockEngineConnectionError {
|
pub enum BlockEngineConnectionError {
|
||||||
@@ -3,7 +3,7 @@ use std::{
|
|||||||
time::{Duration, SystemTime},
|
time::{Duration, SystemTime},
|
||||||
};
|
};
|
||||||
|
|
||||||
use jito_protos::auth::{
|
use crate::protos::auth::{
|
||||||
auth_service_client::AuthServiceClient, GenerateAuthChallengeRequest,
|
auth_service_client::AuthServiceClient, GenerateAuthChallengeRequest,
|
||||||
GenerateAuthTokensRequest, RefreshAccessTokenRequest, Role, Token,
|
GenerateAuthTokensRequest, RefreshAccessTokenRequest, Role, Token,
|
||||||
};
|
};
|
||||||
+3
-3
@@ -1,7 +1,7 @@
|
|||||||
use crate::accounts::BondingCurveAccount;
|
use crate::accounts::BondingCurveAccount;
|
||||||
use crate::{
|
use crate::{
|
||||||
common::{PriorityFee, SolanaRpcClient},
|
common::{PriorityFee, SolanaRpcClient},
|
||||||
swqos::FeeClient,
|
swqos::SwqosClient,
|
||||||
trading::{core::params::PumpFunParams, factory::Protocol, BuyParams, TradeFactory},
|
trading::{core::params::PumpFunParams, factory::Protocol, BuyParams, TradeFactory},
|
||||||
};
|
};
|
||||||
use solana_hash::Hash;
|
use solana_hash::Hash;
|
||||||
@@ -49,7 +49,7 @@ pub async fn buy(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn buy_with_tip(
|
pub async fn buy_with_tip(
|
||||||
fee_clients: Vec<Arc<FeeClient>>,
|
swqos_clients: Vec<Arc<SwqosClient>>,
|
||||||
payer: Arc<Keypair>,
|
payer: Arc<Keypair>,
|
||||||
mint: Pubkey,
|
mint: Pubkey,
|
||||||
creator: Pubkey,
|
creator: Pubkey,
|
||||||
@@ -82,7 +82,7 @@ pub async fn buy_with_tip(
|
|||||||
data_size_limit: MAX_LOADED_ACCOUNTS_DATA_SIZE_LIMIT,
|
data_size_limit: MAX_LOADED_ACCOUNTS_DATA_SIZE_LIMIT,
|
||||||
protocol_params,
|
protocol_params,
|
||||||
};
|
};
|
||||||
let buy_with_tip_params = buy_params.with_tip(fee_clients);
|
let buy_with_tip_params = buy_params.with_tip(swqos_clients);
|
||||||
// 执行买入
|
// 执行买入
|
||||||
executor.buy_with_tip(buy_with_tip_params).await?;
|
executor.buy_with_tip(buy_with_tip_params).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ use spl_associated_token_account::instruction::create_associated_token_account;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
common::{PriorityFee, SolanaRpcClient}, constants, instruction,
|
common::{PriorityFee, SolanaRpcClient}, constants, instruction,
|
||||||
ipfs::TokenMetadataIPFS, swqos::{FeeClient, TradeType},
|
ipfs::TokenMetadataIPFS, swqos::{SwqosClient, TradeType},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::pumpfun::common::{
|
use crate::pumpfun::common::{
|
||||||
@@ -86,7 +86,7 @@ pub async fn create_and_buy(
|
|||||||
|
|
||||||
pub async fn create_and_buy_with_tip(
|
pub async fn create_and_buy_with_tip(
|
||||||
rpc: Arc<SolanaRpcClient>,
|
rpc: Arc<SolanaRpcClient>,
|
||||||
fee_clients: Vec<Arc<FeeClient>>,
|
fee_clients: Vec<Arc<SwqosClient>>,
|
||||||
payer: Arc<Keypair>,
|
payer: Arc<Keypair>,
|
||||||
mint: Keypair,
|
mint: Keypair,
|
||||||
ipfs: TokenMetadataIPFS,
|
ipfs: TokenMetadataIPFS,
|
||||||
|
|||||||
+4
-4
@@ -3,7 +3,7 @@ use crate::trading::{
|
|||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
common::{PriorityFee, SolanaRpcClient},
|
common::{PriorityFee, SolanaRpcClient},
|
||||||
swqos::FeeClient,
|
swqos::SwqosClient,
|
||||||
};
|
};
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use solana_hash::Hash;
|
use solana_hash::Hash;
|
||||||
@@ -99,7 +99,7 @@ pub async fn sell_by_amount(
|
|||||||
|
|
||||||
pub async fn sell_by_percent_with_tip(
|
pub async fn sell_by_percent_with_tip(
|
||||||
rpc: Arc<SolanaRpcClient>,
|
rpc: Arc<SolanaRpcClient>,
|
||||||
fee_clients: Vec<Arc<FeeClient>>,
|
fee_clients: Vec<Arc<SwqosClient>>,
|
||||||
payer: Arc<Keypair>,
|
payer: Arc<Keypair>,
|
||||||
mint: Pubkey,
|
mint: Pubkey,
|
||||||
creator: Pubkey,
|
creator: Pubkey,
|
||||||
@@ -129,7 +129,7 @@ pub async fn sell_by_percent_with_tip(
|
|||||||
|
|
||||||
pub async fn sell_by_amount_with_tip(
|
pub async fn sell_by_amount_with_tip(
|
||||||
rpc: Arc<SolanaRpcClient>,
|
rpc: Arc<SolanaRpcClient>,
|
||||||
fee_clients: Vec<Arc<FeeClient>>,
|
fee_clients: Vec<Arc<SwqosClient>>,
|
||||||
payer: Arc<Keypair>,
|
payer: Arc<Keypair>,
|
||||||
mint: Pubkey,
|
mint: Pubkey,
|
||||||
creator: Pubkey,
|
creator: Pubkey,
|
||||||
@@ -158,7 +158,7 @@ pub async fn sell_by_amount_with_tip(
|
|||||||
/// Sell tokens using Jito
|
/// Sell tokens using Jito
|
||||||
pub async fn sell_with_tip(
|
pub async fn sell_with_tip(
|
||||||
rpc: Arc<SolanaRpcClient>,
|
rpc: Arc<SolanaRpcClient>,
|
||||||
fee_clients: Vec<Arc<FeeClient>>,
|
fee_clients: Vec<Arc<SwqosClient>>,
|
||||||
payer: Arc<Keypair>,
|
payer: Arc<Keypair>,
|
||||||
mint: Pubkey,
|
mint: Pubkey,
|
||||||
creator: Pubkey,
|
creator: Pubkey,
|
||||||
|
|||||||
+3
-3
@@ -2,7 +2,7 @@ use solana_hash::Hash;
|
|||||||
use solana_sdk::{pubkey::Pubkey, signature::Keypair};
|
use solana_sdk::{pubkey::Pubkey, signature::Keypair};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::swqos::FeeClient;
|
use crate::swqos::SwqosClient;
|
||||||
use crate::trading::{core::params::PumpSwapParams, factory::Protocol, BuyParams, TradeFactory};
|
use crate::trading::{core::params::PumpSwapParams, factory::Protocol, BuyParams, TradeFactory};
|
||||||
use crate::{common::PriorityFee, SolanaRpcClient};
|
use crate::{common::PriorityFee, SolanaRpcClient};
|
||||||
|
|
||||||
@@ -62,7 +62,7 @@ pub async fn buy(
|
|||||||
// Buy tokens using a MEV service
|
// Buy tokens using a MEV service
|
||||||
pub async fn buy_with_tip(
|
pub async fn buy_with_tip(
|
||||||
rpc: Arc<SolanaRpcClient>,
|
rpc: Arc<SolanaRpcClient>,
|
||||||
fee_clients: Vec<Arc<FeeClient>>,
|
swqos_clients: Vec<Arc<SwqosClient>>,
|
||||||
payer: Arc<Keypair>,
|
payer: Arc<Keypair>,
|
||||||
mint: Pubkey,
|
mint: Pubkey,
|
||||||
creator: Pubkey,
|
creator: Pubkey,
|
||||||
@@ -104,7 +104,7 @@ pub async fn buy_with_tip(
|
|||||||
data_size_limit: MAX_LOADED_ACCOUNTS_DATA_SIZE_LIMIT,
|
data_size_limit: MAX_LOADED_ACCOUNTS_DATA_SIZE_LIMIT,
|
||||||
protocol_params,
|
protocol_params,
|
||||||
};
|
};
|
||||||
let buy_with_tip_params = buy_params.with_tip(fee_clients);
|
let buy_with_tip_params = buy_params.with_tip(swqos_clients);
|
||||||
// 执行买入
|
// 执行买入
|
||||||
executor.buy_with_tip(buy_with_tip_params).await?;
|
executor.buy_with_tip(buy_with_tip_params).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
use crate::common::{PriorityFee, SolanaRpcClient};
|
use crate::common::{PriorityFee, SolanaRpcClient};
|
||||||
use crate::pumpswap::common::get_token_balance;
|
use crate::pumpswap::common::get_token_balance;
|
||||||
use crate::swqos::FeeClient;
|
use crate::swqos::SwqosClient;
|
||||||
use crate::trading::{core::params::PumpSwapParams, factory::Protocol, SellParams, TradeFactory};
|
use crate::trading::{core::params::PumpSwapParams, factory::Protocol, SellParams, TradeFactory};
|
||||||
|
|
||||||
// Sell tokens to a Pumpswap pool
|
// Sell tokens to a Pumpswap pool
|
||||||
@@ -140,7 +140,7 @@ pub async fn sell_by_amount(
|
|||||||
// Sell tokens using a MEV service
|
// Sell tokens using a MEV service
|
||||||
pub async fn sell_with_tip(
|
pub async fn sell_with_tip(
|
||||||
rpc: Arc<SolanaRpcClient>,
|
rpc: Arc<SolanaRpcClient>,
|
||||||
fee_clients: Vec<Arc<FeeClient>>,
|
swqos_clients: Vec<Arc<SwqosClient>>,
|
||||||
payer: Arc<Keypair>,
|
payer: Arc<Keypair>,
|
||||||
mint: Pubkey,
|
mint: Pubkey,
|
||||||
creator: Pubkey,
|
creator: Pubkey,
|
||||||
@@ -179,7 +179,7 @@ pub async fn sell_with_tip(
|
|||||||
recent_blockhash,
|
recent_blockhash,
|
||||||
protocol_params,
|
protocol_params,
|
||||||
};
|
};
|
||||||
let sell_with_tip_params = sell_params.with_tip(fee_clients);
|
let sell_with_tip_params = sell_params.with_tip(swqos_clients);
|
||||||
// 执行卖出交易
|
// 执行卖出交易
|
||||||
executor.sell_with_tip(sell_with_tip_params).await?;
|
executor.sell_with_tip(sell_with_tip_params).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -188,7 +188,7 @@ pub async fn sell_with_tip(
|
|||||||
// Sell tokens by percentage using a MEV service
|
// Sell tokens by percentage using a MEV service
|
||||||
pub async fn sell_by_percent_with_tip(
|
pub async fn sell_by_percent_with_tip(
|
||||||
rpc: Arc<SolanaRpcClient>,
|
rpc: Arc<SolanaRpcClient>,
|
||||||
fee_clients: Vec<Arc<FeeClient>>,
|
swqos_clients: Vec<Arc<SwqosClient>>,
|
||||||
payer: Arc<Keypair>,
|
payer: Arc<Keypair>,
|
||||||
mint: Pubkey,
|
mint: Pubkey,
|
||||||
creator: Pubkey,
|
creator: Pubkey,
|
||||||
@@ -212,7 +212,7 @@ pub async fn sell_by_percent_with_tip(
|
|||||||
let amount = balance_u64 * percent / 100;
|
let amount = balance_u64 * percent / 100;
|
||||||
sell_with_tip(
|
sell_with_tip(
|
||||||
rpc,
|
rpc,
|
||||||
fee_clients,
|
swqos_clients,
|
||||||
payer,
|
payer,
|
||||||
mint,
|
mint,
|
||||||
creator,
|
creator,
|
||||||
@@ -233,7 +233,7 @@ pub async fn sell_by_percent_with_tip(
|
|||||||
// Sell tokens by amount using a MEV service
|
// Sell tokens by amount using a MEV service
|
||||||
pub async fn sell_by_amount_with_tip(
|
pub async fn sell_by_amount_with_tip(
|
||||||
rpc: Arc<SolanaRpcClient>,
|
rpc: Arc<SolanaRpcClient>,
|
||||||
fee_clients: Vec<Arc<FeeClient>>,
|
swqos_clients: Vec<Arc<SwqosClient>>,
|
||||||
payer: Arc<Keypair>,
|
payer: Arc<Keypair>,
|
||||||
mint: Pubkey,
|
mint: Pubkey,
|
||||||
creator: Pubkey,
|
creator: Pubkey,
|
||||||
@@ -255,7 +255,7 @@ pub async fn sell_by_amount_with_tip(
|
|||||||
|
|
||||||
sell_with_tip(
|
sell_with_tip(
|
||||||
rpc,
|
rpc,
|
||||||
fee_clients,
|
swqos_clients,
|
||||||
payer,
|
payer,
|
||||||
mint,
|
mint,
|
||||||
creator,
|
creator,
|
||||||
|
|||||||
Executable
+66
@@ -0,0 +1,66 @@
|
|||||||
|
|
||||||
|
use tonic::transport::Channel;
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
|
use rand::{rng, seq::IteratorRandom};
|
||||||
|
use anyhow::{anyhow, Result};
|
||||||
|
use std::sync::Arc;
|
||||||
|
use solana_sdk::{transaction::VersionedTransaction, signature::Signature};
|
||||||
|
use crate::protos::searcher::searcher_service_client::SearcherServiceClient;
|
||||||
|
use crate::protos::searcher_client::{self, get_searcher_client_no_auth, send_bundle_with_confirmation};
|
||||||
|
use crate::swqos::{ClientType, TradeType};
|
||||||
|
use crate::swqos::SwqosClientTrait;
|
||||||
|
|
||||||
|
use crate::{common::SolanaRpcClient, constants::pumpfun::accounts::JITO_TIP_ACCOUNTS};
|
||||||
|
|
||||||
|
|
||||||
|
pub struct JitoClient {
|
||||||
|
pub rpc_client: Arc<SolanaRpcClient>,
|
||||||
|
pub searcher_client: Arc<Mutex<SearcherServiceClient<Channel>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl SwqosClientTrait for JitoClient {
|
||||||
|
async fn send_transaction(&self, trade_type: TradeType, transaction: &VersionedTransaction) -> Result<Signature> {
|
||||||
|
self.send_bundle_with_confirmation(trade_type, &vec![transaction.clone()]).await?.first().cloned().ok_or(anyhow!("Failed to send transaction"))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn send_transactions(&self, trade_type: TradeType, transactions: &Vec<VersionedTransaction>) -> Result<Vec<Signature>> {
|
||||||
|
self.send_bundle_with_confirmation(trade_type, transactions).await
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_tip_account(&self) -> Result<String> {
|
||||||
|
if let Some(acc) = JITO_TIP_ACCOUNTS.iter().choose(&mut rng()) {
|
||||||
|
Ok(acc.to_string())
|
||||||
|
} else {
|
||||||
|
Err(anyhow!("no valid tip accounts found"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_client_type(&self) -> ClientType {
|
||||||
|
ClientType::Jito
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl JitoClient {
|
||||||
|
pub async fn new(rpc_url: String, block_engine_url: String) -> Result<Self> {
|
||||||
|
let rpc_client = SolanaRpcClient::new(rpc_url);
|
||||||
|
let searcher_client = get_searcher_client_no_auth(block_engine_url.as_str()).await?;
|
||||||
|
Ok(Self { rpc_client: Arc::new(rpc_client), searcher_client: Arc::new(Mutex::new(searcher_client)) })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn send_bundle_with_confirmation(
|
||||||
|
&self,
|
||||||
|
trade_type: TradeType,
|
||||||
|
transactions: &Vec<VersionedTransaction>,
|
||||||
|
) -> Result<Vec<Signature>> {
|
||||||
|
send_bundle_with_confirmation(self.rpc_client.clone(), trade_type, &transactions, self.searcher_client.clone()).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn send_bundle_no_wait(
|
||||||
|
&self,
|
||||||
|
transactions: &Vec<VersionedTransaction>,
|
||||||
|
) -> Result<Vec<Signature>> {
|
||||||
|
searcher_client::send_bundle_no_wait(&transactions, self.searcher_client.clone()).await
|
||||||
|
}
|
||||||
|
}
|
||||||
+14
-478
@@ -1,35 +1,17 @@
|
|||||||
use api::api_client::ApiClient;
|
|
||||||
use common::{poll_transaction_confirmation, serialize_smart_transaction_and_encode, serialize_transaction_and_encode};
|
|
||||||
use solana_client::rpc_config::RpcSendTransactionConfig;
|
|
||||||
use crate::swqos::jito_grpc::searcher::searcher_service_client::SearcherServiceClient;
|
|
||||||
use reqwest::Client;
|
|
||||||
use searcher_client::{get_searcher_client_no_auth, send_bundle_with_confirmation};
|
|
||||||
use serde_json::json;
|
|
||||||
use tonic::transport::Channel;
|
|
||||||
use yellowstone_grpc_client::Interceptor;
|
|
||||||
use std::{sync::Arc, time::Instant};
|
|
||||||
use tokio::sync::{Mutex, RwLock};
|
|
||||||
|
|
||||||
use solana_sdk::{commitment_config::CommitmentLevel, signature::Signature};
|
|
||||||
|
|
||||||
use std::str::FromStr;
|
|
||||||
use rustls::crypto::{ring::default_provider, CryptoProvider};
|
|
||||||
|
|
||||||
use tonic::{service::interceptor::InterceptedService, transport::Uri, Status};
|
|
||||||
use std::time::Duration;
|
|
||||||
use solana_transaction_status::UiTransactionEncoding;
|
|
||||||
use tonic::transport::ClientTlsConfig;
|
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
|
||||||
use rand::{rng, seq::{IndexedRandom, IteratorRandom}};
|
|
||||||
use solana_sdk::transaction::VersionedTransaction;
|
|
||||||
|
|
||||||
use crate::{common::SolanaRpcClient, constants::pumpfun::accounts::{JITO_TIP_ACCOUNTS, NEXTBLOCK_TIP_ACCOUNTS, ZEROSLOT_TIP_ACCOUNTS, NOZOMI_TIP_ACCOUNTS}};
|
|
||||||
|
|
||||||
pub mod api;
|
|
||||||
pub mod common;
|
pub mod common;
|
||||||
pub mod searcher_client;
|
pub mod solana_rpc;
|
||||||
pub mod jito_grpc;
|
pub mod jito;
|
||||||
|
pub mod nextblock;
|
||||||
|
pub mod zeroslot;
|
||||||
|
pub mod nozomi;
|
||||||
|
pub mod types;
|
||||||
|
|
||||||
|
use solana_sdk::signature::Signature;
|
||||||
|
use solana_sdk::transaction::VersionedTransaction;
|
||||||
|
use tokio::sync::RwLock;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
|
||||||
lazy_static::lazy_static! {
|
lazy_static::lazy_static! {
|
||||||
static ref TIP_ACCOUNT_CACHE: RwLock<Vec<String>> = RwLock::new(Vec::new());
|
static ref TIP_ACCOUNT_CACHE: RwLock<Vec<String>> = RwLock::new(Vec::new());
|
||||||
@@ -64,458 +46,12 @@ pub enum ClientType {
|
|||||||
Rpc,
|
Rpc,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type FeeClient = dyn FeeClientTrait + Send + Sync + 'static;
|
pub type SwqosClient = dyn SwqosClientTrait + Send + Sync + 'static;
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
pub trait FeeClientTrait {
|
pub trait SwqosClientTrait {
|
||||||
async fn send_transaction(&self, trade_type: TradeType, transaction: &VersionedTransaction) -> Result<Signature>;
|
async fn send_transaction(&self, trade_type: TradeType, transaction: &VersionedTransaction) -> Result<Signature>;
|
||||||
async fn send_transactions(&self, trade_type: TradeType, transactions: &Vec<VersionedTransaction>) -> Result<Vec<Signature>>;
|
async fn send_transactions(&self, trade_type: TradeType, transactions: &Vec<VersionedTransaction>) -> Result<Vec<Signature>>;
|
||||||
fn get_tip_account(&self) -> Result<String>;
|
fn get_tip_account(&self) -> Result<String>;
|
||||||
fn get_client_type(&self) -> ClientType;
|
fn get_client_type(&self) -> ClientType;
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct SolRpcClient {
|
|
||||||
pub rpc_client: Arc<SolanaRpcClient>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
|
||||||
impl FeeClientTrait for SolRpcClient {
|
|
||||||
async fn send_transaction(&self, trade_type: TradeType, transaction: &VersionedTransaction) -> Result<Signature> {
|
|
||||||
let signature = self.rpc_client.send_transaction_with_config(transaction, RpcSendTransactionConfig{
|
|
||||||
skip_preflight: true,
|
|
||||||
preflight_commitment: Some(CommitmentLevel::Processed),
|
|
||||||
encoding: Some(UiTransactionEncoding::Base64),
|
|
||||||
max_retries: Some(3),
|
|
||||||
min_context_slot: Some(0),
|
|
||||||
}).await?;
|
|
||||||
|
|
||||||
let start_time = Instant::now();
|
|
||||||
match poll_transaction_confirmation(&self.rpc_client, signature).await {
|
|
||||||
Ok(_) => (),
|
|
||||||
Err(_) => (),
|
|
||||||
}
|
|
||||||
println!(" signature: {:?}", signature);
|
|
||||||
println!(" rpc{}确认: {:?}", trade_type, start_time.elapsed());
|
|
||||||
|
|
||||||
Ok(signature)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn send_transactions(&self, trade_type: TradeType, transactions: &Vec<VersionedTransaction>) -> Result<Vec<Signature>> {
|
|
||||||
let mut signatures = Vec::new();
|
|
||||||
for transaction in transactions {
|
|
||||||
let signature = self.send_transaction(trade_type, transaction).await?;
|
|
||||||
signatures.push(signature);
|
|
||||||
}
|
|
||||||
Ok(signatures)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_tip_account(&self) -> Result<String> {
|
|
||||||
Ok("".to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_client_type(&self) -> ClientType {
|
|
||||||
ClientType::Rpc
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SolRpcClient {
|
|
||||||
pub fn new(rpc_client: Arc<SolanaRpcClient>) -> Self {
|
|
||||||
Self { rpc_client }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct JitoClient {
|
|
||||||
pub rpc_client: Arc<SolanaRpcClient>,
|
|
||||||
pub searcher_client: Arc<Mutex<SearcherServiceClient<Channel>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
|
||||||
impl FeeClientTrait for JitoClient {
|
|
||||||
async fn send_transaction(&self, trade_type: TradeType, transaction: &VersionedTransaction) -> Result<Signature, anyhow::Error> {
|
|
||||||
self.send_bundle_with_confirmation(trade_type, &vec![transaction.clone()]).await?.first().cloned().ok_or(anyhow!("Failed to send transaction"))
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn send_transactions(&self, trade_type: TradeType, transactions: &Vec<VersionedTransaction>) -> Result<Vec<Signature>, anyhow::Error> {
|
|
||||||
self.send_bundle_with_confirmation(trade_type, transactions).await
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_tip_account(&self) -> Result<String, anyhow::Error> {
|
|
||||||
if let Some(acc) = JITO_TIP_ACCOUNTS.iter().choose(&mut rng()) {
|
|
||||||
Ok(acc.to_string())
|
|
||||||
} else {
|
|
||||||
Err(anyhow!("no valid tip accounts found"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_client_type(&self) -> ClientType {
|
|
||||||
ClientType::Jito
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl JitoClient {
|
|
||||||
pub async fn new(rpc_url: String, block_engine_url: String) -> Result<Self> {
|
|
||||||
let rpc_client = SolanaRpcClient::new(rpc_url);
|
|
||||||
let searcher_client = get_searcher_client_no_auth(block_engine_url.as_str()).await?;
|
|
||||||
Ok(Self { rpc_client: Arc::new(rpc_client), searcher_client: Arc::new(Mutex::new(searcher_client)) })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn send_bundle_with_confirmation(
|
|
||||||
&self,
|
|
||||||
trade_type: TradeType,
|
|
||||||
transactions: &Vec<VersionedTransaction>,
|
|
||||||
) -> Result<Vec<Signature>, anyhow::Error> {
|
|
||||||
send_bundle_with_confirmation(self.rpc_client.clone(), trade_type, &transactions, self.searcher_client.clone()).await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn send_bundle_no_wait(
|
|
||||||
&self,
|
|
||||||
transactions: &Vec<VersionedTransaction>,
|
|
||||||
) -> Result<Vec<Signature>, anyhow::Error> {
|
|
||||||
searcher_client::send_bundle_no_wait(&transactions, self.searcher_client.clone()).await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct MyInterceptor {
|
|
||||||
auth_token: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MyInterceptor {
|
|
||||||
pub fn new(auth_token: String) -> Self {
|
|
||||||
Self { auth_token }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Interceptor for MyInterceptor {
|
|
||||||
fn call(&mut self, mut request: tonic::Request<()>) -> Result<tonic::Request<()>, Status> {
|
|
||||||
request.metadata_mut().insert(
|
|
||||||
"authorization",
|
|
||||||
tonic::metadata::MetadataValue::from_str(&self.auth_token)
|
|
||||||
.map_err(|_| Status::invalid_argument("Invalid auth token"))?
|
|
||||||
);
|
|
||||||
Ok(request)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct NextBlockClient {
|
|
||||||
pub rpc_client: Arc<SolanaRpcClient>,
|
|
||||||
pub client: ApiClient<InterceptedService<Channel, MyInterceptor>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
|
||||||
impl FeeClientTrait for NextBlockClient {
|
|
||||||
async fn send_transaction(&self, trade_type: TradeType, transaction: &VersionedTransaction) -> Result<Signature, anyhow::Error> {
|
|
||||||
self.send_transaction(trade_type, transaction).await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn send_transactions(&self, trade_type: TradeType, transactions: &Vec<VersionedTransaction>) -> Result<Vec<Signature>, anyhow::Error> {
|
|
||||||
self.send_transactions(trade_type, transactions).await
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_tip_account(&self) -> Result<String> {
|
|
||||||
let tip_account = *NEXTBLOCK_TIP_ACCOUNTS.choose(&mut rand::rng()).or_else(|| NEXTBLOCK_TIP_ACCOUNTS.first()).unwrap();
|
|
||||||
Ok(tip_account.to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_client_type(&self) -> ClientType {
|
|
||||||
ClientType::NextBlock
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NextBlockClient {
|
|
||||||
pub fn new(rpc_url: String, endpoint: String, auth_token: String) -> Self {
|
|
||||||
if CryptoProvider::get_default().is_none() {
|
|
||||||
let _ = default_provider()
|
|
||||||
.install_default()
|
|
||||||
.map_err(|e| anyhow::anyhow!("Failed to install crypto provider: {:?}", e));
|
|
||||||
}
|
|
||||||
|
|
||||||
let endpoint = endpoint.parse::<Uri>().unwrap();
|
|
||||||
let tls = ClientTlsConfig::new().with_native_roots();
|
|
||||||
let channel = Channel::builder(endpoint)
|
|
||||||
.tls_config(tls).expect("Failed to create TLS config")
|
|
||||||
.tcp_keepalive(Some(Duration::from_secs(60)))
|
|
||||||
.http2_keep_alive_interval(Duration::from_secs(30))
|
|
||||||
.keep_alive_while_idle(true)
|
|
||||||
.timeout(Duration::from_secs(30))
|
|
||||||
.connect_timeout(Duration::from_secs(10))
|
|
||||||
.connect_lazy();
|
|
||||||
|
|
||||||
let client = ApiClient::with_interceptor(channel, MyInterceptor::new(auth_token));
|
|
||||||
let rpc_client = SolanaRpcClient::new(rpc_url);
|
|
||||||
Self { rpc_client: Arc::new(rpc_client), client }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn send_transaction(&self, trade_type: TradeType, transaction: &VersionedTransaction) -> Result<Signature, anyhow::Error> {
|
|
||||||
let start_time = Instant::now();
|
|
||||||
let (content, signature) = serialize_smart_transaction_and_encode(transaction, UiTransactionEncoding::Base64).await?;
|
|
||||||
|
|
||||||
self.client.clone().post_submit_v2(api::PostSubmitRequest {
|
|
||||||
transaction: Some(api::TransactionMessage {
|
|
||||||
content,
|
|
||||||
is_cleanup: false,
|
|
||||||
}),
|
|
||||||
skip_pre_flight: true,
|
|
||||||
front_running_protection: Some(true),
|
|
||||||
experimental_front_running_protection: Some(true),
|
|
||||||
snipe_transaction: Some(true),
|
|
||||||
}).await?;
|
|
||||||
|
|
||||||
println!(" nextblock{}提交: {:?}", trade_type, start_time.elapsed());
|
|
||||||
|
|
||||||
let start_time: Instant = Instant::now();
|
|
||||||
let timeout: Duration = Duration::from_secs(10);
|
|
||||||
while Instant::now().duration_since(start_time) < timeout {
|
|
||||||
match poll_transaction_confirmation(&self.rpc_client, signature).await {
|
|
||||||
Ok(_) => break,
|
|
||||||
Err(_) => continue,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
println!(" nextblock{}确认: {:?}", trade_type, start_time.elapsed());
|
|
||||||
|
|
||||||
Ok(signature)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn send_transactions(&self, trade_type: TradeType, transactions: &Vec<VersionedTransaction>) -> Result<Vec<Signature>, anyhow::Error> {
|
|
||||||
let mut entries = Vec::new();
|
|
||||||
let encoding = UiTransactionEncoding::Base64;
|
|
||||||
|
|
||||||
let mut signatures = Vec::new();
|
|
||||||
for transaction in transactions {
|
|
||||||
let (content, signature) = serialize_smart_transaction_and_encode(transaction, encoding).await?;
|
|
||||||
entries.push(api::PostSubmitRequestEntry {
|
|
||||||
transaction: Some(api::TransactionMessage {
|
|
||||||
content,
|
|
||||||
is_cleanup: false,
|
|
||||||
}),
|
|
||||||
skip_pre_flight: true,
|
|
||||||
});
|
|
||||||
signatures.push(signature);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.client.clone().post_submit_batch_v2(api::PostSubmitBatchRequest {
|
|
||||||
entries,
|
|
||||||
submit_strategy: api::SubmitStrategy::PSubmitAll as i32,
|
|
||||||
use_bundle: Some(true),
|
|
||||||
front_running_protection: Some(true),
|
|
||||||
}).await?;
|
|
||||||
|
|
||||||
let start_time: Instant = Instant::now();
|
|
||||||
for signature in signatures.clone() {
|
|
||||||
match poll_transaction_confirmation(&self.rpc_client, signature).await {
|
|
||||||
Ok(_) => continue,
|
|
||||||
Err(_) => continue,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
println!(" nextblock{}确认: {:?}", trade_type, start_time.elapsed());
|
|
||||||
|
|
||||||
Ok(signatures)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct ZeroSlotClient {
|
|
||||||
pub endpoint: String,
|
|
||||||
pub auth_token: String,
|
|
||||||
pub rpc_client: Arc<SolanaRpcClient>,
|
|
||||||
pub http_client: Client,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
|
||||||
impl FeeClientTrait for ZeroSlotClient {
|
|
||||||
async fn send_transaction(&self, trade_type: TradeType, transaction: &VersionedTransaction) -> Result<Signature, anyhow::Error> {
|
|
||||||
self.send_transaction(trade_type, transaction).await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn send_transactions(&self, trade_type: TradeType, transactions: &Vec<VersionedTransaction>) -> Result<Vec<Signature>, anyhow::Error> {
|
|
||||||
self.send_transactions(trade_type, transactions).await
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_tip_account(&self) -> Result<String> {
|
|
||||||
let tip_account = *ZEROSLOT_TIP_ACCOUNTS.choose(&mut rand::rng()).or_else(|| ZEROSLOT_TIP_ACCOUNTS.first()).unwrap();
|
|
||||||
Ok(tip_account.to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_client_type(&self) -> ClientType {
|
|
||||||
ClientType::ZeroSlot
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ZeroSlotClient {
|
|
||||||
pub fn new(rpc_url: String, endpoint: String, auth_token: String) -> Self {
|
|
||||||
let rpc_client = SolanaRpcClient::new(rpc_url);
|
|
||||||
let http_client = Client::builder()
|
|
||||||
.pool_idle_timeout(Duration::from_secs(60))
|
|
||||||
.pool_max_idle_per_host(64)
|
|
||||||
.tcp_keepalive(Some(Duration::from_secs(1200)))
|
|
||||||
.http2_keep_alive_interval(Duration::from_secs(15))
|
|
||||||
.timeout(Duration::from_secs(10))
|
|
||||||
.connect_timeout(Duration::from_secs(5))
|
|
||||||
.build()
|
|
||||||
.unwrap();
|
|
||||||
Self { rpc_client: Arc::new(rpc_client), endpoint, auth_token, http_client }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn send_transaction(&self, trade_type: TradeType, transaction: &VersionedTransaction) -> Result<Signature, anyhow::Error> {
|
|
||||||
let start_time = Instant::now();
|
|
||||||
let (content, signature) = serialize_transaction_and_encode(transaction, UiTransactionEncoding::Base64).await?;
|
|
||||||
println!(" 交易编码base64: {:?}", start_time.elapsed());
|
|
||||||
|
|
||||||
let request_body = serde_json::to_string(&json!({
|
|
||||||
"jsonrpc": "2.0",
|
|
||||||
"id": 1,
|
|
||||||
"method": "sendTransaction",
|
|
||||||
"params": [
|
|
||||||
content,
|
|
||||||
{ "encoding": "base64", "skipPreflight": true }
|
|
||||||
]
|
|
||||||
}))?;
|
|
||||||
|
|
||||||
let mut url = String::with_capacity(self.endpoint.len() + self.auth_token.len() + 20);
|
|
||||||
url.push_str(&self.endpoint);
|
|
||||||
url.push_str("/?api-key=");
|
|
||||||
url.push_str(&self.auth_token);
|
|
||||||
|
|
||||||
// 4. 直接使用 `text().await?`,避免 `json().await?` 的异步 JSON 解析
|
|
||||||
let response_text = self.http_client.post(&url)
|
|
||||||
.body(request_body) // 直接传字符串,避免 `json()` 开销
|
|
||||||
.header("Content-Type", "application/json") // 显式指定 JSON 头
|
|
||||||
.send()
|
|
||||||
.await?
|
|
||||||
.text()
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
// 5. 用 `serde_json::from_str()` 解析 JSON,减少 `.json().await?` 额外等待
|
|
||||||
if let Ok(response_json) = serde_json::from_str::<serde_json::Value>(&response_text) {
|
|
||||||
if response_json.get("result").is_some() {
|
|
||||||
println!(" 0slot{}提交: {:?}", trade_type, start_time.elapsed());
|
|
||||||
} else if let Some(_error) = response_json.get("error") {
|
|
||||||
eprintln!(" 0slot{}提交失败: {:?}", trade_type, _error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let start_time: Instant = Instant::now();
|
|
||||||
match poll_transaction_confirmation(&self.rpc_client, signature).await {
|
|
||||||
Ok(_) => (),
|
|
||||||
Err(_) => (),
|
|
||||||
}
|
|
||||||
|
|
||||||
println!(" 0slot{}确认: {:?}", trade_type, start_time.elapsed());
|
|
||||||
|
|
||||||
Ok(signature)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn send_transactions(&self, trade_type: TradeType, transactions: &Vec<VersionedTransaction>) -> Result<Vec<Signature>, anyhow::Error> {
|
|
||||||
let mut signatures = Vec::new();
|
|
||||||
for transaction in transactions {
|
|
||||||
let signature = self.send_transaction(trade_type, transaction).await?;
|
|
||||||
signatures.push(signature);
|
|
||||||
}
|
|
||||||
Ok(signatures)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct NozomiClient {
|
|
||||||
pub rpc_client: Arc<SolanaRpcClient>,
|
|
||||||
pub endpoint: String,
|
|
||||||
pub auth_token: String,
|
|
||||||
pub http_client: Client,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
|
||||||
impl FeeClientTrait for NozomiClient {
|
|
||||||
async fn send_transaction(&self, trade_type: TradeType, transaction: &VersionedTransaction) -> Result<Signature, anyhow::Error> {
|
|
||||||
self.send_transaction(trade_type, transaction).await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn send_transactions(&self, trade_type: TradeType, transactions: &Vec<VersionedTransaction>) -> Result<Vec<Signature>, anyhow::Error> {
|
|
||||||
self.send_transactions(trade_type, transactions).await
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_tip_account(&self) -> Result<String> {
|
|
||||||
let tip_account = *NOZOMI_TIP_ACCOUNTS.choose(&mut rand::rng()).or_else(|| NOZOMI_TIP_ACCOUNTS.first()).unwrap();
|
|
||||||
Ok(tip_account.to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_client_type(&self) -> ClientType {
|
|
||||||
ClientType::Nozomi
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NozomiClient {
|
|
||||||
pub fn new(rpc_url: String, endpoint: String, auth_token: String) -> Self {
|
|
||||||
let rpc_client = SolanaRpcClient::new(rpc_url);
|
|
||||||
let http_client = Client::builder()
|
|
||||||
.pool_idle_timeout(Duration::from_secs(60))
|
|
||||||
.pool_max_idle_per_host(64)
|
|
||||||
.tcp_keepalive(Some(Duration::from_secs(1200)))
|
|
||||||
.http2_keep_alive_interval(Duration::from_secs(15))
|
|
||||||
.timeout(Duration::from_secs(10))
|
|
||||||
.connect_timeout(Duration::from_secs(5))
|
|
||||||
.build()
|
|
||||||
.unwrap();
|
|
||||||
Self { rpc_client: Arc::new(rpc_client), endpoint, auth_token, http_client }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn send_transaction(&self, trade_type: TradeType, transaction: &VersionedTransaction) -> Result<Signature, anyhow::Error> {
|
|
||||||
let start_time = Instant::now();
|
|
||||||
let (content, signature) = serialize_transaction_and_encode(transaction, UiTransactionEncoding::Base64).await?;
|
|
||||||
println!(" 交易编码base64: {:?}", start_time.elapsed());
|
|
||||||
|
|
||||||
// 按照 Nozomi 文档要求构建请求体
|
|
||||||
let request_body = serde_json::to_string(&json!({
|
|
||||||
"jsonrpc": "2.0",
|
|
||||||
"id": 1,
|
|
||||||
"method": "sendTransaction",
|
|
||||||
"params": [
|
|
||||||
content,
|
|
||||||
{ "encoding": "base64" }
|
|
||||||
]
|
|
||||||
}))?;
|
|
||||||
|
|
||||||
let mut url = String::with_capacity(self.endpoint.len() + self.auth_token.len() + 20);
|
|
||||||
url.push_str(&self.endpoint);
|
|
||||||
url.push_str("/?c=");
|
|
||||||
url.push_str(&self.auth_token);
|
|
||||||
|
|
||||||
let response_text = self.http_client.post(&url)
|
|
||||||
.body(request_body)
|
|
||||||
.header("Content-Type", "application/json")
|
|
||||||
.send()
|
|
||||||
.await?
|
|
||||||
.text()
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
if let Ok(response_json) = serde_json::from_str::<serde_json::Value>(&response_text) {
|
|
||||||
if response_json.get("result").is_some() {
|
|
||||||
println!(" nozomi{}提交: {:?}", trade_type, start_time.elapsed());
|
|
||||||
} else if let Some(_error) = response_json.get("error") {
|
|
||||||
// eprintln!("nozomi交易提交失败: {:?}", _error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let start_time: Instant = Instant::now();
|
|
||||||
match poll_transaction_confirmation(&self.rpc_client, signature).await {
|
|
||||||
Ok(_) => (),
|
|
||||||
Err(_) => (),
|
|
||||||
}
|
|
||||||
|
|
||||||
println!(" nozomi{}确认: {:?}", trade_type, start_time.elapsed());
|
|
||||||
|
|
||||||
Ok(signature)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn send_transactions(&self, trade_type: TradeType, transactions: &Vec<VersionedTransaction>) -> Result<Vec<Signature>, anyhow::Error> {
|
|
||||||
let mut signatures = Vec::new();
|
|
||||||
for transaction in transactions {
|
|
||||||
let signature = self.send_transaction(trade_type, transaction).await?;
|
|
||||||
signatures.push(signature);
|
|
||||||
}
|
|
||||||
Ok(signatures)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Executable
+166
@@ -0,0 +1,166 @@
|
|||||||
|
use crate::protos::nextblock_grpc;
|
||||||
|
use crate::protos::nextblock_grpc::api_client::ApiClient;
|
||||||
|
use crate::swqos::common::{poll_transaction_confirmation, serialize_smart_transaction_and_encode};
|
||||||
|
use rand::seq::IndexedRandom;
|
||||||
|
use rustls::crypto::ring::default_provider;
|
||||||
|
use rustls::crypto::CryptoProvider;
|
||||||
|
use tonic::transport::Channel;
|
||||||
|
use yellowstone_grpc_client::Interceptor;
|
||||||
|
use std::str::FromStr;
|
||||||
|
use std::{sync::Arc, time::Instant};
|
||||||
|
|
||||||
|
use solana_sdk::{signature::Signature};
|
||||||
|
|
||||||
|
use tonic::{service::interceptor::InterceptedService, transport::Uri, Status};
|
||||||
|
use std::time::Duration;
|
||||||
|
use solana_transaction_status::UiTransactionEncoding;
|
||||||
|
use tonic::transport::ClientTlsConfig;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use solana_sdk::transaction::VersionedTransaction;
|
||||||
|
use crate::swqos::{ClientType, TradeType};
|
||||||
|
use crate::swqos::SwqosClientTrait;
|
||||||
|
|
||||||
|
use crate::{common::SolanaRpcClient, constants::pumpfun::accounts::NEXTBLOCK_TIP_ACCOUNTS};
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct MyInterceptor {
|
||||||
|
auth_token: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MyInterceptor {
|
||||||
|
pub fn new(auth_token: String) -> Self {
|
||||||
|
Self { auth_token }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Interceptor for MyInterceptor {
|
||||||
|
fn call(&mut self, mut request: tonic::Request<()>) -> Result<tonic::Request<()>, Status> {
|
||||||
|
request.metadata_mut().insert(
|
||||||
|
"authorization",
|
||||||
|
tonic::metadata::MetadataValue::from_str(&self.auth_token)
|
||||||
|
.map_err(|_| Status::invalid_argument("Invalid auth token"))?
|
||||||
|
);
|
||||||
|
Ok(request)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct NextBlockClient {
|
||||||
|
pub rpc_client: Arc<SolanaRpcClient>,
|
||||||
|
pub client: ApiClient<InterceptedService<Channel, MyInterceptor>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl SwqosClientTrait for NextBlockClient {
|
||||||
|
async fn send_transaction(&self, trade_type: TradeType, transaction: &VersionedTransaction) -> Result<Signature> {
|
||||||
|
self.send_transaction(trade_type, transaction).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn send_transactions(&self, trade_type: TradeType, transactions: &Vec<VersionedTransaction>) -> Result<Vec<Signature>> {
|
||||||
|
self.send_transactions(trade_type, transactions).await
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_tip_account(&self) -> Result<String> {
|
||||||
|
let tip_account = *NEXTBLOCK_TIP_ACCOUNTS.choose(&mut rand::rng()).or_else(|| NEXTBLOCK_TIP_ACCOUNTS.first()).unwrap();
|
||||||
|
Ok(tip_account.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_client_type(&self) -> ClientType {
|
||||||
|
ClientType::NextBlock
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NextBlockClient {
|
||||||
|
pub fn new(rpc_url: String, endpoint: String, auth_token: String) -> Self {
|
||||||
|
if CryptoProvider::get_default().is_none() {
|
||||||
|
let _ = default_provider()
|
||||||
|
.install_default()
|
||||||
|
.map_err(|e| anyhow::anyhow!("Failed to install crypto provider: {:?}", e));
|
||||||
|
}
|
||||||
|
|
||||||
|
let endpoint = endpoint.parse::<Uri>().unwrap();
|
||||||
|
let tls = ClientTlsConfig::new().with_native_roots();
|
||||||
|
let channel = Channel::builder(endpoint)
|
||||||
|
.tls_config(tls).expect("Failed to create TLS config")
|
||||||
|
.tcp_keepalive(Some(Duration::from_secs(60)))
|
||||||
|
.http2_keep_alive_interval(Duration::from_secs(30))
|
||||||
|
.keep_alive_while_idle(true)
|
||||||
|
.timeout(Duration::from_secs(30))
|
||||||
|
.connect_timeout(Duration::from_secs(10))
|
||||||
|
.connect_lazy();
|
||||||
|
|
||||||
|
let client = ApiClient::with_interceptor(channel, MyInterceptor::new(auth_token));
|
||||||
|
let rpc_client = SolanaRpcClient::new(rpc_url);
|
||||||
|
Self { rpc_client: Arc::new(rpc_client), client }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn send_transaction(&self, trade_type: TradeType, transaction: &VersionedTransaction) -> Result<Signature> {
|
||||||
|
let start_time = Instant::now();
|
||||||
|
let (content, signature) = serialize_smart_transaction_and_encode(transaction, UiTransactionEncoding::Base64).await?;
|
||||||
|
|
||||||
|
self.client.clone().post_submit_v2(nextblock_grpc::PostSubmitRequest {
|
||||||
|
transaction: Some(nextblock_grpc::TransactionMessage {
|
||||||
|
content,
|
||||||
|
is_cleanup: false,
|
||||||
|
}),
|
||||||
|
skip_pre_flight: true,
|
||||||
|
front_running_protection: Some(true),
|
||||||
|
experimental_front_running_protection: Some(true),
|
||||||
|
snipe_transaction: Some(true),
|
||||||
|
}).await?;
|
||||||
|
|
||||||
|
println!(" nextblock{}提交: {:?}", trade_type, start_time.elapsed());
|
||||||
|
|
||||||
|
let start_time: Instant = Instant::now();
|
||||||
|
let timeout: Duration = Duration::from_secs(10);
|
||||||
|
while Instant::now().duration_since(start_time) < timeout {
|
||||||
|
match poll_transaction_confirmation(&self.rpc_client, signature).await {
|
||||||
|
Ok(_) => break,
|
||||||
|
Err(_) => continue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
println!(" nextblock{}确认: {:?}", trade_type, start_time.elapsed());
|
||||||
|
|
||||||
|
Ok(signature)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn send_transactions(&self, trade_type: TradeType, transactions: &Vec<VersionedTransaction>) -> Result<Vec<Signature>> {
|
||||||
|
let mut entries = Vec::new();
|
||||||
|
let encoding = UiTransactionEncoding::Base64;
|
||||||
|
|
||||||
|
let mut signatures = Vec::new();
|
||||||
|
for transaction in transactions {
|
||||||
|
let (content, signature) = serialize_smart_transaction_and_encode(transaction, encoding).await?;
|
||||||
|
entries.push(nextblock_grpc::PostSubmitRequestEntry {
|
||||||
|
transaction: Some(nextblock_grpc::TransactionMessage {
|
||||||
|
content,
|
||||||
|
is_cleanup: false,
|
||||||
|
}),
|
||||||
|
skip_pre_flight: true,
|
||||||
|
});
|
||||||
|
signatures.push(signature);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.client.clone().post_submit_batch_v2(nextblock_grpc::PostSubmitBatchRequest {
|
||||||
|
entries,
|
||||||
|
submit_strategy: nextblock_grpc::SubmitStrategy::PSubmitAll as i32,
|
||||||
|
use_bundle: Some(true),
|
||||||
|
front_running_protection: Some(true),
|
||||||
|
}).await?;
|
||||||
|
|
||||||
|
let start_time: Instant = Instant::now();
|
||||||
|
for signature in signatures.clone() {
|
||||||
|
match poll_transaction_confirmation(&self.rpc_client, signature).await {
|
||||||
|
Ok(_) => continue,
|
||||||
|
Err(_) => continue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
println!(" nextblock{}确认: {:?}", trade_type, start_time.elapsed());
|
||||||
|
|
||||||
|
Ok(signatures)
|
||||||
|
}
|
||||||
|
}
|
||||||
Executable
+120
@@ -0,0 +1,120 @@
|
|||||||
|
|
||||||
|
use crate::swqos::common::{poll_transaction_confirmation, serialize_transaction_and_encode};
|
||||||
|
use rand::seq::IndexedRandom;
|
||||||
|
use reqwest::Client;
|
||||||
|
use serde_json::json;
|
||||||
|
use std::{sync::Arc, time::Instant};
|
||||||
|
|
||||||
|
use solana_sdk::{signature::Signature};
|
||||||
|
|
||||||
|
use std::time::Duration;
|
||||||
|
use solana_transaction_status::UiTransactionEncoding;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use solana_sdk::transaction::VersionedTransaction;
|
||||||
|
use crate::swqos::{ClientType, TradeType};
|
||||||
|
use crate::swqos::SwqosClientTrait;
|
||||||
|
|
||||||
|
use crate::{common::SolanaRpcClient, constants::pumpfun::accounts::NOZOMI_TIP_ACCOUNTS};
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct NozomiClient {
|
||||||
|
pub rpc_client: Arc<SolanaRpcClient>,
|
||||||
|
pub endpoint: String,
|
||||||
|
pub auth_token: String,
|
||||||
|
pub http_client: Client,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl SwqosClientTrait for NozomiClient {
|
||||||
|
async fn send_transaction(&self, trade_type: TradeType, transaction: &VersionedTransaction) -> Result<Signature> {
|
||||||
|
self.send_transaction(trade_type, transaction).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn send_transactions(&self, trade_type: TradeType, transactions: &Vec<VersionedTransaction>) -> Result<Vec<Signature>> {
|
||||||
|
self.send_transactions(trade_type, transactions).await
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_tip_account(&self) -> Result<String> {
|
||||||
|
let tip_account = *NOZOMI_TIP_ACCOUNTS.choose(&mut rand::rng()).or_else(|| NOZOMI_TIP_ACCOUNTS.first()).unwrap();
|
||||||
|
Ok(tip_account.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_client_type(&self) -> ClientType {
|
||||||
|
ClientType::Nozomi
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NozomiClient {
|
||||||
|
pub fn new(rpc_url: String, endpoint: String, auth_token: String) -> Self {
|
||||||
|
let rpc_client = SolanaRpcClient::new(rpc_url);
|
||||||
|
let http_client = Client::builder()
|
||||||
|
.pool_idle_timeout(Duration::from_secs(60))
|
||||||
|
.pool_max_idle_per_host(64)
|
||||||
|
.tcp_keepalive(Some(Duration::from_secs(1200)))
|
||||||
|
.http2_keep_alive_interval(Duration::from_secs(15))
|
||||||
|
.timeout(Duration::from_secs(10))
|
||||||
|
.connect_timeout(Duration::from_secs(5))
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
Self { rpc_client: Arc::new(rpc_client), endpoint, auth_token, http_client }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn send_transaction(&self, trade_type: TradeType, transaction: &VersionedTransaction) -> Result<Signature> {
|
||||||
|
let start_time = Instant::now();
|
||||||
|
let (content, signature) = serialize_transaction_and_encode(transaction, UiTransactionEncoding::Base64).await?;
|
||||||
|
println!(" 交易编码base64: {:?}", start_time.elapsed());
|
||||||
|
|
||||||
|
// 按照 Nozomi 文档要求构建请求体
|
||||||
|
let request_body = serde_json::to_string(&json!({
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"id": 1,
|
||||||
|
"method": "sendTransaction",
|
||||||
|
"params": [
|
||||||
|
content,
|
||||||
|
{ "encoding": "base64" }
|
||||||
|
]
|
||||||
|
}))?;
|
||||||
|
|
||||||
|
let mut url = String::with_capacity(self.endpoint.len() + self.auth_token.len() + 20);
|
||||||
|
url.push_str(&self.endpoint);
|
||||||
|
url.push_str("/?c=");
|
||||||
|
url.push_str(&self.auth_token);
|
||||||
|
|
||||||
|
let response_text = self.http_client.post(&url)
|
||||||
|
.body(request_body)
|
||||||
|
.header("Content-Type", "application/json")
|
||||||
|
.send()
|
||||||
|
.await?
|
||||||
|
.text()
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
if let Ok(response_json) = serde_json::from_str::<serde_json::Value>(&response_text) {
|
||||||
|
if response_json.get("result").is_some() {
|
||||||
|
println!(" nozomi{}提交: {:?}", trade_type, start_time.elapsed());
|
||||||
|
} else if let Some(_error) = response_json.get("error") {
|
||||||
|
// eprintln!("nozomi交易提交失败: {:?}", _error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let start_time: Instant = Instant::now();
|
||||||
|
match poll_transaction_confirmation(&self.rpc_client, signature).await {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(_) => (),
|
||||||
|
}
|
||||||
|
|
||||||
|
println!(" nozomi{}确认: {:?}", trade_type, start_time.elapsed());
|
||||||
|
|
||||||
|
Ok(signature)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn send_transactions(&self, trade_type: TradeType, transactions: &Vec<VersionedTransaction>) -> Result<Vec<Signature>> {
|
||||||
|
let mut signatures = Vec::new();
|
||||||
|
for transaction in transactions {
|
||||||
|
let signature = self.send_transaction(trade_type, transaction).await?;
|
||||||
|
signatures.push(signature);
|
||||||
|
}
|
||||||
|
Ok(signatures)
|
||||||
|
}
|
||||||
|
}
|
||||||
Executable
+64
@@ -0,0 +1,64 @@
|
|||||||
|
use std::{sync::Arc, time::Instant};
|
||||||
|
|
||||||
|
use solana_client::rpc_config::RpcSendTransactionConfig;
|
||||||
|
use solana_sdk::{
|
||||||
|
commitment_config::CommitmentLevel,
|
||||||
|
signature::Signature,
|
||||||
|
transaction::VersionedTransaction,
|
||||||
|
};
|
||||||
|
use solana_transaction_status::UiTransactionEncoding;
|
||||||
|
|
||||||
|
use crate::{common::SolanaRpcClient, swqos::{common::poll_transaction_confirmation, ClientType, TradeType}};
|
||||||
|
use crate::swqos::SwqosClientTrait;
|
||||||
|
use anyhow::Result;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct SolRpcClient {
|
||||||
|
pub rpc_client: Arc<SolanaRpcClient>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl SwqosClientTrait for SolRpcClient {
|
||||||
|
async fn send_transaction(&self, trade_type: TradeType, transaction: &VersionedTransaction) -> Result<Signature> {
|
||||||
|
let signature = self.rpc_client.send_transaction_with_config(transaction, RpcSendTransactionConfig{
|
||||||
|
skip_preflight: true,
|
||||||
|
preflight_commitment: Some(CommitmentLevel::Processed),
|
||||||
|
encoding: Some(UiTransactionEncoding::Base64),
|
||||||
|
max_retries: Some(3),
|
||||||
|
min_context_slot: Some(0),
|
||||||
|
}).await?;
|
||||||
|
|
||||||
|
let start_time = Instant::now();
|
||||||
|
match poll_transaction_confirmation(&self.rpc_client, signature).await {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(_) => (),
|
||||||
|
}
|
||||||
|
println!(" signature: {:?}", signature);
|
||||||
|
println!(" rpc{}确认: {:?}", trade_type, start_time.elapsed());
|
||||||
|
|
||||||
|
Ok(signature)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn send_transactions(&self, trade_type: TradeType, transactions: &Vec<VersionedTransaction>) -> Result<Vec<Signature>> {
|
||||||
|
let mut signatures = Vec::new();
|
||||||
|
for transaction in transactions {
|
||||||
|
let signature = self.send_transaction(trade_type, transaction).await?;
|
||||||
|
signatures.push(signature);
|
||||||
|
}
|
||||||
|
Ok(signatures)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_tip_account(&self) -> Result<String> {
|
||||||
|
Ok("".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_client_type(&self) -> ClientType {
|
||||||
|
ClientType::Rpc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SolRpcClient {
|
||||||
|
pub fn new(rpc_client: Arc<SolanaRpcClient>) -> Self {
|
||||||
|
Self { rpc_client }
|
||||||
|
}
|
||||||
|
}
|
||||||
Executable
Executable
+119
@@ -0,0 +1,119 @@
|
|||||||
|
use crate::swqos::common::{poll_transaction_confirmation, serialize_transaction_and_encode};
|
||||||
|
use rand::seq::IndexedRandom;
|
||||||
|
use reqwest::Client;
|
||||||
|
use serde_json::json;
|
||||||
|
use std::{sync::Arc, time::Instant};
|
||||||
|
|
||||||
|
use std::time::Duration;
|
||||||
|
use solana_transaction_status::UiTransactionEncoding;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use solana_sdk::signature::Signature;
|
||||||
|
use solana_sdk::transaction::VersionedTransaction;
|
||||||
|
use crate::swqos::{ClientType, TradeType};
|
||||||
|
use crate::swqos::SwqosClientTrait;
|
||||||
|
|
||||||
|
use crate::{common::SolanaRpcClient, constants::pumpfun::accounts::ZEROSLOT_TIP_ACCOUNTS};
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct ZeroSlotClient {
|
||||||
|
pub endpoint: String,
|
||||||
|
pub auth_token: String,
|
||||||
|
pub rpc_client: Arc<SolanaRpcClient>,
|
||||||
|
pub http_client: Client,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl SwqosClientTrait for ZeroSlotClient {
|
||||||
|
async fn send_transaction(&self, trade_type: TradeType, transaction: &VersionedTransaction) -> Result<Signature> {
|
||||||
|
self.send_transaction(trade_type, transaction).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn send_transactions(&self, trade_type: TradeType, transactions: &Vec<VersionedTransaction>) -> Result<Vec<Signature>> {
|
||||||
|
self.send_transactions(trade_type, transactions).await
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_tip_account(&self) -> Result<String> {
|
||||||
|
let tip_account = *ZEROSLOT_TIP_ACCOUNTS.choose(&mut rand::rng()).or_else(|| ZEROSLOT_TIP_ACCOUNTS.first()).unwrap();
|
||||||
|
Ok(tip_account.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_client_type(&self) -> ClientType {
|
||||||
|
ClientType::ZeroSlot
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ZeroSlotClient {
|
||||||
|
pub fn new(rpc_url: String, endpoint: String, auth_token: String) -> Self {
|
||||||
|
let rpc_client = SolanaRpcClient::new(rpc_url);
|
||||||
|
let http_client = Client::builder()
|
||||||
|
.pool_idle_timeout(Duration::from_secs(60))
|
||||||
|
.pool_max_idle_per_host(64)
|
||||||
|
.tcp_keepalive(Some(Duration::from_secs(1200)))
|
||||||
|
.http2_keep_alive_interval(Duration::from_secs(15))
|
||||||
|
.timeout(Duration::from_secs(10))
|
||||||
|
.connect_timeout(Duration::from_secs(5))
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
Self { rpc_client: Arc::new(rpc_client), endpoint, auth_token, http_client }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn send_transaction(&self, trade_type: TradeType, transaction: &VersionedTransaction) -> Result<Signature> {
|
||||||
|
let start_time = Instant::now();
|
||||||
|
let (content, signature) = serialize_transaction_and_encode(transaction, UiTransactionEncoding::Base64).await?;
|
||||||
|
println!(" 交易编码base64: {:?}", start_time.elapsed());
|
||||||
|
|
||||||
|
let request_body = serde_json::to_string(&json!({
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"id": 1,
|
||||||
|
"method": "sendTransaction",
|
||||||
|
"params": [
|
||||||
|
content,
|
||||||
|
{ "encoding": "base64", "skipPreflight": true }
|
||||||
|
]
|
||||||
|
}))?;
|
||||||
|
|
||||||
|
let mut url = String::with_capacity(self.endpoint.len() + self.auth_token.len() + 20);
|
||||||
|
url.push_str(&self.endpoint);
|
||||||
|
url.push_str("/?api-key=");
|
||||||
|
url.push_str(&self.auth_token);
|
||||||
|
|
||||||
|
// 4. 直接使用 `text().await?`,避免 `json().await?` 的异步 JSON 解析
|
||||||
|
let response_text = self.http_client.post(&url)
|
||||||
|
.body(request_body) // 直接传字符串,避免 `json()` 开销
|
||||||
|
.header("Content-Type", "application/json") // 显式指定 JSON 头
|
||||||
|
.send()
|
||||||
|
.await?
|
||||||
|
.text()
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// 5. 用 `serde_json::from_str()` 解析 JSON,减少 `.json().await?` 额外等待
|
||||||
|
if let Ok(response_json) = serde_json::from_str::<serde_json::Value>(&response_text) {
|
||||||
|
if response_json.get("result").is_some() {
|
||||||
|
println!(" 0slot{}提交: {:?}", trade_type, start_time.elapsed());
|
||||||
|
} else if let Some(_error) = response_json.get("error") {
|
||||||
|
eprintln!(" 0slot{}提交失败: {:?}", trade_type, _error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let start_time: Instant = Instant::now();
|
||||||
|
match poll_transaction_confirmation(&self.rpc_client, signature).await {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(_) => (),
|
||||||
|
}
|
||||||
|
|
||||||
|
println!(" 0slot{}确认: {:?}", trade_type, start_time.elapsed());
|
||||||
|
|
||||||
|
Ok(signature)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn send_transactions(&self, trade_type: TradeType, transactions: &Vec<VersionedTransaction>) -> Result<Vec<Signature>> {
|
||||||
|
let mut signatures = Vec::new();
|
||||||
|
for transaction in transactions {
|
||||||
|
let signature = self.send_transaction(trade_type, transaction).await?;
|
||||||
|
signatures.push(signature);
|
||||||
|
}
|
||||||
|
Ok(signatures)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -107,7 +107,7 @@ impl TradeExecutor for GenericTradeExecutor {
|
|||||||
|
|
||||||
// 并行执行交易
|
// 并行执行交易
|
||||||
parallel_execute_with_tips(
|
parallel_execute_with_tips(
|
||||||
params.fee_clients,
|
params.swqos_clients,
|
||||||
params.payer,
|
params.payer,
|
||||||
instructions,
|
instructions,
|
||||||
params.priority_fee,
|
params.priority_fee,
|
||||||
@@ -180,7 +180,7 @@ impl TradeExecutor for GenericTradeExecutor {
|
|||||||
|
|
||||||
// 并行执行交易
|
// 并行执行交易
|
||||||
parallel_execute_with_tips(
|
parallel_execute_with_tips(
|
||||||
params.fee_clients,
|
params.swqos_clients,
|
||||||
params.payer,
|
params.payer,
|
||||||
instructions,
|
instructions,
|
||||||
params.priority_fee,
|
params.priority_fee,
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use tokio::task::JoinHandle;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
common::PriorityFee,
|
common::PriorityFee,
|
||||||
swqos::{ClientType, FeeClient, TradeType},
|
swqos::{ClientType, SwqosClient, TradeType},
|
||||||
trading::common::{
|
trading::common::{
|
||||||
build_rpc_transaction, build_sell_tip_transaction_with_priority_fee,
|
build_rpc_transaction, build_sell_tip_transaction_with_priority_fee,
|
||||||
build_sell_transaction, build_tip_transaction_with_priority_fee,
|
build_sell_transaction, build_tip_transaction_with_priority_fee,
|
||||||
@@ -15,7 +15,7 @@ use crate::{
|
|||||||
|
|
||||||
/// 并行执行交易的通用函数
|
/// 并行执行交易的通用函数
|
||||||
pub async fn parallel_execute_with_tips(
|
pub async fn parallel_execute_with_tips(
|
||||||
fee_clients: Vec<Arc<FeeClient>>,
|
swqos_clients: Vec<Arc<SwqosClient>>,
|
||||||
payer: Arc<Keypair>,
|
payer: Arc<Keypair>,
|
||||||
instructions: Vec<Instruction>,
|
instructions: Vec<Instruction>,
|
||||||
priority_fee: PriorityFee,
|
priority_fee: PriorityFee,
|
||||||
@@ -27,8 +27,8 @@ pub async fn parallel_execute_with_tips(
|
|||||||
let cores = core_affinity::get_core_ids().unwrap();
|
let cores = core_affinity::get_core_ids().unwrap();
|
||||||
let mut handles: Vec<JoinHandle<Result<()>>> = vec![];
|
let mut handles: Vec<JoinHandle<Result<()>>> = vec![];
|
||||||
|
|
||||||
for i in 0..fee_clients.len() {
|
for i in 0..swqos_clients.len() {
|
||||||
let fee_client = fee_clients[i].clone();
|
let swqos_client = swqos_clients[i].clone();
|
||||||
let payer = payer.clone();
|
let payer = payer.clone();
|
||||||
let instructions = instructions.clone();
|
let instructions = instructions.clone();
|
||||||
let mut priority_fee = priority_fee.clone();
|
let mut priority_fee = priority_fee.clone();
|
||||||
@@ -37,7 +37,7 @@ pub async fn parallel_execute_with_tips(
|
|||||||
let handle = tokio::spawn(async move {
|
let handle = tokio::spawn(async move {
|
||||||
core_affinity::set_for_current(core_id);
|
core_affinity::set_for_current(core_id);
|
||||||
let transaction = if matches!(trade_type, TradeType::Sell)
|
let transaction = if matches!(trade_type, TradeType::Sell)
|
||||||
&& fee_client.get_client_type() == ClientType::Rpc
|
&& swqos_client.get_client_type() == ClientType::Rpc
|
||||||
{
|
{
|
||||||
build_sell_transaction(
|
build_sell_transaction(
|
||||||
payer,
|
payer,
|
||||||
@@ -48,9 +48,9 @@ pub async fn parallel_execute_with_tips(
|
|||||||
)
|
)
|
||||||
.await?
|
.await?
|
||||||
} else if matches!(trade_type, TradeType::Sell)
|
} else if matches!(trade_type, TradeType::Sell)
|
||||||
&& fee_client.get_client_type() != ClientType::Rpc
|
&& swqos_client.get_client_type() != ClientType::Rpc
|
||||||
{
|
{
|
||||||
let tip_account = fee_client.get_tip_account()?;
|
let tip_account = swqos_client.get_tip_account()?;
|
||||||
let tip_account = Arc::new(Pubkey::from_str(&tip_account).map_err(|e| anyhow!(e))?);
|
let tip_account = Arc::new(Pubkey::from_str(&tip_account).map_err(|e| anyhow!(e))?);
|
||||||
build_sell_tip_transaction_with_priority_fee(
|
build_sell_tip_transaction_with_priority_fee(
|
||||||
payer,
|
payer,
|
||||||
@@ -61,7 +61,7 @@ pub async fn parallel_execute_with_tips(
|
|||||||
recent_blockhash,
|
recent_blockhash,
|
||||||
)
|
)
|
||||||
.await?
|
.await?
|
||||||
} else if fee_client.get_client_type() == ClientType::Rpc {
|
} else if swqos_client.get_client_type() == ClientType::Rpc {
|
||||||
build_rpc_transaction(
|
build_rpc_transaction(
|
||||||
payer,
|
payer,
|
||||||
&priority_fee,
|
&priority_fee,
|
||||||
@@ -72,7 +72,7 @@ pub async fn parallel_execute_with_tips(
|
|||||||
)
|
)
|
||||||
.await?
|
.await?
|
||||||
} else {
|
} else {
|
||||||
let tip_account = fee_client.get_tip_account()?;
|
let tip_account = swqos_client.get_tip_account()?;
|
||||||
let tip_account = Arc::new(Pubkey::from_str(&tip_account).map_err(|e| anyhow!(e))?);
|
let tip_account = Arc::new(Pubkey::from_str(&tip_account).map_err(|e| anyhow!(e))?);
|
||||||
priority_fee.buy_tip_fee = priority_fee.buy_tip_fees[i];
|
priority_fee.buy_tip_fee = priority_fee.buy_tip_fees[i];
|
||||||
|
|
||||||
@@ -88,7 +88,7 @@ pub async fn parallel_execute_with_tips(
|
|||||||
.await?
|
.await?
|
||||||
};
|
};
|
||||||
|
|
||||||
fee_client
|
swqos_client
|
||||||
.send_transaction(trade_type, &transaction)
|
.send_transaction(trade_type, &transaction)
|
||||||
.await?;
|
.await?;
|
||||||
Ok::<(), anyhow::Error>(())
|
Ok::<(), anyhow::Error>(())
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
use super::traits::ProtocolParams;
|
use super::traits::ProtocolParams;
|
||||||
use crate::common::{PriorityFee, SolanaRpcClient};
|
use crate::common::{PriorityFee, SolanaRpcClient};
|
||||||
use crate::swqos::FeeClient;
|
use crate::swqos::SwqosClient;
|
||||||
use crate::accounts::BondingCurveAccount;
|
use crate::accounts::BondingCurveAccount;
|
||||||
|
|
||||||
/// 通用买入参数
|
/// 通用买入参数
|
||||||
@@ -27,7 +27,7 @@ pub struct BuyParams {
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct BuyWithTipParams {
|
pub struct BuyWithTipParams {
|
||||||
pub rpc: Option<Arc<SolanaRpcClient>>,
|
pub rpc: Option<Arc<SolanaRpcClient>>,
|
||||||
pub fee_clients: Vec<Arc<FeeClient>>,
|
pub swqos_clients: Vec<Arc<SwqosClient>>,
|
||||||
pub payer: Arc<Keypair>,
|
pub payer: Arc<Keypair>,
|
||||||
pub mint: Pubkey,
|
pub mint: Pubkey,
|
||||||
pub creator: Pubkey,
|
pub creator: Pubkey,
|
||||||
@@ -59,7 +59,7 @@ pub struct SellParams {
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct SellWithTipParams {
|
pub struct SellWithTipParams {
|
||||||
pub rpc: Option<Arc<SolanaRpcClient>>,
|
pub rpc: Option<Arc<SolanaRpcClient>>,
|
||||||
pub fee_clients: Vec<Arc<FeeClient>>,
|
pub swqos_clients: Vec<Arc<SwqosClient>>,
|
||||||
pub payer: Arc<Keypair>,
|
pub payer: Arc<Keypair>,
|
||||||
pub mint: Pubkey,
|
pub mint: Pubkey,
|
||||||
pub creator: Pubkey,
|
pub creator: Pubkey,
|
||||||
@@ -124,10 +124,10 @@ impl ProtocolParams for PumpSwapParams {
|
|||||||
|
|
||||||
impl BuyParams {
|
impl BuyParams {
|
||||||
/// 转换为BuyWithTipParams
|
/// 转换为BuyWithTipParams
|
||||||
pub fn with_tip(self, fee_clients: Vec<Arc<FeeClient>>) -> BuyWithTipParams {
|
pub fn with_tip(self, swqos_clients: Vec<Arc<SwqosClient>>) -> BuyWithTipParams {
|
||||||
BuyWithTipParams {
|
BuyWithTipParams {
|
||||||
rpc: self.rpc,
|
rpc: self.rpc,
|
||||||
fee_clients,
|
swqos_clients,
|
||||||
payer: self.payer,
|
payer: self.payer,
|
||||||
mint: self.mint,
|
mint: self.mint,
|
||||||
creator: self.creator,
|
creator: self.creator,
|
||||||
@@ -144,10 +144,10 @@ impl BuyParams {
|
|||||||
|
|
||||||
impl SellParams {
|
impl SellParams {
|
||||||
/// 转换为SellWithTipParams
|
/// 转换为SellWithTipParams
|
||||||
pub fn with_tip(self, fee_clients: Vec<Arc<FeeClient>>) -> SellWithTipParams {
|
pub fn with_tip(self, swqos_clients: Vec<Arc<SwqosClient>>) -> SellWithTipParams {
|
||||||
SellWithTipParams {
|
SellWithTipParams {
|
||||||
rpc: self.rpc,
|
rpc: self.rpc,
|
||||||
fee_clients,
|
swqos_clients,
|
||||||
payer: self.payer,
|
payer: self.payer,
|
||||||
mint: self.mint,
|
mint: self.mint,
|
||||||
creator: self.creator,
|
creator: self.creator,
|
||||||
|
|||||||
Reference in New Issue
Block a user