feat(events): add recent_blockhash to EventMetadata

- EventMetadata: add recent_blockhash (Option<Vec<u8>>)
- gRPC path: extract from message.recent_blockhash, thread through parse_instruction_events_from_grpc_transaction and parse_events_from_grpc_instruction
- VersionedTransaction path: get from message.recent_blockhash(), pass into parse_events_from_instruction
- BlockMeta event: pass None for recent_blockhash

Made-with: Cursor
This commit is contained in:
Wood
2026-03-07 16:59:07 +08:00
parent 7270372604
commit 63af06edd3
3 changed files with 22 additions and 0 deletions
@@ -312,6 +312,9 @@ pub struct EventMetadata {
pub swap_data: Option<SwapData>, pub swap_data: Option<SwapData>,
pub outer_index: i64, pub outer_index: i64,
pub inner_index: Option<i64>, pub inner_index: Option<i64>,
/// Transaction message recent blockhash (32 bytes), when available.
#[serde(default)]
pub recent_blockhash: Option<Vec<u8>>,
} }
impl EventMetadata { impl EventMetadata {
@@ -328,6 +331,7 @@ impl EventMetadata {
inner_index: Option<i64>, inner_index: Option<i64>,
recv_us: i64, recv_us: i64,
tx_index: Option<u64>, tx_index: Option<u64>,
recent_blockhash: Option<Vec<u8>>,
) -> Self { ) -> Self {
Self { Self {
signature, signature,
@@ -343,6 +347,7 @@ impl EventMetadata {
outer_index, outer_index,
inner_index, inner_index,
tx_index, tx_index,
recent_blockhash,
} }
} }
@@ -82,6 +82,11 @@ impl EventParser {
.collect(); .collect();
// 解析指令事件 // 解析指令事件
let instructions = &message.instructions; let instructions = &message.instructions;
let recent_blockhash = if message.recent_blockhash.is_empty() {
None
} else {
Some(message.recent_blockhash.clone())
};
Self::parse_instruction_events_from_grpc_transaction( Self::parse_instruction_events_from_grpc_transaction(
protocols, protocols,
event_type_filter, event_type_filter,
@@ -94,6 +99,7 @@ impl EventParser {
&inner_instructions, &inner_instructions,
bot_wallet, bot_wallet,
tx_index, tx_index,
recent_blockhash,
adapter_callback, adapter_callback,
) )
.await?; .await?;
@@ -128,6 +134,7 @@ impl EventParser {
}); });
// 获取交易的指令和账户 // 获取交易的指令和账户
let compiled_instructions = transaction.message.instructions(); let compiled_instructions = transaction.message.instructions();
let recent_blockhash = Some(transaction.message.recent_blockhash().to_bytes().to_vec());
let mut accounts: Vec<Pubkey> = accounts.to_vec(); let mut accounts: Vec<Pubkey> = accounts.to_vec();
// 检查交易中是否包含程序 // 检查交易中是否包含程序
let has_program = accounts let has_program = accounts
@@ -160,6 +167,7 @@ impl EventParser {
None, None,
bot_wallet, bot_wallet,
tx_index, tx_index,
recent_blockhash.as_deref(),
inner_instructions, inner_instructions,
adapter_callback.clone(), adapter_callback.clone(),
)?; )?;
@@ -182,6 +190,7 @@ impl EventParser {
Some(inner_index as i64), Some(inner_index as i64),
bot_wallet, bot_wallet,
tx_index, tx_index,
recent_blockhash.as_deref(),
Some(&inner_instructions), Some(&inner_instructions),
adapter_callback.clone(), adapter_callback.clone(),
)?; )?;
@@ -214,6 +223,7 @@ impl EventParser {
inner_instructions: &[yellowstone_grpc_proto::prelude::InnerInstructions], inner_instructions: &[yellowstone_grpc_proto::prelude::InnerInstructions],
bot_wallet: Option<Pubkey>, bot_wallet: Option<Pubkey>,
tx_index: Option<u64>, tx_index: Option<u64>,
recent_blockhash: Option<Vec<u8>>,
callback: Arc<dyn for<'a> Fn(&'a DexEvent) + Send + Sync>, callback: Arc<dyn for<'a> Fn(&'a DexEvent) + Send + Sync>,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
// 获取交易的指令和账户 // 获取交易的指令和账户
@@ -249,6 +259,7 @@ impl EventParser {
None, None,
bot_wallet, bot_wallet,
tx_index, tx_index,
recent_blockhash.as_deref(),
inner_instructions, inner_instructions,
callback.clone(), callback.clone(),
)?; )?;
@@ -279,6 +290,7 @@ impl EventParser {
Some(inner_index as i64), Some(inner_index as i64),
bot_wallet, bot_wallet,
tx_index, tx_index,
recent_blockhash.as_deref(),
Some(&inner_instructions), Some(&inner_instructions),
callback.clone(), callback.clone(),
)?; )?;
@@ -308,6 +320,7 @@ impl EventParser {
inner_index: Option<i64>, inner_index: Option<i64>,
bot_wallet: Option<Pubkey>, bot_wallet: Option<Pubkey>,
tx_index: Option<u64>, tx_index: Option<u64>,
recent_blockhash: Option<&[u8]>,
inner_instructions: Option<&yellowstone_grpc_proto::prelude::InnerInstructions>, inner_instructions: Option<&yellowstone_grpc_proto::prelude::InnerInstructions>,
callback: Arc<dyn for<'a> Fn(&'a DexEvent) + Send + Sync>, callback: Arc<dyn for<'a> Fn(&'a DexEvent) + Send + Sync>,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
@@ -347,6 +360,7 @@ impl EventParser {
inner_index, inner_index,
recv_us, recv_us,
tx_index, tx_index,
recent_blockhash.map(|s| s.to_vec()),
); );
if is_cu_program { if is_cu_program {
@@ -465,6 +479,7 @@ impl EventParser {
inner_index: Option<i64>, inner_index: Option<i64>,
bot_wallet: Option<Pubkey>, bot_wallet: Option<Pubkey>,
tx_index: Option<u64>, tx_index: Option<u64>,
recent_blockhash: Option<&[u8]>,
inner_instructions: Option<&InnerInstructions>, inner_instructions: Option<&InnerInstructions>,
callback: Arc<dyn for<'a> Fn(&'a DexEvent) + Send + Sync>, callback: Arc<dyn for<'a> Fn(&'a DexEvent) + Send + Sync>,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
@@ -505,6 +520,7 @@ impl EventParser {
inner_index, inner_index,
recv_us, recv_us,
tx_index, tx_index,
recent_blockhash.map(|s| s.to_vec()),
); );
if is_cu_program { if is_cu_program {
@@ -31,6 +31,7 @@ impl BlockMetaEvent {
None, None,
recv_us, recv_us,
None, None,
None, // recent_blockhash not applicable for block meta
); );
Self { metadata, slot, block_hash } Self { metadata, slot, block_hash }
} }