Skip to main content

sol_parser_sdk/instr/
utils.rs

1//! 指令解析通用工具函数
2
3use solana_sdk::{pubkey::Pubkey, signature::Signature};
4use crate::core::events::EventMetadata;
5use yellowstone_grpc_proto::prelude::{Transaction, TransactionStatusMeta};
6
7/// 创建事件元数据的通用函数
8pub fn create_metadata(
9    signature: Signature,
10    slot: u64,
11    tx_index: u64,
12    block_time_us: i64,
13    grpc_recv_us: i64,
14) -> EventMetadata {
15    EventMetadata {
16        signature,
17        slot,
18        tx_index,
19        block_time_us,
20        grpc_recv_us,
21    }
22}
23
24/// 创建事件元数据的兼容性函数(用于指令解析)
25#[inline(always)]
26pub fn create_metadata_simple(
27    signature: Signature,
28    slot: u64,
29    tx_index: u64,
30    block_time_us: Option<i64>,
31    _program_id: Pubkey,
32) -> EventMetadata {
33    let current_time = now_us();
34
35    EventMetadata {
36        signature,
37        slot,
38        tx_index,
39        block_time_us: block_time_us.unwrap_or(0),
40        grpc_recv_us: current_time,
41    }
42}
43
44/// 从指令数据中读取 u64(小端序)- SIMD 优化
45#[inline(always)]
46pub fn read_u64_le(data: &[u8], offset: usize) -> Option<u64> {
47    data.get(offset..offset + 8)
48        .map(|slice| u64::from_le_bytes(slice.try_into().unwrap()))
49}
50
51/// 从指令数据中读取 u32(小端序)- SIMD 优化
52#[inline(always)]
53pub fn read_u32_le(data: &[u8], offset: usize) -> Option<u32> {
54    data.get(offset..offset + 4)
55        .map(|slice| u32::from_le_bytes(slice.try_into().unwrap()))
56}
57
58/// 从指令数据中读取 u16(小端序)- SIMD 优化
59#[inline(always)]
60pub fn read_u16_le(data: &[u8], offset: usize) -> Option<u16> {
61    data.get(offset..offset + 2)
62        .map(|slice| u16::from_le_bytes(slice.try_into().unwrap()))
63}
64
65/// 从指令数据中读取 u8
66#[inline(always)]
67pub fn read_u8(data: &[u8], offset: usize) -> Option<u8> {
68    data.get(offset).copied()
69}
70
71/// 从指令数据中读取 i32(小端序)- SIMD 优化
72#[inline(always)]
73pub fn read_i32_le(data: &[u8], offset: usize) -> Option<i32> {
74    data.get(offset..offset + 4)
75        .map(|slice| i32::from_le_bytes(slice.try_into().unwrap()))
76}
77
78/// 从指令数据中读取 u128(小端序)- SIMD 优化
79#[inline(always)]
80pub fn read_u128_le(data: &[u8], offset: usize) -> Option<u128> {
81    data.get(offset..offset + 16)
82        .map(|slice| u128::from_le_bytes(slice.try_into().unwrap()))
83}
84
85/// 从指令数据中读取布尔值
86#[inline(always)]
87pub fn read_bool(data: &[u8], offset: usize) -> Option<bool> {
88    data.get(offset).map(|&b| b != 0)
89}
90
91/// 从指令数据中读取公钥 - SIMD 优化
92#[inline(always)]
93pub fn read_pubkey(data: &[u8], offset: usize) -> Option<Pubkey> {
94    data.get(offset..offset + 32)
95        .and_then(|slice| Pubkey::try_from(slice).ok())
96}
97
98/// 从账户列表中获取账户
99#[inline(always)]
100pub fn get_account(accounts: &[Pubkey], index: usize) -> Option<Pubkey> {
101    accounts.get(index).copied()
102}
103
104/// 计算滑点基点
105pub fn calculate_slippage_bps(amount_in: u64, amount_out_min: u64) -> u16 {
106    if amount_in == 0 {
107        return 0;
108    }
109
110    // 简化的滑点计算
111    let slippage = ((amount_in.saturating_sub(amount_out_min)) * 10000) / amount_in;
112    slippage.min(10000) as u16
113}
114
115/// 计算价格影响基点
116pub fn calculate_price_impact_bps(_amount_in: u64, amount_out: u64, expected_out: u64) -> u16 {
117    if expected_out == 0 {
118        return 0;
119    }
120
121    let impact = ((expected_out.saturating_sub(amount_out)) * 10000) / expected_out;
122    impact.min(10000) as u16
123}
124
125/// Read bytes from instruction data
126pub fn read_bytes(data: &[u8], offset: usize, length: usize) -> Option<&[u8]> {
127    if data.len() < offset + length {
128        return None;
129    }
130    Some(&data[offset..offset + length])
131}
132
133/// Read string with 4-byte length prefix (Borsh format)
134/// Returns (string slice, total bytes consumed including length prefix)
135#[inline]
136pub fn read_str_unchecked(data: &[u8], offset: usize) -> Option<(&str, usize)> {
137    if data.len() < offset + 4 {
138        return None;
139    }
140    let len = u32::from_le_bytes(data[offset..offset + 4].try_into().ok()?) as usize;
141    if data.len() < offset + 4 + len {
142        return None;
143    }
144    let string_bytes = &data[offset + 4..offset + 4 + len];
145    let s = std::str::from_utf8(string_bytes).ok()?;
146    Some((s, 4 + len))
147}
148
149/// 从指令数据中读取u64向量(简化版本)
150pub fn read_vec_u64(_data: &[u8], _offset: usize) -> Option<Vec<u64>> {
151    // 简化版本:返回默认的两个元素向量
152    // 实际实现需要根据具体的数据格式来解析
153    Some(vec![0, 0])
154}
155
156/// 快速读取 Pubkey(从字节数组)
157#[inline(always)]
158pub fn read_pubkey_fast(bytes: &[u8]) -> Pubkey {
159    crate::logs::utils::read_pubkey(bytes, 0).unwrap_or_default()
160}
161
162/// 获取指令账户访问器
163/// 返回一个可以通过索引获取 Pubkey 的闭包
164pub fn get_instruction_account_getter<'a>(
165    meta: &'a TransactionStatusMeta,
166    transaction: &'a Option<Transaction>,
167    account_keys: Option<&'a Vec<Vec<u8>>>,
168    // 地址表
169    loaded_writable_addresses: &'a Vec<Vec<u8>>,
170    loaded_readonly_addresses: &'a Vec<Vec<u8>>,
171    index: &(i32, i32), // (outer_index, inner_index)
172) -> Option<impl Fn(usize) -> Pubkey + 'a> {
173    // 1. 获取指令的账户索引数组
174    let accounts = if index.1 >= 0 {
175        // 内层指令 - 使用二分查找优化 (inner_instructions 按 index 升序排列)
176        let outer_idx = index.0 as u32;
177        meta.inner_instructions
178            .binary_search_by_key(&outer_idx, |i| i.index)
179            .ok()
180            .and_then(|pos| meta.inner_instructions.get(pos))
181            .or_else(|| {
182                // 回退到线性查找(以防数据未排序)
183                meta.inner_instructions.iter().find(|i| i.index == outer_idx)
184            })?
185            .instructions
186            .get(index.1 as usize)?
187            .accounts
188            .as_slice()
189    } else {
190        // 外层指令
191        transaction
192            .as_ref()?
193            .message
194            .as_ref()?
195            .instructions
196            .get(index.0 as usize)?
197            .accounts
198            .as_slice()
199    };
200
201    // 2. 创建高性能的账户查找闭包
202    Some(move |acc_index: usize| -> Pubkey {
203        // 获取账户在交易中的索引
204        let account_index = match accounts.get(acc_index) {
205            Some(&idx) => idx as usize,
206            None => return Pubkey::default(),
207        };
208        // 早期返回优化
209        let Some(keys) = account_keys else {
210            return Pubkey::default();
211        };
212        // 主账户列表
213        if let Some(key_bytes) = keys.get(account_index) {
214            return read_pubkey_fast(key_bytes);
215        }
216        // 可写地址
217        let writable_offset = account_index.saturating_sub(keys.len());
218        if let Some(key_bytes) = loaded_writable_addresses.get(writable_offset) {
219            return read_pubkey_fast(key_bytes);
220        }
221        // 只读地址
222        let readonly_offset = writable_offset.saturating_sub(loaded_writable_addresses.len());
223        if let Some(key_bytes) = loaded_readonly_addresses.get(readonly_offset) {
224            return read_pubkey_fast(key_bytes);
225        }
226        Pubkey::default()
227    })
228}
229
230/// 预构建的 inner_instructions 索引,用于 O(1) 查找
231use std::collections::HashMap;
232use crate::core::clock::now_us;
233
234/// InnerInstructions 索引缓存
235pub struct InnerInstructionsIndex<'a> {
236    /// outer_index -> &InnerInstructions
237    index_map: HashMap<u32, &'a yellowstone_grpc_proto::prelude::InnerInstructions>,
238}
239
240impl<'a> InnerInstructionsIndex<'a> {
241    /// 从 TransactionStatusMeta 构建索引
242    #[inline]
243    pub fn new(meta: &'a TransactionStatusMeta) -> Self {
244        let mut index_map = HashMap::with_capacity(meta.inner_instructions.len());
245        for inner in &meta.inner_instructions {
246            index_map.insert(inner.index, inner);
247        }
248        Self { index_map }
249    }
250
251    /// O(1) 查找 inner_instructions
252    #[inline]
253    pub fn get(&self, outer_index: u32) -> Option<&'a yellowstone_grpc_proto::prelude::InnerInstructions> {
254        self.index_map.get(&outer_index).copied()
255    }
256}
257
258/// 使用预构建索引的账户获取器(O(1) 查找)
259pub fn get_instruction_account_getter_indexed<'a>(
260    inner_index: &InnerInstructionsIndex<'a>,
261    transaction: &'a Option<Transaction>,
262    account_keys: Option<&'a Vec<Vec<u8>>>,
263    loaded_writable_addresses: &'a Vec<Vec<u8>>,
264    loaded_readonly_addresses: &'a Vec<Vec<u8>>,
265    index: &(i32, i32),
266) -> Option<impl Fn(usize) -> Pubkey + 'a> {
267    let accounts = if index.1 >= 0 {
268        // O(1) 查找
269        inner_index.get(index.0 as u32)?
270            .instructions
271            .get(index.1 as usize)?
272            .accounts
273            .as_slice()
274    } else {
275        transaction
276            .as_ref()?
277            .message
278            .as_ref()?
279            .instructions
280            .get(index.0 as usize)?
281            .accounts
282            .as_slice()
283    };
284
285    Some(move |acc_index: usize| -> Pubkey {
286        let account_index = match accounts.get(acc_index) {
287            Some(&idx) => idx as usize,
288            None => return Pubkey::default(),
289        };
290        let Some(keys) = account_keys else {
291            return Pubkey::default();
292        };
293        if let Some(key_bytes) = keys.get(account_index) {
294            return read_pubkey_fast(key_bytes);
295        }
296        let writable_offset = account_index.saturating_sub(keys.len());
297        if let Some(key_bytes) = loaded_writable_addresses.get(writable_offset) {
298            return read_pubkey_fast(key_bytes);
299        }
300        let readonly_offset = writable_offset.saturating_sub(loaded_writable_addresses.len());
301        if let Some(key_bytes) = loaded_readonly_addresses.get(readonly_offset) {
302            return read_pubkey_fast(key_bytes);
303        }
304        Pubkey::default()
305    })
306}