use hopper_runtime::{ProgramError, AccountView, ProgramResult};
#[cfg(feature = "programs")]
use crate::programs;
#[cfg(target_os = "solana")]
extern "C" {
fn sol_get_clock_sysvar(addr: *mut u8) -> u64;
fn sol_get_rent_sysvar(addr: *mut u8) -> u64;
}
#[inline(always)]
pub fn clock_timestamp() -> Result<i64, ProgramError> {
let buf = get_clock_buf()?;
Ok(i64::from_le_bytes([
buf[32], buf[33], buf[34], buf[35], buf[36], buf[37], buf[38], buf[39],
]))
}
#[inline(always)]
pub fn clock_slot() -> Result<u64, ProgramError> {
let buf = get_clock_buf()?;
Ok(u64::from_le_bytes([
buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7],
]))
}
#[inline(always)]
pub fn clock_slot_and_timestamp() -> Result<(u64, i64), ProgramError> {
let buf = get_clock_buf()?;
let slot = u64::from_le_bytes([
buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7],
]);
let ts = i64::from_le_bytes([
buf[32], buf[33], buf[34], buf[35], buf[36], buf[37], buf[38], buf[39],
]);
Ok((slot, ts))
}
#[inline(always)]
pub fn clock_epoch() -> Result<u64, ProgramError> {
let buf = get_clock_buf()?;
Ok(u64::from_le_bytes([
buf[16], buf[17], buf[18], buf[19], buf[20], buf[21], buf[22], buf[23],
]))
}
#[inline(always)]
pub fn rent_lamports_per_byte_year() -> Result<u64, ProgramError> {
let buf = get_rent_buf()?;
Ok(u64::from_le_bytes([
buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7],
]))
}
#[inline(always)]
fn get_clock_buf() -> Result<[u8; CLOCK_LEN], ProgramError> {
#[cfg(target_os = "solana")]
{
let mut buf = [0u8; CLOCK_LEN];
let rc = unsafe { sol_get_clock_sysvar(buf.as_mut_ptr()) };
if rc != 0 {
return Err(ProgramError::InvalidArgument);
}
Ok(buf)
}
#[cfg(not(target_os = "solana"))]
{
Err(ProgramError::InvalidArgument)
}
}
#[inline(always)]
fn get_rent_buf() -> Result<[u8; RENT_LEN], ProgramError> {
#[cfg(target_os = "solana")]
{
let mut buf = [0u8; RENT_LEN];
let rc = unsafe { sol_get_rent_sysvar(buf.as_mut_ptr()) };
if rc != 0 {
return Err(ProgramError::InvalidArgument);
}
Ok(buf)
}
#[cfg(not(target_os = "solana"))]
{
Err(ProgramError::InvalidArgument)
}
}
const CLOCK_LEN: usize = 40;
#[cfg(feature = "programs")]
#[inline(always)]
pub fn check_clock_sysvar(account: &AccountView) -> ProgramResult {
if *account.address() != programs::SYSVAR_CLOCK {
return Err(ProgramError::InvalidArgument);
}
Ok(())
}
#[cfg(feature = "programs")]
#[inline(always)]
pub fn read_clock(account: &AccountView) -> Result<(u64, i64), ProgramError> {
check_clock_sysvar(account)?;
let data = account.try_borrow()?;
if data.len() < CLOCK_LEN {
return Err(ProgramError::AccountDataTooSmall);
}
let slot = u64::from_le_bytes(
data[0..8]
.try_into()
.map_err(|_| ProgramError::InvalidAccountData)?,
);
let timestamp = i64::from_le_bytes(
data[32..40]
.try_into()
.map_err(|_| ProgramError::InvalidAccountData)?,
);
Ok((slot, timestamp))
}
#[cfg(feature = "programs")]
#[inline(always)]
pub fn read_clock_slot(account: &AccountView) -> Result<u64, ProgramError> {
check_clock_sysvar(account)?;
let data = account.try_borrow()?;
if data.len() < CLOCK_LEN {
return Err(ProgramError::AccountDataTooSmall);
}
let slot = u64::from_le_bytes(
data[0..8]
.try_into()
.map_err(|_| ProgramError::InvalidAccountData)?,
);
Ok(slot)
}
#[cfg(feature = "programs")]
#[inline(always)]
pub fn read_clock_timestamp(account: &AccountView) -> Result<i64, ProgramError> {
check_clock_sysvar(account)?;
let data = account.try_borrow()?;
if data.len() < CLOCK_LEN {
return Err(ProgramError::AccountDataTooSmall);
}
let timestamp = i64::from_le_bytes(
data[32..40]
.try_into()
.map_err(|_| ProgramError::InvalidAccountData)?,
);
Ok(timestamp)
}
#[cfg(feature = "programs")]
#[inline(always)]
pub fn read_clock_epoch(account: &AccountView) -> Result<u64, ProgramError> {
check_clock_sysvar(account)?;
let data = account.try_borrow()?;
if data.len() < CLOCK_LEN {
return Err(ProgramError::AccountDataTooSmall);
}
let epoch = u64::from_le_bytes(
data[16..24]
.try_into()
.map_err(|_| ProgramError::InvalidAccountData)?,
);
Ok(epoch)
}
const RENT_LEN: usize = 17;
#[cfg(feature = "programs")]
#[inline(always)]
pub fn check_rent_sysvar(account: &AccountView) -> ProgramResult {
if *account.address() != programs::SYSVAR_RENT {
return Err(ProgramError::InvalidArgument);
}
Ok(())
}
#[cfg(feature = "programs")]
#[inline(always)]
pub fn read_rent_lamports_per_byte_year(account: &AccountView) -> Result<u64, ProgramError> {
check_rent_sysvar(account)?;
let data = account.try_borrow()?;
if data.len() < RENT_LEN {
return Err(ProgramError::AccountDataTooSmall);
}
let rate = u64::from_le_bytes(
data[0..8]
.try_into()
.map_err(|_| ProgramError::InvalidAccountData)?,
);
Ok(rate)
}