wynd_utils/
scalable_curve.rs

1use schemars::JsonSchema;
2use serde::{Deserialize, Serialize};
3
4use cosmwasm_std::{Decimal, Uint128};
5
6use crate::{Curve, CurveError, PiecewiseLinear, SaturatingLinear};
7
8#[derive(Serialize, Deserialize, JsonSchema, Debug, Clone, PartialEq)]
9#[serde(rename_all = "snake_case")]
10pub enum ScalableCurve {
11    Constant { ratio: Decimal },
12    ScalableLinear(ScalableLinear),
13    ScalablePiecewise(ScalablePiecewise),
14}
15
16impl ScalableCurve {
17    pub fn scale(self, amount: Uint128) -> Curve {
18        match self {
19            ScalableCurve::Constant { ratio } => Curve::Constant { y: amount * ratio },
20            ScalableCurve::ScalableLinear(s) => s.scale(amount),
21            ScalableCurve::ScalablePiecewise(p) => p.scale(amount),
22        }
23    }
24
25    pub fn validate_monotonic_increasing(&self) -> Result<(), CurveError> {
26        self.clone()
27            .scale(Uint128::new(1_000_000_000))
28            .validate_monotonic_increasing()
29    }
30
31    pub fn validate_monotonic_decreasing(&self) -> Result<(), CurveError> {
32        self.clone()
33            .scale(Uint128::new(1_000_000_000))
34            .validate_monotonic_decreasing()
35    }
36
37    pub fn linear((min_x, min_percent): (u64, u64), (max_x, max_percent): (u64, u64)) -> Self {
38        ScalableCurve::ScalableLinear(ScalableLinear {
39            min_x,
40            min_y: Decimal::percent(min_percent),
41            max_x,
42            max_y: Decimal::percent(max_percent),
43        })
44    }
45}
46
47/// min_y for all x <= min_x, max_y for all x >= max_x, linear in between
48#[derive(Serialize, Deserialize, JsonSchema, Debug, Clone, PartialEq)]
49pub struct ScalableLinear {
50    pub min_x: u64,
51    pub min_y: Decimal,
52    pub max_x: u64,
53    pub max_y: Decimal,
54}
55
56impl ScalableLinear {
57    pub fn scale(self, amount: Uint128) -> Curve {
58        Curve::SaturatingLinear(SaturatingLinear {
59            min_x: self.min_x,
60            min_y: amount * self.min_y,
61            max_x: self.max_x,
62            max_y: amount * self.max_y,
63        })
64    }
65}
66
67#[derive(Serialize, Deserialize, JsonSchema, Debug, Clone, PartialEq)]
68pub struct ScalablePiecewise {
69    pub steps: Vec<(u64, Decimal)>,
70}
71
72impl ScalablePiecewise {
73    pub fn scale(self, amount: Uint128) -> Curve {
74        let steps = self
75            .steps
76            .into_iter()
77            .map(|(x, y)| (x, amount * y))
78            .collect();
79        Curve::PiecewiseLinear(PiecewiseLinear { steps })
80    }
81}
82
83#[cfg(test)]
84mod test {
85    use super::*;
86
87    #[test]
88    fn scale_constant() {
89        let flex = ScalableCurve::Constant {
90            ratio: Decimal::percent(50),
91        };
92        let curve = flex.scale(Uint128::new(444));
93        assert_eq!(curve, Curve::constant(222));
94    }
95
96    #[test]
97    fn scale_linear() {
98        let (min_x, max_x) = (10000, 20000);
99        let flex = ScalableCurve::ScalableLinear(ScalableLinear {
100            min_x,
101            min_y: Decimal::percent(80),
102            max_x,
103            max_y: Decimal::percent(20),
104        });
105        let curve = flex.scale(Uint128::new(1100));
106        assert_eq!(curve, Curve::saturating_linear((min_x, 880), (max_x, 220)));
107    }
108
109    #[test]
110    fn scale_piecewise() {
111        let (x1, x2, x3) = (10000, 20000, 25000);
112        let flex = ScalableCurve::ScalablePiecewise(ScalablePiecewise {
113            steps: vec![
114                (x1, Decimal::percent(100)),
115                (x2, Decimal::percent(70)),
116                (x3, Decimal::percent(0)),
117            ],
118        });
119        let curve = flex.scale(Uint128::new(8000));
120        assert_eq!(
121            curve,
122            Curve::PiecewiseLinear(PiecewiseLinear {
123                steps: vec![
124                    (x1, Uint128::new(8000)),
125                    (x2, Uint128::new(5600)),
126                    (x3, Uint128::new(0)),
127                ]
128            })
129        );
130    }
131}