wynd_utils/
scalable_curve.rs1use 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#[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}