sol_parser_sdk/
wrapped_pumpfun_parser.rs1use crate::core::events::{DexEvent, RawInstructionEvent};
4use serde::{Deserialize, Serialize};
5use solana_sdk::pubkey::Pubkey;
6
7pub type ThirdProgram = crate::core::events::RawInstructionPayload;
10
11#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
14pub struct WrappedPumpfunRule {
15 pub rule_id: String,
16 pub program_id: Pubkey,
17 pub outer_account_count: usize,
18 pub data_pattern: Vec<Option<u8>>,
20 pub pump_discriminator: [u8; 8],
21 pub argument_offsets: [usize; 2],
23 pub account_map: Vec<usize>,
25 pub fixed_accounts: Vec<(usize, Pubkey)>,
26 pub equal_accounts: Vec<(usize, usize)>,
29}
30
31pub fn pump_trade_name(disc: &[u8]) -> Option<&'static str> {
34 use crate::instr::pump::discriminators::*;
35 match disc {
36 d if d == BUY_V2 => Some("buy_v2"),
37 d if d == BUY_EXACT_QUOTE_IN_V2 => Some("buy_exact_quote_in_v2"),
38 d if d == SELL_V2 => Some("sell_v2"),
39 _ => None,
40 }
41}
42
43pub fn restore_instruction(
46 raw: &RawInstructionEvent,
47 rule: &WrappedPumpfunRule,
48) -> Option<([u8; 24], Vec<Pubkey>)> {
49 if raw.program_id != rule.program_id
50 || raw.accounts.len() != rule.outer_account_count
51 || raw.instruction_data.len() != rule.data_pattern.len()
52 || rule.data_pattern.is_empty()
53 || rule.data_pattern.iter().all(Option::is_none)
54 || pump_trade_name(&rule.pump_discriminator).is_none()
55 || raw
56 .instruction_data
57 .iter()
58 .zip(&rule.data_pattern)
59 .any(|(actual, expected)| expected.is_some_and(|v| v != *actual))
60 {
61 return None;
62 }
63 if raw.accounts.iter().any(Option::is_none) {
64 return None;
65 }
66 let accounts = &raw.accounts;
67 for &(index, key) in &rule.fixed_accounts {
68 if accounts.get(index) != Some(&Some(key)) {
69 return None;
70 }
71 }
72 for &(a, b) in &rule.equal_accounts {
73 if accounts.get(a).is_none() || accounts.get(a) != accounts.get(b) {
74 return None;
75 }
76 }
77 let mut data = [0u8; 24];
78 data[..8].copy_from_slice(&rule.pump_discriminator);
79 for (i, offset) in rule.argument_offsets.iter().copied().enumerate() {
80 data[8 + i * 8..16 + i * 8]
81 .copy_from_slice(raw.instruction_data.get(offset..offset.checked_add(8)?)?);
82 }
83 let mapped = rule
84 .account_map
85 .iter()
86 .map(|i| accounts.get(*i).copied().flatten())
87 .collect::<Option<Vec<_>>>()?;
88 Some((data, mapped))
89}
90
91pub fn parse_wrapped_pumpfun_instruction(
95 raw: &RawInstructionEvent,
96 rule: &WrappedPumpfunRule,
97 filter: Option<&crate::grpc::EventTypeFilter>,
98) -> Option<DexEvent> {
99 if filter.is_some_and(|f| !f.includes_pumpfun()) {
100 return None;
101 }
102 let (data, accounts) = restore_instruction(raw, rule)?;
103 let m = &raw.metadata;
104 let event = crate::instr::pump::parse_instruction(
105 &data,
106 &accounts,
107 m.signature,
108 m.slot,
109 m.tx_index,
110 Some(m.block_time_us),
111 m.grpc_recv_us,
112 )?;
113 let mut trade = match event {
114 DexEvent::PumpFunTrade(t)
115 | DexEvent::PumpFunBuy(t)
116 | DexEvent::PumpFunSell(t)
117 | DexEvent::PumpFunBuyExactSolIn(t) => t,
118 _ => return None,
119 };
120 trade.metadata = m.clone();
121 trade.sol_amount = 0;
122 trade.quote_amount = 0;
123 trade.token_amount = 0;
124 trade.third_program = Some(raw.into());
125 trade.parse_status = crate::core::events::PumpFunParseStatus::InstructionDecoded;
126 let event = DexEvent::PumpFunTrade(trade);
127 filter.is_none_or(|f| f.should_include_dex_event(&event)).then_some(event)
128}