levana_perpswap_cosmos/number/
ratio.rs

1//! Provides specialized types that define a ratio that must be within a specific range.
2//! This can be helpful when defining an interface that contains a ratio represented by a
3//! decimal but the ratio is logically constrained by specific bounds.
4
5use crate::prelude::cw_serde;
6use anyhow::{ensure, Result};
7use cosmwasm_std::Decimal256;
8use std::ops::Bound;
9
10fn validate_ratio(
11    value: &Decimal256,
12    lower: Bound<Decimal256>,
13    upper: Bound<Decimal256>,
14) -> Result<()> {
15    let is_valid = (match lower {
16        Bound::Included(lower) => lower <= *value,
17        Bound::Excluded(lower) => lower < *value,
18        Bound::Unbounded => true,
19    }) && (match upper {
20        Bound::Included(upper) => *value <= upper,
21        Bound::Excluded(upper) => *value < upper,
22        Bound::Unbounded => true,
23    });
24
25    ensure!(
26        is_valid,
27        "Invalid ratio, {} is out of range ({:?}, {:?})",
28        value,
29        lower,
30        upper
31    );
32
33    Ok(())
34}
35
36#[cw_serde]
37/// Represents a ratio between 0 and 1 inclusive
38pub struct InclusiveRatio(Decimal256);
39
40impl InclusiveRatio {
41    /// Create a new InclusiveRatio
42    pub fn new(value: Decimal256) -> Result<Self> {
43        validate_ratio(
44            &value,
45            Bound::Included(Decimal256::zero()),
46            Bound::Included(Decimal256::one()),
47        )?;
48
49        Ok(InclusiveRatio(value))
50    }
51
52    /// Get the underlying raw value.
53    pub fn raw(&self) -> Decimal256 {
54        self.0
55    }
56}
57
58#[cfg(test)]
59mod tests {
60    use super::*;
61
62    #[test]
63    fn test_bounded_ratio() {
64        // InclusiveRatio
65
66        let raw_ratio = Decimal256::zero();
67        InclusiveRatio::new(raw_ratio).unwrap();
68
69        let raw_ratio = Decimal256::one();
70        InclusiveRatio::new(raw_ratio).unwrap();
71
72        let raw_ratio = Decimal256::from_ratio(1u64, 2u64);
73        InclusiveRatio::new(raw_ratio).unwrap();
74
75        let raw_ratio = Decimal256::from_ratio(2u64, 1u64);
76        InclusiveRatio::new(raw_ratio).unwrap_err();
77    }
78}