sol_parser_sdk/logs/
raydium_launchlab.rs1use super::utils::*;
7use crate::core::events::*;
8use solana_sdk::{pubkey::Pubkey, signature::Signature};
9
10pub 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
18pub const PROGRAM_ID: &str = "LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj";
20
21pub fn is_raydium_launchlab_log(log: &str) -> bool {
23 log.contains(&format!("Program {} invoke", PROGRAM_ID))
24 || log.contains(&format!("Program {} success", PROGRAM_ID))
25}
26
27pub 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#[inline]
61pub fn parse_trade_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
62 const TRADE_EVENT_LEN: usize = 32 + 13 * 8 + 3;
63 if data.len() != TRADE_EVENT_LEN {
64 return None;
65 }
66
67 let pool_state = read_pubkey(data, 0)?;
68 let amount_in = read_u64_le(data, 88)?;
69 let amount_out = read_u64_le(data, 96)?;
70 let trade_direction = *data.get(136)?;
71 if trade_direction > 1 {
72 return None;
73 }
74 let pool_status = *data.get(137)?;
75 if pool_status > 2 {
76 return None;
77 }
78 let exact_in_raw = *data.get(138)?;
79 if exact_in_raw > 1 {
80 return None;
81 }
82 let exact_in = exact_in_raw == 1;
83 let is_buy = trade_direction == 0;
84
85 Some(DexEvent::RaydiumLaunchlabTrade(RaydiumLaunchlabTradeEvent {
86 metadata,
87 pool_state,
88 user: Pubkey::default(),
89 amount_in,
90 amount_out,
91 is_buy,
92 trade_direction: if is_buy { TradeDirection::Buy } else { TradeDirection::Sell },
93 exact_in,
94 }))
95}
96
97#[inline]
99pub fn parse_pool_create_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
100 let mut offset = 0usize;
101 let pool_state = read_pubkey(data, offset)?;
102 offset += 32;
103 let creator = read_pubkey(data, offset)?;
104 offset += 32;
105 let _config = read_pubkey(data, offset)?;
106 offset += 32;
107 let base_mint_param = parse_mint_params(data, &mut offset)?;
108
109 Some(DexEvent::RaydiumLaunchlabPoolCreate(RaydiumLaunchlabPoolCreateEvent {
110 metadata,
111 base_mint_param,
112 pool_state,
113 creator,
114 }))
115}
116
117fn parse_mint_params(data: &[u8], offset: &mut usize) -> Option<BaseMintParam> {
118 let decimals = *data.get(*offset)?;
119 *offset += 1;
120 let name = read_borsh_string(data, offset)?;
121 let symbol = read_borsh_string(data, offset)?;
122 let uri = read_borsh_string(data, offset)?;
123 Some(BaseMintParam { symbol, name, uri, decimals })
124}
125
126fn read_borsh_string(data: &[u8], offset: &mut usize) -> Option<String> {
127 let len = read_u32_le(data, *offset)? as usize;
128 *offset += 4;
129 let end = (*offset).checked_add(len)?;
130 let bytes = data.get(*offset..end)?;
131 *offset = end;
132 std::str::from_utf8(bytes).ok().map(str::to_owned)
133}
134
135#[inline]
136fn read_u32_le(data: &[u8], offset: usize) -> Option<u32> {
137 let bytes = data.get(offset..offset + 4)?;
138 Some(u32::from_le_bytes(bytes.try_into().ok()?))
139}