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