1use super::program_ids;
6use super::utils::*;
7use crate::core::events::*;
8use solana_sdk::{pubkey::Pubkey, signature::Signature};
9
10pub mod discriminators {
12 pub const BUY: [u8; 8] = [102, 6, 61, 18, 1, 218, 235, 234];
14 pub const SELL: [u8; 8] = [51, 230, 133, 164, 1, 127, 131, 173];
16 pub const CREATE: [u8; 8] = [24, 30, 200, 40, 5, 28, 7, 119];
18 pub const CREATE_V2: [u8; 8] = [214, 144, 76, 236, 95, 139, 49, 180];
20 pub const BUY_EXACT_SOL_IN: [u8; 8] = [56, 252, 116, 8, 158, 223, 205, 95];
22 pub const MIGRATE_EVENT_LOG: [u8; 8] = [189, 233, 93, 185, 92, 148, 234, 148];
24}
25
26pub const PROGRAM_ID_PUBKEY: Pubkey = program_ids::PUMPFUN_PROGRAM_ID;
28
29pub fn parse_instruction(
34 instruction_data: &[u8],
35 accounts: &[Pubkey],
36 signature: Signature,
37 slot: u64,
38 tx_index: u64,
39 block_time_us: Option<i64>,
40 grpc_recv_us: i64,
41) -> Option<DexEvent> {
42 if instruction_data.len() < 8 {
43 return None;
44 }
45 let outer_disc: [u8; 8] = instruction_data[0..8].try_into().ok()?;
46 let data = &instruction_data[8..];
47
48 if outer_disc == discriminators::CREATE_V2 {
50 return parse_create_v2_instruction(
51 data,
52 accounts,
53 signature,
54 slot,
55 tx_index,
56 block_time_us,
57 grpc_recv_us,
58 );
59 }
60 if outer_disc == discriminators::CREATE {
61 return parse_create_instruction(
62 data,
63 accounts,
64 signature,
65 slot,
66 tx_index,
67 block_time_us,
68 grpc_recv_us,
69 );
70 }
71
72 if instruction_data.len() >= 16 {
74 let cpi_disc: [u8; 8] = instruction_data[8..16].try_into().ok()?;
75 if cpi_disc == discriminators::MIGRATE_EVENT_LOG {
76 return parse_migrate_log_instruction(
77 &instruction_data[16..],
78 accounts,
79 signature,
80 slot,
81 tx_index,
82 block_time_us,
83 grpc_recv_us,
84 );
85 }
86 }
87 None
88}
89
90#[allow(dead_code)]
100fn parse_buy_instruction(
101 data: &[u8],
102 accounts: &[Pubkey],
103 signature: Signature,
104 slot: u64,
105 tx_index: u64,
106 block_time_us: Option<i64>,
107 grpc_recv_us: i64,
108) -> Option<DexEvent> {
109 if accounts.len() < 7 {
110 return None;
111 }
112
113 let (sol_amount, token_amount) = if data.len() >= 16 {
115 (read_u64_le(data, 0).unwrap_or(0), read_u64_le(data, 8).unwrap_or(0))
116 } else {
117 (0, 0)
118 };
119
120 let mint = get_account(accounts, 2)?;
121 let metadata =
122 create_metadata(signature, slot, tx_index, block_time_us.unwrap_or_default(), grpc_recv_us);
123
124 Some(DexEvent::PumpFunTrade(PumpFunTradeEvent {
125 metadata,
126 mint,
127 is_buy: true,
128 bonding_curve: get_account(accounts, 3).unwrap_or_default(),
129 user: get_account(accounts, 6).unwrap_or_default(),
130 sol_amount,
131 token_amount,
132 fee_recipient: get_account(accounts, 1).unwrap_or_default(),
133 ..Default::default()
134 }))
135}
136
137#[allow(dead_code)]
146fn parse_sell_instruction(
147 data: &[u8],
148 accounts: &[Pubkey],
149 signature: Signature,
150 slot: u64,
151 tx_index: u64,
152 block_time_us: Option<i64>,
153 grpc_recv_us: i64,
154) -> Option<DexEvent> {
155 if accounts.len() < 7 {
156 return None;
157 }
158
159 let (token_amount, sol_amount) = if data.len() >= 16 {
161 (read_u64_le(data, 0).unwrap_or(0), read_u64_le(data, 8).unwrap_or(0))
162 } else {
163 (0, 0)
164 };
165
166 let mint = get_account(accounts, 2)?;
167 let metadata =
168 create_metadata(signature, slot, tx_index, block_time_us.unwrap_or_default(), grpc_recv_us);
169
170 Some(DexEvent::PumpFunTrade(PumpFunTradeEvent {
171 metadata,
172 mint,
173 is_buy: false,
174 bonding_curve: get_account(accounts, 3).unwrap_or_default(),
175 user: get_account(accounts, 6).unwrap_or_default(),
176 sol_amount,
177 token_amount,
178 fee_recipient: get_account(accounts, 1).unwrap_or_default(),
179 ..Default::default()
180 }))
181}
182
183fn parse_create_instruction(
189 data: &[u8],
190 accounts: &[Pubkey],
191 signature: Signature,
192 slot: u64,
193 tx_index: u64,
194 block_time_us: Option<i64>,
195 grpc_recv_us: i64,
196) -> Option<DexEvent> {
197 if accounts.len() < 8 {
198 return None;
199 }
200
201 let mut offset = 0;
202
203 let name = if let Some((s, len)) = read_str_unchecked(data, offset) {
206 offset += len;
207 s.to_string()
208 } else {
209 String::new()
210 };
211
212 let symbol = if let Some((s, len)) = read_str_unchecked(data, offset) {
213 offset += len;
214 s.to_string()
215 } else {
216 String::new()
217 };
218
219 let uri = if let Some((s, len)) = read_str_unchecked(data, offset) {
220 offset += len;
221 s.to_string()
222 } else {
223 String::new()
224 };
225
226 if data.len() < offset + 32 + 32 + 32 + 32 {
228 return None;
229 }
230
231 let mint = read_pubkey(data, offset).unwrap_or_default();
232 offset += 32;
233
234 let bonding_curve = read_pubkey(data, offset).unwrap_or_default();
235 offset += 32;
236
237 let user = read_pubkey(data, offset).unwrap_or_default();
238 offset += 32;
239
240 let creator = read_pubkey(data, offset).unwrap_or_default();
241
242 let metadata =
243 create_metadata(signature, slot, tx_index, block_time_us.unwrap_or_default(), grpc_recv_us);
244
245 Some(DexEvent::PumpFunCreate(PumpFunCreateTokenEvent {
246 metadata,
247 name,
248 symbol,
249 uri,
250 mint,
251 bonding_curve,
252 user,
253 creator,
254 ..Default::default()
255 }))
256}
257
258fn parse_create_v2_instruction(
267 data: &[u8],
268 accounts: &[Pubkey],
269 signature: Signature,
270 slot: u64,
271 tx_index: u64,
272 block_time_us: Option<i64>,
273 grpc_recv_us: i64,
274) -> Option<DexEvent> {
275 const CREATE_V2_MIN_ACCOUNTS: usize = 16;
276 if accounts.len() < CREATE_V2_MIN_ACCOUNTS {
277 return None;
278 }
279 let acc = &accounts[0..CREATE_V2_MIN_ACCOUNTS];
280
281 let mut offset = 0usize;
283 let name = if let Some((s, len)) = read_str_unchecked(data, offset) {
284 offset += len;
285 s.to_string()
286 } else {
287 String::new()
288 };
289 let symbol = if let Some((s, len)) = read_str_unchecked(data, offset) {
290 offset += len;
291 s.to_string()
292 } else {
293 String::new()
294 };
295 let uri = if let Some((s, len)) = read_str_unchecked(data, offset) {
296 offset += len;
297 s.to_string()
298 } else {
299 String::new()
300 };
301 if data.len() < offset + 32 + 1 {
302 return None;
303 }
304 let creator = read_pubkey(data, offset)?;
305 offset += 32;
306 let is_mayhem_mode = read_bool(data, offset)?;
307 offset += 1;
308 let is_cashback_enabled = read_option_bool_idl(data, offset).unwrap_or(false);
309
310 let mint = acc[0];
311 let bonding_curve = acc[2];
312 let user = acc[5];
313
314 let metadata =
315 create_metadata(signature, slot, tx_index, block_time_us.unwrap_or_default(), grpc_recv_us);
316
317 Some(DexEvent::PumpFunCreateV2(PumpFunCreateV2TokenEvent {
318 metadata,
319 name,
320 symbol,
321 uri,
322 mint,
323 bonding_curve,
324 user,
325 creator,
326 mint_authority: acc[1],
327 associated_bonding_curve: acc[3],
328 global: acc[4],
329 system_program: acc[6],
330 token_program: acc[7],
331 associated_token_program: acc[8],
332 mayhem_program_id: acc[9],
333 global_params: acc[10],
334 sol_vault: acc[11],
335 mayhem_state: acc[12],
336 mayhem_token_vault: acc[13],
337 event_authority: acc[14],
338 program: acc[15],
339 is_mayhem_mode,
340 is_cashback_enabled,
341 ..Default::default()
342 }))
343}
344
345#[allow(unused_variables)]
347fn parse_migrate_log_instruction(
348 data: &[u8],
349 accounts: &[Pubkey],
350 signature: Signature,
351 slot: u64,
352 tx_index: u64,
353 block_time_us: Option<i64>,
354 rpc_recv_us: i64,
355) -> Option<DexEvent> {
356 let mut offset = 0;
357
358 let user = read_pubkey(data, offset)?;
360 offset += 32;
361
362 let mint = read_pubkey(data, offset)?;
364 offset += 32;
365
366 let mint_amount = read_u64_le(data, offset)?;
368 offset += 8;
369
370 let sol_amount = read_u64_le(data, offset)?;
372 offset += 8;
373
374 let pool_migration_fee = read_u64_le(data, offset)?;
376 offset += 8;
377
378 let bonding_curve = read_pubkey(data, offset)?;
380 offset += 32;
381
382 let timestamp = read_u64_le(data, offset)? as i64;
384 offset += 8;
385
386 let pool = read_pubkey(data, offset)?;
388
389 let metadata =
390 create_metadata(signature, slot, tx_index, block_time_us.unwrap_or_default(), rpc_recv_us);
391
392 Some(DexEvent::PumpFunMigrate(PumpFunMigrateEvent {
393 metadata,
394 user,
395 mint,
396 mint_amount,
397 sol_amount,
398 pool_migration_fee,
399 bonding_curve,
400 timestamp,
401 pool,
402 }))
403}