vertex_sdk/vertex_utils/
math.rs

1use ethers::types::{I256, U256};
2use eyre::{eyre, Result};
3
4pub const ONE_X18: i128 = 1000000000000000000;
5pub const ONE_X6: i128 = 1_000_000;
6const UONE: u128 = 1000000000000000000;
7
8fn signed_to_unsigned(x: i128, y: i128) -> (u128, u128, i128) {
9    if x >= 0 && y >= 0 {
10        (x as u128, y as u128, 1)
11    } else if x >= 0 && y < 0 {
12        (x as u128, (-y) as u128, -1)
13    } else if x < 0 && y >= 0 {
14        ((-x) as u128, y as u128, -1)
15    } else {
16        ((-x) as u128, (-y) as u128, 1)
17    }
18}
19
20pub fn mul_x18(x: i128, y: i128) -> i128 {
21    let (mut x, mut y, sign) = signed_to_unsigned(x, y);
22    if x > y {
23        std::mem::swap(&mut x, &mut y);
24    }
25    (if y < UONE {
26        x * y / UONE
27    } else if x < UONE {
28        let (c, d) = (y / UONE, y % UONE);
29        x * c + x * d / UONE
30    } else {
31        let (a, b) = (x / UONE, x % UONE);
32        let (c, d) = (y / UONE, y % UONE);
33        a * c * UONE + a * d + b * c + b * d / UONE
34    } as i128)
35        * sign
36}
37
38// TODO: replacing mul_x18 with fmul_x18 can result in 1.5-2x speedup
39pub fn fmul_x18(x: i128, y: i128) -> i128 {
40    ((x as f64 * y as f64) / 1e18) as i128
41}
42
43pub fn div_x18(x: i128, y: i128) -> i128 {
44    let (mut x, y, sign) = signed_to_unsigned(x, y);
45    let mut ret = 0;
46    if x >= y {
47        ret += x / y * UONE;
48        x %= y;
49    }
50    if x <= UONE {
51        ret += x * UONE / y;
52    } else {
53        ret += (U256::from(x) * U256::from(UONE) / U256::from(y)).low_u128()
54    }
55    (ret as i128) * sign
56}
57
58pub fn pow_x18(x: i128, y: i128) -> i128 {
59    let xf = x18_to_f64(x);
60    let yf = x18_to_f64(y);
61    let resultf = xf.powf(yf);
62    let mut result = (resultf.trunc() as i128) * ONE_X18;
63    result += (resultf.fract() * 1e18f64) as i128;
64    result
65}
66
67pub fn sqrt_x18(x: i128) -> i128 {
68    pow_x18(x, ONE_X18 / 2)
69}
70
71pub fn mul_div_x18(x: i128, y: i128, z: i128) -> i128 {
72    (I256::from(x) * I256::from(y) / I256::from(z)).low_i128()
73}
74
75pub fn x18_to_f64_mil(x: i128) -> f64 {
76    let x = x / 1_000_000;
77    x18_to_f64(x)
78}
79
80pub fn x18_to_f64(x: i128) -> f64 {
81    let mut result = (x / ONE_X18) as f64;
82    result += (x % ONE_X18) as f64 / 1e18;
83    result
84}
85
86pub fn f64_to_x18(x: f64) -> i128 {
87    let mut result = (x.trunc() as i128) * ONE_X18;
88    result += (x.fract() * 1e18) as i128;
89    result
90}
91
92pub fn split_i256(x: I256) -> (i128, i128) {
93    let base = I256::from(2).pow(127);
94    ((x / base).as_i128(), (x % base).as_i128())
95}
96
97pub fn merge_i128(x: i128, y: i128) -> I256 {
98    let base = I256::from(2).pow(127);
99    I256::from(x) * base + I256::from(y)
100}
101
102pub fn i256_to_f64(x: I256) -> f64 {
103    let (high, low) = split_i256(x);
104    x18_to_f64(high) * (2.0_f64).powi(127) + x18_to_f64(low)
105}
106
107pub fn to_u128_x18(x: u128) -> u128 {
108    x * (ONE_X18 as u128)
109}
110
111pub fn to_i128_x18(x: i128) -> i128 {
112    x * ONE_X18
113}
114
115pub fn to_i128_fp(x: f64) -> i128 {
116    (x * 10.0_f64.powi(9)) as i128 * 1000000000
117}
118
119pub fn to_i32_fp(x: f64) -> i32 {
120    (x * 10.0_f64.powi(9)) as i32
121}
122
123pub fn to_u128_x6(x: u128) -> u128 {
124    x * 1000000
125}
126
127pub fn to_i128_x6(x: i128) -> i128 {
128    x * 1000000
129}
130
131pub fn fexp_x18(mut x: i128, y: i128) -> i128 {
132    assert!(y >= 0);
133    let mut i = 1;
134    let mut ret = to_i128_x18(1);
135    while i <= y {
136        if i & y != 0 {
137            ret = mul_x18(ret, x);
138        }
139        x = mul_x18(x, x);
140        i <<= 1;
141    }
142    ret
143}
144
145pub fn fexp(mut x: i128, y: i128) -> i128 {
146    assert!(y >= 0);
147    let mut i = 1;
148    let mut ret = 1;
149    while i <= y {
150        if i & y != 0 {
151            ret *= x;
152        }
153        x *= x;
154        i <<= 1;
155    }
156    ret
157}
158
159pub fn check_diff_gt_threshold_x18(left_x18: i128, right_x18: i128, threshold: f64) -> bool {
160    let diff = (left_x18 - right_x18).abs();
161    let percent_diff = div_x18(diff, right_x18);
162    let percent_threshold: f64 = threshold * 1e18f64;
163    percent_diff > percent_threshold as i128
164}
165
166pub fn check_within_range_x18(
167    left_x18: i128,
168    right_x18: i128,
169    threshold_lower: f64,
170    threshold_upper: f64,
171) -> bool {
172    let percent = div_x18(left_x18, right_x18);
173    let percent_threshold_lower: f64 = threshold_lower * 1e18f64;
174    let percent_threshold_upper: f64 = threshold_upper * 1e18f64;
175    (percent > percent_threshold_lower as i128) && (percent < percent_threshold_upper as i128)
176}
177
178pub trait TryMath {
179    fn try_add(self, v: i128) -> Result<i128>;
180    fn try_div(self, v: i128) -> Result<i128>;
181    fn try_mul(self, v: i128) -> Result<i128>;
182    fn try_sub(self, v: i128) -> Result<i128>;
183    fn try_rem(self, v: i128) -> Result<i128>;
184    fn try_mul_x18(self, v: i128) -> Result<i128>;
185    fn try_div_x18(self, v: i128) -> Result<i128>;
186    fn try_sqrt_x18(self) -> Result<i128>;
187}
188
189impl TryMath for i128 {
190    fn try_add(self, v: i128) -> Result<i128> {
191        self.checked_add(v).ok_or(eyre!("Overflow: add"))
192    }
193
194    fn try_div(self, v: i128) -> Result<i128> {
195        self.checked_div(v).ok_or(eyre!("Overflow: div"))
196    }
197
198    fn try_mul(self, v: i128) -> Result<i128> {
199        self.checked_mul(v).ok_or(eyre!("Overflow: mul"))
200    }
201
202    fn try_sub(self, v: i128) -> Result<i128> {
203        self.checked_sub(v).ok_or(eyre!("Overflow: sub"))
204    }
205
206    fn try_rem(self, v: i128) -> Result<i128> {
207        self.checked_rem(v).ok_or(eyre!("Overflow: rem"))
208    }
209
210    fn try_mul_x18(self, v: i128) -> Result<i128> {
211        Ok((I256::from(self) * I256::from(v) / I256::exp10(18)).as_i128())
212    }
213
214    fn try_div_x18(self, v: i128) -> Result<i128> {
215        Ok((I256::from(self) * I256::exp10(18) / I256::from(v)).as_i128())
216    }
217
218    fn try_sqrt_x18(self) -> Result<i128> {
219        let mut hi = 1;
220        while hi.try_mul_x18(hi)? < self {
221            hi = hi.try_mul(2)?;
222        }
223        let mut lo = hi.try_div(2)?;
224        while lo < hi {
225            let mid = lo.try_add(hi)?.try_div(2)?;
226            if mid.try_mul_x18(mid)? < self {
227                lo = mid.try_add(1)?;
228            } else {
229                hi = mid;
230            }
231        }
232        Ok(lo)
233    }
234}
235
236pub fn lp_value(balance: i128, x: i128, y: i128, supply: i128, price: i128) -> i128 {
237    if supply == 0 {
238        0
239    } else {
240        let pool_total_value = mul_x18(x, price) + y;
241        mul_div_x18(balance, pool_total_value, supply)
242    }
243}
244
245pub fn round(value: i128, increment: i128) -> i128 {
246    return value - value % increment;
247}