Skip to main content

sol_parser_sdk/logs/
raydium_cpmm.rs

1//! Raydium CPMM 日志解析器
2//!
3//! 使用 match discriminator 模式解析 Raydium CPMM 事件
4
5use super::utils::*;
6use crate::core::events::*;
7use solana_sdk::{pubkey::Pubkey, signature::Signature};
8
9/// Raydium CPMM discriminator 常量
10pub mod discriminators {
11    pub const SWAP_EVENT: [u8; 8] = [64, 198, 205, 232, 38, 8, 113, 226];
12    pub const SWAP_BASE_IN: [u8; 8] = [143, 190, 90, 218, 196, 30, 51, 222];
13    pub const SWAP_BASE_OUT: [u8; 8] = [55, 217, 98, 86, 163, 74, 180, 173];
14    pub const CREATE_POOL: [u8; 8] = [233, 146, 209, 142, 207, 104, 64, 188];
15    pub const DEPOSIT: [u8; 8] = [242, 35, 198, 137, 82, 225, 242, 182];
16    pub const WITHDRAW: [u8; 8] = [183, 18, 70, 156, 148, 109, 161, 34];
17}
18
19/// Raydium CPMM 程序 ID
20pub const PROGRAM_ID: &str = "CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C";
21
22/// 检查日志是否来自 Raydium CPMM 程序
23pub fn is_raydium_cpmm_log(log: &str) -> bool {
24    log.contains(&format!("Program {} invoke", PROGRAM_ID))
25        || log.contains(&format!("Program {} success", PROGRAM_ID))
26        || (log.contains("raydium") && log.contains("cpmm"))
27}
28
29/// 主要的 Raydium CPMM 日志解析函数
30#[inline(always)] // 零延迟优化:内联热路径
31pub fn parse_log(
32    log: &str,
33    signature: Signature,
34    slot: u64,
35    tx_index: u64,
36    block_time_us: Option<i64>,
37    grpc_recv_us: i64,
38) -> Option<DexEvent> {
39    parse_structured_log(log, signature, slot, tx_index, block_time_us, grpc_recv_us)
40}
41
42/// 结构化日志解析(基于 Program data)
43fn parse_structured_log(
44    log: &str,
45    signature: Signature,
46    slot: u64,
47    tx_index: u64,
48    block_time_us: Option<i64>,
49    grpc_recv_us: i64,
50) -> Option<DexEvent> {
51    let program_data = extract_program_data(log)?;
52    if program_data.len() < 8 {
53        return None;
54    }
55
56    let discriminator: [u8; 8] = program_data[0..8].try_into().ok()?;
57    let data = &program_data[8..];
58
59    match discriminator {
60        discriminators::SWAP_EVENT => parse_swap_event_from_data(
61            data,
62            create_metadata_simple(
63                signature,
64                slot,
65                tx_index,
66                block_time_us,
67                Pubkey::default(),
68                grpc_recv_us,
69            ),
70        ),
71        discriminators::SWAP_BASE_IN => {
72            parse_swap_base_in_event(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
73        }
74        discriminators::SWAP_BASE_OUT => {
75            parse_swap_base_out_event(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
76        }
77        discriminators::CREATE_POOL => {
78            parse_create_pool_event(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
79        }
80        discriminators::DEPOSIT => {
81            parse_deposit_event(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
82        }
83        discriminators::WITHDRAW => {
84            parse_withdraw_event(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
85        }
86        _ => None,
87    }
88}
89
90// =============================================================================
91// Public from_data parsers - Accept pre-decoded data, eliminate double decode
92// =============================================================================
93
94/// Parse the stable prefix of the current Anchor `SwapEvent` payload.
95#[inline(always)]
96pub fn parse_swap_event_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
97    if data.len() < 32 + (6 * 8) + 1 {
98        return None;
99    }
100    let mut offset = 0;
101    let pool_id = read_pubkey(data, offset)?;
102    offset += 32;
103    let input_vault_before = read_u64_le(data, offset)?;
104    offset += 8;
105    let output_vault_before = read_u64_le(data, offset)?;
106    offset += 8;
107    let input_amount = read_u64_le(data, offset)?;
108    offset += 8;
109    let output_amount = read_u64_le(data, offset)?;
110    offset += 8;
111    let input_transfer_fee = read_u64_le(data, offset)?;
112    offset += 8;
113    let output_transfer_fee = read_u64_le(data, offset)?;
114    offset += 8;
115    let base_input = read_bool(data, offset)?;
116
117    Some(DexEvent::RaydiumCpmmSwap(RaydiumCpmmSwapEvent {
118        metadata,
119        pool_id,
120        input_vault_before,
121        output_vault_before,
122        input_amount,
123        output_amount,
124        input_transfer_fee,
125        output_transfer_fee,
126        base_input,
127    }))
128}
129
130/// Parse Raydium CPMM SwapBaseIn event from pre-decoded data
131#[inline(always)]
132pub fn parse_swap_base_in_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
133    let mut offset = 0;
134
135    let pool_state = read_pubkey(data, offset)?;
136    offset += 32;
137
138    let _user = read_pubkey(data, offset)?;
139    offset += 32;
140
141    let amount_in = read_u64_le(data, offset)?;
142    offset += 8;
143
144    let _minimum_amount_out = read_u64_le(data, offset)?;
145    offset += 8;
146
147    let amount_out = read_u64_le(data, offset)?;
148    offset += 8;
149
150    let is_base_input = read_bool(data, offset)?;
151
152    Some(DexEvent::RaydiumCpmmSwap(RaydiumCpmmSwapEvent {
153        metadata,
154        pool_id: pool_state,
155        input_vault_before: 0,
156        output_vault_before: 0,
157        input_amount: amount_in,
158        output_amount: amount_out,
159        input_transfer_fee: 0,
160        output_transfer_fee: 0,
161        base_input: is_base_input,
162    }))
163}
164
165/// Parse Raydium CPMM SwapBaseOut event from pre-decoded data
166#[inline(always)]
167pub fn parse_swap_base_out_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
168    let mut offset = 0;
169
170    let pool_state = read_pubkey(data, offset)?;
171    offset += 32;
172
173    let _user = read_pubkey(data, offset)?;
174    offset += 32;
175
176    let _maximum_amount_in = read_u64_le(data, offset)?;
177    offset += 8;
178
179    let amount_out = read_u64_le(data, offset)?;
180    offset += 8;
181
182    let amount_in = read_u64_le(data, offset)?;
183    offset += 8;
184
185    let is_base_output = read_bool(data, offset)?;
186
187    Some(DexEvent::RaydiumCpmmSwap(RaydiumCpmmSwapEvent {
188        metadata,
189        pool_id: pool_state,
190        input_vault_before: 0,
191        output_vault_before: 0,
192        input_amount: amount_in,
193        output_amount: amount_out,
194        input_transfer_fee: 0,
195        output_transfer_fee: 0,
196        base_input: !is_base_output,
197    }))
198}
199
200/// Parse Raydium CPMM CreatePool event from pre-decoded data
201#[inline(always)]
202pub fn parse_create_pool_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
203    let mut offset = 0;
204
205    let pool_state = read_pubkey(data, offset)?;
206    offset += 32;
207
208    let _token_0_mint = read_pubkey(data, offset)?;
209    offset += 32;
210
211    let _token_1_mint = read_pubkey(data, offset)?;
212    offset += 32;
213
214    let creator = read_pubkey(data, offset)?;
215    offset += 32;
216
217    let initial_amount_0 = read_u64_le(data, offset)?;
218    offset += 8;
219
220    let initial_amount_1 = read_u64_le(data, offset)?;
221
222    Some(DexEvent::RaydiumCpmmInitialize(RaydiumCpmmInitializeEvent {
223        metadata,
224        pool: pool_state,
225        creator,
226        init_amount0: initial_amount_0,
227        init_amount1: initial_amount_1,
228    }))
229}
230
231/// Parse Raydium CPMM Deposit event from pre-decoded data
232#[inline(always)]
233pub fn parse_deposit_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
234    let mut offset = 0;
235
236    let pool_state = read_pubkey(data, offset)?;
237    offset += 32;
238
239    let user = read_pubkey(data, offset)?;
240    offset += 32;
241
242    let lp_token_amount = read_u64_le(data, offset)?;
243    offset += 8;
244
245    let token_0_amount = read_u64_le(data, offset)?;
246    offset += 8;
247
248    let token_1_amount = read_u64_le(data, offset)?;
249
250    Some(DexEvent::RaydiumCpmmDeposit(RaydiumCpmmDepositEvent {
251        metadata,
252        pool: pool_state,
253        user,
254        lp_token_amount,
255        token0_amount: token_0_amount,
256        token1_amount: token_1_amount,
257    }))
258}
259
260/// Parse Raydium CPMM Withdraw event from pre-decoded data
261#[inline(always)]
262pub fn parse_withdraw_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
263    let mut offset = 0;
264
265    let pool_state = read_pubkey(data, offset)?;
266    offset += 32;
267
268    let user = read_pubkey(data, offset)?;
269    offset += 32;
270
271    let lp_token_amount = read_u64_le(data, offset)?;
272    offset += 8;
273
274    let token_0_amount = read_u64_le(data, offset)?;
275    offset += 8;
276
277    let token_1_amount = read_u64_le(data, offset)?;
278
279    Some(DexEvent::RaydiumCpmmWithdraw(RaydiumCpmmWithdrawEvent {
280        metadata,
281        pool: pool_state,
282        user,
283        lp_token_amount,
284        token0_amount: token_0_amount,
285        token1_amount: token_1_amount,
286    }))
287}
288
289/// 解析 Base In 交换事件
290fn parse_swap_base_in_event(
291    data: &[u8],
292    signature: Signature,
293    slot: u64,
294    tx_index: u64,
295    block_time_us: Option<i64>,
296    grpc_recv_us: i64,
297) -> Option<DexEvent> {
298    let mut offset = 0;
299
300    let pool_state = read_pubkey(data, offset)?;
301    offset += 32;
302
303    let user = read_pubkey(data, offset)?;
304    offset += 32;
305
306    let amount_in = read_u64_le(data, offset)?;
307    offset += 8;
308
309    let minimum_amount_out = read_u64_le(data, offset)?;
310    offset += 8;
311
312    let amount_out = read_u64_le(data, offset)?;
313    offset += 8;
314
315    let is_base_input = read_bool(data, offset)?;
316
317    let metadata =
318        create_metadata_simple(signature, slot, tx_index, block_time_us, pool_state, grpc_recv_us);
319
320    Some(DexEvent::RaydiumCpmmSwap(RaydiumCpmmSwapEvent {
321        metadata,
322
323        // IDL SwapEvent 事件字段
324        pool_id: pool_state,
325        input_vault_before: 0,  // 需要从日志数据解析
326        output_vault_before: 0, // 需要从日志数据解析
327        input_amount: amount_in,
328        output_amount: amount_out,
329        input_transfer_fee: 0,  // 需要从日志数据解析
330        output_transfer_fee: 0, // 需要从日志数据解析
331        base_input: is_base_input,
332        // 暂时注释,以后会用,AI禁止改动
333        // 指令参数字段
334        // amount_in,
335        // minimum_amount_out,
336
337        // 指令账户字段 - 从account_filler填充
338        // payer: user,
339        // authority: Pubkey::default(),
340        // amm_config: Pubkey::default(),
341        // pool_state,
342        // input_token_account: Pubkey::default(),
343        // output_token_account: Pubkey::default(),
344        // input_vault: Pubkey::default(),
345        // output_vault: Pubkey::default(),
346        // input_token_mint: Pubkey::default(),
347        // output_token_mint: Pubkey::default(),
348    }))
349}
350
351/// 解析 Base Out 交换事件
352fn parse_swap_base_out_event(
353    data: &[u8],
354    signature: Signature,
355    slot: u64,
356    tx_index: u64,
357    block_time_us: Option<i64>,
358    grpc_recv_us: i64,
359) -> Option<DexEvent> {
360    let mut offset = 0;
361
362    let pool_state = read_pubkey(data, offset)?;
363    offset += 32;
364
365    let user = read_pubkey(data, offset)?;
366    offset += 32;
367
368    let maximum_amount_in = read_u64_le(data, offset)?;
369    offset += 8;
370
371    let amount_out = read_u64_le(data, offset)?;
372    offset += 8;
373
374    let amount_in = read_u64_le(data, offset)?;
375    offset += 8;
376
377    let is_base_output = read_bool(data, offset)?;
378
379    let metadata =
380        create_metadata_simple(signature, slot, tx_index, block_time_us, pool_state, grpc_recv_us);
381
382    Some(DexEvent::RaydiumCpmmSwap(RaydiumCpmmSwapEvent {
383        metadata,
384
385        // IDL SwapEvent 事件字段
386        pool_id: pool_state,
387        input_vault_before: 0,  // 需要从日志数据解析
388        output_vault_before: 0, // 需要从日志数据解析
389        input_amount: amount_in,
390        output_amount: amount_out,
391        input_transfer_fee: 0,  // 需要从日志数据解析
392        output_transfer_fee: 0, // 需要从日志数据解析
393        base_input: !is_base_output,
394        // 暂时注释,以后会用,AI禁止改动
395        // 指令参数字段
396        // amount_in: maximum_amount_in,
397        // minimum_amount_out: amount_out,
398
399        // 指令账户字段 - 从account_filler填充
400        // payer: user,
401        // authority: Pubkey::default(),
402        // amm_config: Pubkey::default(),
403        // pool_state,
404        // input_token_account: Pubkey::default(),
405        // output_token_account: Pubkey::default(),
406        // input_vault: Pubkey::default(),
407        // output_vault: Pubkey::default(),
408        // input_token_mint: Pubkey::default(),
409        // output_token_mint: Pubkey::default(),
410    }))
411}
412
413/// 解析池创建事件
414fn parse_create_pool_event(
415    data: &[u8],
416    signature: Signature,
417    slot: u64,
418    tx_index: u64,
419    block_time_us: Option<i64>,
420    grpc_recv_us: i64,
421) -> Option<DexEvent> {
422    let mut offset = 0;
423
424    let pool_state = read_pubkey(data, offset)?;
425    offset += 32;
426
427    let token_0_mint = read_pubkey(data, offset)?;
428    offset += 32;
429
430    let token_1_mint = read_pubkey(data, offset)?;
431    offset += 32;
432
433    let creator = read_pubkey(data, offset)?;
434    offset += 32;
435
436    let initial_amount_0 = read_u64_le(data, offset)?;
437    offset += 8;
438
439    let initial_amount_1 = read_u64_le(data, offset)?;
440
441    let metadata =
442        create_metadata_simple(signature, slot, tx_index, block_time_us, pool_state, grpc_recv_us);
443
444    Some(DexEvent::RaydiumCpmmInitialize(RaydiumCpmmInitializeEvent {
445        metadata,
446        pool: pool_state,
447        creator,
448        init_amount0: initial_amount_0,
449        init_amount1: initial_amount_1,
450    }))
451}
452
453/// 解析存款事件
454fn parse_deposit_event(
455    data: &[u8],
456    signature: Signature,
457    slot: u64,
458    tx_index: u64,
459    block_time_us: Option<i64>,
460    grpc_recv_us: i64,
461) -> Option<DexEvent> {
462    let mut offset = 0;
463
464    let pool_state = read_pubkey(data, offset)?;
465    offset += 32;
466
467    let user = read_pubkey(data, offset)?;
468    offset += 32;
469
470    let lp_token_amount = read_u64_le(data, offset)?;
471    offset += 8;
472
473    let token_0_amount = read_u64_le(data, offset)?;
474    offset += 8;
475
476    let token_1_amount = read_u64_le(data, offset)?;
477
478    let metadata =
479        create_metadata_simple(signature, slot, tx_index, block_time_us, pool_state, grpc_recv_us);
480
481    Some(DexEvent::RaydiumCpmmDeposit(RaydiumCpmmDepositEvent {
482        metadata,
483        pool: pool_state,
484        user,
485        lp_token_amount,
486        token0_amount: token_0_amount,
487        token1_amount: token_1_amount,
488    }))
489}
490
491/// 解析提款事件
492fn parse_withdraw_event(
493    data: &[u8],
494    signature: Signature,
495    slot: u64,
496    tx_index: u64,
497    block_time_us: Option<i64>,
498    grpc_recv_us: i64,
499) -> Option<DexEvent> {
500    let mut offset = 0;
501
502    let pool_state = read_pubkey(data, offset)?;
503    offset += 32;
504
505    let user = read_pubkey(data, offset)?;
506    offset += 32;
507
508    let lp_token_amount = read_u64_le(data, offset)?;
509    offset += 8;
510
511    let token_0_amount = read_u64_le(data, offset)?;
512    offset += 8;
513
514    let token_1_amount = read_u64_le(data, offset)?;
515
516    let metadata =
517        create_metadata_simple(signature, slot, tx_index, block_time_us, pool_state, grpc_recv_us);
518
519    Some(DexEvent::RaydiumCpmmWithdraw(RaydiumCpmmWithdrawEvent {
520        metadata,
521        pool: pool_state,
522        user,
523        lp_token_amount,
524        token0_amount: token_0_amount,
525        token1_amount: token_1_amount,
526    }))
527}
528
529/// 文本回退解析
530fn parse_text_log(
531    log: &str,
532    signature: Signature,
533    slot: u64,
534    tx_index: u64,
535    block_time_us: Option<i64>,
536    grpc_recv_us: i64,
537) -> Option<DexEvent> {
538    use super::utils::text_parser::*;
539
540    if log.contains("swap") || log.contains("Swap") {
541        if log.contains("base_in") {
542            return parse_swap_base_in_from_text(
543                log,
544                signature,
545                slot,
546                tx_index,
547                block_time_us,
548                grpc_recv_us,
549            );
550        } else if log.contains("base_out") {
551            return parse_swap_base_out_from_text(
552                log,
553                signature,
554                slot,
555                tx_index,
556                block_time_us,
557                grpc_recv_us,
558            );
559        } else {
560            return parse_swap_base_in_from_text(
561                log,
562                signature,
563                slot,
564                tx_index,
565                block_time_us,
566                grpc_recv_us,
567            );
568        }
569    }
570
571    if log.contains("deposit") || log.contains("Deposit") {
572        return parse_deposit_from_text(
573            log,
574            signature,
575            slot,
576            tx_index,
577            block_time_us,
578            grpc_recv_us,
579        );
580    }
581
582    if log.contains("withdraw") || log.contains("Withdraw") {
583        return parse_withdraw_from_text(
584            log,
585            signature,
586            slot,
587            tx_index,
588            block_time_us,
589            grpc_recv_us,
590        );
591    }
592
593    if log.contains("create") && log.contains("pool") {
594        return parse_create_pool_from_text(
595            log,
596            signature,
597            slot,
598            tx_index,
599            block_time_us,
600            grpc_recv_us,
601        );
602    }
603
604    None
605}
606
607/// 从文本解析 Base In 交换事件
608fn parse_swap_base_in_from_text(
609    log: &str,
610    signature: Signature,
611    slot: u64,
612    tx_index: u64,
613    block_time_us: Option<i64>,
614    grpc_recv_us: i64,
615) -> Option<DexEvent> {
616    use super::utils::text_parser::*;
617
618    let metadata = create_metadata_simple(
619        signature,
620        slot,
621        tx_index,
622        block_time_us,
623        Pubkey::default(),
624        grpc_recv_us,
625    );
626
627    Some(DexEvent::RaydiumCpmmSwap(RaydiumCpmmSwapEvent {
628        metadata,
629
630        // IDL SwapEvent 事件字段
631        pool_id: Pubkey::default(),
632        input_vault_before: 0,
633        output_vault_before: 0,
634        input_amount: extract_number_from_text(log, "amount_in").unwrap_or(1_000_000_000),
635        output_amount: extract_number_from_text(log, "amount_out").unwrap_or(950_000_000),
636        input_transfer_fee: 0,
637        output_transfer_fee: 0,
638        base_input: true,
639        // 暂时注释,以后会用,AI禁止改动
640        // 指令参数字段
641        // amount_in: extract_number_from_text(log, "amount_in").unwrap_or(1_000_000_000),
642        // minimum_amount_out: extract_number_from_text(log, "amount_out").unwrap_or(950_000_000),
643
644        // 指令账户字段
645        // payer: Pubkey::default(),
646        // authority: Pubkey::default(),
647        // amm_config: Pubkey::default(),
648        // pool_state: Pubkey::default(),
649        // input_token_account: Pubkey::default(),
650        // output_token_account: Pubkey::default(),
651        // input_vault: Pubkey::default(),
652        // output_vault: Pubkey::default(),
653        // input_token_mint: Pubkey::default(),
654        // output_token_mint: Pubkey::default(),
655    }))
656}
657
658/// 从文本解析 Base Out 交换事件
659fn parse_swap_base_out_from_text(
660    log: &str,
661    signature: Signature,
662    slot: u64,
663    tx_index: u64,
664    block_time_us: Option<i64>,
665    grpc_recv_us: i64,
666) -> Option<DexEvent> {
667    use super::utils::text_parser::*;
668
669    let metadata = create_metadata_simple(
670        signature,
671        slot,
672        tx_index,
673        block_time_us,
674        Pubkey::default(),
675        grpc_recv_us,
676    );
677
678    Some(DexEvent::RaydiumCpmmSwap(RaydiumCpmmSwapEvent {
679        metadata,
680
681        // IDL SwapEvent 事件字段
682        pool_id: Pubkey::default(),
683        input_vault_before: 0,
684        output_vault_before: 0,
685        input_amount: extract_number_from_text(log, "amount_in").unwrap_or(1_000_000_000),
686        output_amount: extract_number_from_text(log, "amount_out").unwrap_or(950_000_000),
687        input_transfer_fee: 0,
688        output_transfer_fee: 0,
689        base_input: false,
690        // 暂时注释,以后会用,AI禁止改动
691        // 指令参数字段
692        // amount_in: extract_number_from_text(log, "amount_in").unwrap_or(1_000_000_000),
693        // minimum_amount_out: extract_number_from_text(log, "amount_out").unwrap_or(950_000_000),
694
695        // 指令账户字段
696        // payer: Pubkey::default(),
697        // authority: Pubkey::default(),
698        // amm_config: Pubkey::default(),
699        // pool_state: Pubkey::default(),
700        // input_token_account: Pubkey::default(),
701        // output_token_account: Pubkey::default(),
702        // input_vault: Pubkey::default(),
703        // output_vault: Pubkey::default(),
704        // input_token_mint: Pubkey::default(),
705        // output_token_mint: Pubkey::default(),
706    }))
707}
708
709/// 从文本解析池创建事件
710fn parse_create_pool_from_text(
711    log: &str,
712    signature: Signature,
713    slot: u64,
714    tx_index: u64,
715    block_time_us: Option<i64>,
716    grpc_recv_us: i64,
717) -> Option<DexEvent> {
718    use super::utils::text_parser::*;
719
720    let metadata = create_metadata_simple(
721        signature,
722        slot,
723        tx_index,
724        block_time_us,
725        Pubkey::default(),
726        grpc_recv_us,
727    );
728
729    Some(DexEvent::RaydiumCpmmInitialize(RaydiumCpmmInitializeEvent {
730        metadata,
731        pool: Pubkey::default(),
732        creator: Pubkey::default(),
733        init_amount0: extract_number_from_text(log, "amount_0").unwrap_or(1_000_000_000),
734        init_amount1: extract_number_from_text(log, "amount_1").unwrap_or(1_000_000_000),
735    }))
736}
737
738/// 从文本解析存款事件
739fn parse_deposit_from_text(
740    log: &str,
741    signature: Signature,
742    slot: u64,
743    tx_index: u64,
744    block_time_us: Option<i64>,
745    grpc_recv_us: i64,
746) -> Option<DexEvent> {
747    use super::utils::text_parser::*;
748
749    let metadata = create_metadata_simple(
750        signature,
751        slot,
752        tx_index,
753        block_time_us,
754        Pubkey::default(),
755        grpc_recv_us,
756    );
757
758    Some(DexEvent::RaydiumCpmmDeposit(RaydiumCpmmDepositEvent {
759        metadata,
760        pool: Pubkey::default(),
761        user: Pubkey::default(),
762        lp_token_amount: extract_number_from_text(log, "lp_token").unwrap_or(1_000_000),
763        token0_amount: extract_number_from_text(log, "token_0").unwrap_or(1_000_000_000),
764        token1_amount: extract_number_from_text(log, "token_1").unwrap_or(1_000_000_000),
765    }))
766}
767
768/// 从文本解析提款事件
769fn parse_withdraw_from_text(
770    log: &str,
771    signature: Signature,
772    slot: u64,
773    tx_index: u64,
774    block_time_us: Option<i64>,
775    grpc_recv_us: i64,
776) -> Option<DexEvent> {
777    use super::utils::text_parser::*;
778
779    let metadata = create_metadata_simple(
780        signature,
781        slot,
782        tx_index,
783        block_time_us,
784        Pubkey::default(),
785        grpc_recv_us,
786    );
787
788    Some(DexEvent::RaydiumCpmmWithdraw(RaydiumCpmmWithdrawEvent {
789        metadata,
790        pool: Pubkey::default(),
791        user: Pubkey::default(),
792        lp_token_amount: extract_number_from_text(log, "lp_token").unwrap_or(1_000_000),
793        token0_amount: extract_number_from_text(log, "token_0").unwrap_or(1_000_000_000),
794        token1_amount: extract_number_from_text(log, "token_1").unwrap_or(1_000_000_000),
795    }))
796}