sol_parser_sdk/instr/
raydium_cpmm.rs

1//! Raydium CPMM 指令解析器
2//!
3//! 使用 match discriminator 模式解析 Raydium CPMM 指令
4
5use solana_sdk::{pubkey::Pubkey, signature::Signature};
6use crate::core::events::*;
7use super::utils::*;
8use super::program_ids;
9
10/// Raydium CPMM discriminator 常量
11pub mod discriminators {
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 INITIALIZE: [u8; 8] = [175, 175, 109, 31, 13, 152, 155, 237];
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_PUBKEY: Pubkey = program_ids::RAYDIUM_CPMM_PROGRAM_ID;
21
22/// 主要的 Raydium CPMM 指令解析函数
23pub fn parse_instruction(
24    instruction_data: &[u8],
25    accounts: &[Pubkey],
26    signature: Signature,
27    slot: u64,
28    tx_index: u64,
29    block_time: Option<i64>,
30) -> Option<DexEvent> {
31    if instruction_data.len() < 8 {
32        return None;
33    }
34
35    let discriminator: [u8; 8] = instruction_data[0..8].try_into().ok()?;
36    let data = &instruction_data[8..];
37
38    match discriminator {
39        discriminators::SWAP_BASE_IN => {
40            parse_swap_base_in_instruction(data, accounts, signature, slot, tx_index, block_time)
41        },
42        discriminators::SWAP_BASE_OUT => {
43            parse_swap_base_out_instruction(data, accounts, signature, slot, tx_index, block_time)
44        },
45        discriminators::INITIALIZE => {
46            parse_initialize_instruction(data, accounts, signature, slot, tx_index, block_time)
47        },
48        discriminators::DEPOSIT => {
49            parse_deposit_instruction(data, accounts, signature, slot, tx_index, block_time)
50        },
51        discriminators::WITHDRAW => {
52            parse_withdraw_instruction(data, accounts, signature, slot, tx_index, block_time)
53        },
54        _ => None,
55    }
56}
57
58/// 解析 Base In 交换指令
59fn parse_swap_base_in_instruction(
60    data: &[u8],
61    accounts: &[Pubkey],
62    signature: Signature,
63    slot: u64,
64    tx_index: u64,
65    block_time: Option<i64>,
66) -> Option<DexEvent> {
67    let mut offset = 0;
68
69    let amount_in = read_u64_le(data, offset)?;
70    offset += 8;
71
72    let minimum_amount_out = read_u64_le(data, offset)?;
73
74    let pool = get_account(accounts, 0)?;
75    let metadata = create_metadata_simple(signature, slot, tx_index, block_time, pool);
76
77    Some(DexEvent::RaydiumCpmmSwap(RaydiumCpmmSwapEvent {
78        metadata,
79
80        // IDL SwapEvent 事件字段 - 从日志填充,这里设置默认值
81        pool_id: Pubkey::default(), // 将从日志填充
82        input_vault_before: 0, // 将从日志填充
83        output_vault_before: 0, // 将从日志填充
84        input_amount: 0, // 将从日志填充
85        output_amount: 0, // 将从日志填充
86        input_transfer_fee: 0, // 将从日志填充
87        output_transfer_fee: 0, // 将从日志填充
88        base_input: true,
89
90        // 暂时注释,以后会用,AI禁止改动
91        // 指令参数字段
92        // amount_in,
93        // minimum_amount_out,
94
95        // 指令账户字段 - 从account_filler填充
96        // payer: Pubkey::default(),
97        // authority: Pubkey::default(),
98        // amm_config: Pubkey::default(),
99        // pool_state: Pubkey::default(),
100        // input_token_account: Pubkey::default(),
101        // output_token_account: Pubkey::default(),
102        // input_vault: Pubkey::default(),
103        // output_vault: Pubkey::default(),
104        // input_token_mint: Pubkey::default(),
105        // output_token_mint: Pubkey::default(),
106
107    }))
108}
109
110/// 解析 Base Out 交换指令
111fn parse_swap_base_out_instruction(
112    data: &[u8],
113    accounts: &[Pubkey],
114    signature: Signature,
115    slot: u64,
116    tx_index: u64,
117    block_time: Option<i64>,
118) -> Option<DexEvent> {
119    let mut offset = 0;
120
121    let maximum_amount_in = read_u64_le(data, offset)?;
122    offset += 8;
123
124    let amount_out = read_u64_le(data, offset)?;
125
126    let pool = get_account(accounts, 0)?;
127    let metadata = create_metadata_simple(signature, slot, tx_index, block_time, pool);
128
129    Some(DexEvent::RaydiumCpmmSwap(RaydiumCpmmSwapEvent {
130        metadata,
131
132        // IDL SwapEvent 事件字段 - 从日志填充,这里设置默认值
133        pool_id: Pubkey::default(), // 将从日志填充
134        input_vault_before: 0, // 将从日志填充
135        output_vault_before: 0, // 将从日志填充
136        input_amount: 0, // 将从日志填充
137        output_amount: 0, // 将从日志填充
138        input_transfer_fee: 0, // 将从日志填充
139        output_transfer_fee: 0, // 将从日志填充
140        base_input: false,
141
142        // 暂时注释,以后会用,AI禁止改动
143        // 指令参数字段
144        // amount_in: maximum_amount_in,
145        // minimum_amount_out: amount_out,
146
147        // 指令账户字段 - 从account_filler填充
148        // payer: Pubkey::default(),
149        // authority: Pubkey::default(),
150        // amm_config: Pubkey::default(),
151        // pool_state: Pubkey::default(),
152        // input_token_account: Pubkey::default(),
153        // output_token_account: Pubkey::default(),
154        // input_vault: Pubkey::default(),
155        // output_vault: Pubkey::default(),
156        // input_token_mint: Pubkey::default(),
157        // output_token_mint: Pubkey::default(),
158
159    }))
160}
161
162/// 解析初始化指令
163fn parse_initialize_instruction(
164    data: &[u8],
165    accounts: &[Pubkey],
166    signature: Signature,
167    slot: u64,
168    tx_index: u64,
169    block_time: Option<i64>,
170) -> Option<DexEvent> {
171    let mut offset = 0;
172
173    let init_amount0 = read_u64_le(data, offset)?;
174    offset += 8;
175
176    let init_amount1 = read_u64_le(data, offset)?;
177    offset += 8;
178
179    let open_time = read_u64_le(data, offset)?;
180
181    let pool = get_account(accounts, 0)?;
182    let metadata = create_metadata_simple(signature, slot, tx_index, block_time, pool);
183
184    Some(DexEvent::RaydiumCpmmInitialize(RaydiumCpmmInitializeEvent {
185        metadata,
186        pool,
187        creator: get_account(accounts, 1).unwrap_or_default(),
188        init_amount0,
189        init_amount1,
190    }))
191}
192
193/// 解析存款指令
194fn parse_deposit_instruction(
195    data: &[u8],
196    accounts: &[Pubkey],
197    signature: Signature,
198    slot: u64,
199    tx_index: u64,
200    block_time: Option<i64>,
201) -> Option<DexEvent> {
202    let mut offset = 0;
203
204    let lp_token_amount = read_u64_le(data, offset)?;
205    offset += 8;
206
207    let maximum_token_0_amount = read_u64_le(data, offset)?;
208    offset += 8;
209
210    let maximum_token_1_amount = read_u64_le(data, offset)?;
211
212    let pool = get_account(accounts, 0)?;
213    let metadata = create_metadata_simple(signature, slot, tx_index, block_time, pool);
214
215    Some(DexEvent::RaydiumCpmmDeposit(RaydiumCpmmDepositEvent {
216        metadata,
217        pool,
218        user: get_account(accounts, 1).unwrap_or_default(),
219        lp_token_amount,
220        token0_amount: maximum_token_0_amount, // 先赋值为maximum,logs会覆盖
221        token1_amount: maximum_token_1_amount, // 先赋值为maximum,logs会覆盖
222    }))
223}
224
225/// 解析提款指令
226fn parse_withdraw_instruction(
227    data: &[u8],
228    accounts: &[Pubkey],
229    signature: Signature,
230    slot: u64,
231    tx_index: u64,
232    block_time: Option<i64>,
233) -> Option<DexEvent> {
234    let mut offset = 0;
235
236    let lp_token_amount = read_u64_le(data, offset)?;
237    offset += 8;
238
239    let minimum_token_0_amount = read_u64_le(data, offset)?;
240    offset += 8;
241
242    let minimum_token_1_amount = read_u64_le(data, offset)?;
243
244    let pool = get_account(accounts, 0)?;
245    let metadata = create_metadata_simple(signature, slot, tx_index, block_time, pool);
246
247    Some(DexEvent::RaydiumCpmmWithdraw(RaydiumCpmmWithdrawEvent {
248        metadata,
249        pool,
250        user: get_account(accounts, 1).unwrap_or_default(),
251        lp_token_amount,
252        token0_amount: minimum_token_0_amount, // 先赋值为minimum,logs会覆盖
253        token1_amount: minimum_token_1_amount, // 先赋值为minimum,logs会覆盖
254    }))
255}