hopper_core/sysvar/
cache.rs1use hopper_runtime::error::ProgramError;
11
12pub struct CachedClock {
18 pub slot: u64,
19 pub epoch: u64,
20 pub unix_timestamp: i64,
21}
22
23impl CachedClock {
24 #[inline]
29 pub fn from_account_data(data: &[u8]) -> Result<Self, ProgramError> {
30 if data.len() < 40 {
31 return Err(ProgramError::InvalidAccountData);
32 }
33 Ok(Self {
34 slot: u64::from_le_bytes([
35 data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7],
36 ]),
37 epoch: u64::from_le_bytes([
38 data[16], data[17], data[18], data[19], data[20], data[21], data[22], data[23],
39 ]),
40 unix_timestamp: i64::from_le_bytes([
41 data[32], data[33], data[34], data[35], data[36], data[37], data[38], data[39],
42 ]),
43 })
44 }
45
46 #[inline(always)]
48 pub fn check_not_expired(&self, deadline: i64) -> Result<(), ProgramError> {
49 if self.unix_timestamp > deadline {
50 return Err(ProgramError::InvalidAccountData);
51 }
52 Ok(())
53 }
54
55 #[inline(always)]
57 pub fn check_expired(&self, deadline: i64) -> Result<(), ProgramError> {
58 if self.unix_timestamp <= deadline {
59 return Err(ProgramError::InvalidAccountData);
60 }
61 Ok(())
62 }
63
64 #[inline(always)]
66 pub fn check_within_window(&self, start: i64, end: i64) -> Result<(), ProgramError> {
67 if self.unix_timestamp < start || self.unix_timestamp > end {
68 return Err(ProgramError::InvalidAccountData);
69 }
70 Ok(())
71 }
72
73 #[inline(always)]
75 pub fn check_cooldown(&self, last_action: i64, cooldown_secs: i64) -> Result<(), ProgramError> {
76 if self.unix_timestamp < last_action + cooldown_secs {
77 return Err(ProgramError::InvalidAccountData);
78 }
79 Ok(())
80 }
81
82 #[inline(always)]
84 pub fn check_slot_staleness(
85 &self,
86 last_update_slot: u64,
87 max_age: u64,
88 ) -> Result<(), ProgramError> {
89 if self.slot.saturating_sub(last_update_slot) > max_age {
90 return Err(ProgramError::InvalidAccountData);
91 }
92 Ok(())
93 }
94}
95
96pub struct CachedRent {
98 pub lamports_per_byte_year: u64,
99}
100
101impl CachedRent {
102 #[inline]
104 pub fn from_account_data(data: &[u8]) -> Result<Self, ProgramError> {
105 if data.len() < 8 {
106 return Err(ProgramError::InvalidAccountData);
107 }
108 Ok(Self {
109 lamports_per_byte_year: u64::from_le_bytes([
110 data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7],
111 ]),
112 })
113 }
114
115 #[inline(always)]
117 pub fn exempt_min(&self, data_len: usize) -> u64 {
118 ((128 + data_len) as u64) * 6960
121 }
122}
123
124pub struct SysvarContext {
138 clock: Option<CachedClock>,
139 rent: Option<CachedRent>,
140}
141
142impl SysvarContext {
143 #[inline(always)]
145 pub const fn new() -> Self {
146 Self {
147 clock: None,
148 rent: None,
149 }
150 }
151
152 #[inline]
154 pub fn with_clock(mut self, clock_data: &[u8]) -> Result<Self, ProgramError> {
155 self.clock = Some(CachedClock::from_account_data(clock_data)?);
156 Ok(self)
157 }
158
159 #[inline]
161 pub fn with_rent(mut self, rent_data: &[u8]) -> Result<Self, ProgramError> {
162 self.rent = Some(CachedRent::from_account_data(rent_data)?);
163 Ok(self)
164 }
165
166 #[inline(always)]
168 pub fn clock(&self) -> Result<&CachedClock, ProgramError> {
169 match &self.clock {
170 Some(c) => Ok(c),
171 None => Err(ProgramError::InvalidArgument),
172 }
173 }
174
175 #[inline(always)]
177 pub fn rent(&self) -> Result<&CachedRent, ProgramError> {
178 match &self.rent {
179 Some(r) => Ok(r),
180 None => Err(ProgramError::InvalidArgument),
181 }
182 }
183
184 #[inline(always)]
186 pub fn has_clock(&self) -> bool {
187 self.clock.is_some()
188 }
189
190 #[inline(always)]
192 pub fn has_rent(&self) -> bool {
193 self.rent.is_some()
194 }
195}
196
197impl Default for SysvarContext {
198 fn default() -> Self {
199 Self::new()
200 }
201}