hylo_core/
fee_controller.rs1use anchor_lang::prelude::*;
2use fix::prelude::*;
3
4use crate::error::CoreError::{
5 FeeExtraction, InvalidFees, NoValidLevercoinMintFee,
6 NoValidLevercoinRedeemFee, NoValidStablecoinMintFee, NoValidSwapFee,
7};
8use crate::stability_mode::StabilityMode::{self, Depeg, Mode1, Mode2, Normal};
9
10#[derive(Copy, Clone, InitSpace, AnchorSerialize, AnchorDeserialize)]
14pub struct FeePair {
15 mint: UFixValue64,
16 redeem: UFixValue64,
17}
18
19impl FeePair {
20 #[must_use]
21 pub fn new(mint: UFixValue64, redeem: UFixValue64) -> FeePair {
22 FeePair { mint, redeem }
23 }
24
25 pub fn mint(&self) -> Result<UFix64<N4>> {
26 self.mint.try_into()
27 }
28
29 pub fn redeem(&self) -> Result<UFix64<N4>> {
30 self.redeem.try_into()
31 }
32
33 pub fn validate(&self) -> Result<()> {
35 let one = UFix64::one();
36 if self.mint()? < one && self.redeem()? < one {
37 Ok(())
38 } else {
39 Err(InvalidFees.into())
40 }
41 }
42}
43
44pub trait FeeController {
46 fn mint_fee(&self, mode: StabilityMode) -> Result<UFix64<N4>>;
47 fn redeem_fee(&self, mode: StabilityMode) -> Result<UFix64<N4>>;
48 fn validate(&self) -> Result<()>;
49}
50
51pub struct FeeExtract<Exp> {
54 pub fees_extracted: UFix64<Exp>,
55 pub amount_remaining: UFix64<Exp>,
56}
57
58impl<Exp> FeeExtract<Exp> {
59 pub fn new(
60 fee: UFix64<N4>,
61 amount_in: UFix64<Exp>,
62 ) -> Result<FeeExtract<Exp>> {
63 let fees_extracted = amount_in
64 .mul_div_ceil(fee, UFix64::<N4>::one())
65 .ok_or(FeeExtraction)?;
66
67 let amount_remaining = amount_in
68 .checked_sub(&fees_extracted)
69 .ok_or(FeeExtraction)?;
70
71 Ok(FeeExtract {
72 fees_extracted,
73 amount_remaining,
74 })
75 }
76}
77
78#[derive(Copy, Clone, InitSpace, AnchorSerialize, AnchorDeserialize)]
79pub struct StablecoinFees {
80 normal: FeePair,
81 mode_1: FeePair,
82}
83
84impl StablecoinFees {
85 #[must_use]
86 pub fn new(normal: FeePair, mode_1: FeePair) -> StablecoinFees {
87 StablecoinFees { normal, mode_1 }
88 }
89}
90
91impl FeeController for StablecoinFees {
92 fn mint_fee(&self, mode: StabilityMode) -> Result<UFix64<N4>> {
95 match mode {
96 Normal => self.normal.mint.try_into(),
97 Mode1 => self.mode_1.mint.try_into(),
98 Mode2 | Depeg => Err(NoValidStablecoinMintFee.into()),
99 }
100 }
101
102 fn redeem_fee(&self, mode: StabilityMode) -> Result<UFix64<N4>> {
104 match mode {
105 Normal => self.normal.redeem.try_into(),
106 Mode1 => self.mode_1.redeem.try_into(),
107 Mode2 | Depeg => Ok(UFix64::zero()),
108 }
109 }
110
111 fn validate(&self) -> Result<()> {
113 self.normal.validate()?;
114 self.mode_1.validate()
115 }
116}
117
118#[derive(Copy, Clone, InitSpace, AnchorDeserialize, AnchorSerialize)]
119pub struct LevercoinFees {
120 normal: FeePair,
121 mode_1: FeePair,
122 mode_2: FeePair,
123}
124
125impl FeeController for LevercoinFees {
126 fn mint_fee(&self, mode: StabilityMode) -> Result<UFix64<N4>> {
129 match mode {
130 Normal => self.normal.mint.try_into(),
131 Mode1 => self.mode_1.mint.try_into(),
132 Mode2 => self.mode_2.mint.try_into(),
133 Depeg => Err(NoValidLevercoinMintFee.into()),
134 }
135 }
136
137 fn redeem_fee(&self, mode: StabilityMode) -> Result<UFix64<N4>> {
140 match mode {
141 Normal => self.normal.redeem.try_into(),
142 Mode1 => self.mode_1.redeem.try_into(),
143 Mode2 => self.mode_2.redeem.try_into(),
144 Depeg => Err(NoValidLevercoinRedeemFee.into()),
145 }
146 }
147
148 fn validate(&self) -> Result<()> {
150 self.normal.validate()?;
151 self.mode_1.validate()?;
152 self.mode_2.validate()
153 }
154}
155
156impl LevercoinFees {
157 #[must_use]
158 pub fn new(
159 normal: FeePair,
160 mode_1: FeePair,
161 mode_2: FeePair,
162 ) -> LevercoinFees {
163 LevercoinFees {
164 normal,
165 mode_1,
166 mode_2,
167 }
168 }
169
170 pub fn swap_to_stablecoin_fee(
172 &self,
173 mode: StabilityMode,
174 ) -> Result<UFix64<N4>> {
175 match mode {
176 Normal => self.normal.redeem.try_into(),
177 Mode1 => self.mode_1.redeem.try_into(),
178 Mode2 | Depeg => Err(NoValidSwapFee.into()),
179 }
180 }
181
182 pub fn swap_from_stablecoin_fee(
184 &self,
185 mode: StabilityMode,
186 ) -> Result<UFix64<N4>> {
187 match mode {
188 Normal => self.normal.mint(),
189 Mode1 => self.mode_1.mint(),
190 Mode2 => self.mode_2.mint(),
191 Depeg => Err(NoValidSwapFee.into()),
192 }
193 }
194}
195
196#[cfg(test)]
197mod tests {
198 use super::*;
199
200 #[test]
201 fn fee_extraction() -> Result<()> {
202 let fee = UFix64::new(50);
203 let amount = UFix64::<N9>::new(69_618_816_010);
204 let out = FeeExtract::new(fee, amount)?;
205 assert_eq!(out.fees_extracted, UFix64::new(348_094_081));
206 assert_eq!(out.amount_remaining, UFix64::new(69_270_721_929));
207 Ok(())
208 }
209
210 #[test]
211 fn fee_extraction_underflow() {
212 let fee = UFix64::new(10001);
213 let amount = UFix64::<N9>::new(69_618_816_010);
214 let out = FeeExtract::new(fee, amount);
215 assert!(out.is_err());
216 }
217}