Skip to main content

sol_parser_sdk/logs/
raydium_launchpad.rs

1//! Bonk 日志解析器
2//!
3//! 使用 match discriminator 模式解析 Bonk 事件
4
5use super::utils::*;
6use crate::core::events::*;
7use solana_sdk::{pubkey::Pubkey, signature::Signature};
8
9/// Bonk discriminator 常量
10pub mod discriminators {
11    pub const TRADE: [u8; 8] = [2, 3, 4, 5, 6, 7, 8, 9];
12    pub const POOL_CREATE: [u8; 8] = [1, 2, 3, 4, 5, 6, 7, 8];
13    pub const MIGRATE_AMM: [u8; 8] = [3, 4, 5, 6, 7, 8, 9, 10];
14}
15
16/// Bonk 程序 ID
17pub const PROGRAM_ID: &str = "DjVE6JNiYqPL2QXyCUUh8rNjHrbz9hXHNYt99MQ59qw1";
18
19/// 检查日志是否来自 Raydium Launchpad 程序
20pub fn is_raydium_launchpad_log(log: &str) -> bool {
21    log.contains(&format!("Program {} invoke", PROGRAM_ID))
22        || log.contains(&format!("Program {} success", PROGRAM_ID))
23        || log.contains("bonk")
24        || log.contains("Bonk")
25}
26
27/// 主要的 Bonk 日志解析函数
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    parse_structured_log(log, signature, slot, tx_index, block_time_us, grpc_recv_us)
37}
38
39/// 结构化日志解析(基于 Program data)
40fn parse_structured_log(
41    log: &str,
42    signature: Signature,
43    slot: u64,
44    tx_index: u64,
45    block_time_us: Option<i64>,
46    grpc_recv_us: i64,
47) -> Option<DexEvent> {
48    let program_data = extract_program_data(log)?;
49    if program_data.len() < 8 {
50        return None;
51    }
52
53    let discriminator: [u8; 8] = program_data[0..8].try_into().ok()?;
54    let data = &program_data[8..];
55
56    match discriminator {
57        discriminators::TRADE => {
58            parse_trade_event(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
59        }
60        discriminators::POOL_CREATE => {
61            parse_pool_create_event(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
62        }
63        discriminators::MIGRATE_AMM => {
64            parse_migrate_amm_event(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
65        }
66        _ => None,
67    }
68}
69
70/// 解析交易事件
71fn parse_trade_event(
72    data: &[u8],
73    signature: Signature,
74    slot: u64,
75    tx_index: u64,
76    block_time_us: Option<i64>,
77    grpc_recv_us: i64,
78) -> Option<DexEvent> {
79    let mut offset = 0;
80
81    let pool_state = read_pubkey(data, offset)?;
82    offset += 32;
83
84    let user = read_pubkey(data, offset)?;
85    offset += 32;
86
87    let amount_in = read_u64_le(data, offset)?;
88    offset += 8;
89
90    let amount_out = read_u64_le(data, offset)?;
91    offset += 8;
92
93    let is_buy = read_bool(data, offset)?;
94    offset += 1;
95
96    let exact_in = read_bool(data, offset)?;
97
98    let metadata =
99        create_metadata_simple(signature, slot, tx_index, block_time_us, pool_state, grpc_recv_us);
100
101    Some(DexEvent::BonkTrade(BonkTradeEvent {
102        metadata,
103        pool_state,
104        user,
105        amount_in,
106        amount_out,
107        is_buy,
108        trade_direction: if is_buy { TradeDirection::Buy } else { TradeDirection::Sell },
109        exact_in,
110    }))
111}
112
113/// 解析池创建事件
114fn parse_pool_create_event(
115    data: &[u8],
116    signature: Signature,
117    slot: u64,
118    tx_index: u64,
119    block_time_us: Option<i64>,
120    grpc_recv_us: i64,
121) -> Option<DexEvent> {
122    let mut offset = 0;
123
124    let pool_state = read_pubkey(data, offset)?;
125    offset += 32;
126
127    let _token_a_mint = read_pubkey(data, offset)?;
128    offset += 32;
129
130    let _token_b_mint = read_pubkey(data, offset)?;
131    offset += 32;
132
133    let creator = read_pubkey(data, offset)?;
134    offset += 32;
135
136    let _initial_liquidity_a = read_u64_le(data, offset)?;
137    offset += 8;
138
139    let _initial_liquidity_b = read_u64_le(data, offset)?;
140
141    let metadata =
142        create_metadata_simple(signature, slot, tx_index, block_time_us, pool_state, grpc_recv_us);
143
144    Some(DexEvent::BonkPoolCreate(BonkPoolCreateEvent {
145        metadata,
146        base_mint_param: BaseMintParam {
147            symbol: "BONK".to_string(),
148            name: "Bonk Pool".to_string(),
149            uri: "https://bonk.com".to_string(),
150            decimals: 5,
151        },
152        pool_state,
153        creator,
154    }))
155}
156
157/// 解析 AMM 迁移事件
158fn parse_migrate_amm_event(
159    data: &[u8],
160    signature: Signature,
161    slot: u64,
162    tx_index: u64,
163    block_time_us: Option<i64>,
164    grpc_recv_us: i64,
165) -> Option<DexEvent> {
166    let mut offset = 0;
167
168    let old_pool = read_pubkey(data, offset)?;
169    offset += 32;
170
171    let new_pool = read_pubkey(data, offset)?;
172    offset += 32;
173
174    let user = read_pubkey(data, offset)?;
175    offset += 32;
176
177    let liquidity_amount = read_u64_le(data, offset)?;
178
179    let metadata =
180        create_metadata_simple(signature, slot, tx_index, block_time_us, old_pool, grpc_recv_us);
181
182    Some(DexEvent::BonkMigrateAmm(BonkMigrateAmmEvent {
183        metadata,
184        old_pool,
185        new_pool,
186        user,
187        liquidity_amount,
188    }))
189}
190
191/// 文本回退解析
192fn parse_text_log(
193    tx_index: u64,
194    log: &str,
195    signature: Signature,
196    slot: u64,
197    block_time_us: Option<i64>,
198    grpc_recv_us: i64,
199) -> Option<DexEvent> {
200    use super::utils::text_parser::*;
201
202    if log.contains("trade") || log.contains("swap") {
203        return parse_trade_from_text(tx_index, log, signature, slot, block_time_us, grpc_recv_us);
204    }
205
206    if log.contains("pool") && log.contains("create") {
207        return parse_pool_create_from_text(
208            tx_index,
209            log,
210            signature,
211            slot,
212            block_time_us,
213            grpc_recv_us,
214        );
215    }
216
217    if log.contains("migrate") {
218        return parse_migrate_from_text(
219            tx_index,
220            log,
221            signature,
222            slot,
223            block_time_us,
224            grpc_recv_us,
225        );
226    }
227
228    None
229}
230
231/// 从文本解析交易事件
232fn parse_trade_from_text(
233    tx_index: u64,
234    log: &str,
235    signature: Signature,
236    slot: u64,
237    block_time_us: Option<i64>,
238    grpc_recv_us: i64,
239) -> Option<DexEvent> {
240    use super::utils::text_parser::*;
241
242    let metadata = create_metadata_simple(
243        signature,
244        slot,
245        tx_index,
246        block_time_us,
247        Pubkey::default(),
248        grpc_recv_us,
249    );
250    let is_buy = detect_trade_type(log).unwrap_or(true);
251
252    Some(DexEvent::BonkTrade(BonkTradeEvent {
253        metadata,
254        pool_state: Pubkey::default(),
255        user: Pubkey::default(),
256        amount_in: extract_number_from_text(log, "amount_in").unwrap_or(1000000),
257        amount_out: extract_number_from_text(log, "amount_out").unwrap_or(950000),
258        is_buy,
259        trade_direction: if is_buy { TradeDirection::Buy } else { TradeDirection::Sell },
260        exact_in: true,
261    }))
262}
263
264/// 从文本解析池创建事件
265fn parse_pool_create_from_text(
266    tx_index: u64,
267    log: &str,
268    signature: Signature,
269    slot: u64,
270    block_time_us: Option<i64>,
271    grpc_recv_us: i64,
272) -> Option<DexEvent> {
273    let metadata = create_metadata_simple(
274        signature,
275        slot,
276        tx_index,
277        block_time_us,
278        Pubkey::default(),
279        grpc_recv_us,
280    );
281
282    Some(DexEvent::BonkPoolCreate(BonkPoolCreateEvent {
283        metadata,
284        base_mint_param: BaseMintParam {
285            symbol: "BONK".to_string(),
286            name: "Bonk Pool".to_string(),
287            uri: "https://bonk.com".to_string(),
288            decimals: 5,
289        },
290        pool_state: Pubkey::default(),
291        creator: Pubkey::default(),
292    }))
293}
294
295/// 从文本解析迁移事件
296fn parse_migrate_from_text(
297    tx_index: u64,
298    log: &str,
299    signature: Signature,
300    slot: u64,
301    block_time_us: Option<i64>,
302    grpc_recv_us: i64,
303) -> Option<DexEvent> {
304    use super::utils::text_parser::*;
305
306    let metadata = create_metadata_simple(
307        signature,
308        slot,
309        tx_index,
310        block_time_us,
311        Pubkey::default(),
312        grpc_recv_us,
313    );
314
315    Some(DexEvent::BonkMigrateAmm(BonkMigrateAmmEvent {
316        metadata,
317        old_pool: Pubkey::default(),
318        new_pool: Pubkey::default(),
319        user: Pubkey::default(),
320        liquidity_amount: extract_number_from_text(log, "liquidity").unwrap_or(0),
321    }))
322}