Skip to main content

hylo_core/lst/
total_sol_cache.rs

1use anchor_lang::prelude::{
2  borsh, AnchorDeserialize, AnchorSerialize, InitSpace,
3};
4use fix::prelude::*;
5use serde::{Deserialize, Serialize};
6
7use crate::error::CoreError;
8use crate::error::CoreError::{
9  TotalSolCacheDecrement, TotalSolCacheIncrement, TotalSolCacheOutdated,
10  TotalSolCacheOverflow, TotalSolCacheUnderflow,
11};
12
13#[derive(
14  AnchorSerialize,
15  AnchorDeserialize,
16  InitSpace,
17  Clone,
18  Copy,
19  Serialize,
20  Deserialize,
21)]
22pub struct TotalSolCache {
23  pub current_update_epoch: u64,
24  pub total_sol: UFixValue64,
25}
26
27impl TotalSolCache {
28  #[must_use]
29  pub fn new(current_update_epoch: u64) -> TotalSolCache {
30    let total_sol = UFix64::<N9>::zero().into();
31    TotalSolCache {
32      current_update_epoch,
33      total_sol,
34    }
35  }
36
37  /// Adds lamports to the cached amount.
38  pub fn increment(
39    &mut self,
40    sol_in: UFix64<N9>,
41    current_epoch: u64,
42  ) -> Result<(), CoreError> {
43    if current_epoch == self.current_update_epoch {
44      let prev_total: UFix64<N9> = self.total_sol.try_into()?;
45      let new_total = prev_total
46        .checked_add(&sol_in)
47        .ok_or(TotalSolCacheOverflow)?;
48      self.total_sol = new_total.into();
49      Ok(())
50    } else {
51      Err(TotalSolCacheIncrement)
52    }
53  }
54
55  /// Subtracts lamports from the cached amount.
56  pub fn decrement(
57    &mut self,
58    sol_out: UFix64<N9>,
59    current_epoch: u64,
60  ) -> Result<(), CoreError> {
61    if current_epoch == self.current_update_epoch {
62      let prev_total: UFix64<N9> = self.total_sol.try_into()?;
63      let new_total = prev_total
64        .checked_sub(&sol_out)
65        .ok_or(TotalSolCacheUnderflow)?;
66      self.total_sol = new_total.into();
67      Ok(())
68    } else {
69      Err(TotalSolCacheDecrement)
70    }
71  }
72
73  /// Resets cache and current epoch. Used only in price update instruction.
74  pub fn set(
75    &mut self,
76    total_sol: UFix64<N9>,
77    current_epoch: u64,
78  ) -> Result<(), CoreError> {
79    self.current_update_epoch = current_epoch;
80    self.total_sol = total_sol.into();
81    Ok(())
82  }
83
84  /// Gets the total SOL cached value, if last update epoch is same as current.
85  pub fn get_validated(
86    &self,
87    current_epoch: u64,
88  ) -> Result<UFix64<N9>, CoreError> {
89    if current_epoch == self.current_update_epoch {
90      Ok(self.total_sol.try_into()?)
91    } else {
92      Err(TotalSolCacheOutdated)
93    }
94  }
95}
96
97#[cfg(test)]
98mod tests {
99  use super::*;
100
101  const CURRENT_EPOCH: u64 = 0;
102
103  #[test]
104  fn increment_ok() -> Result<(), CoreError> {
105    let mut cache = TotalSolCache::new(CURRENT_EPOCH);
106    cache.increment(UFix64::new(364), CURRENT_EPOCH)?;
107    cache.increment(UFix64::new(69), CURRENT_EPOCH)?;
108    assert_eq!(UFix64::<N9>::new(433), cache.total_sol.try_into()?);
109    Ok(())
110  }
111
112  #[test]
113  fn increment_decrement_ok() -> Result<(), CoreError> {
114    let mut cache = TotalSolCache::new(CURRENT_EPOCH);
115    cache.increment(UFix64::new(420), CURRENT_EPOCH)?;
116    cache.decrement(UFix64::new(69), CURRENT_EPOCH)?;
117    assert_eq!(UFix64::<N9>::new(351), cache.total_sol.try_into()?);
118    Ok(())
119  }
120
121  #[test]
122  fn wrong_epoch_err() {
123    let mut cache = TotalSolCache::new(CURRENT_EPOCH);
124    let inc = cache.increment(UFix64::new(420), CURRENT_EPOCH + 1);
125    let dec = cache.decrement(UFix64::new(420), CURRENT_EPOCH + 1);
126    assert!(inc.is_err_and(|e| e == TotalSolCacheIncrement));
127    assert!(dec.is_err_and(|e| e == TotalSolCacheDecrement));
128  }
129
130  #[test]
131  fn overflow_underflow_err() -> Result<(), CoreError> {
132    let mut cache = TotalSolCache::new(CURRENT_EPOCH);
133    cache.set(UFix64::new(u64::MAX), CURRENT_EPOCH)?;
134    let inc = cache.increment(UFix64::new(1), CURRENT_EPOCH);
135    cache.set(UFix64::new(u64::MIN), CURRENT_EPOCH)?;
136    let dec = cache.decrement(UFix64::new(1), CURRENT_EPOCH);
137    assert!(inc.is_err_and(|e| e == TotalSolCacheOverflow));
138    assert!(dec.is_err_and(|e| e == TotalSolCacheUnderflow));
139    Ok(())
140  }
141}