136 lines
5.2 KiB
Rust
Executable File
136 lines
5.2 KiB
Rust
Executable File
#[cfg(test)]
|
|
mod subscribe_tx_tests {
|
|
use crate::common::{
|
|
event::{PumpEvent, RaydiumEvent, SwapBaseInLog, TradeEvent},
|
|
myerror::AppError,
|
|
yellowstone_grpc::{TransactionPretty, YellowstoneGrpc},
|
|
};
|
|
use anyhow::anyhow;
|
|
use chrono::Local;
|
|
use dotenvy::dotenv;
|
|
use futures::{channel::mpsc, sink::SinkExt, stream::StreamExt};
|
|
use log::{error, info};
|
|
use solana_sdk::pubkey;
|
|
use solana_sdk::pubkey::Pubkey;
|
|
use solana_transaction_status::{
|
|
option_serializer::OptionSerializer, EncodedTransactionWithStatusMeta,
|
|
};
|
|
use std::env;
|
|
use tokio::test;
|
|
use yellowstone_grpc_proto::geyser::{
|
|
subscribe_update::UpdateOneof, SubscribeRequest, SubscribeRequestPing,
|
|
};
|
|
const AMM_V4: Pubkey = pubkey!("675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8");
|
|
const PUMP_PROGRAM_ID: Pubkey = pubkey!("6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P");
|
|
pub enum SwapType {
|
|
Pump,
|
|
Raydium,
|
|
}
|
|
|
|
#[test]
|
|
async fn test_subscribe_tx() -> Result<(), AppError> {
|
|
dotenv().ok();
|
|
pretty_env_logger::init_custom_env("RUST_LOG");
|
|
let yellowstone_url = env::var("YELLOWSTONE_URL")?;
|
|
|
|
info!("::: {:?}", yellowstone_url);
|
|
let yellowstone_grpc = YellowstoneGrpc::new(yellowstone_url);
|
|
|
|
let addrs = vec![
|
|
"Aa4QWNkS3RLUv7DA9BM1a2Hzm4HDQo5PyRefqDJnpump".to_string(),
|
|
"BnDssYyGDF9aj5j2N5BwsJFk9YMneQ8P7LQkoYkrpump".to_string(),
|
|
];
|
|
let transactions = yellowstone_grpc.subscribe_transaction(addrs, vec![], vec![]);
|
|
|
|
let (mut subscribe_tx, mut stream) = yellowstone_grpc.connect(transactions).await??;
|
|
|
|
let (mut tx, mut rx) = mpsc::channel::<TransactionPretty>(1000);
|
|
|
|
tokio::spawn(async move {
|
|
while let Some(message) = stream.next().await {
|
|
match message {
|
|
Ok(msg) => {
|
|
match msg.update_oneof {
|
|
Some(UpdateOneof::Transaction(sut)) => {
|
|
let transaction_pretty: TransactionPretty = sut.into();
|
|
let _ = tx.try_send(transaction_pretty);
|
|
}
|
|
Some(UpdateOneof::Ping(_)) => {
|
|
// This is necessary to keep load balancers that expect client pings alive. If your load balancer doesn't
|
|
// require periodic client pings then this is unnecessary
|
|
let _ = subscribe_tx
|
|
.send(SubscribeRequest {
|
|
ping: Some(SubscribeRequestPing { id: 1 }),
|
|
..Default::default()
|
|
})
|
|
.await;
|
|
info!("service is ping: {}", Local::now());
|
|
}
|
|
Some(UpdateOneof::Pong(_)) => {
|
|
info!("service is pong: {}", Local::now());
|
|
}
|
|
_ => {}
|
|
}
|
|
continue;
|
|
}
|
|
Err(error) => {
|
|
error!("error: {error:?}");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
while let Some(transaction_pretty) = rx.next().await {
|
|
let trade_raw = transaction_pretty.tx.clone();
|
|
let meta = &trade_raw.meta.clone().unwrap();
|
|
if meta.err.is_some() {
|
|
continue;
|
|
}
|
|
|
|
let logs = if let OptionSerializer::Some(logs) = &meta.log_messages {
|
|
logs
|
|
} else {
|
|
&vec![]
|
|
};
|
|
|
|
if let Ok(swap_type) = get_swap_type(&trade_raw) {
|
|
match swap_type {
|
|
SwapType::Raydium => {
|
|
let event = RaydiumEvent::parse_logs::<SwapBaseInLog>(logs);
|
|
info!("RaydiumEvent {:#?}", event);
|
|
}
|
|
SwapType::Pump => {
|
|
let event = PumpEvent::parse_logs::<TradeEvent>(logs);
|
|
info!("PumpEvent {:#?}", event);
|
|
}
|
|
}
|
|
};
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
pub fn get_swap_type(
|
|
trade_raw: &EncodedTransactionWithStatusMeta,
|
|
) -> Result<SwapType, AppError> {
|
|
let transaction = &trade_raw.transaction.decode();
|
|
if let Some(transaction) = transaction {
|
|
let account_keys = transaction.message.static_account_keys();
|
|
|
|
let program_index = account_keys
|
|
.iter()
|
|
.position(|item| item == &AMM_V4 || item == &PUMP_PROGRAM_ID)
|
|
.ok_or(anyhow!("swap type program_id not found"))?;
|
|
|
|
let program_id = account_keys[program_index];
|
|
let _type = match program_id {
|
|
AMM_V4 => Ok(SwapType::Raydium),
|
|
PUMP_PROGRAM_ID => Ok(SwapType::Pump),
|
|
_ => Err(AppError::from(anyhow!("program_id ix not found"))),
|
|
};
|
|
return _type;
|
|
}
|
|
Err(AppError::from(anyhow!("program_id ix not found")))
|
|
}
|
|
} |