hylo_core/
stability_mode.rs

1use std::fmt::Display;
2
3use anchor_lang::prelude::*;
4use fix::prelude::*;
5
6use crate::error::CoreError::StabilityValidation;
7use crate::stability_mode::StabilityMode::{Depeg, Mode1, Mode2, Normal};
8
9/// Mode of operation based on the protocol's current collateral ratio.
10/// See whitepaper for more.
11#[derive(
12  Copy, Clone, Debug, AnchorSerialize, AnchorDeserialize, PartialEq, PartialOrd,
13)]
14pub enum StabilityMode {
15  Normal,
16  Mode1,
17  Mode2,
18  Depeg,
19}
20
21impl Display for StabilityMode {
22  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
23    match self {
24      Normal => f.write_str("Normal"),
25      Mode1 => f.write_str("Mode1"),
26      Mode2 => f.write_str("Mode2"),
27      Depeg => f.write_str("Depeg"),
28    }
29  }
30}
31
32#[derive(Copy, Clone)]
33pub struct StabilityController {
34  pub stability_threshold_1: UFix64<N2>,
35  pub stability_threshold_2: UFix64<N2>,
36}
37
38impl StabilityController {
39  /// Parses stability thresholds into controller.
40  pub fn new(
41    stability_threshold_1: UFix64<N2>,
42    stability_threshold_2: UFix64<N2>,
43  ) -> Result<StabilityController> {
44    let controller = StabilityController {
45      stability_threshold_1,
46      stability_threshold_2,
47    };
48    controller.validate()?;
49    Ok(controller)
50  }
51
52  /// Determines which mode the protocol is in from the current collateral ratio
53  /// and configured stability thresholds.
54  pub fn stability_mode(
55    &self,
56    collateral_ratio: UFix64<N9>,
57  ) -> Result<StabilityMode> {
58    Ok(
59      if collateral_ratio >= self.stability_threshold_1.convert() {
60        Normal
61      } else if collateral_ratio >= self.stability_threshold_2.convert() {
62        Mode1
63      } else if collateral_ratio >= UFix64::one() {
64        Mode2
65      } else {
66        Depeg
67      },
68    )
69  }
70
71  /// Like [`Self::next_stability_threshold`] but in reverse order.
72  /// Yields the previously higher threshold.
73  #[must_use]
74  pub fn prev_stability_threshold(
75    &self,
76    mode: StabilityMode,
77  ) -> Option<UFix64<N2>> {
78    match mode {
79      Normal => None,
80      Mode1 => Some(self.stability_threshold_1),
81      Mode2 => Some(self.stability_threshold_2),
82      Depeg => Some(UFix64::one()),
83    }
84  }
85
86  /// Given the current stability mode, returns the next lower CR threshold.
87  /// Should be used when computing the maximum mintable stablecoin.
88  /// When stablecoin is depegged, returns None.
89  #[must_use]
90  pub fn next_stability_threshold(
91    &self,
92    mode: StabilityMode,
93  ) -> Option<UFix64<N2>> {
94    match mode {
95      Normal => Some(self.stability_threshold_1),
96      Mode1 => Some(self.stability_threshold_2),
97      Mode2 => Some(UFix64::one()),
98      Depeg => None,
99    }
100  }
101
102  /// Lowest tolerable threshold.
103  #[must_use]
104  pub fn min_stability_threshold(&self) -> UFix64<N2> {
105    self.stability_threshold_2
106  }
107
108  /// Ensures stability thresholds:
109  ///   - Are greater than 1.0
110  ///   - Have 2 decimal places `X.XX`
111  ///   - Mode 1 threshold is greater than mode 2 threshold
112  pub fn validate(&self) -> Result<()> {
113    let t1 = self.stability_threshold_1;
114    let t2 = self.stability_threshold_2;
115    let one = UFix64::one();
116    if t1 > t2 && t1 > one && t2 > one {
117      Ok(())
118    } else {
119      Err(StabilityValidation.into())
120    }
121  }
122}
123
124#[cfg(test)]
125mod tests {
126  #[test]
127  fn stability_mode_ord() {
128    use super::StabilityMode::*;
129    assert!(Normal < Mode1);
130    assert!(Mode1 < Mode2);
131    assert!(Mode2 < Depeg);
132  }
133}