1use super::program_ids;
7use super::utils::*;
8use crate::core::events::*;
9use solana_sdk::{pubkey::Pubkey, signature::Signature};
10
11pub mod discriminators {
13 pub const BUY_EXACT_IN: [u8; 8] = [250, 234, 13, 123, 213, 156, 19, 236];
14 pub const BUY_EXACT_OUT: [u8; 8] = [24, 211, 116, 40, 105, 3, 153, 56];
15 pub const INITIALIZE: [u8; 8] = [175, 175, 109, 31, 13, 152, 155, 237];
16 pub const INITIALIZE_V2: [u8; 8] = [67, 153, 175, 39, 218, 16, 38, 32];
17 pub const INITIALIZE_WITH_TOKEN_2022: [u8; 8] = [37, 190, 126, 222, 44, 154, 171, 17];
18 pub const MIGRATE_TO_AMM: [u8; 8] = [207, 82, 192, 145, 254, 207, 145, 223];
19 pub const MIGRATE_TO_CPSWAP: [u8; 8] = [136, 92, 200, 103, 28, 218, 144, 140];
20 pub const SELL_EXACT_IN: [u8; 8] = [149, 39, 222, 155, 211, 124, 152, 26];
21 pub const SELL_EXACT_OUT: [u8; 8] = [95, 200, 71, 34, 8, 9, 11, 166];
22}
23
24pub const PROGRAM_ID_PUBKEY: Pubkey = program_ids::RAYDIUM_LAUNCHLAB_PROGRAM_ID;
26
27pub fn parse_instruction(
29 instruction_data: &[u8],
30 accounts: &[Pubkey],
31 signature: Signature,
32 slot: u64,
33 tx_index: u64,
34 block_time_us: Option<i64>,
35) -> Option<DexEvent> {
36 if instruction_data.len() < 8 {
37 return None;
38 }
39
40 let discriminator: [u8; 8] = instruction_data[0..8].try_into().ok()?;
41 let data = &instruction_data[8..];
42
43 match discriminator {
44 discriminators::BUY_EXACT_IN => parse_trade_instruction(
45 data,
46 accounts,
47 signature,
48 slot,
49 tx_index,
50 block_time_us,
51 true,
52 true,
53 ),
54 discriminators::BUY_EXACT_OUT => parse_trade_instruction(
55 data,
56 accounts,
57 signature,
58 slot,
59 tx_index,
60 block_time_us,
61 true,
62 false,
63 ),
64 discriminators::SELL_EXACT_IN => parse_trade_instruction(
65 data,
66 accounts,
67 signature,
68 slot,
69 tx_index,
70 block_time_us,
71 false,
72 true,
73 ),
74 discriminators::SELL_EXACT_OUT => parse_trade_instruction(
75 data,
76 accounts,
77 signature,
78 slot,
79 tx_index,
80 block_time_us,
81 false,
82 false,
83 ),
84 discriminators::INITIALIZE
85 | discriminators::INITIALIZE_V2
86 | discriminators::INITIALIZE_WITH_TOKEN_2022 => {
87 parse_pool_create_instruction(data, accounts, signature, slot, tx_index, block_time_us)
88 }
89 discriminators::MIGRATE_TO_AMM | discriminators::MIGRATE_TO_CPSWAP => None,
92 _ => None,
93 }
94}
95
96fn parse_trade_instruction(
100 data: &[u8],
101 accounts: &[Pubkey],
102 signature: Signature,
103 slot: u64,
104 tx_index: u64,
105 block_time_us: Option<i64>,
106 is_buy: bool,
107 exact_in: bool,
108) -> Option<DexEvent> {
109 let first_amount = read_u64_le(data, 0)?;
110 let second_amount = read_u64_le(data, 8)?;
111
112 let (amount_in, amount_out) =
113 if exact_in { (first_amount, second_amount) } else { (second_amount, first_amount) };
114
115 let pool_state = get_account(accounts, 4)?;
116 let metadata = create_metadata_simple(signature, slot, tx_index, block_time_us, pool_state);
117
118 Some(DexEvent::RaydiumLaunchlabTrade(RaydiumLaunchlabTradeEvent {
119 metadata,
120 pool_state,
121 user: get_account(accounts, 0).unwrap_or_default(),
122 amount_in,
123 amount_out,
124 is_buy,
125 trade_direction: if is_buy { TradeDirection::Buy } else { TradeDirection::Sell },
126 exact_in,
127 }))
128}
129
130fn parse_pool_create_instruction(
132 data: &[u8],
133 accounts: &[Pubkey],
134 signature: Signature,
135 slot: u64,
136 tx_index: u64,
137 block_time_us: Option<i64>,
138) -> Option<DexEvent> {
139 let base_mint_param = parse_mint_params(data)?;
140
141 let pool_state = get_account(accounts, 5)?;
142 let metadata = create_metadata_simple(signature, slot, tx_index, block_time_us, pool_state);
143
144 Some(DexEvent::RaydiumLaunchlabPoolCreate(RaydiumLaunchlabPoolCreateEvent {
145 metadata,
146 base_mint_param,
147 pool_state,
148 creator: get_account(accounts, 1).unwrap_or_default(),
149 }))
150}
151
152fn parse_mint_params(data: &[u8]) -> Option<BaseMintParam> {
153 let mut offset = 0usize;
154 let decimals = *data.get(offset)?;
155 offset += 1;
156 let name = read_borsh_string(data, &mut offset)?;
157 let symbol = read_borsh_string(data, &mut offset)?;
158 let uri = read_borsh_string(data, &mut offset)?;
159 Some(BaseMintParam { symbol, name, uri, decimals })
160}
161
162fn read_borsh_string(data: &[u8], offset: &mut usize) -> Option<String> {
163 let len = read_u32_le(data, *offset)? as usize;
164 *offset += 4;
165 let end = (*offset).checked_add(len)?;
166 let bytes = data.get(*offset..end)?;
167 *offset = end;
168 std::str::from_utf8(bytes).ok().map(str::to_owned)
169}
170
171#[inline]
172fn read_u32_le(data: &[u8], offset: usize) -> Option<u32> {
173 let bytes = data.get(offset..offset + 4)?;
174 Some(u32::from_le_bytes(bytes.try_into().ok()?))
175}