Skip to main content

sol_parser_sdk/logs/
raydium_amm.rs

1//! Raydium AMM V4 日志解析器
2//!
3//! 使用 match discriminator 模式解析 Raydium AMM V4 事件
4
5use solana_sdk::{pubkey::Pubkey, signature::Signature};
6use crate::core::events::*;
7use super::utils::*;
8
9/// Raydium AMM V4 日志事件 discriminator 常量
10pub mod discriminators {
11    // 事件鉴别器 - 基于参考代码,Raydium AMM V4 使用的可能的日志事件标识
12    pub const SWAP_BASE_IN_EVENT: [u8; 8] = [0, 0, 0, 0, 0, 0, 0, 9];
13    pub const SWAP_BASE_OUT_EVENT: [u8; 8] = [0, 0, 0, 0, 0, 0, 0, 11];
14    pub const DEPOSIT_EVENT: [u8; 8] = [0, 0, 0, 0, 0, 0, 0, 3];
15    pub const WITHDRAW_EVENT: [u8; 8] = [0, 0, 0, 0, 0, 0, 0, 4];
16    pub const INITIALIZE2_EVENT: [u8; 8] = [0, 0, 0, 0, 0, 0, 0, 1];
17    pub const WITHDRAW_PNL_EVENT: [u8; 8] = [0, 0, 0, 0, 0, 0, 0, 7];
18}
19
20/// Raydium AMM V4 程序 ID
21pub const PROGRAM_ID: &str = "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8";
22
23/// 解析 Raydium AMM V4 日志
24#[inline]
25pub fn parse_log(log: &str, signature: Signature, slot: u64, tx_index: u64, block_time_us: Option<i64>, grpc_recv_us: i64) -> Option<DexEvent> {
26    parse_structured_log(log, signature, slot, tx_index, block_time_us, grpc_recv_us)
27}
28
29/// 结构化日志解析(基于 Program data)
30fn parse_structured_log(
31    log: &str,
32    signature: Signature,
33    slot: u64,
34    tx_index: u64,
35    block_time_us: Option<i64>,
36    grpc_recv_us: i64,
37) -> Option<DexEvent> {
38    let program_data = extract_program_data(log)?;
39    if program_data.len() < 8 {
40        return None;
41    }
42
43    let discriminator: [u8; 8] = program_data[0..8].try_into().ok()?;
44    let data = &program_data[8..];
45
46    match discriminator {
47        discriminators::SWAP_BASE_IN_EVENT => {
48            parse_swap_base_in_event(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
49        },
50        discriminators::SWAP_BASE_OUT_EVENT => {
51            parse_swap_base_out_event(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
52        },
53        discriminators::DEPOSIT_EVENT => {
54            parse_deposit_event(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
55        },
56        discriminators::WITHDRAW_EVENT => {
57            parse_withdraw_event(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
58        },
59        discriminators::INITIALIZE2_EVENT => {
60            parse_initialize2_event(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
61        },
62        discriminators::WITHDRAW_PNL_EVENT => {
63            parse_withdraw_pnl_event(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
64        },
65        _ => None,
66    }
67}
68
69// =============================================================================
70// Public from_data parsers - Accept pre-decoded data, eliminate double decode
71// =============================================================================
72
73/// Parse Raydium AMM V4 SwapBaseIn event from pre-decoded data
74#[inline(always)]
75pub fn parse_swap_base_in_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
76    let mut offset = 0;
77
78    let amm = read_pubkey(data, offset)?;
79    offset += 32;
80
81    let user = read_pubkey(data, offset)?;
82    offset += 32;
83
84    let amount_in = read_u64_le(data, offset)?;
85    offset += 8;
86
87    let minimum_amount_out = read_u64_le(data, offset)?;
88
89    Some(DexEvent::RaydiumAmmV4Swap(RaydiumAmmV4SwapEvent {
90        metadata,
91        amount_in,
92        minimum_amount_out,
93        max_amount_in: 0,
94        amount_out: 0,
95        token_program: Pubkey::default(),
96        amm,
97        amm_authority: Pubkey::default(),
98        amm_open_orders: Pubkey::default(),
99        amm_target_orders: None,
100        pool_coin_token_account: Pubkey::default(),
101        pool_pc_token_account: Pubkey::default(),
102        serum_program: Pubkey::default(),
103        serum_market: Pubkey::default(),
104        serum_bids: Pubkey::default(),
105        serum_asks: Pubkey::default(),
106        serum_event_queue: Pubkey::default(),
107        serum_coin_vault_account: Pubkey::default(),
108        serum_pc_vault_account: Pubkey::default(),
109        serum_vault_signer: Pubkey::default(),
110        user_source_token_account: Pubkey::default(),
111        user_destination_token_account: Pubkey::default(),
112        user_source_owner: user,
113    }))
114}
115
116/// Parse Raydium AMM V4 SwapBaseOut event from pre-decoded data
117#[inline(always)]
118pub fn parse_swap_base_out_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
119    let mut offset = 0;
120
121    let amm = read_pubkey(data, offset)?;
122    offset += 32;
123
124    let user = read_pubkey(data, offset)?;
125    offset += 32;
126
127    let max_amount_in = read_u64_le(data, offset)?;
128    offset += 8;
129
130    let amount_out = read_u64_le(data, offset)?;
131
132    Some(DexEvent::RaydiumAmmV4Swap(RaydiumAmmV4SwapEvent {
133        metadata,
134        amount_in: 0,
135        minimum_amount_out: 0,
136        max_amount_in,
137        amount_out,
138        token_program: Pubkey::default(),
139        amm,
140        amm_authority: Pubkey::default(),
141        amm_open_orders: Pubkey::default(),
142        amm_target_orders: None,
143        pool_coin_token_account: Pubkey::default(),
144        pool_pc_token_account: Pubkey::default(),
145        serum_program: Pubkey::default(),
146        serum_market: Pubkey::default(),
147        serum_bids: Pubkey::default(),
148        serum_asks: Pubkey::default(),
149        serum_event_queue: Pubkey::default(),
150        serum_coin_vault_account: Pubkey::default(),
151        serum_pc_vault_account: Pubkey::default(),
152        serum_vault_signer: Pubkey::default(),
153        user_source_token_account: Pubkey::default(),
154        user_destination_token_account: Pubkey::default(),
155        user_source_owner: user,
156    }))
157}
158
159/// Parse Raydium AMM V4 Deposit event from pre-decoded data
160#[inline(always)]
161pub fn parse_deposit_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
162    let mut offset = 0;
163
164    let amm = read_pubkey(data, offset)?;
165    offset += 32;
166
167    let user = read_pubkey(data, offset)?;
168    offset += 32;
169
170    let max_coin_amount = read_u64_le(data, offset)?;
171    offset += 8;
172
173    let max_pc_amount = read_u64_le(data, offset)?;
174    offset += 8;
175
176    let base_side = read_u64_le(data, offset)?;
177
178    Some(DexEvent::RaydiumAmmV4Deposit(RaydiumAmmV4DepositEvent {
179        metadata,
180        max_coin_amount,
181        max_pc_amount,
182        base_side,
183        token_program: Pubkey::default(),
184        amm,
185        amm_authority: Pubkey::default(),
186        amm_open_orders: Pubkey::default(),
187        amm_target_orders: Pubkey::default(),
188        lp_mint_address: Pubkey::default(),
189        pool_coin_token_account: Pubkey::default(),
190        pool_pc_token_account: Pubkey::default(),
191        serum_market: Pubkey::default(),
192        user_coin_token_account: Pubkey::default(),
193        user_pc_token_account: Pubkey::default(),
194        user_lp_token_account: Pubkey::default(),
195        user_owner: user,
196        serum_event_queue: Pubkey::default(),
197    }))
198}
199
200/// Parse Raydium AMM V4 Withdraw event from pre-decoded data
201#[inline(always)]
202pub fn parse_withdraw_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
203    let mut offset = 0;
204
205    let amm = read_pubkey(data, offset)?;
206    offset += 32;
207
208    let user = read_pubkey(data, offset)?;
209    offset += 32;
210
211    let amount = read_u64_le(data, offset)?;
212
213    Some(DexEvent::RaydiumAmmV4Withdraw(RaydiumAmmV4WithdrawEvent {
214        metadata,
215        amount,
216        token_program: Pubkey::default(),
217        amm,
218        amm_authority: Pubkey::default(),
219        amm_open_orders: Pubkey::default(),
220        amm_target_orders: Pubkey::default(),
221        lp_mint_address: Pubkey::default(),
222        pool_coin_token_account: Pubkey::default(),
223        pool_pc_token_account: Pubkey::default(),
224        pool_withdraw_queue: Pubkey::default(),
225        pool_temp_lp_token_account: Pubkey::default(),
226        serum_program: Pubkey::default(),
227        serum_market: Pubkey::default(),
228        serum_coin_vault_account: Pubkey::default(),
229        serum_pc_vault_account: Pubkey::default(),
230        serum_vault_signer: Pubkey::default(),
231        user_lp_token_account: Pubkey::default(),
232        user_coin_token_account: Pubkey::default(),
233        user_pc_token_account: Pubkey::default(),
234        user_owner: user,
235        serum_event_queue: Pubkey::default(),
236        serum_bids: Pubkey::default(),
237        serum_asks: Pubkey::default(),
238    }))
239}
240
241/// Parse Raydium AMM V4 Initialize2 event from pre-decoded data
242#[inline(always)]
243pub fn parse_initialize2_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
244    let mut offset = 0;
245
246    let amm = read_pubkey(data, offset)?;
247    offset += 32;
248
249    let user = read_pubkey(data, offset)?;
250    offset += 32;
251
252    let nonce = data.get(offset)?.clone();
253    offset += 1;
254
255    let open_time = read_u64_le(data, offset)?;
256    offset += 8;
257
258    let init_pc_amount = read_u64_le(data, offset)?;
259    offset += 8;
260
261    let init_coin_amount = read_u64_le(data, offset)?;
262
263    Some(DexEvent::RaydiumAmmV4Initialize2(RaydiumAmmV4Initialize2Event {
264        metadata,
265        nonce,
266        open_time,
267        init_pc_amount,
268        init_coin_amount,
269        token_program: Pubkey::default(),
270        spl_associated_token_account: Pubkey::default(),
271        system_program: Pubkey::default(),
272        rent: Pubkey::default(),
273        amm,
274        amm_authority: Pubkey::default(),
275        amm_open_orders: Pubkey::default(),
276        lp_mint: Pubkey::default(),
277        coin_mint: Pubkey::default(),
278        pc_mint: Pubkey::default(),
279        pool_coin_token_account: Pubkey::default(),
280        pool_pc_token_account: Pubkey::default(),
281        pool_withdraw_queue: Pubkey::default(),
282        amm_target_orders: Pubkey::default(),
283        pool_temp_lp: Pubkey::default(),
284        serum_program: Pubkey::default(),
285        serum_market: Pubkey::default(),
286        user_wallet: user,
287        user_token_coin: Pubkey::default(),
288        user_token_pc: Pubkey::default(),
289        user_lp_token_account: Pubkey::default(),
290    }))
291}
292
293/// 解析 SwapBaseIn 事件
294fn parse_swap_base_in_event(
295    data: &[u8],
296    signature: Signature,
297    slot: u64,
298    tx_index: u64,
299    block_time_us: Option<i64>,
300    grpc_recv_us: i64,
301) -> Option<DexEvent> {
302    let mut offset = 0;
303
304    let amm = read_pubkey(data, offset)?;
305    offset += 32;
306
307    let user = read_pubkey(data, offset)?;
308    offset += 32;
309
310    let amount_in = read_u64_le(data, offset)?;
311    offset += 8;
312
313    let minimum_amount_out = read_u64_le(data, offset)?;
314
315    let metadata = create_metadata_simple(signature, slot, tx_index, block_time_us, amm, grpc_recv_us);
316
317    Some(DexEvent::RaydiumAmmV4Swap(RaydiumAmmV4SwapEvent {
318        metadata,
319        amount_in,
320        minimum_amount_out,
321        max_amount_in: 0,
322        amount_out: 0,
323        token_program: Pubkey::default(),
324        amm,
325        amm_authority: Pubkey::default(),
326        amm_open_orders: Pubkey::default(),
327        amm_target_orders: None,
328        pool_coin_token_account: Pubkey::default(),
329        pool_pc_token_account: Pubkey::default(),
330        serum_program: Pubkey::default(),
331        serum_market: Pubkey::default(),
332        serum_bids: Pubkey::default(),
333        serum_asks: Pubkey::default(),
334        serum_event_queue: Pubkey::default(),
335        serum_coin_vault_account: Pubkey::default(),
336        serum_pc_vault_account: Pubkey::default(),
337        serum_vault_signer: Pubkey::default(),
338        user_source_token_account: Pubkey::default(),
339        user_destination_token_account: Pubkey::default(),
340        user_source_owner: user,
341    }))
342}
343
344/// 解析 SwapBaseOut 事件
345fn parse_swap_base_out_event(
346    data: &[u8],
347    signature: Signature,
348    slot: u64,
349    tx_index: u64,
350    block_time_us: Option<i64>,
351    grpc_recv_us: i64,
352) -> Option<DexEvent> {
353    let mut offset = 0;
354
355    let amm = read_pubkey(data, offset)?;
356    offset += 32;
357
358    let user = read_pubkey(data, offset)?;
359    offset += 32;
360
361    let max_amount_in = read_u64_le(data, offset)?;
362    offset += 8;
363
364    let amount_out = read_u64_le(data, offset)?;
365
366    let metadata = create_metadata_simple(signature, slot, tx_index, block_time_us, amm, grpc_recv_us);
367
368    Some(DexEvent::RaydiumAmmV4Swap(RaydiumAmmV4SwapEvent {
369        metadata,
370        amount_in: 0,
371        minimum_amount_out: 0,
372        max_amount_in,
373        amount_out,
374        token_program: Pubkey::default(),
375        amm,
376        amm_authority: Pubkey::default(),
377        amm_open_orders: Pubkey::default(),
378        amm_target_orders: None,
379        pool_coin_token_account: Pubkey::default(),
380        pool_pc_token_account: Pubkey::default(),
381        serum_program: Pubkey::default(),
382        serum_market: Pubkey::default(),
383        serum_bids: Pubkey::default(),
384        serum_asks: Pubkey::default(),
385        serum_event_queue: Pubkey::default(),
386        serum_coin_vault_account: Pubkey::default(),
387        serum_pc_vault_account: Pubkey::default(),
388        serum_vault_signer: Pubkey::default(),
389        user_source_token_account: Pubkey::default(),
390        user_destination_token_account: Pubkey::default(),
391        user_source_owner: user,
392    }))
393}
394
395/// 解析存款事件
396fn parse_deposit_event(
397    data: &[u8],
398    signature: Signature,
399    slot: u64,
400    tx_index: u64,
401    block_time_us: Option<i64>,
402    grpc_recv_us: i64,
403) -> Option<DexEvent> {
404    let mut offset = 0;
405
406    let amm = read_pubkey(data, offset)?;
407    offset += 32;
408
409    let user = read_pubkey(data, offset)?;
410    offset += 32;
411
412    let max_coin_amount = read_u64_le(data, offset)?;
413    offset += 8;
414
415    let max_pc_amount = read_u64_le(data, offset)?;
416    offset += 8;
417
418    let base_side = read_u64_le(data, offset)?;
419
420    let metadata = create_metadata_simple(signature, slot, tx_index, block_time_us, amm, grpc_recv_us);
421
422    Some(DexEvent::RaydiumAmmV4Deposit(RaydiumAmmV4DepositEvent {
423        metadata,
424        max_coin_amount,
425        max_pc_amount,
426        base_side,
427        token_program: Pubkey::default(),
428        amm,
429        amm_authority: Pubkey::default(),
430        amm_open_orders: Pubkey::default(),
431        amm_target_orders: Pubkey::default(),
432        lp_mint_address: Pubkey::default(),
433        pool_coin_token_account: Pubkey::default(),
434        pool_pc_token_account: Pubkey::default(),
435        serum_market: Pubkey::default(),
436        user_coin_token_account: Pubkey::default(),
437        user_pc_token_account: Pubkey::default(),
438        user_lp_token_account: Pubkey::default(),
439        user_owner: user,
440        serum_event_queue: Pubkey::default(),
441    }))
442}
443
444/// 解析提取事件
445fn parse_withdraw_event(
446    data: &[u8],
447    signature: Signature,
448    slot: u64,
449    tx_index: u64,
450    block_time_us: Option<i64>,
451    grpc_recv_us: i64,
452) -> Option<DexEvent> {
453    let mut offset = 0;
454
455    let amm = read_pubkey(data, offset)?;
456    offset += 32;
457
458    let user = read_pubkey(data, offset)?;
459    offset += 32;
460
461    let amount = read_u64_le(data, offset)?;
462
463    let metadata = create_metadata_simple(signature, slot, tx_index, block_time_us, amm, grpc_recv_us);
464
465    Some(DexEvent::RaydiumAmmV4Withdraw(RaydiumAmmV4WithdrawEvent {
466        metadata,
467        amount,
468        token_program: Pubkey::default(),
469        amm,
470        amm_authority: Pubkey::default(),
471        amm_open_orders: Pubkey::default(),
472        amm_target_orders: Pubkey::default(),
473        lp_mint_address: Pubkey::default(),
474        pool_coin_token_account: Pubkey::default(),
475        pool_pc_token_account: Pubkey::default(),
476        pool_withdraw_queue: Pubkey::default(),
477        pool_temp_lp_token_account: Pubkey::default(),
478        serum_program: Pubkey::default(),
479        serum_market: Pubkey::default(),
480        serum_coin_vault_account: Pubkey::default(),
481        serum_pc_vault_account: Pubkey::default(),
482        serum_vault_signer: Pubkey::default(),
483        user_lp_token_account: Pubkey::default(),
484        user_coin_token_account: Pubkey::default(),
485        user_pc_token_account: Pubkey::default(),
486        user_owner: user,
487        serum_event_queue: Pubkey::default(),
488        serum_bids: Pubkey::default(),
489        serum_asks: Pubkey::default(),
490    }))
491}
492
493/// 解析初始化事件
494fn parse_initialize2_event(
495    data: &[u8],
496    signature: Signature,
497    slot: u64,
498    tx_index: u64,
499    block_time_us: Option<i64>,
500    grpc_recv_us: i64,
501) -> Option<DexEvent> {
502    let mut offset = 0;
503
504    let amm = read_pubkey(data, offset)?;
505    offset += 32;
506
507    let user = read_pubkey(data, offset)?;
508    offset += 32;
509
510    let nonce = data.get(offset)?.clone();
511    offset += 1;
512
513    let open_time = read_u64_le(data, offset)?;
514    offset += 8;
515
516    let init_pc_amount = read_u64_le(data, offset)?;
517    offset += 8;
518
519    let init_coin_amount = read_u64_le(data, offset)?;
520
521    let metadata = create_metadata_simple(signature, slot, tx_index, block_time_us, amm, grpc_recv_us);
522
523    Some(DexEvent::RaydiumAmmV4Initialize2(RaydiumAmmV4Initialize2Event {
524        metadata,
525        nonce,
526        open_time,
527        init_pc_amount,
528        init_coin_amount,
529        token_program: Pubkey::default(),
530        spl_associated_token_account: Pubkey::default(),
531        system_program: Pubkey::default(),
532        rent: Pubkey::default(),
533        amm,
534        amm_authority: Pubkey::default(),
535        amm_open_orders: Pubkey::default(),
536        lp_mint: Pubkey::default(),
537        coin_mint: Pubkey::default(),
538        pc_mint: Pubkey::default(),
539        pool_coin_token_account: Pubkey::default(),
540        pool_pc_token_account: Pubkey::default(),
541        pool_withdraw_queue: Pubkey::default(),
542        amm_target_orders: Pubkey::default(),
543        pool_temp_lp: Pubkey::default(),
544        serum_program: Pubkey::default(),
545        serum_market: Pubkey::default(),
546        user_wallet: user,
547        user_token_coin: Pubkey::default(),
548        user_token_pc: Pubkey::default(),
549        user_lp_token_account: Pubkey::default(),
550    }))
551}
552
553/// 解析提取 PnL 事件
554fn parse_withdraw_pnl_event(
555    data: &[u8],
556    signature: Signature,
557    slot: u64,
558    tx_index: u64,
559    block_time_us: Option<i64>,
560    grpc_recv_us: i64,
561) -> Option<DexEvent> {
562    let mut offset = 0;
563
564    let amm = read_pubkey(data, offset)?;
565    offset += 32;
566
567    let pnl_owner = read_pubkey(data, offset)?;
568
569    let metadata = create_metadata_simple(signature, slot, tx_index, block_time_us, amm, grpc_recv_us);
570
571    Some(DexEvent::RaydiumAmmV4WithdrawPnl(RaydiumAmmV4WithdrawPnlEvent {
572        metadata,
573        token_program: Pubkey::default(),
574        amm,
575        amm_config: Pubkey::default(),
576        amm_authority: Pubkey::default(),
577        amm_open_orders: Pubkey::default(),
578        pool_coin_token_account: Pubkey::default(),
579        pool_pc_token_account: Pubkey::default(),
580        coin_pnl_token_account: Pubkey::default(),
581        pc_pnl_token_account: Pubkey::default(),
582        pnl_owner,
583        amm_target_orders: Pubkey::default(),
584        serum_program: Pubkey::default(),
585        serum_market: Pubkey::default(),
586        serum_event_queue: Pubkey::default(),
587        serum_coin_vault_account: Pubkey::default(),
588        serum_pc_vault_account: Pubkey::default(),
589        serum_vault_signer: Pubkey::default(),
590    }))
591}
592
593/// 文本日志解析(回退方案)
594fn parse_text_log(
595    log: &str,
596    signature: Signature,
597    slot: u64,
598    tx_index: u64,
599    block_time_us: Option<i64>,
600    grpc_recv_us: i64,
601) -> Option<DexEvent> {
602    // 检查是否是交换相关的日志
603    if log.contains("swap") || log.contains("Swap") {
604        return parse_swap_log_fallback(log, signature, slot, tx_index, block_time_us, grpc_recv_us);
605    }
606
607    // 检查是否是存款相关的日志
608    if log.contains("deposit") || log.contains("Deposit") {
609        return parse_deposit_log_fallback(log, signature, slot, tx_index, block_time_us, grpc_recv_us);
610    }
611
612    // 检查是否是提取相关的日志
613    if log.contains("withdraw") || log.contains("Withdraw") {
614        return parse_withdraw_log_fallback(log, signature, slot, tx_index, block_time_us, grpc_recv_us);
615    }
616
617    None
618}
619
620/// 文本回退解析交换事件
621fn parse_swap_log_fallback(
622    log: &str,
623    signature: Signature,
624    slot: u64,
625    tx_index: u64,
626    block_time_us: Option<i64>,
627    grpc_recv_us: i64,
628) -> Option<DexEvent> {
629    // 尝试从日志文本中提取基本信息
630    let amount_in = super::utils::text_parser::extract_number_from_text(log, "amount_in")
631        .or_else(|| super::utils::text_parser::extract_number_from_text(log, "amountIn"))
632        .unwrap_or(0);
633
634    let amount_out = super::utils::text_parser::extract_number_from_text(log, "amount_out")
635        .or_else(|| super::utils::text_parser::extract_number_from_text(log, "amountOut"))
636        .unwrap_or(0);
637
638    let minimum_amount_out = super::utils::text_parser::extract_number_from_text(log, "minimum_amount_out")
639        .or_else(|| super::utils::text_parser::extract_number_from_text(log, "minimumAmountOut"))
640        .unwrap_or(0);
641
642    let max_amount_in = super::utils::text_parser::extract_number_from_text(log, "max_amount_in")
643        .or_else(|| super::utils::text_parser::extract_number_from_text(log, "maxAmountIn"))
644        .unwrap_or(0);
645
646    let default_pubkey = Pubkey::default();
647    let metadata = create_metadata_simple(signature, slot, tx_index, block_time_us, default_pubkey, grpc_recv_us);
648
649    Some(DexEvent::RaydiumAmmV4Swap(RaydiumAmmV4SwapEvent {
650        metadata,
651        amount_in,
652        minimum_amount_out,
653        max_amount_in,
654        amount_out,
655        token_program: default_pubkey,
656        amm: default_pubkey,
657        amm_authority: default_pubkey,
658        amm_open_orders: default_pubkey,
659        amm_target_orders: None,
660        pool_coin_token_account: default_pubkey,
661        pool_pc_token_account: default_pubkey,
662        serum_program: default_pubkey,
663        serum_market: default_pubkey,
664        serum_bids: default_pubkey,
665        serum_asks: default_pubkey,
666        serum_event_queue: default_pubkey,
667        serum_coin_vault_account: default_pubkey,
668        serum_pc_vault_account: default_pubkey,
669        serum_vault_signer: default_pubkey,
670        user_source_token_account: default_pubkey,
671        user_destination_token_account: default_pubkey,
672        user_source_owner: default_pubkey,
673    }))
674}
675
676/// 文本回退解析存款事件
677fn parse_deposit_log_fallback(
678    log: &str,
679    signature: Signature,
680    slot: u64,
681    tx_index: u64,
682    block_time_us: Option<i64>,
683    grpc_recv_us: i64,
684) -> Option<DexEvent> {
685    let max_coin_amount = super::utils::text_parser::extract_number_from_text(log, "max_coin_amount")
686        .or_else(|| super::utils::text_parser::extract_number_from_text(log, "maxCoinAmount"))
687        .unwrap_or(0);
688
689    let max_pc_amount = super::utils::text_parser::extract_number_from_text(log, "max_pc_amount")
690        .or_else(|| super::utils::text_parser::extract_number_from_text(log, "maxPcAmount"))
691        .unwrap_or(0);
692
693    let base_side = super::utils::text_parser::extract_number_from_text(log, "base_side")
694        .or_else(|| super::utils::text_parser::extract_number_from_text(log, "baseSide"))
695        .unwrap_or(0);
696
697    let default_pubkey = Pubkey::default();
698    let metadata = create_metadata_simple(signature, slot, tx_index, block_time_us, default_pubkey, grpc_recv_us);
699
700    Some(DexEvent::RaydiumAmmV4Deposit(RaydiumAmmV4DepositEvent {
701        metadata,
702        max_coin_amount,
703        max_pc_amount,
704        base_side,
705        token_program: default_pubkey,
706        amm: default_pubkey,
707        amm_authority: default_pubkey,
708        amm_open_orders: default_pubkey,
709        amm_target_orders: default_pubkey,
710        lp_mint_address: default_pubkey,
711        pool_coin_token_account: default_pubkey,
712        pool_pc_token_account: default_pubkey,
713        serum_market: default_pubkey,
714        user_coin_token_account: default_pubkey,
715        user_pc_token_account: default_pubkey,
716        user_lp_token_account: default_pubkey,
717        user_owner: default_pubkey,
718        serum_event_queue: default_pubkey,
719    }))
720}
721
722/// 文本回退解析提取事件
723fn parse_withdraw_log_fallback(
724    log: &str,
725    signature: Signature,
726    slot: u64,
727    tx_index: u64,
728    block_time_us: Option<i64>,
729    grpc_recv_us: i64,
730) -> Option<DexEvent> {
731    let amount = super::utils::text_parser::extract_number_from_text(log, "amount")
732        .unwrap_or(0);
733
734    let default_pubkey = Pubkey::default();
735    let metadata = create_metadata_simple(signature, slot, tx_index, block_time_us, default_pubkey, grpc_recv_us);
736
737    Some(DexEvent::RaydiumAmmV4Withdraw(RaydiumAmmV4WithdrawEvent {
738        metadata,
739        amount,
740        token_program: default_pubkey,
741        amm: default_pubkey,
742        amm_authority: default_pubkey,
743        amm_open_orders: default_pubkey,
744        amm_target_orders: default_pubkey,
745        lp_mint_address: default_pubkey,
746        pool_coin_token_account: default_pubkey,
747        pool_pc_token_account: default_pubkey,
748        pool_withdraw_queue: default_pubkey,
749        pool_temp_lp_token_account: default_pubkey,
750        serum_program: default_pubkey,
751        serum_market: default_pubkey,
752        serum_coin_vault_account: default_pubkey,
753        serum_pc_vault_account: default_pubkey,
754        serum_vault_signer: default_pubkey,
755        user_lp_token_account: default_pubkey,
756        user_coin_token_account: default_pubkey,
757        user_pc_token_account: default_pubkey,
758        user_owner: default_pubkey,
759        serum_event_queue: default_pubkey,
760        serum_bids: default_pubkey,
761        serum_asks: default_pubkey,
762    }))
763}