use hopper_runtime::error::ProgramError;
pub struct CachedClock {
pub slot: u64,
pub epoch: u64,
pub unix_timestamp: i64,
}
impl CachedClock {
#[inline]
pub fn from_account_data(data: &[u8]) -> Result<Self, ProgramError> {
if data.len() < 40 {
return Err(ProgramError::InvalidAccountData);
}
Ok(Self {
slot: u64::from_le_bytes([
data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7],
]),
epoch: u64::from_le_bytes([
data[16], data[17], data[18], data[19], data[20], data[21], data[22], data[23],
]),
unix_timestamp: i64::from_le_bytes([
data[32], data[33], data[34], data[35], data[36], data[37], data[38], data[39],
]),
})
}
#[inline(always)]
pub fn check_not_expired(&self, deadline: i64) -> Result<(), ProgramError> {
if self.unix_timestamp > deadline {
return Err(ProgramError::InvalidAccountData);
}
Ok(())
}
#[inline(always)]
pub fn check_expired(&self, deadline: i64) -> Result<(), ProgramError> {
if self.unix_timestamp <= deadline {
return Err(ProgramError::InvalidAccountData);
}
Ok(())
}
#[inline(always)]
pub fn check_within_window(&self, start: i64, end: i64) -> Result<(), ProgramError> {
if self.unix_timestamp < start || self.unix_timestamp > end {
return Err(ProgramError::InvalidAccountData);
}
Ok(())
}
#[inline(always)]
pub fn check_cooldown(&self, last_action: i64, cooldown_secs: i64) -> Result<(), ProgramError> {
if self.unix_timestamp < last_action + cooldown_secs {
return Err(ProgramError::InvalidAccountData);
}
Ok(())
}
#[inline(always)]
pub fn check_slot_staleness(
&self,
last_update_slot: u64,
max_age: u64,
) -> Result<(), ProgramError> {
if self.slot.saturating_sub(last_update_slot) > max_age {
return Err(ProgramError::InvalidAccountData);
}
Ok(())
}
}
pub struct CachedRent {
pub lamports_per_byte_year: u64,
}
impl CachedRent {
#[inline]
pub fn from_account_data(data: &[u8]) -> Result<Self, ProgramError> {
if data.len() < 8 {
return Err(ProgramError::InvalidAccountData);
}
Ok(Self {
lamports_per_byte_year: u64::from_le_bytes([
data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7],
]),
})
}
#[inline(always)]
pub fn exempt_min(&self, data_len: usize) -> u64 {
((128 + data_len) as u64) * 6960
}
}
pub struct SysvarContext {
clock: Option<CachedClock>,
rent: Option<CachedRent>,
}
impl SysvarContext {
#[inline(always)]
pub const fn new() -> Self {
Self {
clock: None,
rent: None,
}
}
#[inline]
pub fn with_clock(mut self, clock_data: &[u8]) -> Result<Self, ProgramError> {
self.clock = Some(CachedClock::from_account_data(clock_data)?);
Ok(self)
}
#[inline]
pub fn with_rent(mut self, rent_data: &[u8]) -> Result<Self, ProgramError> {
self.rent = Some(CachedRent::from_account_data(rent_data)?);
Ok(self)
}
#[inline(always)]
pub fn clock(&self) -> Result<&CachedClock, ProgramError> {
match &self.clock {
Some(c) => Ok(c),
None => Err(ProgramError::InvalidArgument),
}
}
#[inline(always)]
pub fn rent(&self) -> Result<&CachedRent, ProgramError> {
match &self.rent {
Some(r) => Ok(r),
None => Err(ProgramError::InvalidArgument),
}
}
#[inline(always)]
pub fn has_clock(&self) -> bool {
self.clock.is_some()
}
#[inline(always)]
pub fn has_rent(&self) -> bool {
self.rent.is_some()
}
}
impl Default for SysvarContext {
fn default() -> Self {
Self::new()
}
}