use crate::core::events::EventMetadata;
use solana_sdk::{pubkey::Pubkey, signature::Signature};
use yellowstone_grpc_proto::prelude::{Transaction, TransactionStatusMeta};
pub fn create_metadata(
signature: Signature,
slot: u64,
tx_index: u64,
block_time_us: i64,
grpc_recv_us: i64,
) -> EventMetadata {
EventMetadata { signature, slot, tx_index, block_time_us, grpc_recv_us, recent_blockhash: None }
}
#[inline(always)]
pub fn create_metadata_simple(
signature: Signature,
slot: u64,
tx_index: u64,
block_time_us: Option<i64>,
_program_id: Pubkey,
) -> EventMetadata {
let current_time = now_us();
EventMetadata {
signature,
slot,
tx_index,
block_time_us: block_time_us.unwrap_or(0),
grpc_recv_us: current_time,
recent_blockhash: None,
}
}
#[inline(always)]
pub fn read_u64_le(data: &[u8], offset: usize) -> Option<u64> {
data.get(offset..offset.checked_add(8)?)
.map(|slice| u64::from_le_bytes(slice.try_into().unwrap()))
}
#[inline(always)]
pub fn read_u32_le(data: &[u8], offset: usize) -> Option<u32> {
data.get(offset..offset.checked_add(4)?)
.map(|slice| u32::from_le_bytes(slice.try_into().unwrap()))
}
#[inline(always)]
pub fn read_i64_le(data: &[u8], offset: usize) -> Option<i64> {
data.get(offset..offset.checked_add(8)?)
.map(|slice| i64::from_le_bytes(slice.try_into().unwrap()))
}
#[inline(always)]
pub fn read_u16_le(data: &[u8], offset: usize) -> Option<u16> {
data.get(offset..offset.checked_add(2)?)
.map(|slice| u16::from_le_bytes(slice.try_into().unwrap()))
}
#[inline(always)]
pub fn read_u8(data: &[u8], offset: usize) -> Option<u8> {
data.get(offset).copied()
}
#[inline(always)]
pub fn read_i32_le(data: &[u8], offset: usize) -> Option<i32> {
data.get(offset..offset.checked_add(4)?)
.map(|slice| i32::from_le_bytes(slice.try_into().unwrap()))
}
#[inline(always)]
pub fn read_u128_le(data: &[u8], offset: usize) -> Option<u128> {
data.get(offset..offset.checked_add(16)?)
.map(|slice| u128::from_le_bytes(slice.try_into().unwrap()))
}
#[inline(always)]
pub fn read_bool(data: &[u8], offset: usize) -> Option<bool> {
read_option_bool_idl(data, offset)
}
#[inline(always)]
pub fn read_option_bool_idl(data: &[u8], offset: usize) -> Option<bool> {
match data.get(offset).copied()? {
0 => Some(false),
1 => Some(true),
_ => None,
}
}
#[inline(always)]
pub fn read_option_u64_idl(data: &[u8], offset: usize) -> Option<u64> {
read_u64_le(data, offset)
}
#[inline(always)]
pub fn read_pubkey(data: &[u8], offset: usize) -> Option<Pubkey> {
data.get(offset..offset.checked_add(32)?).and_then(|slice| Pubkey::try_from(slice).ok())
}
#[inline(always)]
pub fn get_account(accounts: &[Pubkey], index: usize) -> Option<Pubkey> {
accounts.get(index).copied()
}
pub fn calculate_slippage_bps(amount_in: u64, amount_out_min: u64) -> u16 {
if amount_in == 0 {
return 0;
}
let slippage =
(u128::from(amount_in.saturating_sub(amount_out_min)) * 10000) / u128::from(amount_in);
slippage.min(10000) as u16
}
pub fn calculate_price_impact_bps(_amount_in: u64, amount_out: u64, expected_out: u64) -> u16 {
if expected_out == 0 {
return 0;
}
let impact =
(u128::from(expected_out.saturating_sub(amount_out)) * 10000) / u128::from(expected_out);
impact.min(10000) as u16
}
pub fn read_bytes(data: &[u8], offset: usize, length: usize) -> Option<&[u8]> {
data.get(offset..offset.checked_add(length)?)
}
#[inline]
pub fn parse_create_v2_tail_fields(
data_after_discriminator: &[u8],
) -> Option<(Pubkey, bool, bool, u64, bool)> {
let mut offset = 0usize;
let (_, l) = read_str_unchecked(data_after_discriminator, offset)?;
offset += l;
let (_, l) = read_str_unchecked(data_after_discriminator, offset)?;
offset += l;
let (_, l) = read_str_unchecked(data_after_discriminator, offset)?;
offset += l;
if data_after_discriminator.len() < offset + 32 + 1 {
return None;
}
let creator = read_pubkey(data_after_discriminator, offset)?;
offset += 32;
let is_mayhem_mode = read_option_bool_idl(data_after_discriminator, offset)?;
offset += 1;
let (is_cashback_enabled, creator_fee_bps, is_holder_reward) =
parse_create_v2_optional_tail(&data_after_discriminator[offset..])?;
Some((creator, is_mayhem_mode, is_cashback_enabled, creator_fee_bps, is_holder_reward))
}
#[inline]
pub(crate) fn parse_create_v2_optional_tail(tail: &[u8]) -> Option<(bool, u64, bool)> {
if tail.is_empty() {
return Some((false, 0, false));
}
let cashback = read_option_bool_idl(tail, 0)?;
if tail.len() == 1 {
return Some((cashback, 0, false));
}
let fee = read_u64_le(tail, 1)?;
let holder_reward = if tail.len() == 9 { false } else { read_option_bool_idl(tail, 9)? };
Some((cashback, fee, holder_reward))
}
#[inline]
pub fn read_str_unchecked(data: &[u8], offset: usize) -> Option<(&str, usize)> {
let tail = data.get(offset..)?;
let len = read_u32_le(tail, 0)? as usize;
let bytes = tail.get(4..)?.get(..len)?;
Some((std::str::from_utf8(bytes).ok()?, len.checked_add(4)?))
}
pub fn read_vec_u64(data: &[u8], offset: usize) -> Option<Vec<u64>> {
let tail = data.get(offset..)?;
let count = read_u32_le(tail, 0)? as usize;
let bytes = tail.get(4..)?.get(..count.checked_mul(8)?)?;
Some(bytes.chunks_exact(8).map(|value| u64::from_le_bytes(value.try_into().unwrap())).collect())
}
#[inline(always)]
pub fn read_pubkey_fast(bytes: &[u8]) -> Pubkey {
crate::logs::utils::read_pubkey(bytes, 0).unwrap_or_default()
}
pub fn get_instruction_account_getter<'a>(
meta: &'a TransactionStatusMeta,
transaction: &'a Option<Transaction>,
account_keys: Option<&'a Vec<Vec<u8>>>,
loaded_writable_addresses: &'a [Vec<u8>],
loaded_readonly_addresses: &'a [Vec<u8>],
index: &(i32, i32), ) -> Option<impl Fn(usize) -> Pubkey + 'a> {
let accounts = if index.1 >= 0 {
let outer_idx = index.0 as u32;
meta.inner_instructions
.binary_search_by_key(&outer_idx, |i| i.index)
.ok()
.and_then(|pos| meta.inner_instructions.get(pos))
.or_else(|| {
meta.inner_instructions.iter().find(|i| i.index == outer_idx)
})?
.instructions
.get(index.1 as usize)?
.accounts
.as_slice()
} else {
transaction
.as_ref()?
.message
.as_ref()?
.instructions
.get(index.0 as usize)?
.accounts
.as_slice()
};
Some(move |acc_index: usize| -> Pubkey {
let account_index = match accounts.get(acc_index) {
Some(&idx) => idx as usize,
None => return Pubkey::default(),
};
let Some(keys) = account_keys else {
return Pubkey::default();
};
if let Some(key_bytes) = keys.get(account_index) {
return Pubkey::try_from(key_bytes.as_slice()).unwrap_or_default();
}
let writable_offset = account_index.saturating_sub(keys.len());
if let Some(key_bytes) = loaded_writable_addresses.get(writable_offset) {
return Pubkey::try_from(key_bytes.as_slice()).unwrap_or_default();
}
let readonly_offset = writable_offset.saturating_sub(loaded_writable_addresses.len());
if let Some(key_bytes) = loaded_readonly_addresses.get(readonly_offset) {
return Pubkey::try_from(key_bytes.as_slice()).unwrap_or_default();
}
Pubkey::default()
})
}
use crate::core::clock::now_us;
use std::collections::HashMap;
pub struct InnerInstructionsIndex<'a> {
index_map: HashMap<u32, &'a yellowstone_grpc_proto::prelude::InnerInstructions>,
}
impl<'a> InnerInstructionsIndex<'a> {
#[inline]
pub fn new(meta: &'a TransactionStatusMeta) -> Self {
let mut index_map = HashMap::with_capacity(meta.inner_instructions.len());
for inner in &meta.inner_instructions {
index_map.insert(inner.index, inner);
}
Self { index_map }
}
#[inline]
pub fn get(
&self,
outer_index: u32,
) -> Option<&'a yellowstone_grpc_proto::prelude::InnerInstructions> {
self.index_map.get(&outer_index).copied()
}
}
pub fn get_instruction_account_getter_indexed<'a>(
inner_index: &InnerInstructionsIndex<'a>,
transaction: &'a Option<Transaction>,
account_keys: Option<&'a Vec<Vec<u8>>>,
loaded_writable_addresses: &'a [Vec<u8>],
loaded_readonly_addresses: &'a [Vec<u8>],
index: &(i32, i32),
) -> Option<impl Fn(usize) -> Pubkey + 'a> {
let accounts = if index.1 >= 0 {
inner_index.get(index.0 as u32)?.instructions.get(index.1 as usize)?.accounts.as_slice()
} else {
transaction
.as_ref()?
.message
.as_ref()?
.instructions
.get(index.0 as usize)?
.accounts
.as_slice()
};
Some(move |acc_index: usize| -> Pubkey {
let account_index = match accounts.get(acc_index) {
Some(&idx) => idx as usize,
None => return Pubkey::default(),
};
let Some(keys) = account_keys else {
return Pubkey::default();
};
if let Some(key_bytes) = keys.get(account_index) {
return Pubkey::try_from(key_bytes.as_slice()).unwrap_or_default();
}
let writable_offset = account_index.saturating_sub(keys.len());
if let Some(key_bytes) = loaded_writable_addresses.get(writable_offset) {
return Pubkey::try_from(key_bytes.as_slice()).unwrap_or_default();
}
let readonly_offset = writable_offset.saturating_sub(loaded_writable_addresses.len());
if let Some(key_bytes) = loaded_readonly_addresses.get(readonly_offset) {
return Pubkey::try_from(key_bytes.as_slice()).unwrap_or_default();
}
Pubkey::default()
})
}
#[cfg(test)]
mod option_bool_tests {
use super::*;
#[test]
fn read_option_bool_idl_strict() {
assert_eq!(read_option_bool_idl(&[0], 0), Some(false));
assert_eq!(read_option_bool_idl(&[1], 0), Some(true));
assert_eq!(read_option_bool_idl(&[2], 0), None);
}
#[test]
fn parse_create_v2_tail_matches_anchor_len() {
let mut p = Vec::new();
p.extend_from_slice(&(1u32.to_le_bytes()));
p.push(b'a');
p.extend_from_slice(&(1u32.to_le_bytes()));
p.push(b'b');
p.extend_from_slice(&(1u32.to_le_bytes()));
p.push(b'c');
p.extend_from_slice(&[0u8; 32]);
p.push(1u8); p.push(1u8); assert_eq!(p.len(), 49);
let (creator, mayhem, cb, creator_fee_bps, holder_reward) =
parse_create_v2_tail_fields(&p).expect("parse");
assert_eq!(creator, Pubkey::default());
assert!(mayhem);
assert!(cb);
assert_eq!(creator_fee_bps, 0);
assert!(!holder_reward);
}
#[test]
fn parse_create_v2_tail_reads_holder_rewards_fields() {
let mut p = Vec::new();
for value in ["a", "b", "c"] {
p.extend_from_slice(&(value.len() as u32).to_le_bytes());
p.extend_from_slice(value.as_bytes());
}
p.extend_from_slice(&[0u8; 32]);
p.push(0); p.push(0); p.extend_from_slice(&250u64.to_le_bytes());
p.push(1);
let (_, mayhem, cashback, creator_fee_bps, holder_reward) =
parse_create_v2_tail_fields(&p).expect("parse");
assert!(!mayhem);
assert!(!cashback);
assert_eq!(creator_fee_bps, 250);
assert!(holder_reward);
}
}
#[cfg(test)]
mod review_tail_regressions {
use super::*;
#[test]
fn optional_create_tail_rejects_partial_fee_and_invalid_boolean() {
for n in 1..8 {
let mut tail = vec![0];
tail.extend(std::iter::repeat_n(1, n));
assert!(parse_create_v2_optional_tail(&tail).is_none());
}
assert_eq!(parse_create_v2_optional_tail(&[]), Some((false, 0, false)));
assert_eq!(parse_create_v2_optional_tail(&[1]), Some((true, 0, false)));
let mut tail = vec![0];
tail.extend_from_slice(&1u64.to_le_bytes());
assert_eq!(parse_create_v2_optional_tail(&tail), Some((false, 1, false)));
tail.push(2);
assert!(parse_create_v2_optional_tail(&tail).is_none());
tail[9] = 1;
assert_eq!(parse_create_v2_optional_tail(&tail), Some((false, 1, true)));
tail[0] = 2;
assert!(parse_create_v2_optional_tail(&tail).is_none());
}
}
#[cfg(test)]
mod review_numeric_bounds_tests {
use super::*;
#[test]
fn oversized_offsets_and_lengths_return_none_without_panicking() {
let data = [0u8; 64];
for offset in [65, usize::MAX - 32, usize::MAX - 1, usize::MAX] {
assert_eq!(read_u16_le(&data, offset), None);
assert_eq!(read_u32_le(&data, offset), None);
assert_eq!(read_i32_le(&data, offset), None);
assert_eq!(read_u64_le(&data, offset), None);
assert_eq!(read_i64_le(&data, offset), None);
assert_eq!(read_u128_le(&data, offset), None);
assert_eq!(read_pubkey(&data, offset), None);
assert_eq!(read_str_unchecked(&data, offset), None);
assert_eq!(read_bytes(&data, offset, 8), None);
assert_eq!(read_vec_u64(&data, offset), None);
}
assert_eq!(read_bytes(&data, 1, usize::MAX), None);
assert_eq!(read_str_unchecked(&u32::MAX.to_le_bytes(), 0), None);
assert_eq!(read_str_unchecked(&[1, 0, 0, 0, 255], 0), None);
assert_eq!(read_str_unchecked(&[1, 0, 0, 0, b'x'], 0), Some(("x", 5)));
}
#[test]
fn borsh_vector_reads_values_and_rejects_truncation_before_allocation() {
let values = [0u64, 7, u64::MAX];
let mut data = vec![99, 99];
data.extend_from_slice(&3u32.to_le_bytes());
for value in values {
data.extend_from_slice(&value.to_le_bytes());
}
assert_eq!(read_vec_u64(&data, 2), Some(values.to_vec()));
for end in 0..data.len() {
assert_eq!(read_vec_u64(&data[..end], 2), None);
}
assert_eq!(read_vec_u64(&0u32.to_le_bytes(), 0), Some(Vec::new()));
assert_eq!(read_vec_u64(&u32::MAX.to_le_bytes(), 0), None);
}
#[test]
fn bps_ratio_is_bounded_and_correct_for_full_u64_range() {
for denominator in [0, 1, 10_000, u64::MAX / 2, u64::MAX] {
for amount in [0, 1, 10_000, u64::MAX / 2, u64::MAX] {
let expected = if denominator == 0 {
0
} else {
(u128::from(denominator.saturating_sub(amount)) * 10_000
/ u128::from(denominator)) as u16
};
assert_eq!(calculate_slippage_bps(denominator, amount), expected);
assert_eq!(calculate_price_impact_bps(0, amount, denominator), expected);
}
}
}
}
#[cfg(test)]
mod transaction_account_key_length_regressions {
use super::*;
use yellowstone_grpc_proto::prelude::{CompiledInstruction, Message};
#[test]
fn both_getters_keep_malformed_static_and_alt_keys_unresolved() {
let expected = Pubkey::new_unique();
for source in 0..3 {
for length in [0, 31, 32, 33, 64] {
let mut bytes = expected.to_bytes().to_vec();
bytes.resize(length, 0);
let keys =
vec![if source == 0 { bytes.clone() } else { expected.to_bytes().to_vec() }];
let writable =
vec![if source == 1 { bytes.clone() } else { expected.to_bytes().to_vec() }];
let readonly = vec![if source == 2 { bytes } else { expected.to_bytes().to_vec() }];
let tx = Some(Transaction {
message: Some(Message {
account_keys: keys.clone(),
instructions: vec![CompiledInstruction {
accounts: vec![0, 1, 2],
..Default::default()
}],
..Default::default()
}),
..Default::default()
});
let meta = TransactionStatusMeta::default();
let index = InnerInstructionsIndex::new(&meta);
let position = (0, -1);
let get = get_instruction_account_getter(
&meta,
&tx,
Some(&keys),
&writable,
&readonly,
&position,
)
.unwrap();
let indexed = get_instruction_account_getter_indexed(
&index,
&tx,
Some(&keys),
&writable,
&readonly,
&position,
)
.unwrap();
let actual = if length == 32 { expected } else { Pubkey::default() };
assert_eq!(get(source), actual);
assert_eq!(indexed(source), actual);
assert_eq!(get(3), Pubkey::default());
}
}
}
}
#[cfg(test)]
mod review_canonical_bool_regressions {
use super::*;
#[test]
fn bool_readers_accept_only_borsh_zero_and_one() {
for byte in 0..=u8::MAX {
let expected = match byte { 0 => Some(false), 1 => Some(true), _ => None };
assert_eq!(read_bool(&[byte],0),expected);
assert_eq!(read_option_bool_idl(&[byte],0),expected);
}
assert_eq!(read_bool(&[],0),None);
assert_eq!(read_bool(&[0],usize::MAX),None);
}
}