use anyhow::Result; use solana_sdk::commitment_config::CommitmentConfig; use solana_sdk::message::v0::LoadedAddresses; use solana_streamer_sdk::streaming::event_parser::{ protocols::MutilEventParser, EventParser, Protocol, }; use solana_transaction_status::{ option_serializer::OptionSerializer, TransactionStatusMeta, TransactionWithStatusMeta, VersionedTransactionWithStatusMeta, }; use std::str::FromStr; use std::sync::Arc; /// Get transaction data based on transaction signature #[tokio::main] async fn main() -> Result<()> { let signatures = vec![ "5cnxDiHzUTUutMwnTCsvnMhyL9jEQsRWimmWU1gKxpQBngDaGTkou1YbGhUJhAhmTgvu49PYMmFQbbR38wdZDxJF", ]; // Validate signature format let mut valid_signatures = Vec::new(); for sig_str in &signatures { match solana_sdk::signature::Signature::from_str(sig_str) { Ok(_) => valid_signatures.push(*sig_str), Err(e) => println!("Invalid signature format: {}", e), } } if valid_signatures.is_empty() { println!("No valid transaction signatures"); return Ok(()); } for signature in valid_signatures { println!("Starting transaction parsing: {}", signature); get_single_transaction_details(signature).await?; println!("Transaction parsing completed: {}\n", signature); println!("Visit link to compare data: \nhttps://solscan.io/tx/{}\n", signature); println!("--------------------------------"); } Ok(()) } /// Get details of a single transaction async fn get_single_transaction_details(signature_str: &str) -> Result<()> { use solana_sdk::signature::Signature; use solana_transaction_status::UiTransactionEncoding; let signature = Signature::from_str(signature_str)?; // Create Solana RPC client let rpc_url = "https://api.mainnet-beta.solana.com"; println!("Connecting to Solana RPC: {}", rpc_url); let client = solana_client::nonblocking::rpc_client::RpcClient::new(rpc_url.to_string()); match client .get_transaction_with_config( &signature, solana_client::rpc_config::RpcTransactionConfig { encoding: Some(UiTransactionEncoding::Base64), commitment: Some(CommitmentConfig::confirmed()), max_supported_transaction_version: Some(0), }, ) .await { Ok(transaction) => { println!("Transaction signature: {}", signature_str); println!("Block slot: {}", transaction.slot); if let Some(block_time) = transaction.block_time { println!("Block time: {}", block_time); } if let Some(meta) = &transaction.transaction.meta { println!("Transaction fee: {} lamports", meta.fee); println!("Status: {}", if meta.err.is_none() { "Success" } else { "Failed" }); if let Some(err) = &meta.err { println!("Error details: {:?}", err); } // Compute units consumed if let solana_transaction_status::option_serializer::OptionSerializer::Some(units) = &meta.compute_units_consumed { println!("Compute units consumed: {}", units); } // Display logs (all) if let solana_transaction_status::option_serializer::OptionSerializer::Some(logs) = &meta.log_messages { println!("Transaction logs (all {} entries):", logs.len()); for (i, log) in logs.iter().enumerate() { println!(" [{}] {}", i + 1, log); } } } let protocols = vec![ Protocol::Bonk, Protocol::RaydiumClmm, Protocol::PumpSwap, Protocol::PumpFun, Protocol::RaydiumCpmm, Protocol::RaydiumAmmV4, ]; let parser: Arc = Arc::new(MutilEventParser::new(protocols, None)); let start_time = std::time::Instant::now(); // 从 EncodedTransaction 获取 VersionedTransaction let versioned_tx = match transaction.transaction.transaction.decode() { Some(tx) => tx, None => { println!("Failed to decode transaction"); return Ok(()); } }; // 创建 TransactionWithStatusMeta let tx = TransactionWithStatusMeta::Complete(VersionedTransactionWithStatusMeta { transaction: versioned_tx, meta: TransactionStatusMeta { status: Ok(()), fee: transaction.transaction.meta.as_ref().map_or(0, |m| m.fee), pre_balances: transaction .transaction .meta .as_ref() .map_or(vec![], |m| m.pre_balances.clone()), post_balances: transaction .transaction .meta .as_ref() .map_or(vec![], |m| m.post_balances.clone()), inner_instructions: transaction.transaction.meta.as_ref().and_then(|m| { if let OptionSerializer::Some(inner_instructions) = &m.inner_instructions { // 手动将每个UiInnerInstructions转换为InnerInstructions Some(inner_instructions.iter().map(|ui_inner| { solana_transaction_status::InnerInstructions { index: ui_inner.index, instructions: ui_inner.instructions.iter().map(|ui_inst| { solana_transaction_status::InnerInstruction { instruction: solana_sdk::instruction::Instruction { program_id: solana_sdk::pubkey::Pubkey::new_from_array([0; 32]), accounts: vec![], data: vec![], }, stack_height: None, } }).collect(), } }).collect()) } else { None } }), log_messages: transaction.transaction.meta.as_ref().and_then(|m| { if let OptionSerializer::Some(logs) = &m.log_messages { Some(logs.clone()) } else { None } }), pre_token_balances: None, post_token_balances: None, rewards: None, loaded_addresses: LoadedAddresses::default(), return_data: None, compute_units_consumed: None, cost_units: None, }, }); // TransactionWithStatusMeta let events = parser .parse_transaction( tx, &signature.to_string(), Some(transaction.slot), None, chrono::Utc::now().timestamp_micros(), None, ) .await .unwrap_or_else(|_e| vec![]); println!("Parsing time: {:?}", start_time.elapsed()); for event in events { println!("{:?}\n", event); } } Err(e) => { println!("Failed to get transaction: {}", e); } } Ok(()) }