use crate::core::clock::now_us;
use crate::core::events::EventMetadata;
#[cfg(target_os = "windows")]
use crate::core::now_micros;
use base64::{engine::general_purpose, Engine as _};
use solana_sdk::{pubkey::Pubkey, signature::Signature};
#[inline]
pub fn extract_program_data(log: &str) -> Option<Vec<u8>> {
use memchr::memmem;
let log_bytes = log.as_bytes();
let pos = memmem::find(log_bytes, b"Program data: ")?;
let data_part = &log[pos + 14..];
general_purpose::STANDARD.decode(data_part.trim()).ok()
}
#[inline]
pub fn extract_discriminator_fast(log: &str) -> Option<[u8; 8]> {
use memchr::memmem;
let log_bytes = log.as_bytes();
let pos = memmem::find(log_bytes, b"Program data: ")?;
let data_part = log[pos + 14..].trim();
if data_part.len() < 12 {
return None;
}
let prefix = &data_part[..16];
let mut buf = [0u8; 12];
let decoded_len = general_purpose::STANDARD.decode_slice(prefix.as_bytes(), &mut buf).ok()?;
if decoded_len >= 8 {
Some(buf[0..8].try_into().unwrap())
} else {
None
}
}
#[inline]
pub fn read_u64_le(data: &[u8], offset: usize) -> Option<u64> {
data.get(offset..offset + 8).map(|slice| u64::from_le_bytes(slice.try_into().unwrap()))
}
#[inline]
pub fn read_u32_le(data: &[u8], offset: usize) -> Option<u32> {
data.get(offset..offset + 4).map(|slice| u32::from_le_bytes(slice.try_into().unwrap()))
}
pub fn read_i64_le(data: &[u8], offset: usize) -> Option<i64> {
data.get(offset..offset + 8).map(|slice| i64::from_le_bytes(slice.try_into().unwrap()))
}
pub fn read_i32_le(data: &[u8], offset: usize) -> Option<i32> {
data.get(offset..offset + 4).map(|slice| i32::from_le_bytes(slice.try_into().unwrap()))
}
pub fn read_u128_le(data: &[u8], offset: usize) -> Option<u128> {
data.get(offset..offset + 16).map(|slice| u128::from_le_bytes(slice.try_into().unwrap()))
}
pub fn read_u16_le(data: &[u8], offset: usize) -> Option<u16> {
data.get(offset..offset + 2).map(|slice| u16::from_le_bytes(slice.try_into().unwrap()))
}
pub fn read_u8(data: &[u8], offset: usize) -> Option<u8> {
data.get(offset).copied()
}
#[inline]
pub fn read_pubkey(data: &[u8], offset: usize) -> Option<Pubkey> {
data.get(offset..offset + 32).and_then(|slice| {
let key_bytes: [u8; 32] = slice.try_into().ok()?;
Some(Pubkey::new_from_array(key_bytes))
})
}
pub fn read_string(data: &[u8], offset: usize) -> Option<(String, usize)> {
let (string_ref, consumed) = read_string_ref(data, offset)?;
Some((string_ref.to_string(), consumed))
}
#[inline(always)] pub fn read_string_ref(data: &[u8], offset: usize) -> Option<(&str, usize)> {
if data.len() < offset + 4 {
return None;
}
let len = read_u32_le(data, offset)? as usize;
if data.len() < offset + 4 + len {
return None;
}
let string_bytes = &data[offset + 4..offset + 4 + len];
let string_ref = std::str::from_utf8(string_bytes).ok()?; Some((string_ref, 4 + len))
}
pub fn read_bool(data: &[u8], offset: usize) -> Option<bool> {
if data.len() <= offset {
return None;
}
Some(data[offset] == 1)
}
pub fn timestamp_to_microseconds(seconds: i64, nanos: i32) -> i128 {
seconds as i128 * 1_000_000 + (nanos as i128 / 1_000)
}
pub fn create_metadata_simple(
signature: Signature,
slot: u64,
tx_index: u64,
block_time_us: Option<i64>,
program_id: Pubkey,
grpc_recv_us: i64,
) -> EventMetadata {
EventMetadata {
signature,
slot,
tx_index,
block_time_us: block_time_us.unwrap_or(0),
grpc_recv_us,
recent_blockhash: None,
}
}
pub fn create_metadata_default(
signature: Signature,
slot: u64,
tx_index: u64,
block_time_us: Option<i64>,
) -> 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,
}
}
pub mod text_parser {
pub fn extract_number_from_text(text: &str, field: &str) -> Option<u64> {
if let Some(start) = text.find(&format!("{}:", field)) {
let after_colon = &text[start + field.len() + 1..];
if let Some(end) = after_colon.find(' ').or_else(|| after_colon.find(',')) {
after_colon[..end].trim().parse().ok()
} else {
after_colon.trim().parse().ok()
}
} else {
None
}
}
pub fn extract_text_field(text: &str, field: &str) -> Option<String> {
extract_text_field_ref(text, field).map(|s| s.to_string())
}
#[inline(always)] pub fn extract_text_field_ref<'a>(text: &'a str, field: &str) -> Option<&'a str> {
let start = text.find(&format!("{}:", field))?;
let after_colon = &text[start + field.len() + 1..];
if let Some(end) = after_colon.find(',').or_else(|| after_colon.find(' ')) {
Some(after_colon[..end].trim())
} else {
Some(after_colon.trim())
}
}
pub fn detect_trade_type(log: &str) -> Option<bool> {
if log.contains("buy") || log.contains("Buy") {
Some(true)
} else if log.contains("sell") || log.contains("Sell") {
Some(false)
} else {
None
}
}
}