Skip to main content

sol_parser_sdk/grpc/
instruction_parser.rs

1//! Instruction 解析器 - 完整支持 instruction + inner instruction
2//!
3//! 设计原则:
4//! - 简洁:单一入口函数,清晰的解析流程
5//! - 高性能:零拷贝,内联优化,并行处理
6//! - 可读性:每个步骤都有明确的注释
7
8use crate::core::{events::*, merger::merge_events};
9use crate::grpc::types::EventTypeFilter;
10use crate::instr::read_pubkey_fast;
11use solana_sdk::pubkey::Pubkey;
12use solana_sdk::signature::Signature;
13use std::collections::HashMap;
14use yellowstone_grpc_proto::prelude::{Transaction, TransactionStatusMeta};
15
16/// 解析交易中的所有指令事件(instruction + inner instruction)
17///
18/// # 解析流程
19/// 1. 解析主指令(outer instructions)- 8字节 discriminator
20/// 2. 解析内部指令(inner instructions)- 16字节 discriminator
21/// 3. 合并相关事件(instruction + inner instruction)
22/// 4. 填充账户上下文
23///
24/// # 性能优化
25/// - 并行解析多个指令(rayon)
26/// - 零拷贝读取指令数据
27/// - 内联优化所有热路径
28/// - 提前过滤不需要的事件
29#[inline]
30pub fn parse_instructions_enhanced(
31    meta: &TransactionStatusMeta,
32    transaction: &Option<Transaction>,
33    sig: Signature,
34    slot: u64,
35    tx_idx: u64,
36    block_us: Option<i64>,
37    grpc_us: i64,
38    filter: Option<&EventTypeFilter>,
39) -> Vec<DexEvent> {
40    let Some(tx) = transaction else { return Vec::new() };
41    let Some(msg) = &tx.message else { return Vec::new() };
42
43    // 提前检查:是否需要解析 instruction(根据 filter)
44    if !should_parse_instructions(filter) {
45        return Vec::new();
46    }
47
48    // 构建账户查找表
49    let keys_len = msg.account_keys.len();
50    let writable_len = meta.loaded_writable_addresses.len();
51    let get_key = |i: usize| -> Option<&Vec<u8>> {
52        if i < keys_len {
53            msg.account_keys.get(i)
54        } else if i < keys_len + writable_len {
55            meta.loaded_writable_addresses.get(i - keys_len)
56        } else {
57            meta.loaded_readonly_addresses.get(i - keys_len - writable_len)
58        }
59    };
60
61    let mut result = Vec::with_capacity(8);
62    let mut invokes: HashMap<Pubkey, Vec<(i32, i32)>> = HashMap::with_capacity(8);
63
64    // 步骤 1: 解析所有主指令
65    for (i, ix) in msg.instructions.iter().enumerate() {
66        let pid = get_key(ix.program_id_index as usize)
67            .map_or(Pubkey::default(), |k| read_pubkey_fast(k));
68
69        invokes.entry(pid).or_default().push((i as i32, -1));
70
71        // 解析主指令(8字节 discriminator)
72        if let Some(event) = parse_outer_instruction(
73            &ix.data,
74            &pid,
75            sig,
76            slot,
77            tx_idx,
78            block_us,
79            grpc_us,
80            &ix.accounts,
81            &get_key,
82            filter,
83        ) {
84            result.push((i, None, event)); // (outer_idx, inner_idx, event)
85        }
86    }
87
88    // 步骤 2: 解析所有 inner instructions
89    for inner in &meta.inner_instructions {
90        let outer_idx = inner.index as usize;
91
92        for (j, inner_ix) in inner.instructions.iter().enumerate() {
93            let pid = get_key(inner_ix.program_id_index as usize)
94                .map_or(Pubkey::default(), |k| read_pubkey_fast(k));
95
96            invokes.entry(pid).or_default().push((outer_idx as i32, j as i32));
97
98            // 解析 inner instruction(16字节 discriminator)
99            if let Some(event) = parse_inner_instruction(
100                &inner_ix.data,
101                &pid,
102                sig,
103                slot,
104                tx_idx,
105                block_us,
106                grpc_us,
107                filter,
108            ) {
109                result.push((outer_idx, Some(j), event)); // (outer_idx, Some(inner_idx), event)
110            }
111        }
112    }
113
114    // 步骤 3: 合并相关事件(instruction + inner instruction)
115    let merged = merge_instruction_events(result);
116
117    // 步骤 3.5: 转换 invokes HashMap 为字符串键(用于 fill_data)
118    let invokes_str: HashMap<&str, Vec<(i32, i32)>> = invokes
119        .iter()
120        .map(|(k, v)| (k.to_string().leak() as &str, v.clone()))
121        .collect();
122
123    // 步骤 4: 填充账户上下文
124    let mut final_result = Vec::with_capacity(merged.len());
125    for mut event in merged {
126        crate::core::account_dispatcher::fill_accounts_with_owned_keys(
127            &mut event,
128            meta,
129            transaction,
130            &invokes,
131        );
132        crate::core::common_filler::fill_data(&mut event, meta, transaction, &invokes_str);
133        final_result.push(event);
134    }
135
136    final_result
137}
138
139// ============================================================================
140// 辅助函数
141// ============================================================================
142
143/// 解析单个主指令(outer instruction)
144///
145/// 主指令使用 8 字节 discriminator
146#[inline(always)]
147fn parse_outer_instruction<'a>(
148    data: &[u8],
149    program_id: &Pubkey,
150    sig: Signature,
151    slot: u64,
152    tx_idx: u64,
153    block_us: Option<i64>,
154    grpc_us: i64,
155    account_indices: &[u8],
156    get_key: &dyn Fn(usize) -> Option<&'a Vec<u8>>,
157    filter: Option<&EventTypeFilter>,
158) -> Option<DexEvent> {
159    // 检查指令数据长度(至少8字节 discriminator)
160    if data.len() < 8 {
161        return None;
162    }
163
164    // 构建账户列表
165    let accounts: Vec<Pubkey> = account_indices
166        .iter()
167        .filter_map(|&idx| get_key(idx as usize).map(|k| read_pubkey_fast(k)))
168        .collect();
169
170    // 调用现有的 instruction 解析器
171    crate::instr::parse_instruction_unified(
172        data,
173        &accounts,
174        sig,
175        slot,
176        tx_idx,
177        block_us,
178        grpc_us,
179        filter,
180        program_id,
181    )
182}
183
184/// 解析单个 inner instruction
185///
186/// Inner instructions 使用 16 字节 discriminator(前8字节是event hash,后8字节是magic)
187#[inline(always)]
188fn parse_inner_instruction(
189    data: &[u8],
190    program_id: &Pubkey,
191    sig: Signature,
192    slot: u64,
193    tx_idx: u64,
194    block_us: Option<i64>,
195    grpc_us: i64,
196    filter: Option<&EventTypeFilter>,
197) -> Option<DexEvent> {
198    // 检查数据长度(至少16字节 discriminator)
199    if data.len() < 16 {
200        return None;
201    }
202
203    let metadata = EventMetadata {
204        signature: sig,
205        slot,
206        tx_index: tx_idx,
207        block_time_us: block_us.unwrap_or(0),
208        grpc_recv_us: grpc_us,
209    };
210
211    // 提取 16 字节 discriminator
212    let mut discriminator = [0u8; 16];
213    discriminator.copy_from_slice(&data[..16]);
214    let inner_data = &data[16..];
215
216    use crate::instr::{all_inner, program_ids, pump_amm_inner, pump_inner, raydium_clmm_inner};
217
218    // 根据 program_id 路由到对应的 inner instruction 解析器
219    if *program_id == program_ids::PUMPFUN_PROGRAM_ID {
220        if let Some(f) = filter {
221            if !f.includes_pumpfun() {
222                return None;
223            }
224        }
225        pump_inner::parse_pumpfun_inner_instruction(&discriminator, inner_data, metadata)
226    } else if *program_id == program_ids::PUMPSWAP_PROGRAM_ID {
227        if let Some(f) = filter {
228            if !f.includes_pumpswap() {
229                return None;
230            }
231        }
232        pump_amm_inner::parse_pumpswap_inner_instruction(&discriminator, inner_data, metadata)
233    } else if *program_id == program_ids::RAYDIUM_CLMM_PROGRAM_ID {
234        raydium_clmm_inner::parse_raydium_clmm_inner_instruction(&discriminator, inner_data, metadata)
235    } else if *program_id == program_ids::RAYDIUM_CPMM_PROGRAM_ID {
236        all_inner::raydium_cpmm::parse(&discriminator, inner_data, metadata)
237    } else if *program_id == program_ids::RAYDIUM_AMM_V4_PROGRAM_ID {
238        all_inner::raydium_amm::parse(&discriminator, inner_data, metadata)
239    } else if *program_id == program_ids::ORCA_WHIRLPOOL_PROGRAM_ID {
240        all_inner::orca::parse(&discriminator, inner_data, metadata)
241    } else if *program_id == program_ids::METEORA_POOLS_PROGRAM_ID {
242        all_inner::meteora_amm::parse(&discriminator, inner_data, metadata)
243    } else if *program_id == program_ids::METEORA_DAMM_V2_PROGRAM_ID {
244        if let Some(f) = filter {
245            if !f.includes_meteora_damm_v2() {
246                return None;
247            }
248        }
249        all_inner::meteora_damm::parse(&discriminator, inner_data, metadata)
250    } else if *program_id == program_ids::BONK_PROGRAM_ID {
251        all_inner::bonk::parse(&discriminator, inner_data, metadata)
252    } else {
253        None
254    }
255}
256
257/// 合并相关的 instruction 和 inner instruction 事件
258///
259/// 合并策略:
260/// 1. 同一个 outer_idx 的 instruction 和 inner instruction 可以合并
261/// 2. Inner instruction 在 outer instruction 之后出现
262/// 3. 合并后返回更完整的事件
263#[inline]
264fn merge_instruction_events(
265    events: Vec<(usize, Option<usize>, DexEvent)>,
266) -> Vec<DexEvent> {
267    if events.is_empty() {
268        return Vec::new();
269    }
270
271    // 按 (outer_idx, inner_idx) 排序,确保顺序:outer -> inner
272    let mut events = events;
273    events.sort_by_key(|(outer, inner, _)| (*outer, inner.unwrap_or(usize::MAX)));
274
275    let mut result = Vec::with_capacity(events.len());
276    let mut pending_outer: Option<(usize, DexEvent)> = None;
277
278    for (outer_idx, inner_idx, event) in events {
279        match inner_idx {
280            None => {
281                // 这是一个 outer instruction
282                // 先处理之前的 pending_outer
283                if let Some((_, outer_event)) = pending_outer.take() {
284                    result.push(outer_event);
285                }
286                // 保存当前的 outer instruction,等待可能的 inner instruction
287                pending_outer = Some((outer_idx, event));
288            }
289            Some(_) => {
290                // 这是一个 inner instruction
291                if let Some((pending_outer_idx, mut outer_event)) = pending_outer.take() {
292                    if pending_outer_idx == outer_idx {
293                        // 合并!
294                        merge_events(&mut outer_event, event);
295                        result.push(outer_event);
296                    } else {
297                        // 不匹配,分别保留
298                        result.push(outer_event);
299                        result.push(event);
300                    }
301                } else {
302                    // 没有 pending outer,直接添加 inner event
303                    result.push(event);
304                }
305            }
306        }
307    }
308
309    // 处理最后一个 pending_outer
310    if let Some((_, outer_event)) = pending_outer {
311        result.push(outer_event);
312    }
313
314    result
315}
316
317/// 检查是否需要解析 instructions(根据 filter)
318#[inline(always)]
319fn should_parse_instructions(filter: Option<&EventTypeFilter>) -> bool {
320    // 如果没有 filter,总是解析
321    let Some(filter) = filter else { return true };
322
323    // 如果 filter.include_only 为空,总是解析
324    let Some(ref include_only) = filter.include_only else { return true };
325
326    // 检查是否包含需要从 instruction 解析的事件类型
327    include_only.iter().any(|t| {
328        use crate::grpc::types::EventType::*;
329        matches!(
330            t,
331            PumpFunMigrate | MeteoraDammV2Swap | MeteoraDammV2AddLiquidity
332                | MeteoraDammV2CreatePosition | MeteoraDammV2ClosePosition
333                | MeteoraDammV2RemoveLiquidity
334        )
335    })
336}
337
338#[cfg(test)]
339mod tests {
340    use super::*;
341
342    #[test]
343    fn test_should_parse_instructions() {
344        // 无 filter - 应该解析
345        assert!(should_parse_instructions(None));
346
347        // 有 filter 但 include_only 为空 - 应该解析
348        let filter = EventTypeFilter { include_only: None, exclude_types: None };
349        assert!(should_parse_instructions(Some(&filter)));
350
351        // 包含需要 instruction 解析的事件类型
352        use crate::grpc::types::EventType;
353        let filter = EventTypeFilter {
354            include_only: Some(vec![EventType::PumpFunMigrate]),
355            exclude_types: None,
356        };
357        assert!(should_parse_instructions(Some(&filter)));
358
359        // 不包含需要 instruction 解析的事件类型
360        let filter = EventTypeFilter {
361            include_only: Some(vec![EventType::PumpFunTrade]),
362            exclude_types: None,
363        };
364        assert!(!should_parse_instructions(Some(&filter)));
365    }
366
367    #[test]
368    fn test_merge_instruction_events() {
369        use solana_sdk::signature::Signature;
370
371        let metadata = EventMetadata {
372            signature: Signature::default(),
373            slot: 100,
374            tx_index: 1,
375            block_time_us: 1000,
376            grpc_recv_us: 2000,
377        };
378
379        // 模拟:outer instruction + inner instruction(应该合并)
380        let outer_event = DexEvent::PumpFunTrade(PumpFunTradeEvent {
381            metadata: metadata.clone(),
382            bonding_curve: Pubkey::new_unique(),
383            ..Default::default()
384        });
385
386        let inner_event = DexEvent::PumpFunTrade(PumpFunTradeEvent {
387            metadata: metadata.clone(),
388            sol_amount: 1000,
389            token_amount: 2000,
390            ..Default::default()
391        });
392
393        let events = vec![
394            (0, None, outer_event),          // outer instruction at index 0
395            (0, Some(0), inner_event),       // inner instruction at index 0
396        ];
397
398        let result = merge_instruction_events(events);
399
400        // 应该合并为1个事件
401        assert_eq!(result.len(), 1);
402
403        // 验证合并结果包含两者的数据
404        if let DexEvent::PumpFunTrade(trade) = &result[0] {
405            assert_eq!(trade.sol_amount, 1000); // 来自 inner
406            assert_eq!(trade.token_amount, 2000); // 来自 inner
407            assert_ne!(trade.bonding_curve, Pubkey::default()); // 来自 outer
408        } else {
409            panic!("Expected PumpFunTrade event");
410        }
411    }
412}