sol_parser_sdk/logs/
raydium_launchpad.rs

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