mirror of
https://github.com/0xfnzero/solana-streamer.git
synced 2026-08-09 23:20:57 +00:00
feat: add Compute Budget Program instruction parsing support
Add support for parsing SetComputeUnitLimit and SetComputeUnitPrice instructions from Solana's Compute Budget Program. Update event parser to handle compute budget instructions and bump version to 1.1.3.
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "solana-streamer-sdk"
|
||||
version = "1.1.2"
|
||||
version = "1.1.3"
|
||||
edition = "2021"
|
||||
authors = ["William <byteblock6@gmail.com>", "sgxiang <sgxiang@gmail.com>", "wei <1415121722@qq.com>"]
|
||||
repository = "https://github.com/0xfnzero/solana-streamer"
|
||||
|
||||
@@ -109,14 +109,14 @@ Add the dependency to your `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
# Add to your Cargo.toml
|
||||
solana-streamer-sdk = { path = "./solana-streamer", version = "1.1.2" }
|
||||
solana-streamer-sdk = { path = "./solana-streamer", version = "1.1.3" }
|
||||
```
|
||||
|
||||
### Use crates.io
|
||||
|
||||
```toml
|
||||
# Add to your Cargo.toml
|
||||
solana-streamer-sdk = "1.1.2"
|
||||
solana-streamer-sdk = "1.1.3"
|
||||
```
|
||||
|
||||
## 🔄 Migration Guide
|
||||
|
||||
+2
-2
@@ -108,14 +108,14 @@ git clone https://github.com/0xfnzero/solana-streamer
|
||||
|
||||
```toml
|
||||
# 添加到您的 Cargo.toml
|
||||
solana-streamer-sdk = { path = "./solana-streamer", version = "1.1.2" }
|
||||
solana-streamer-sdk = { path = "./solana-streamer", version = "1.1.3" }
|
||||
```
|
||||
|
||||
### 使用 crates.io
|
||||
|
||||
```toml
|
||||
# 添加到您的 Cargo.toml
|
||||
solana-streamer-sdk = "1.1.2"
|
||||
solana-streamer-sdk = "1.1.3"
|
||||
```
|
||||
|
||||
## 🔄 迁移指南
|
||||
|
||||
@@ -139,6 +139,8 @@ pub enum EventType {
|
||||
|
||||
// Common events
|
||||
BlockMeta,
|
||||
SetComputeUnitLimit,
|
||||
SetComputeUnitPrice,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
@@ -233,6 +235,8 @@ impl fmt::Display for EventType {
|
||||
EventType::TokenAccount => write!(f, "TokenAccount"),
|
||||
EventType::NonceAccount => write!(f, "NonceAccount"),
|
||||
EventType::BlockMeta => write!(f, "BlockMeta"),
|
||||
EventType::SetComputeUnitLimit => write!(f, "SetComputeUnitLimit"),
|
||||
EventType::SetComputeUnitPrice => write!(f, "SetComputeUnitPrice"),
|
||||
EventType::Unknown => write!(f, "Unknown"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,33 @@
|
||||
use crate::streaming::event_parser::common::high_performance_clock::elapsed_micros_since;
|
||||
use crate::streaming::event_parser::common::types::{EventType, ProtocolType};
|
||||
use crate::streaming::event_parser::common::EventMetadata;
|
||||
use crate::streaming::event_parser::core::traits::DexEvent;
|
||||
use crate::streaming::event_parser::protocols::block::block_meta_event::BlockMetaEvent;
|
||||
use borsh::BorshDeserialize;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
|
||||
// Compute Budget Program ID
|
||||
pub const COMPUTE_BUDGET_PROGRAM_ID: Pubkey =
|
||||
solana_sdk::pubkey!("ComputeBudget111111111111111111111111111111");
|
||||
|
||||
/// SetComputeUnitLimit 事件
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)]
|
||||
pub struct SetComputeUnitLimitEvent {
|
||||
#[borsh(skip)]
|
||||
pub metadata: EventMetadata,
|
||||
/// 请求的计算单元数量
|
||||
pub units: u32,
|
||||
}
|
||||
|
||||
/// SetComputeUnitPrice 事件
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)]
|
||||
pub struct SetComputeUnitPriceEvent {
|
||||
#[borsh(skip)]
|
||||
pub metadata: EventMetadata,
|
||||
/// 每个计算单元的价格 (micro-lamports)
|
||||
pub micro_lamports: u64,
|
||||
}
|
||||
|
||||
pub struct CommonEventParser {}
|
||||
|
||||
@@ -15,4 +42,42 @@ impl CommonEventParser {
|
||||
block_meta_event.metadata.handle_us = elapsed_micros_since(recv_us);
|
||||
DexEvent::BlockMetaEvent(block_meta_event)
|
||||
}
|
||||
|
||||
/// 解析 Compute Budget 指令
|
||||
pub fn parse_compute_budget_instruction(
|
||||
instruction_data: &[u8],
|
||||
mut metadata: EventMetadata,
|
||||
) -> Option<DexEvent> {
|
||||
if instruction_data.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// 设置 protocol 为 Common
|
||||
metadata.protocol = ProtocolType::Common;
|
||||
|
||||
// Compute Budget 指令使用单字节判别器
|
||||
match instruction_data[0] {
|
||||
// SetComputeUnitLimit: discriminator = 2
|
||||
2 => {
|
||||
if instruction_data.len() < 5 {
|
||||
return None;
|
||||
}
|
||||
let units = u32::from_le_bytes(instruction_data[1..5].try_into().ok()?);
|
||||
metadata.event_type = EventType::SetComputeUnitLimit;
|
||||
let event = SetComputeUnitLimitEvent { metadata, units };
|
||||
Some(DexEvent::SetComputeUnitLimitEvent(event))
|
||||
}
|
||||
// SetComputeUnitPrice: discriminator = 3
|
||||
3 => {
|
||||
if instruction_data.len() < 9 {
|
||||
return None;
|
||||
}
|
||||
let micro_lamports = u64::from_le_bytes(instruction_data[1..9].try_into().ok()?);
|
||||
metadata.event_type = EventType::SetComputeUnitPrice;
|
||||
let event = SetComputeUnitPriceEvent { metadata, micro_lamports };
|
||||
Some(DexEvent::SetComputeUnitPriceEvent(event))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
use crate::streaming::event_parser::{
|
||||
common::EventMetadata,
|
||||
core::common_event_parser::{CommonEventParser, COMPUTE_BUDGET_PROGRAM_ID},
|
||||
protocols::{
|
||||
bonk::parser as bonk, meteora_damm_v2::parser as meteora_damm_v2, pumpfun::parser as pumpfun,
|
||||
pumpswap::parser as pumpswap, raydium_amm_v4::parser as raydium_amm_v4,
|
||||
@@ -191,6 +192,28 @@ impl EventDispatcher {
|
||||
}
|
||||
}
|
||||
|
||||
/// 检查是否为 Compute Budget Program
|
||||
#[inline]
|
||||
pub fn is_compute_budget_program(program_id: &Pubkey) -> bool {
|
||||
program_id == &COMPUTE_BUDGET_PROGRAM_ID
|
||||
}
|
||||
|
||||
/// 解析 Compute Budget 指令
|
||||
///
|
||||
/// # 参数
|
||||
/// - `instruction_data`: 指令数据
|
||||
/// - `metadata`: 事件元数据
|
||||
///
|
||||
/// # 返回
|
||||
/// 解析成功返回 `Some(DexEvent)`,否则返回 `None`
|
||||
#[inline]
|
||||
pub fn dispatch_compute_budget_instruction(
|
||||
instruction_data: &[u8],
|
||||
metadata: EventMetadata,
|
||||
) -> Option<DexEvent> {
|
||||
CommonEventParser::parse_compute_budget_instruction(instruction_data, metadata)
|
||||
}
|
||||
|
||||
/// 获取指定协议的 program_id
|
||||
#[inline]
|
||||
pub fn get_program_id(protocol: Protocol) -> Pubkey {
|
||||
|
||||
@@ -325,28 +325,13 @@ impl EventParser {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// 使用 EventDispatcher 匹配协议
|
||||
let protocol = match EventDispatcher::match_protocol_by_program_id(&program_id) {
|
||||
Some(p) => p,
|
||||
None => return Ok(()),
|
||||
};
|
||||
let is_cu_program = EventDispatcher::is_compute_budget_program(&program_id);
|
||||
|
||||
// 检查指令数据长度(至少需要 8 字节的 discriminator)
|
||||
if instruction.data.len() < 8 {
|
||||
if !is_cu_program && instruction.data.len() < 8 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// 提取 discriminator 和数据
|
||||
let instruction_discriminator = &instruction.data[..8];
|
||||
let instruction_data = &instruction.data[8..];
|
||||
|
||||
// 构建账户公钥列表
|
||||
let account_pubkeys: Vec<Pubkey> = instruction
|
||||
.accounts
|
||||
.iter()
|
||||
.filter_map(|&idx| accounts.get(idx as usize).copied())
|
||||
.collect();
|
||||
|
||||
// 创建元数据
|
||||
let timestamp = block_time.unwrap_or(Timestamp { seconds: 0, nanos: 0 });
|
||||
let block_time_ms = timestamp.seconds * 1000 + (timestamp.nanos as i64) / 1_000_000;
|
||||
@@ -364,6 +349,33 @@ impl EventParser {
|
||||
transaction_index,
|
||||
);
|
||||
|
||||
if is_cu_program {
|
||||
if let Some(event) = EventDispatcher::dispatch_compute_budget_instruction(
|
||||
&instruction.data,
|
||||
metadata.clone(),
|
||||
) {
|
||||
callback(&event);
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// 使用 EventDispatcher 匹配协议
|
||||
let protocol = match EventDispatcher::match_protocol_by_program_id(&program_id) {
|
||||
Some(p) => p,
|
||||
None => return Ok(()),
|
||||
};
|
||||
|
||||
// 提取 discriminator 和数据
|
||||
let instruction_discriminator = &instruction.data[..8];
|
||||
let instruction_data = &instruction.data[8..];
|
||||
|
||||
// 构建账户公钥列表
|
||||
let account_pubkeys: Vec<Pubkey> = instruction
|
||||
.accounts
|
||||
.iter()
|
||||
.filter_map(|&idx| accounts.get(idx as usize).copied())
|
||||
.collect();
|
||||
|
||||
// 使用 EventDispatcher 解析 instruction 事件
|
||||
let mut event = match EventDispatcher::dispatch_instruction(
|
||||
protocol.clone(),
|
||||
@@ -483,28 +495,13 @@ impl EventParser {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// 使用 EventDispatcher 匹配协议
|
||||
let protocol = match EventDispatcher::match_protocol_by_program_id(&program_id) {
|
||||
Some(p) => p,
|
||||
None => return Ok(()),
|
||||
};
|
||||
let is_cu_program = EventDispatcher::is_compute_budget_program(&program_id);
|
||||
|
||||
// 检查指令数据长度(至少需要 8 字节的 discriminator)
|
||||
if instruction.data.len() < 8 {
|
||||
if !is_cu_program && instruction.data.len() < 8 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// 提取 discriminator 和数据
|
||||
let instruction_discriminator = &instruction.data[..8];
|
||||
let instruction_data = &instruction.data[8..];
|
||||
|
||||
// 构建账户公钥列表
|
||||
let account_pubkeys: Vec<Pubkey> = instruction
|
||||
.accounts
|
||||
.iter()
|
||||
.filter_map(|&idx| accounts.get(idx as usize).copied())
|
||||
.collect();
|
||||
|
||||
// 创建元数据
|
||||
let timestamp = block_time.unwrap_or(Timestamp { seconds: 0, nanos: 0 });
|
||||
let block_time_ms = timestamp.seconds * 1000 + (timestamp.nanos as i64) / 1_000_000;
|
||||
@@ -522,6 +519,33 @@ impl EventParser {
|
||||
transaction_index,
|
||||
);
|
||||
|
||||
if is_cu_program {
|
||||
if let Some(event) = EventDispatcher::dispatch_compute_budget_instruction(
|
||||
&instruction.data,
|
||||
metadata.clone(),
|
||||
) {
|
||||
callback(&event);
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// 使用 EventDispatcher 匹配协议
|
||||
let protocol = match EventDispatcher::match_protocol_by_program_id(&program_id) {
|
||||
Some(p) => p,
|
||||
None => return Ok(()),
|
||||
};
|
||||
|
||||
// 提取 discriminator 和数据
|
||||
let instruction_discriminator = &instruction.data[..8];
|
||||
let instruction_data = &instruction.data[8..];
|
||||
|
||||
// 构建账户公钥列表
|
||||
let account_pubkeys: Vec<Pubkey> = instruction
|
||||
.accounts
|
||||
.iter()
|
||||
.filter_map(|&idx| accounts.get(idx as usize).copied())
|
||||
.collect();
|
||||
|
||||
// 使用 EventDispatcher 解析 instruction 事件
|
||||
let mut event = match EventDispatcher::dispatch_instruction(
|
||||
protocol.clone(),
|
||||
@@ -621,6 +645,8 @@ impl EventParser {
|
||||
// 使用 EventDispatcher 来匹配协议
|
||||
if let Some(protocol) = EventDispatcher::match_protocol_by_program_id(program_id) {
|
||||
protocols.contains(&protocol)
|
||||
} else if EventDispatcher::is_compute_budget_program(program_id) {
|
||||
return true;
|
||||
} else {
|
||||
false
|
||||
}
|
||||
@@ -648,6 +674,14 @@ impl EventParser {
|
||||
}
|
||||
DexEvent::PumpFunCreateTokenEvent(token_info)
|
||||
}
|
||||
DexEvent::PumpFunCreateV2TokenEvent(token_info) => {
|
||||
add_dev_address(&signature, token_info.user);
|
||||
if token_info.creator != Pubkey::default() && token_info.creator != token_info.user
|
||||
{
|
||||
add_dev_address(&signature, token_info.creator);
|
||||
}
|
||||
DexEvent::PumpFunCreateV2TokenEvent(token_info)
|
||||
}
|
||||
DexEvent::PumpFunTradeEvent(mut trade_info) => {
|
||||
trade_info.is_dev_create_token_trade =
|
||||
is_dev_address_in_signature(&signature, &trade_info.user)
|
||||
|
||||
@@ -2,6 +2,9 @@ use crate::streaming::event_parser::common::EventMetadata;
|
||||
use crate::streaming::event_parser::core::account_event_parser::{
|
||||
NonceAccountEvent, TokenAccountEvent, TokenInfoEvent,
|
||||
};
|
||||
use crate::streaming::event_parser::core::common_event_parser::{
|
||||
SetComputeUnitLimitEvent, SetComputeUnitPriceEvent,
|
||||
};
|
||||
use crate::streaming::event_parser::protocols::block::block_meta_event::BlockMetaEvent;
|
||||
use crate::streaming::event_parser::protocols::bonk::events::*;
|
||||
use crate::streaming::event_parser::protocols::meteora_damm_v2::events::*;
|
||||
@@ -83,6 +86,8 @@ pub enum DexEvent {
|
||||
NonceAccountEvent(NonceAccountEvent),
|
||||
TokenInfoEvent(TokenInfoEvent),
|
||||
BlockMetaEvent(BlockMetaEvent),
|
||||
SetComputeUnitLimitEvent(SetComputeUnitLimitEvent),
|
||||
SetComputeUnitPriceEvent(SetComputeUnitPriceEvent),
|
||||
}
|
||||
|
||||
impl DexEvent {
|
||||
@@ -140,6 +145,8 @@ impl DexEvent {
|
||||
DexEvent::NonceAccountEvent(e) => &e.metadata,
|
||||
DexEvent::TokenInfoEvent(e) => &e.metadata,
|
||||
DexEvent::BlockMetaEvent(e) => &e.metadata,
|
||||
DexEvent::SetComputeUnitLimitEvent(e) => &e.metadata,
|
||||
DexEvent::SetComputeUnitPriceEvent(e) => &e.metadata,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,6 +204,8 @@ impl DexEvent {
|
||||
DexEvent::NonceAccountEvent(e) => &mut e.metadata,
|
||||
DexEvent::TokenInfoEvent(e) => &mut e.metadata,
|
||||
DexEvent::BlockMetaEvent(e) => &mut e.metadata,
|
||||
DexEvent::SetComputeUnitLimitEvent(e) => &mut e.metadata,
|
||||
DexEvent::SetComputeUnitPriceEvent(e) => &mut e.metadata,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user