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/// 解析 SwapBaseIn 事件
301fn parse_swap_base_in_event(
302    data: &[u8],
303    signature: Signature,
304    slot: u64,
305    tx_index: u64,
306    block_time_us: Option<i64>,
307    grpc_recv_us: i64,
308) -> Option<DexEvent> {
309    let mut offset = 0;
310
311    let amm = read_pubkey(data, offset)?;
312    offset += 32;
313
314    let user = read_pubkey(data, offset)?;
315    offset += 32;
316
317    let amount_in = read_u64_le(data, offset)?;
318    offset += 8;
319
320    let minimum_amount_out = read_u64_le(data, offset)?;
321
322    let metadata =
323        create_metadata_simple(signature, slot, tx_index, block_time_us, amm, grpc_recv_us);
324
325    Some(DexEvent::RaydiumAmmV4Swap(RaydiumAmmV4SwapEvent {
326        metadata,
327        amount_in,
328        minimum_amount_out,
329        max_amount_in: 0,
330        amount_out: 0,
331        token_program: Pubkey::default(),
332        amm,
333        amm_authority: Pubkey::default(),
334        amm_open_orders: Pubkey::default(),
335        amm_target_orders: None,
336        pool_coin_token_account: Pubkey::default(),
337        pool_pc_token_account: Pubkey::default(),
338        serum_program: Pubkey::default(),
339        serum_market: Pubkey::default(),
340        serum_bids: Pubkey::default(),
341        serum_asks: Pubkey::default(),
342        serum_event_queue: Pubkey::default(),
343        serum_coin_vault_account: Pubkey::default(),
344        serum_pc_vault_account: Pubkey::default(),
345        serum_vault_signer: Pubkey::default(),
346        user_source_token_account: Pubkey::default(),
347        user_destination_token_account: Pubkey::default(),
348        user_source_owner: user,
349    }))
350}
351
352/// 解析 SwapBaseOut 事件
353fn parse_swap_base_out_event(
354    data: &[u8],
355    signature: Signature,
356    slot: u64,
357    tx_index: u64,
358    block_time_us: Option<i64>,
359    grpc_recv_us: i64,
360) -> Option<DexEvent> {
361    let mut offset = 0;
362
363    let amm = read_pubkey(data, offset)?;
364    offset += 32;
365
366    let user = read_pubkey(data, offset)?;
367    offset += 32;
368
369    let max_amount_in = read_u64_le(data, offset)?;
370    offset += 8;
371
372    let amount_out = read_u64_le(data, offset)?;
373
374    let metadata =
375        create_metadata_simple(signature, slot, tx_index, block_time_us, amm, grpc_recv_us);
376
377    Some(DexEvent::RaydiumAmmV4Swap(RaydiumAmmV4SwapEvent {
378        metadata,
379        amount_in: 0,
380        minimum_amount_out: 0,
381        max_amount_in,
382        amount_out,
383        token_program: Pubkey::default(),
384        amm,
385        amm_authority: Pubkey::default(),
386        amm_open_orders: Pubkey::default(),
387        amm_target_orders: None,
388        pool_coin_token_account: Pubkey::default(),
389        pool_pc_token_account: Pubkey::default(),
390        serum_program: Pubkey::default(),
391        serum_market: Pubkey::default(),
392        serum_bids: Pubkey::default(),
393        serum_asks: Pubkey::default(),
394        serum_event_queue: Pubkey::default(),
395        serum_coin_vault_account: Pubkey::default(),
396        serum_pc_vault_account: Pubkey::default(),
397        serum_vault_signer: Pubkey::default(),
398        user_source_token_account: Pubkey::default(),
399        user_destination_token_account: Pubkey::default(),
400        user_source_owner: user,
401    }))
402}
403
404/// 解析存款事件
405fn parse_deposit_event(
406    data: &[u8],
407    signature: Signature,
408    slot: u64,
409    tx_index: u64,
410    block_time_us: Option<i64>,
411    grpc_recv_us: i64,
412) -> Option<DexEvent> {
413    let mut offset = 0;
414
415    let amm = read_pubkey(data, offset)?;
416    offset += 32;
417
418    let user = read_pubkey(data, offset)?;
419    offset += 32;
420
421    let max_coin_amount = read_u64_le(data, offset)?;
422    offset += 8;
423
424    let max_pc_amount = read_u64_le(data, offset)?;
425    offset += 8;
426
427    let base_side = read_u64_le(data, offset)?;
428
429    let metadata =
430        create_metadata_simple(signature, slot, tx_index, block_time_us, amm, grpc_recv_us);
431
432    Some(DexEvent::RaydiumAmmV4Deposit(RaydiumAmmV4DepositEvent {
433        metadata,
434        max_coin_amount,
435        max_pc_amount,
436        base_side,
437        token_program: Pubkey::default(),
438        amm,
439        amm_authority: Pubkey::default(),
440        amm_open_orders: Pubkey::default(),
441        amm_target_orders: Pubkey::default(),
442        lp_mint_address: Pubkey::default(),
443        pool_coin_token_account: Pubkey::default(),
444        pool_pc_token_account: Pubkey::default(),
445        serum_market: Pubkey::default(),
446        user_coin_token_account: Pubkey::default(),
447        user_pc_token_account: Pubkey::default(),
448        user_lp_token_account: Pubkey::default(),
449        user_owner: user,
450        serum_event_queue: Pubkey::default(),
451    }))
452}
453
454/// 解析提取事件
455fn parse_withdraw_event(
456    data: &[u8],
457    signature: Signature,
458    slot: u64,
459    tx_index: u64,
460    block_time_us: Option<i64>,
461    grpc_recv_us: i64,
462) -> Option<DexEvent> {
463    let mut offset = 0;
464
465    let amm = read_pubkey(data, offset)?;
466    offset += 32;
467
468    let user = read_pubkey(data, offset)?;
469    offset += 32;
470
471    let amount = read_u64_le(data, offset)?;
472
473    let metadata =
474        create_metadata_simple(signature, slot, tx_index, block_time_us, amm, grpc_recv_us);
475
476    Some(DexEvent::RaydiumAmmV4Withdraw(RaydiumAmmV4WithdrawEvent {
477        metadata,
478        amount,
479        token_program: Pubkey::default(),
480        amm,
481        amm_authority: Pubkey::default(),
482        amm_open_orders: Pubkey::default(),
483        amm_target_orders: Pubkey::default(),
484        lp_mint_address: Pubkey::default(),
485        pool_coin_token_account: Pubkey::default(),
486        pool_pc_token_account: Pubkey::default(),
487        pool_withdraw_queue: Pubkey::default(),
488        pool_temp_lp_token_account: Pubkey::default(),
489        serum_program: Pubkey::default(),
490        serum_market: Pubkey::default(),
491        serum_coin_vault_account: Pubkey::default(),
492        serum_pc_vault_account: Pubkey::default(),
493        serum_vault_signer: Pubkey::default(),
494        user_lp_token_account: Pubkey::default(),
495        user_coin_token_account: Pubkey::default(),
496        user_pc_token_account: Pubkey::default(),
497        user_owner: user,
498        serum_event_queue: Pubkey::default(),
499        serum_bids: Pubkey::default(),
500        serum_asks: Pubkey::default(),
501    }))
502}
503
504/// 解析初始化事件
505fn parse_initialize2_event(
506    data: &[u8],
507    signature: Signature,
508    slot: u64,
509    tx_index: u64,
510    block_time_us: Option<i64>,
511    grpc_recv_us: i64,
512) -> Option<DexEvent> {
513    let mut offset = 0;
514
515    let amm = read_pubkey(data, offset)?;
516    offset += 32;
517
518    let user = read_pubkey(data, offset)?;
519    offset += 32;
520
521    let nonce = data.get(offset)?.clone();
522    offset += 1;
523
524    let open_time = read_u64_le(data, offset)?;
525    offset += 8;
526
527    let init_pc_amount = read_u64_le(data, offset)?;
528    offset += 8;
529
530    let init_coin_amount = read_u64_le(data, offset)?;
531
532    let metadata =
533        create_metadata_simple(signature, slot, tx_index, block_time_us, amm, grpc_recv_us);
534
535    Some(DexEvent::RaydiumAmmV4Initialize2(RaydiumAmmV4Initialize2Event {
536        metadata,
537        nonce,
538        open_time,
539        init_pc_amount,
540        init_coin_amount,
541        token_program: Pubkey::default(),
542        spl_associated_token_account: Pubkey::default(),
543        system_program: Pubkey::default(),
544        rent: Pubkey::default(),
545        amm,
546        amm_authority: Pubkey::default(),
547        amm_open_orders: Pubkey::default(),
548        lp_mint: Pubkey::default(),
549        coin_mint: Pubkey::default(),
550        pc_mint: Pubkey::default(),
551        pool_coin_token_account: Pubkey::default(),
552        pool_pc_token_account: Pubkey::default(),
553        pool_withdraw_queue: Pubkey::default(),
554        amm_target_orders: Pubkey::default(),
555        pool_temp_lp: Pubkey::default(),
556        serum_program: Pubkey::default(),
557        serum_market: Pubkey::default(),
558        user_wallet: user,
559        user_token_coin: Pubkey::default(),
560        user_token_pc: Pubkey::default(),
561        user_lp_token_account: Pubkey::default(),
562    }))
563}
564
565/// 解析提取 PnL 事件
566fn parse_withdraw_pnl_event(
567    data: &[u8],
568    signature: Signature,
569    slot: u64,
570    tx_index: u64,
571    block_time_us: Option<i64>,
572    grpc_recv_us: i64,
573) -> Option<DexEvent> {
574    let mut offset = 0;
575
576    let amm = read_pubkey(data, offset)?;
577    offset += 32;
578
579    let pnl_owner = read_pubkey(data, offset)?;
580
581    let metadata =
582        create_metadata_simple(signature, slot, tx_index, block_time_us, amm, grpc_recv_us);
583
584    Some(DexEvent::RaydiumAmmV4WithdrawPnl(RaydiumAmmV4WithdrawPnlEvent {
585        metadata,
586        token_program: Pubkey::default(),
587        amm,
588        amm_config: Pubkey::default(),
589        amm_authority: Pubkey::default(),
590        amm_open_orders: Pubkey::default(),
591        pool_coin_token_account: Pubkey::default(),
592        pool_pc_token_account: Pubkey::default(),
593        coin_pnl_token_account: Pubkey::default(),
594        pc_pnl_token_account: Pubkey::default(),
595        pnl_owner,
596        amm_target_orders: Pubkey::default(),
597        serum_program: Pubkey::default(),
598        serum_market: Pubkey::default(),
599        serum_event_queue: Pubkey::default(),
600        serum_coin_vault_account: Pubkey::default(),
601        serum_pc_vault_account: Pubkey::default(),
602        serum_vault_signer: Pubkey::default(),
603    }))
604}
605
606/// 文本日志解析(回退方案)
607fn parse_text_log(
608    log: &str,
609    signature: Signature,
610    slot: u64,
611    tx_index: u64,
612    block_time_us: Option<i64>,
613    grpc_recv_us: i64,
614) -> Option<DexEvent> {
615    // 检查是否是交换相关的日志
616    if log.contains("swap") || log.contains("Swap") {
617        return parse_swap_log_fallback(
618            log,
619            signature,
620            slot,
621            tx_index,
622            block_time_us,
623            grpc_recv_us,
624        );
625    }
626
627    // 检查是否是存款相关的日志
628    if log.contains("deposit") || log.contains("Deposit") {
629        return parse_deposit_log_fallback(
630            log,
631            signature,
632            slot,
633            tx_index,
634            block_time_us,
635            grpc_recv_us,
636        );
637    }
638
639    // 检查是否是提取相关的日志
640    if log.contains("withdraw") || log.contains("Withdraw") {
641        return parse_withdraw_log_fallback(
642            log,
643            signature,
644            slot,
645            tx_index,
646            block_time_us,
647            grpc_recv_us,
648        );
649    }
650
651    None
652}
653
654/// 文本回退解析交换事件
655fn parse_swap_log_fallback(
656    log: &str,
657    signature: Signature,
658    slot: u64,
659    tx_index: u64,
660    block_time_us: Option<i64>,
661    grpc_recv_us: i64,
662) -> Option<DexEvent> {
663    // 尝试从日志文本中提取基本信息
664    let amount_in = super::utils::text_parser::extract_number_from_text(log, "amount_in")
665        .or_else(|| super::utils::text_parser::extract_number_from_text(log, "amountIn"))
666        .unwrap_or(0);
667
668    let amount_out = super::utils::text_parser::extract_number_from_text(log, "amount_out")
669        .or_else(|| super::utils::text_parser::extract_number_from_text(log, "amountOut"))
670        .unwrap_or(0);
671
672    let minimum_amount_out =
673        super::utils::text_parser::extract_number_from_text(log, "minimum_amount_out")
674            .or_else(|| {
675                super::utils::text_parser::extract_number_from_text(log, "minimumAmountOut")
676            })
677            .unwrap_or(0);
678
679    let max_amount_in = super::utils::text_parser::extract_number_from_text(log, "max_amount_in")
680        .or_else(|| super::utils::text_parser::extract_number_from_text(log, "maxAmountIn"))
681        .unwrap_or(0);
682
683    let default_pubkey = Pubkey::default();
684    let metadata = create_metadata_simple(
685        signature,
686        slot,
687        tx_index,
688        block_time_us,
689        default_pubkey,
690        grpc_recv_us,
691    );
692
693    Some(DexEvent::RaydiumAmmV4Swap(RaydiumAmmV4SwapEvent {
694        metadata,
695        amount_in,
696        minimum_amount_out,
697        max_amount_in,
698        amount_out,
699        token_program: default_pubkey,
700        amm: default_pubkey,
701        amm_authority: default_pubkey,
702        amm_open_orders: default_pubkey,
703        amm_target_orders: None,
704        pool_coin_token_account: default_pubkey,
705        pool_pc_token_account: default_pubkey,
706        serum_program: default_pubkey,
707        serum_market: default_pubkey,
708        serum_bids: default_pubkey,
709        serum_asks: default_pubkey,
710        serum_event_queue: default_pubkey,
711        serum_coin_vault_account: default_pubkey,
712        serum_pc_vault_account: default_pubkey,
713        serum_vault_signer: default_pubkey,
714        user_source_token_account: default_pubkey,
715        user_destination_token_account: default_pubkey,
716        user_source_owner: default_pubkey,
717    }))
718}
719
720/// 文本回退解析存款事件
721fn parse_deposit_log_fallback(
722    log: &str,
723    signature: Signature,
724    slot: u64,
725    tx_index: u64,
726    block_time_us: Option<i64>,
727    grpc_recv_us: i64,
728) -> Option<DexEvent> {
729    let max_coin_amount =
730        super::utils::text_parser::extract_number_from_text(log, "max_coin_amount")
731            .or_else(|| super::utils::text_parser::extract_number_from_text(log, "maxCoinAmount"))
732            .unwrap_or(0);
733
734    let max_pc_amount = super::utils::text_parser::extract_number_from_text(log, "max_pc_amount")
735        .or_else(|| super::utils::text_parser::extract_number_from_text(log, "maxPcAmount"))
736        .unwrap_or(0);
737
738    let base_side = super::utils::text_parser::extract_number_from_text(log, "base_side")
739        .or_else(|| super::utils::text_parser::extract_number_from_text(log, "baseSide"))
740        .unwrap_or(0);
741
742    let default_pubkey = Pubkey::default();
743    let metadata = create_metadata_simple(
744        signature,
745        slot,
746        tx_index,
747        block_time_us,
748        default_pubkey,
749        grpc_recv_us,
750    );
751
752    Some(DexEvent::RaydiumAmmV4Deposit(RaydiumAmmV4DepositEvent {
753        metadata,
754        max_coin_amount,
755        max_pc_amount,
756        base_side,
757        token_program: default_pubkey,
758        amm: default_pubkey,
759        amm_authority: default_pubkey,
760        amm_open_orders: default_pubkey,
761        amm_target_orders: default_pubkey,
762        lp_mint_address: default_pubkey,
763        pool_coin_token_account: default_pubkey,
764        pool_pc_token_account: default_pubkey,
765        serum_market: default_pubkey,
766        user_coin_token_account: default_pubkey,
767        user_pc_token_account: default_pubkey,
768        user_lp_token_account: default_pubkey,
769        user_owner: default_pubkey,
770        serum_event_queue: default_pubkey,
771    }))
772}
773
774/// 文本回退解析提取事件
775fn parse_withdraw_log_fallback(
776    log: &str,
777    signature: Signature,
778    slot: u64,
779    tx_index: u64,
780    block_time_us: Option<i64>,
781    grpc_recv_us: i64,
782) -> Option<DexEvent> {
783    let amount = super::utils::text_parser::extract_number_from_text(log, "amount").unwrap_or(0);
784
785    let default_pubkey = Pubkey::default();
786    let metadata = create_metadata_simple(
787        signature,
788        slot,
789        tx_index,
790        block_time_us,
791        default_pubkey,
792        grpc_recv_us,
793    );
794
795    Some(DexEvent::RaydiumAmmV4Withdraw(RaydiumAmmV4WithdrawEvent {
796        metadata,
797        amount,
798        token_program: default_pubkey,
799        amm: default_pubkey,
800        amm_authority: default_pubkey,
801        amm_open_orders: default_pubkey,
802        amm_target_orders: default_pubkey,
803        lp_mint_address: default_pubkey,
804        pool_coin_token_account: default_pubkey,
805        pool_pc_token_account: default_pubkey,
806        pool_withdraw_queue: default_pubkey,
807        pool_temp_lp_token_account: default_pubkey,
808        serum_program: default_pubkey,
809        serum_market: default_pubkey,
810        serum_coin_vault_account: default_pubkey,
811        serum_pc_vault_account: default_pubkey,
812        serum_vault_signer: default_pubkey,
813        user_lp_token_account: default_pubkey,
814        user_coin_token_account: default_pubkey,
815        user_pc_token_account: default_pubkey,
816        user_owner: default_pubkey,
817        serum_event_queue: default_pubkey,
818        serum_bids: default_pubkey,
819        serum_asks: default_pubkey,
820    }))
821}