Skip to main content

sol_parser_sdk/logs/
raydium_launchpad.rs

1//! Raydium Launchpad 日志解析器
2//!
3//! 事件类型名称沿用历史的 `Bonk*`,底层按 `idl/raydium_launchpad.json`
4//! 的真实 event discriminator 和 Borsh 布局解析。
5
6use super::utils::*;
7use crate::core::events::*;
8use solana_sdk::{pubkey::Pubkey, signature::Signature};
9
10/// Raydium Launchpad event discriminators from `idl/raydium_launchpad.json`.
11pub mod discriminators {
12    pub const CLAIM_VESTED: [u8; 8] = [21, 194, 114, 87, 120, 211, 226, 32];
13    pub const CREATE_VESTING: [u8; 8] = [150, 152, 11, 179, 52, 210, 191, 125];
14    pub const POOL_CREATE: [u8; 8] = [151, 215, 226, 9, 118, 161, 115, 174];
15    pub const TRADE: [u8; 8] = [189, 219, 127, 211, 78, 230, 97, 238];
16}
17
18/// Raydium Launchpad 程序 ID
19pub const PROGRAM_ID: &str = "LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj";
20
21/// 检查日志是否来自 Raydium Launchpad 程序
22pub fn is_raydium_launchpad_log(log: &str) -> bool {
23    log.contains(&format!("Program {} invoke", PROGRAM_ID))
24        || log.contains(&format!("Program {} success", PROGRAM_ID))
25}
26
27/// 主要的 Raydium Launchpad 日志解析函数
28pub fn parse_log(
29    log: &str,
30    signature: Signature,
31    slot: u64,
32    tx_index: u64,
33    block_time_us: Option<i64>,
34    grpc_recv_us: i64,
35) -> Option<DexEvent> {
36    let program_data = extract_program_data(log)?;
37    if program_data.len() < 8 {
38        return None;
39    }
40
41    let discriminator: [u8; 8] = program_data[0..8].try_into().ok()?;
42    let data = &program_data[8..];
43    let metadata = EventMetadata {
44        signature,
45        slot,
46        tx_index,
47        block_time_us: block_time_us.unwrap_or(0),
48        grpc_recv_us,
49        recent_blockhash: None,
50    };
51
52    match discriminator {
53        discriminators::TRADE => parse_trade_from_data(data, metadata),
54        discriminators::POOL_CREATE => parse_pool_create_from_data(data, metadata),
55        _ => None,
56    }
57}
58
59/// Parse Raydium Launchpad TradeEvent from pre-decoded event data.
60#[inline]
61pub fn parse_trade_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
62    let pool_state = read_pubkey(data, 0)?;
63    let amount_in = read_u64_le(data, 88)?;
64    let amount_out = read_u64_le(data, 96)?;
65    let trade_direction = *data.get(136)?;
66    let exact_in = read_bool(data, 138)?;
67    let is_buy = trade_direction == 0;
68
69    Some(DexEvent::BonkTrade(BonkTradeEvent {
70        metadata,
71        pool_state,
72        user: Pubkey::default(),
73        amount_in,
74        amount_out,
75        is_buy,
76        trade_direction: if is_buy { TradeDirection::Buy } else { TradeDirection::Sell },
77        exact_in,
78    }))
79}
80
81/// Parse Raydium Launchpad PoolCreateEvent from pre-decoded event data.
82#[inline]
83pub fn parse_pool_create_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
84    let mut offset = 0usize;
85    let pool_state = read_pubkey(data, offset)?;
86    offset += 32;
87    let creator = read_pubkey(data, offset)?;
88    offset += 32;
89    let _config = read_pubkey(data, offset)?;
90    offset += 32;
91    let base_mint_param = parse_mint_params(data, &mut offset)?;
92
93    Some(DexEvent::BonkPoolCreate(BonkPoolCreateEvent {
94        metadata,
95        base_mint_param,
96        pool_state,
97        creator,
98    }))
99}
100
101fn parse_mint_params(data: &[u8], offset: &mut usize) -> Option<BaseMintParam> {
102    let decimals = *data.get(*offset)?;
103    *offset += 1;
104    let name = read_borsh_string(data, offset)?;
105    let symbol = read_borsh_string(data, offset)?;
106    let uri = read_borsh_string(data, offset)?;
107    Some(BaseMintParam { symbol, name, uri, decimals })
108}
109
110fn read_borsh_string(data: &[u8], offset: &mut usize) -> Option<String> {
111    let len = read_u32_le(data, *offset)? as usize;
112    *offset += 4;
113    let end = (*offset).checked_add(len)?;
114    let bytes = data.get(*offset..end)?;
115    *offset = end;
116    std::str::from_utf8(bytes).ok().map(str::to_owned)
117}
118
119#[inline]
120fn read_u32_le(data: &[u8], offset: usize) -> Option<u32> {
121    let bytes = data.get(offset..offset + 4)?;
122    Some(u32::from_le_bytes(bytes.try_into().ok()?))
123}