Skip to main content

sol_parser_sdk/logs/
orca_whirlpool.rs

1//! Orca Whirlpool 日志解析器
2//!
3//! 解析 Orca Whirlpool 程序的日志事件
4
5use super::utils::*;
6use crate::core::events::*;
7use solana_sdk::pubkey::Pubkey;
8use solana_sdk::signature::Signature;
9
10/// Orca Whirlpool 事件 discriminator 常量
11pub mod discriminators {
12    pub const TRADED_EVENT: [u8; 8] = [225, 202, 73, 175, 147, 43, 160, 150];
13    pub const LIQUIDITY_INCREASED_EVENT: [u8; 8] = [30, 7, 144, 181, 102, 254, 155, 161];
14    pub const LIQUIDITY_DECREASED_EVENT: [u8; 8] = [166, 1, 36, 71, 112, 202, 181, 171];
15    pub const POOL_INITIALIZED_EVENT: [u8; 8] = [100, 118, 173, 87, 12, 198, 254, 229];
16}
17
18/// 主要的 Orca Whirlpool 日志解析函数
19pub fn parse_log(
20    log: &str,
21    signature: Signature,
22    slot: u64,
23    tx_index: u64,
24    block_time_us: Option<i64>,
25    grpc_recv_us: i64,
26) -> Option<DexEvent> {
27    parse_structured_log(log, signature, slot, tx_index, block_time_us, grpc_recv_us)
28}
29
30/// 解析结构化日志(基于 discriminator)
31fn parse_structured_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    let program_data = extract_program_data(log)?;
40
41    if program_data.len() < 8 {
42        return None;
43    }
44
45    let discriminator: [u8; 8] = program_data[0..8].try_into().ok()?;
46    let data = &program_data[8..];
47
48    match discriminator {
49        discriminators::TRADED_EVENT => {
50            parse_traded_event(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
51        }
52        discriminators::LIQUIDITY_INCREASED_EVENT => parse_liquidity_increased_event(
53            data,
54            signature,
55            slot,
56            tx_index,
57            block_time_us,
58            grpc_recv_us,
59        ),
60        discriminators::LIQUIDITY_DECREASED_EVENT => parse_liquidity_decreased_event(
61            data,
62            signature,
63            slot,
64            tx_index,
65            block_time_us,
66            grpc_recv_us,
67        ),
68        discriminators::POOL_INITIALIZED_EVENT => parse_pool_initialized_event(
69            data,
70            signature,
71            slot,
72            tx_index,
73            block_time_us,
74            grpc_recv_us,
75        ),
76        _ => None,
77    }
78}
79
80// =============================================================================
81// Public from_data parsers - Accept pre-decoded data, eliminate double decode
82// =============================================================================
83
84/// Parse Orca Whirlpool Traded (Swap) event from pre-decoded data
85#[inline(always)]
86pub fn parse_traded_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
87    let mut offset = 0;
88
89    let whirlpool = read_pubkey(data, offset)?;
90    offset += 32;
91
92    let a_to_b = read_bool(data, offset)?;
93    offset += 1;
94
95    let pre_sqrt_price = read_u128_le(data, offset)?;
96    offset += 16;
97
98    let post_sqrt_price = read_u128_le(data, offset)?;
99    offset += 16;
100
101    let input_amount = read_u64_le(data, offset)?;
102    offset += 8;
103
104    let output_amount = read_u64_le(data, offset)?;
105    offset += 8;
106
107    let input_transfer_fee = read_u64_le(data, offset)?;
108    offset += 8;
109
110    let output_transfer_fee = read_u64_le(data, offset)?;
111    offset += 8;
112
113    let lp_fee = read_u64_le(data, offset)?;
114    offset += 8;
115
116    let protocol_fee = read_u64_le(data, offset)?;
117
118    Some(DexEvent::OrcaWhirlpoolSwap(OrcaWhirlpoolSwapEvent {
119        metadata,
120        whirlpool,
121        a_to_b,
122        pre_sqrt_price,
123        post_sqrt_price,
124        input_amount,
125        output_amount,
126        input_transfer_fee,
127        output_transfer_fee,
128        lp_fee,
129        protocol_fee,
130    }))
131}
132
133/// Parse Orca Whirlpool LiquidityIncreased event from pre-decoded data
134#[inline(always)]
135pub fn parse_liquidity_increased_from_data(
136    data: &[u8],
137    metadata: EventMetadata,
138) -> Option<DexEvent> {
139    let mut offset = 0;
140
141    let whirlpool = read_pubkey(data, offset)?;
142    offset += 32;
143
144    let position = read_pubkey(data, offset)?;
145    offset += 32;
146
147    let tick_lower_index = read_i32_le(data, offset)?;
148    offset += 4;
149
150    let tick_upper_index = read_i32_le(data, offset)?;
151    offset += 4;
152
153    let liquidity = read_u128_le(data, offset)?;
154    offset += 16;
155
156    let token_a_amount = read_u64_le(data, offset)?;
157    offset += 8;
158
159    let token_b_amount = read_u64_le(data, offset)?;
160    offset += 8;
161
162    let token_a_transfer_fee = read_u64_le(data, offset)?;
163    offset += 8;
164
165    let token_b_transfer_fee = read_u64_le(data, offset)?;
166
167    Some(DexEvent::OrcaWhirlpoolLiquidityIncreased(OrcaWhirlpoolLiquidityIncreasedEvent {
168        metadata,
169        whirlpool,
170        position,
171        tick_lower_index,
172        tick_upper_index,
173        liquidity,
174        token_a_amount,
175        token_b_amount,
176        token_a_transfer_fee,
177        token_b_transfer_fee,
178    }))
179}
180
181/// Parse Orca Whirlpool LiquidityDecreased event from pre-decoded data
182#[inline(always)]
183pub fn parse_liquidity_decreased_from_data(
184    data: &[u8],
185    metadata: EventMetadata,
186) -> Option<DexEvent> {
187    let mut offset = 0;
188
189    let whirlpool = read_pubkey(data, offset)?;
190    offset += 32;
191
192    let position = read_pubkey(data, offset)?;
193    offset += 32;
194
195    let tick_lower_index = read_i32_le(data, offset)?;
196    offset += 4;
197
198    let tick_upper_index = read_i32_le(data, offset)?;
199    offset += 4;
200
201    let liquidity = read_u128_le(data, offset)?;
202    offset += 16;
203
204    let token_a_amount = read_u64_le(data, offset)?;
205    offset += 8;
206
207    let token_b_amount = read_u64_le(data, offset)?;
208    offset += 8;
209
210    let token_a_transfer_fee = read_u64_le(data, offset)?;
211    offset += 8;
212
213    let token_b_transfer_fee = read_u64_le(data, offset)?;
214
215    Some(DexEvent::OrcaWhirlpoolLiquidityDecreased(OrcaWhirlpoolLiquidityDecreasedEvent {
216        metadata,
217        whirlpool,
218        position,
219        tick_lower_index,
220        tick_upper_index,
221        liquidity,
222        token_a_amount,
223        token_b_amount,
224        token_a_transfer_fee,
225        token_b_transfer_fee,
226    }))
227}
228
229/// Parse Orca Whirlpool PoolInitialized event from pre-decoded data
230#[inline(always)]
231pub fn parse_pool_initialized_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
232    let mut offset = 0;
233
234    let whirlpool = read_pubkey(data, offset)?;
235    offset += 32;
236
237    let whirlpools_config = read_pubkey(data, offset)?;
238    offset += 32;
239
240    let token_mint_a = read_pubkey(data, offset)?;
241    offset += 32;
242
243    let token_mint_b = read_pubkey(data, offset)?;
244    offset += 32;
245
246    let tick_spacing = read_u16_le(data, offset)?;
247    offset += 2;
248
249    let token_program_a = read_pubkey(data, offset)?;
250    offset += 32;
251
252    let token_program_b = read_pubkey(data, offset)?;
253    offset += 32;
254
255    let decimals_a = read_u8(data, offset)?;
256    offset += 1;
257
258    let decimals_b = read_u8(data, offset)?;
259    offset += 1;
260
261    let initial_sqrt_price = read_u128_le(data, offset)?;
262
263    Some(DexEvent::OrcaWhirlpoolPoolInitialized(OrcaWhirlpoolPoolInitializedEvent {
264        metadata,
265        whirlpool,
266        whirlpools_config,
267        token_mint_a,
268        token_mint_b,
269        tick_spacing,
270        token_program_a,
271        token_program_b,
272        decimals_a,
273        decimals_b,
274        initial_sqrt_price,
275    }))
276}
277
278/// 解析 Traded 事件
279fn parse_traded_event(
280    data: &[u8],
281    signature: Signature,
282    slot: u64,
283    tx_index: u64,
284    block_time_us: Option<i64>,
285    grpc_recv_us: i64,
286) -> Option<DexEvent> {
287    let mut offset = 0;
288
289    let whirlpool = read_pubkey(data, offset)?;
290    offset += 32;
291
292    let a_to_b = read_bool(data, offset)?;
293    offset += 1;
294
295    let pre_sqrt_price = read_u128_le(data, offset)?;
296    offset += 16;
297
298    let post_sqrt_price = read_u128_le(data, offset)?;
299    offset += 16;
300
301    let input_amount = read_u64_le(data, offset)?;
302    offset += 8;
303
304    let output_amount = read_u64_le(data, offset)?;
305    offset += 8;
306
307    let input_transfer_fee = read_u64_le(data, offset)?;
308    offset += 8;
309
310    let output_transfer_fee = read_u64_le(data, offset)?;
311    offset += 8;
312
313    let lp_fee = read_u64_le(data, offset)?;
314    offset += 8;
315
316    let protocol_fee = read_u64_le(data, offset)?;
317
318    let metadata =
319        create_metadata_simple(signature, slot, tx_index, block_time_us, whirlpool, grpc_recv_us);
320
321    Some(DexEvent::OrcaWhirlpoolSwap(OrcaWhirlpoolSwapEvent {
322        metadata,
323
324        // IDL TradedEvent 事件字段
325        whirlpool,
326        a_to_b,
327        pre_sqrt_price,
328        post_sqrt_price,
329        input_amount,
330        output_amount,
331        input_transfer_fee,
332        output_transfer_fee,
333        lp_fee,
334        protocol_fee,
335        // 暂时注释,以后会用,AI禁止改动
336        // 指令参数字段
337        // amount: input_amount,
338        // other_amount_threshold: output_amount,
339        // sqrt_price_limit: pre_sqrt_price,
340        // amount_specified_is_input: true,
341
342        // 指令账户字段
343        // token_authority: solana_sdk::pubkey::Pubkey::default(),
344        // token_owner_account_a: solana_sdk::pubkey::Pubkey::default(),
345        // token_vault_a: solana_sdk::pubkey::Pubkey::default(),
346        // token_owner_account_b: solana_sdk::pubkey::Pubkey::default(),
347        // token_vault_b: solana_sdk::pubkey::Pubkey::default(),
348        // tick_array_0: solana_sdk::pubkey::Pubkey::default(),
349        // tick_array_1: solana_sdk::pubkey::Pubkey::default(),
350        // tick_array_2: solana_sdk::pubkey::Pubkey::default(),
351    }))
352}
353
354/// 解析 Liquidity Increased 事件
355fn parse_liquidity_increased_event(
356    data: &[u8],
357    signature: Signature,
358    slot: u64,
359    tx_index: u64,
360    block_time_us: Option<i64>,
361    grpc_recv_us: i64,
362) -> Option<DexEvent> {
363    let mut offset = 0;
364
365    let whirlpool = read_pubkey(data, offset)?;
366    offset += 32;
367
368    let position = read_pubkey(data, offset)?;
369    offset += 32;
370
371    let tick_lower_index = read_i32_le(data, offset)?;
372    offset += 4;
373
374    let tick_upper_index = read_i32_le(data, offset)?;
375    offset += 4;
376
377    let liquidity = read_u128_le(data, offset)?;
378    offset += 16;
379
380    let token_a_amount = read_u64_le(data, offset)?;
381    offset += 8;
382
383    let token_b_amount = read_u64_le(data, offset)?;
384    offset += 8;
385
386    let token_a_transfer_fee = read_u64_le(data, offset)?;
387    offset += 8;
388
389    let token_b_transfer_fee = read_u64_le(data, offset)?;
390
391    let metadata =
392        create_metadata_simple(signature, slot, tx_index, block_time_us, whirlpool, grpc_recv_us);
393
394    Some(DexEvent::OrcaWhirlpoolLiquidityIncreased(OrcaWhirlpoolLiquidityIncreasedEvent {
395        metadata,
396        whirlpool,
397        position,
398        tick_lower_index,
399        tick_upper_index,
400        liquidity,
401        token_a_amount,
402        token_b_amount,
403        token_a_transfer_fee,
404        token_b_transfer_fee,
405    }))
406}
407
408/// 解析 Liquidity Decreased 事件
409fn parse_liquidity_decreased_event(
410    data: &[u8],
411    signature: Signature,
412    slot: u64,
413    tx_index: u64,
414    block_time_us: Option<i64>,
415    grpc_recv_us: i64,
416) -> Option<DexEvent> {
417    let mut offset = 0;
418
419    let whirlpool = read_pubkey(data, offset)?;
420    offset += 32;
421
422    let position = read_pubkey(data, offset)?;
423    offset += 32;
424
425    let tick_lower_index = read_i32_le(data, offset)?;
426    offset += 4;
427
428    let tick_upper_index = read_i32_le(data, offset)?;
429    offset += 4;
430
431    let liquidity = read_u128_le(data, offset)?;
432    offset += 16;
433
434    let token_a_amount = read_u64_le(data, offset)?;
435    offset += 8;
436
437    let token_b_amount = read_u64_le(data, offset)?;
438    offset += 8;
439
440    let token_a_transfer_fee = read_u64_le(data, offset)?;
441    offset += 8;
442
443    let token_b_transfer_fee = read_u64_le(data, offset)?;
444
445    let metadata =
446        create_metadata_simple(signature, slot, tx_index, block_time_us, whirlpool, grpc_recv_us);
447
448    Some(DexEvent::OrcaWhirlpoolLiquidityDecreased(OrcaWhirlpoolLiquidityDecreasedEvent {
449        metadata,
450        whirlpool,
451        position,
452        tick_lower_index,
453        tick_upper_index,
454        liquidity,
455        token_a_amount,
456        token_b_amount,
457        token_a_transfer_fee,
458        token_b_transfer_fee,
459    }))
460}
461
462/// 解析 Pool Initialized 事件
463fn parse_pool_initialized_event(
464    data: &[u8],
465    signature: Signature,
466    slot: u64,
467    tx_index: u64,
468    block_time_us: Option<i64>,
469    grpc_recv_us: i64,
470) -> Option<DexEvent> {
471    let mut offset = 0;
472
473    let whirlpool = read_pubkey(data, offset)?;
474    offset += 32;
475
476    let whirlpools_config = read_pubkey(data, offset)?;
477    offset += 32;
478
479    let token_mint_a = read_pubkey(data, offset)?;
480    offset += 32;
481
482    let token_mint_b = read_pubkey(data, offset)?;
483    offset += 32;
484
485    let tick_spacing = read_u16_le(data, offset)?;
486    offset += 2;
487
488    let token_program_a = read_pubkey(data, offset)?;
489    offset += 32;
490
491    let token_program_b = read_pubkey(data, offset)?;
492    offset += 32;
493
494    let decimals_a = read_u8(data, offset)?;
495    offset += 1;
496
497    let decimals_b = read_u8(data, offset)?;
498    offset += 1;
499
500    let initial_sqrt_price = read_u128_le(data, offset)?;
501
502    let metadata =
503        create_metadata_simple(signature, slot, tx_index, block_time_us, whirlpool, grpc_recv_us);
504
505    Some(DexEvent::OrcaWhirlpoolPoolInitialized(OrcaWhirlpoolPoolInitializedEvent {
506        metadata,
507        whirlpool,
508        whirlpools_config,
509        token_mint_a,
510        token_mint_b,
511        tick_spacing,
512        token_program_a,
513        token_program_b,
514        decimals_a,
515        decimals_b,
516        initial_sqrt_price,
517    }))
518}
519
520/// 解析文本格式日志
521fn parse_text_log(
522    _log: &str,
523    _signature: Signature,
524    _slot: u64,
525    _block_time_us: Option<i64>,
526) -> Option<DexEvent> {
527    // 目前暂不实现文本解析,主要依赖结构化解析
528    None
529}