use clap::{Parser, Subcommand};
use color_eyre::{Result, eyre::eyre};
use solana_client::nonblocking::rpc_client::RpcClient;
use solana_client::rpc_config::RpcTransactionConfig;
use solana_commitment_config::CommitmentConfig;
use solana_sdk::pubkey::Pubkey;
use solana_transaction_status::UiTransactionEncoding;
use solana_transaction_status::option_serializer::OptionSerializer;
use std::str::FromStr;
mod pump_accounts;
mod pump_events;
mod utils;
use crate::pump_accounts::decode_account;
use crate::pump_events::decode_event;
const BANNER: &str = r#"
đ Pump.fun Pool Inspector & Event Decoder đ
âââââââ âââ âââââââ âââââââââââ âââââââ âââ âââââââ âââââââââââ
âââââââââââ ââââââââ âââââââââââââ âââââââââââ ââââââââ âââââââââââââ
âââââââââââ âââââââââââââââââââââââââââââââ ââââââ ââââââââââââââââââââââ
âââââââ âââ âââââââââââââââââââââ âââââââââ ââââââ âââââââââââââââââââââ
âââ ââââââââââââ âââ ââââââ ââââââââââââââââââââ âââ ââââââ
âââ âââââââ âââ ââââââ âââââââ âââââââ âââ ââââââ "#;
#[derive(Parser)]
#[command(author, version, about = "Pump.fun pool inspector and event decoder", long_about = None)]
#[command(before_help = BANNER)]
struct Cli {
#[arg(
short = 'u',
long,
default_value = "https://api.mainnet-beta.solana.com"
)]
rpc_url: String,
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
Event {
data: String,
},
Account {
address: String,
},
Tx {
signature: String,
},
}
#[tokio::main]
async fn main() -> Result<()> {
color_eyre::install()?;
let cli = Cli::parse();
let rpc = RpcClient::new_with_commitment(cli.rpc_url.clone(), CommitmentConfig::confirmed());
match cli.command {
Commands::Event { data } => {
println!("đ Decoding pump.fun event...\n");
let event = decode_event(&data).map_err(|e| eyre!("Failed to decode event: {}", e))?;
println!("{}", event);
}
Commands::Account { address } => {
println!("đĄ Fetching account data from {}\n", cli.rpc_url);
let pubkey = Pubkey::from_str(&address)
.map_err(|e| eyre!("Invalid account address '{}': {}", address, e))?;
let account_data = rpc
.get_account_data(&pubkey)
.await
.map_err(|e| eyre!("Failed to fetch account data: {}", e))?;
let account = decode_account(&account_data)
.map_err(|e| eyre!("Failed to decode account data: {}", e))?;
println!("{}", account);
}
Commands::Tx { signature } => {
println!(
"đ Fetching transaction {} from {}\n",
signature, cli.rpc_url
);
let sig = signature
.parse()
.map_err(|e| eyre!("Invalid transaction signature '{}': {}", signature, e))?;
let transaction = rpc
.get_transaction_with_config(
&sig,
RpcTransactionConfig {
encoding: Some(UiTransactionEncoding::Json),
commitment: Some(CommitmentConfig::confirmed()),
max_supported_transaction_version: Some(0),
},
)
.await
.map_err(|e| eyre!("Failed to fetch transaction: {}", e))?;
if let Some(meta) = transaction.transaction.meta {
if let OptionSerializer::Some(logs) = meta.log_messages {
let mut found_events = false;
for log in &logs {
if let Some(data_start) = log.find("Program data: ") {
let base64_data = &log[data_start + 14..];
if let Ok(event) = decode_event(base64_data) {
if !found_events {
println!("đ Found pump.fun events:");
found_events = true;
}
println!("{}", event);
}
}
}
if !found_events {
println!("âšī¸ No pump.fun events found in transaction logs");
println!("\nđ Transaction logs:");
for (i, log) in logs.iter().enumerate() {
println!("{:2}: {}", i + 1, log);
}
}
} else {
println!("â ī¸ No logs found in transaction");
}
} else {
println!("â ī¸ No transaction metadata available");
}
}
}
Ok(())
}