use crate::account::{FixedLayout, Pod};
use hopper_runtime::error::ProgramError;
#[inline(always)]
pub fn emit_event<T: Pod + FixedLayout>(value: &T) -> Result<(), ProgramError> {
let bytes = unsafe { core::slice::from_raw_parts(value as *const T as *const u8, T::SIZE) };
emit_slices(&[bytes]);
Ok(())
}
#[inline]
pub fn emit_event_tagged<T: Pod + FixedLayout>(disc: u8, value: &T) -> Result<(), ProgramError> {
let value_bytes =
unsafe { core::slice::from_raw_parts(value as *const T as *const u8, T::SIZE) };
let disc_bytes = [disc];
emit_slices(&[&disc_bytes[..], value_bytes]);
Ok(())
}
#[inline(always)]
pub fn emit_slices(segments: &[&[u8]]) {
#[cfg(target_os = "solana")]
{
unsafe {
hopper_runtime::syscalls::sol_log_data(
segments.as_ptr() as *const u8,
segments.len() as u64,
);
}
}
#[cfg(not(target_os = "solana"))]
{
let _ = segments;
}
}
#[cfg(feature = "cpi")]
#[inline]
pub fn emit_event_cpi<T: Pod + FixedLayout>(
disc: u8,
value: &T,
program_id: &hopper_runtime::Address,
accounts: &[&hopper_runtime::AccountView],
) -> Result<(), ProgramError> {
let value_bytes =
unsafe { core::slice::from_raw_parts(value as *const T as *const u8, T::SIZE) };
let disc_byte = [disc];
emit_slices(&[&disc_byte[..], value_bytes]);
#[cfg(target_os = "solana")]
{
const EVENT_CPI_PREFIX: [u8; 2] = [0xFF, 0xFE];
use hopper_runtime::instruction::{InstructionAccount, InstructionView};
let data_len = 3 + T::SIZE;
if data_len > 1024 {
return Err(ProgramError::InvalidArgument);
}
let mut data_buf = [0u8; 1024];
data_buf[0] = EVENT_CPI_PREFIX[0];
data_buf[1] = EVENT_CPI_PREFIX[1];
data_buf[2] = disc;
unsafe {
core::ptr::copy_nonoverlapping(
value_bytes.as_ptr(),
data_buf.as_mut_ptr().add(3),
T::SIZE,
);
}
let empty_accounts: [InstructionAccount; 0] = [];
let instruction = InstructionView {
program_id,
data: &data_buf[..data_len],
accounts: &empty_accounts,
};
let mut cpi_accounts_buf: [core::mem::MaybeUninit<hopper_runtime::instruction::CpiAccount>; 32] =
unsafe { core::mem::MaybeUninit::uninit().assume_init() };
let count = accounts.len().min(32);
let mut i = 0;
while i < count {
cpi_accounts_buf[i] = core::mem::MaybeUninit::new(
hopper_runtime::instruction::CpiAccount::from(accounts[i]),
);
i += 1;
}
let cpi_accounts = unsafe {
core::slice::from_raw_parts(
cpi_accounts_buf.as_ptr() as *const hopper_runtime::instruction::CpiAccount,
count,
)
};
unsafe {
hopper_runtime::cpi::invoke_unchecked(&instruction, cpi_accounts)?;
}
}
#[cfg(not(target_os = "solana"))]
{
let _ = (program_id, accounts);
}
Ok(())
}