Skip to main content

sol_parser_sdk/logs/
raydium_amm.rs

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