grpc optimization

This commit is contained in:
周威威
2025-05-31 14:45:20 +08:00
parent f261b8a66d
commit a7c9721877
33 changed files with 616 additions and 4114 deletions
+8 -1
View File
@@ -1,5 +1,5 @@
use borsh::{BorshDeserialize, BorshSerialize};
use solana_sdk::pubkey::Pubkey;
use solana_sdk::{pubkey::Pubkey, transaction::VersionedTransaction};
use crate::error::{ClientError, ClientResult};
@@ -61,6 +61,13 @@ pub struct SwapBaseInLog {
pub out_amount: u64,
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct TransferInfo {
pub slot: u64,
pub signature: String,
pub tx: Option<VersionedTransaction>,
}
pub trait EventTrait: Sized + std::fmt::Debug {
fn from_bytes(bytes: &[u8]) -> ClientResult<Self>;
}
+9 -2
View File
@@ -1,7 +1,7 @@
use base64::engine::general_purpose;
use base64::Engine;
use regex::Regex;
use crate::common::logs_data::{CreateTokenInfo, TradeInfo, EventTrait};
use crate::common::logs_data::{CreateTokenInfo, TradeInfo, EventTrait, TransferInfo};
pub const PROGRAM_DATA: &str = "Program data: ";
@@ -23,6 +23,12 @@ pub enum DexEvent {
Error(String),
}
#[derive(Debug)]
pub enum SystemEvent {
NewTransfer(TransferInfo),
Error(String),
}
// #[derive(Debug, Clone, Copy)]
// pub struct PumpEvent {}
@@ -67,9 +73,10 @@ impl RaydiumEvent {
if !logs.is_empty() {
let logs_iter = logs.iter().peekable();
let re = Regex::new(r"ray_log: (?P<base64>[A-Za-z0-9+/=]+)").unwrap();
for l in logs_iter.rev() {
let re = Regex::new(r"ray_log: (?P<base64>[A-Za-z0-9+/=]+)").unwrap();
if let Some(caps) = re.captures(l) {
if let Some(base64) = caps.name("base64") {
let bytes = general_purpose::STANDARD.decode(base64.as_str()).unwrap();
+66 -2
View File
@@ -1,11 +1,75 @@
use crate::common::logs_data::DexInstruction;
use crate::common::logs_parser::{parse_create_token_data, parse_trade_data};
use crate::common::logs_parser::{parse_create_token_data, parse_trade_data, parse_instruction_create_token_data, parse_instruction_trade_data};
use crate::error::ClientResult;
use solana_sdk::pubkey::Pubkey;
pub struct LogFilter;
use solana_sdk::pubkey::Pubkey;
use std::str::FromStr;
use solana_sdk::transaction::VersionedTransaction;
impl LogFilter {
const PROGRAM_ID: &'static str = "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P";
/// Parse transaction logs and return instruction type and data
pub fn parse_compiled_instruction(
versioned_tx: VersionedTransaction,
bot_wallet: Option<Pubkey>) -> ClientResult<Vec<DexInstruction>> {
let compiled_instructions = versioned_tx.message.instructions();
let accounts = versioned_tx.message.static_account_keys();
let program_id = Pubkey::from_str(Self::PROGRAM_ID).unwrap_or_default();
let pump_index = accounts.iter().position(|key| key == &program_id);
let mut instructions: Vec<DexInstruction> = Vec::new();
if let Some(index) = pump_index {
for instruction in compiled_instructions {
if instruction.program_id_index as usize == index {
let all_accounts_valid = instruction.accounts.iter()
.all(|&acc_idx| (acc_idx as usize) < accounts.len());
if !all_accounts_valid {
continue;
}
match instruction.data.first() {
// create
Some(&24) => {
if let Ok(token_info) = parse_instruction_create_token_data(instruction, accounts) {
instructions.push(DexInstruction::CreateToken(token_info));
};
}
// buy
Some(&102) if instruction.data.len() == 24 && instruction.accounts.len() >= 12 => {
if let Ok(trade_info) = parse_instruction_trade_data(instruction, accounts, true) {
if let Some(bot_wallet_pubkey) = bot_wallet {
if trade_info.user.to_string() == bot_wallet_pubkey.to_string() {
instructions.push(DexInstruction::BotTrade(trade_info));
} else {
instructions.push(DexInstruction::UserTrade(trade_info));
}
} else {
instructions.push(DexInstruction::UserTrade(trade_info));
}
};
}
// sell
Some(&51) if instruction.data.len() == 24 && instruction.accounts.len() >= 12 => {
if let Ok(trade_info) = parse_instruction_trade_data(instruction, accounts, false) {
if let Some(bot_wallet_pubkey) = bot_wallet {
if trade_info.user.to_string() == bot_wallet_pubkey.to_string() {
instructions.push(DexInstruction::BotTrade(trade_info));
} else {
instructions.push(DexInstruction::UserTrade(trade_info));
}
} else {
instructions.push(DexInstruction::UserTrade(trade_info));
}
};
}
_ => {}
}
}
}
}
Ok(instructions)
}
/// Parse transaction logs and return instruction type and data
pub fn parse_instruction(logs: &[String], bot_wallet: Option<Pubkey>) -> ClientResult<Vec<DexInstruction>> {
+62
View File
@@ -9,6 +9,8 @@ use crate::common::{
};
use solana_sdk::pubkey::Pubkey;
use solana_sdk::instruction::CompiledInstruction;
use std::time::{SystemTime, UNIX_EPOCH};
pub async fn process_logs<F>(
signature: &str,
@@ -171,4 +173,64 @@ pub fn parse_trade_data(data: &str) -> ClientResult<TradeInfo> {
real_sol_reserves,
real_token_reserves,
})
}
fn current_timestamp_millis() -> i64 {
let duration = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("Time went backwards");
duration.as_millis() as i64
}
pub fn parse_instruction_create_token_data(instruction: &CompiledInstruction, accounts: &[Pubkey]) -> ClientResult<CreateTokenInfo> {
let data = instruction.data.clone();
let mut offset = 0;
offset += 8;
let len1 = u32::from_le_bytes(data[offset..offset + 4].try_into().unwrap()) as usize;
offset += 4;
let name = String::from_utf8_lossy(&data[offset..offset + len1]);
offset += len1;
let len2 = u32::from_le_bytes(data[offset..offset + 4].try_into().unwrap()) as usize;
offset += 4;
let symbol = String::from_utf8_lossy(&data[offset..offset + len2]);
offset += len2;
let _flag = u32::from_le_bytes(data[offset..offset + 4].try_into().unwrap());
offset += 4;
let hash_start = data.len() - 32;
let ipfs_bytes = &data[offset..hash_start];
let uri = String::from_utf8_lossy(ipfs_bytes);
let mint = accounts[instruction.accounts[0] as usize];
let user = accounts[instruction.accounts[7] as usize];
let bonding_curve= accounts[instruction.accounts[2] as usize];
Ok(CreateTokenInfo {
slot: 0,
name: name.to_string(),
symbol: symbol.to_string(),
uri: uri.to_string(),
mint,
bonding_curve,
user,
})
}
pub fn parse_instruction_trade_data(instruction: &CompiledInstruction, accounts: &[Pubkey], is_buy: bool) -> ClientResult<TradeInfo> {
let data = instruction.data.clone();
let amount = u64::from_le_bytes(data[8..16].try_into().unwrap());
let max_sol_cost_or_min_sol_output = u64::from_le_bytes(data[16..24].try_into().unwrap());
let user = accounts[instruction.accounts[6] as usize];
let mint = accounts[instruction.accounts[2] as usize];
Ok(TradeInfo {
slot: 0,
mint,
sol_amount: max_sol_cost_or_min_sol_output,
token_amount: amount,
is_buy,
user,
timestamp: current_timestamp_millis(),
virtual_sol_reserves: 0,
virtual_token_reserves: 0,
real_sol_reserves: 0,
real_token_reserves: 0,
})
}
+4 -4
View File
@@ -8,9 +8,9 @@ use std::sync::Arc;
use tokio::sync::mpsc;
use tokio::task::JoinHandle;
use futures::StreamExt;
use crate::{constants, common::{
logs_data::DexInstruction, logs_events::DexEvent, logs_filters::LogFilter
}};
use crate::common::{
logs_data::DexInstruction, logs_filters::LogFilter
};
use super::logs_events::PumpfunEvent;
@@ -41,7 +41,7 @@ pub async fn tokens_subscription<F>(
where
F: Fn(PumpfunEvent) + Send + Sync + 'static,
{
let program_address = constants::accounts::PUMPFUN.to_string();
let program_address = "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P".to_string();
let logs_filter = RpcTransactionLogsFilter::Mentions(vec![program_address]);
let logs_config = RpcTransactionLogsConfig {
-2
View File
@@ -3,6 +3,4 @@ pub mod logs_parser;
pub mod logs_filters;
pub mod logs_subscribe;
pub mod logs_events;
pub mod types;
pub use types::*;
-95
View File
@@ -1,95 +0,0 @@
use std::sync::Arc;
use solana_client::rpc_client::RpcClient;
use solana_sdk::{commitment_config::CommitmentConfig, signature::Keypair};
use serde::Deserialize;
use crate::{constants::trade::{DEFAULT_BUY_TIP_FEE, DEFAULT_COMPUTE_UNIT_LIMIT, DEFAULT_COMPUTE_UNIT_PRICE, DEFAULT_SELL_TIP_FEE}, swqos::FeeClient};
#[derive(Debug, Clone, PartialEq)]
pub enum FeeType {
Jito,
NextBlock,
}
#[derive(Debug, Clone)]
pub struct Cluster {
pub rpc_url: String,
pub block_engine_url: String,
pub nextblock_url: String,
pub nextblock_auth_token: String,
pub zeroslot_url: String,
pub zeroslot_auth_token: String,
pub use_jito: bool,
pub use_nextblock: bool,
pub use_zeroslot: bool,
pub priority_fee: PriorityFee,
pub commitment: CommitmentConfig,
}
impl Cluster {
pub fn new(
rpc_url: String,
block_engine_url:
String, nextblock_url:
String, nextblock_auth_token:
String, zeroslot_url: String,
zeroslot_auth_token: String,
priority_fee: PriorityFee,
commitment: CommitmentConfig,
use_jito: bool,
use_nextblock: bool,
use_zeroslot: bool
) -> Self {
Self {
rpc_url,
block_engine_url,
nextblock_url,
nextblock_auth_token,
zeroslot_url,
zeroslot_auth_token,
priority_fee,
commitment,
use_jito,
use_nextblock,
use_zeroslot
}
}
}
#[derive(Debug, Deserialize, Clone, Copy, PartialEq)]
pub struct PriorityFee {
pub unit_limit: u32,
pub unit_price: u64,
pub buy_tip_fee: f64,
pub sell_tip_fee: f64,
}
impl Default for PriorityFee {
fn default() -> Self {
Self {
unit_limit: DEFAULT_COMPUTE_UNIT_LIMIT,
unit_price: DEFAULT_COMPUTE_UNIT_PRICE,
buy_tip_fee: DEFAULT_BUY_TIP_FEE,
sell_tip_fee: DEFAULT_SELL_TIP_FEE
}
}
}
pub type SolanaRpcClient = solana_client::nonblocking::rpc_client::RpcClient;
pub struct MethodArgs {
pub payer: Arc<Keypair>,
pub rpc: Arc<RpcClient>,
pub nonblocking_rpc: Arc<SolanaRpcClient>,
pub jito_client: Arc<FeeClient>,
}
impl MethodArgs {
pub fn new(payer: Arc<Keypair>, rpc: Arc<RpcClient>, nonblocking_rpc: Arc<SolanaRpcClient>, jito_client: Arc<FeeClient>) -> Self {
Self { payer, rpc, nonblocking_rpc, jito_client }
}
}
pub type AnyResult<T> = anyhow::Result<T>;