mirror of
https://github.com/0xfnzero/solana-streamer.git
synced 2026-08-09 15:10:56 +00:00
grpc optimization
This commit is contained in:
@@ -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>;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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>> {
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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::*;
|
||||
|
||||
@@ -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>;
|
||||
|
||||
Reference in New Issue
Block a user