sol_parser_sdk/instr/
raydium_clmm.rs

1//! Raydium CLMM 指令解析器
2//!
3//! 使用 match discriminator 模式解析 Raydium CLMM 指令
4
5use solana_sdk::{pubkey::Pubkey, signature::Signature};
6use crate::core::events::*;
7use super::utils::*;
8use super::program_ids;
9
10/// Raydium CLMM discriminator 常量
11pub mod discriminators {
12    pub const SWAP: [u8; 8] = [248, 198, 158, 145, 225, 117, 135, 200];
13    pub const INCREASE_LIQUIDITY: [u8; 8] = [133, 29, 89, 223, 69, 238, 176, 10];
14    pub const DECREASE_LIQUIDITY: [u8; 8] = [160, 38, 208, 111, 104, 91, 44, 1];
15    pub const CREATE_POOL: [u8; 8] = [233, 146, 209, 142, 207, 104, 64, 188];
16    pub const OPEN_POSITION: [u8; 8] = [135, 128, 47, 77, 15, 152, 240, 49];
17    pub const CLOSE_POSITION: [u8; 8] = [123, 134, 81, 0, 49, 68, 98, 98];
18}
19
20/// Raydium CLMM 程序 ID
21pub const PROGRAM_ID_PUBKEY: Pubkey = program_ids::RAYDIUM_CLMM_PROGRAM_ID;
22
23/// 主要的 Raydium CLMM 指令解析函数
24pub fn parse_instruction(
25    instruction_data: &[u8],
26    accounts: &[Pubkey],
27    signature: Signature,
28    slot: u64,
29    tx_index: u64,
30    block_time: Option<i64>,
31) -> Option<DexEvent> {
32    if instruction_data.len() < 8 {
33        return None;
34    }
35
36    let discriminator: [u8; 8] = instruction_data[0..8].try_into().ok()?;
37    let data = &instruction_data[8..];
38
39    match discriminator {
40        discriminators::SWAP => {
41            parse_swap_instruction(data, accounts, signature, slot, tx_index, block_time)
42        },
43        discriminators::INCREASE_LIQUIDITY => {
44            parse_increase_liquidity_instruction(data, accounts, signature, slot, tx_index, block_time)
45        },
46        discriminators::DECREASE_LIQUIDITY => {
47            parse_decrease_liquidity_instruction(data, accounts, signature, slot, tx_index, block_time)
48        },
49        discriminators::CREATE_POOL => {
50            parse_create_pool_instruction(data, accounts, signature, slot, tx_index, block_time)
51        },
52        discriminators::OPEN_POSITION => {
53            parse_open_position_instruction(data, accounts, signature, slot, tx_index, block_time)
54        },
55        discriminators::CLOSE_POSITION => {
56            parse_close_position_instruction(data, accounts, signature, slot, tx_index, block_time)
57        },
58        _ => None,
59    }
60}
61
62/// 解析交换指令
63fn parse_swap_instruction(
64    data: &[u8],
65    accounts: &[Pubkey],
66    signature: Signature,
67    slot: u64,
68    tx_index: u64,
69    block_time: Option<i64>,
70) -> Option<DexEvent> {
71    let mut offset = 0;
72
73    let amount = read_u64_le(data, offset)?;
74    offset += 8;
75
76    let other_amount_threshold = read_u64_le(data, offset)?;
77    offset += 8;
78
79    let sqrt_price_limit_x64 = read_u64_le(data, offset)? as u128;
80    offset += 8;
81
82    let is_base_input = data.get(offset)? == &1;
83
84    let pool = get_account(accounts, 0)?;
85    let metadata = create_metadata_simple(signature, slot, tx_index, block_time, pool);
86
87    Some(DexEvent::RaydiumClmmSwap(RaydiumClmmSwapEvent {
88        metadata,
89
90        // IDL SwapEvent 事件字段
91        pool_state: pool,
92        sender: get_account(accounts, 1).unwrap_or_default(),
93        token_account_0: Pubkey::default(),
94        token_account_1: Pubkey::default(),
95        amount_0: 0, // 从日志填充
96        transfer_fee_0: 0, // 从日志填充
97        amount_1: 0, // 从日志填充
98        transfer_fee_1: 0, // 从日志填充
99        zero_for_one: is_base_input,
100        sqrt_price_x64: sqrt_price_limit_x64,
101        // is_base_input,
102        liquidity: 0, // 从日志填充
103        tick: 0, // 从日志填充
104
105        // 暂时注释,以后会用,AI禁止改动
106        // 指令参数字段
107        // amount,
108        // other_amount_threshold,
109        // sqrt_price_limit_x64,
110    }))
111}
112
113/// 解析增加流动性指令
114fn parse_increase_liquidity_instruction(
115    data: &[u8],
116    accounts: &[Pubkey],
117    signature: Signature,
118    slot: u64,
119    tx_index: u64,
120    block_time: Option<i64>,
121) -> Option<DexEvent> {
122    let mut offset = 0;
123
124    let liquidity = read_u64_le(data, offset)? as u128;
125    offset += 8;
126
127    let amount_0_max = read_u64_le(data, offset)?;
128    offset += 8;
129
130    let amount_1_max = read_u64_le(data, offset)?;
131
132    let pool = get_account(accounts, 0)?;
133    let metadata = create_metadata_simple(signature, slot, tx_index, block_time, pool);
134
135    Some(DexEvent::RaydiumClmmIncreaseLiquidity(RaydiumClmmIncreaseLiquidityEvent {
136        metadata,
137        pool,
138        user: get_account(accounts, 2).unwrap_or_default(),
139        liquidity,
140        amount0_max: amount_0_max,
141        amount1_max: amount_1_max,
142    }))
143}
144
145/// 解析减少流动性指令
146fn parse_decrease_liquidity_instruction(
147    data: &[u8],
148    accounts: &[Pubkey],
149    signature: Signature,
150    slot: u64,
151    tx_index: u64,
152    block_time: Option<i64>,
153) -> Option<DexEvent> {
154    let mut offset = 0;
155
156    let liquidity = read_u64_le(data, offset)? as u128;
157    offset += 8;
158
159    let amount_0_min = read_u64_le(data, offset)?;
160    offset += 8;
161
162    let amount_1_min = read_u64_le(data, offset)?;
163
164    let pool = get_account(accounts, 0)?;
165    let metadata = create_metadata_simple(signature, slot, tx_index, block_time, pool);
166
167    Some(DexEvent::RaydiumClmmDecreaseLiquidity(RaydiumClmmDecreaseLiquidityEvent {
168        metadata,
169        pool,
170        user: get_account(accounts, 1).unwrap_or_default(),
171        liquidity,
172        amount0_min: amount_0_min,
173        amount1_min: amount_1_min,
174    }))
175}
176
177/// 解析池创建指令
178fn parse_create_pool_instruction(
179    data: &[u8],
180    accounts: &[Pubkey],
181    signature: Signature,
182    slot: u64,
183    tx_index: u64,
184    block_time: Option<i64>,
185) -> Option<DexEvent> {
186    let mut offset = 0;
187
188    let sqrt_price_x64 = read_u64_le(data, offset)? as u128;
189    offset += 8;
190
191    let open_time = read_u64_le(data, offset)?;
192
193    let pool = get_account(accounts, 0)?;
194    let metadata = create_metadata_simple(signature, slot, tx_index, block_time, pool);
195
196    Some(DexEvent::RaydiumClmmCreatePool(RaydiumClmmCreatePoolEvent {
197        metadata,
198        pool,
199        creator: get_account(accounts, 1).unwrap_or_default(),
200        sqrt_price_x64,
201        open_time,
202    }))
203}
204
205/// 解析开启头寸指令
206fn parse_open_position_instruction(
207    data: &[u8],
208    accounts: &[Pubkey],
209    signature: Signature,
210    slot: u64,
211    tx_index: u64,
212    block_time: Option<i64>,
213) -> Option<DexEvent> {
214    let mut offset = 0;
215
216    let tick_lower_index = read_u32_le(data, offset)? as i32;
217    offset += 4;
218
219    let tick_upper_index = read_u32_le(data, offset)? as i32;
220    offset += 4;
221
222    let _tick_array_lower_start_index = read_u32_le(data, offset)? as i32;
223    offset += 4;
224
225    let _tick_array_upper_start_index = read_u32_le(data, offset)? as i32;
226    offset += 4;
227
228    let liquidity = read_u64_le(data, offset)? as u128;
229    offset += 8;
230
231    let _amount_0_max = read_u64_le(data, offset)?;
232    offset += 8;
233
234    let _amount_1_max = read_u64_le(data, offset)?;
235
236    let pool = get_account(accounts, 0)?;
237    let metadata = create_metadata_simple(signature, slot, tx_index, block_time, pool);
238
239    Some(DexEvent::RaydiumClmmOpenPosition(RaydiumClmmOpenPositionEvent {
240        metadata,
241        pool,
242        user: get_account(accounts, 1).unwrap_or_default(),
243        position_nft_mint: get_account(accounts, 2).unwrap_or_default(),
244        tick_lower_index,
245        tick_upper_index,
246        liquidity,
247    }))
248}
249
250/// 解析关闭头寸指令
251fn parse_close_position_instruction(
252    _data: &[u8],
253    accounts: &[Pubkey],
254    signature: Signature,
255    slot: u64,
256    tx_index: u64,
257    block_time: Option<i64>,
258) -> Option<DexEvent> {
259    let pool = get_account(accounts, 0)?;
260    let metadata = create_metadata_simple(signature, slot, tx_index, block_time, pool);
261
262    Some(DexEvent::RaydiumClmmClosePosition(RaydiumClmmClosePositionEvent {
263        metadata,
264        pool,
265        user: get_account(accounts, 1).unwrap_or_default(),
266        position_nft_mint: get_account(accounts, 2).unwrap_or_default(),
267    }))
268}