hylo_core/lst/
total_sol_cache.rs1use 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 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 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 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 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}