kabu_defi_uniswap_v3_math/
tick_math.rs1use alloy::primitives::{I256, U256};
2use std::ops::{BitOr, Neg, Shl, Shr};
3
4use crate::{
5 error::UniswapV3MathError, U256_1, U256_1024, U256_127, U256_128, U256_131072, U256_15, U256_16, U256_16384, U256_2, U256_2048,
6 U256_255, U256_256, U256_262144, U256_3, U256_32, U256_32768, U256_4, U256_4096, U256_5, U256_512, U256_524288, U256_6, U256_64,
7 U256_65536, U256_7, U256_8, U256_8192, U256_MAX_TICK,
8};
9
10pub const MIN_TICK: i32 = -887272;
11pub const MAX_TICK: i32 = -MIN_TICK;
12
13pub const MIN_SQRT_RATIO: U256 = U256::from_limbs([4295128739, 0, 0, 0]);
14pub const MAX_SQRT_RATIO: U256 = U256::from_limbs([6743328256752651558, 17280870778742802505, 4294805859, 0]);
15
16pub const SQRT_10001: I256 = I256::from_raw(U256::from_limbs([11745905768312294533, 13863, 0, 0]));
17pub const TICK_LOW: I256 = I256::from_raw(U256::from_limbs([6552757943157144234, 184476617836266586, 0, 0]));
18pub const TICK_HIGH: I256 = I256::from_raw(U256::from_limbs([4998474450511881007, 15793544031827761793, 0, 0]));
19
20pub fn get_sqrt_ratio_at_tick(tick: i32) -> Result<U256, UniswapV3MathError> {
21 let abs_tick = if tick < 0 { U256::from(tick.neg()) } else { U256::from(tick) };
22
23 if abs_tick > U256_MAX_TICK {
24 return Err(UniswapV3MathError::T);
25 }
26
27 let mut ratio = if abs_tick & (U256_1) != U256::ZERO {
28 U256::from_limbs([12262481743371124737, 18445821805675392311, 0, 0])
29 } else {
30 U256::from_limbs([0, 0, 1, 0])
31 };
32
33 if !(abs_tick & U256_2).is_zero() {
34 ratio = (ratio * U256::from_limbs([6459403834229662010, 18444899583751176498, 0, 0])) >> 128
35 }
36 if !(abs_tick & U256_4).is_zero() {
37 ratio = (ratio * U256::from_limbs([17226890335427755468, 18443055278223354162, 0, 0])) >> 128
38 }
39 if !(abs_tick & U256_8).is_zero() {
40 ratio = (ratio * U256::from_limbs([2032852871939366096, 18439367220385604838, 0, 0])) >> 128
41 }
42 if !(abs_tick & U256_16).is_zero() {
43 ratio = (ratio * U256::from_limbs([14545316742740207172, 18431993317065449817, 0, 0])) >> 128
44 }
45 if !(abs_tick & U256_32).is_zero() {
46 ratio = (ratio * U256::from_limbs([5129152022828963008, 18417254355718160513, 0, 0])) >> 128
47 }
48 if !(abs_tick & U256_64).is_zero() {
49 ratio = (ratio * U256::from_limbs([4894419605888772193, 18387811781193591352, 0, 0])) >> 128
50 }
51 if !(abs_tick & U256_128).is_zero() {
52 ratio = (ratio * U256::from_limbs([1280255884321894483, 18329067761203520168, 0, 0])) >> 128
53 }
54 if !(abs_tick & U256_256).is_zero() {
55 ratio = (ratio * U256::from_limbs([15924666964335305636, 18212142134806087854, 0, 0])) >> 128
56 }
57 if !(abs_tick & U256_512).is_zero() {
58 ratio = (ratio * U256::from_limbs([8010504389359918676, 17980523815641551639, 0, 0])) >> 128
59 }
60 if !(abs_tick & U256_1024).is_zero() {
61 ratio = (ratio * U256::from_limbs([10668036004952895731, 17526086738831147013, 0, 0])) >> 128
62 }
63 if !(abs_tick & U256_2048).is_zero() {
64 ratio = (ratio * U256::from_limbs([4878133418470705625, 16651378430235024244, 0, 0])) >> 128
65 }
66 if !(abs_tick & U256_4096).is_zero() {
67 ratio = (ratio * U256::from_limbs([9537173718739605541, 15030750278693429944, 0, 0])) >> 128
68 }
69 if !(abs_tick & U256_8192).is_zero() {
70 ratio = (ratio * U256::from_limbs([9972618978014552549, 12247334978882834399, 0, 0])) >> 128
71 }
72 if !(abs_tick & U256_16384).is_zero() {
73 ratio = (ratio * U256::from_limbs([10428997489610666743, 8131365268884726200, 0, 0])) >> 128
74 }
75 if !(abs_tick & U256_32768).is_zero() {
76 ratio = (ratio * U256::from_limbs([9305304367709015974, 3584323654723342297, 0, 0])) >> 128
77 }
78 if !(abs_tick & U256_65536).is_zero() {
79 ratio = (ratio * U256::from_limbs([14301143598189091785, 696457651847595233, 0, 0])) >> 128
80 }
81 if !(abs_tick & U256_131072).is_zero() {
82 ratio = (ratio * U256::from_limbs([7393154844743099908, 26294789957452057, 0, 0])) >> 128
83 }
84 if !(abs_tick & U256_262144).is_zero() {
85 ratio = (ratio * U256::from_limbs([2209338891292245656, 37481735321082, 0, 0])) >> 128
86 }
87 if !(abs_tick & U256_524288).is_zero() {
88 ratio = (ratio * U256::from_limbs([10518117631919034274, 76158723, 0, 0])) >> 128
89 }
90
91 if tick > 0 {
92 ratio = U256::MAX / ratio;
93 }
94
95 Ok((ratio >> 32) + if (ratio.wrapping_rem(U256_1 << 32)).is_zero() { U256::ZERO } else { U256_1 })
96}
97
98pub fn get_tick_at_sqrt_ratio(sqrt_price_x_96: U256) -> Result<i32, UniswapV3MathError> {
99 if !(sqrt_price_x_96 >= MIN_SQRT_RATIO && sqrt_price_x_96 < MAX_SQRT_RATIO) {
100 return Err(UniswapV3MathError::R);
101 }
102
103 let ratio: U256 = sqrt_price_x_96.shl(32);
104 let mut r = ratio;
105 let mut msb = U256::ZERO;
106
107 let mut f = if r > U256::from_limbs([18446744073709551615, 18446744073709551615, 0, 0]) { U256_1.shl(U256_7) } else { U256::ZERO };
108 msb = msb.bitor(f);
109 r = r.shr(f);
110
111 f = if r > U256::from_limbs([18446744073709551615, 0, 0, 0]) { U256_1.shl(U256_6) } else { U256::ZERO };
112 msb = msb.bitor(f);
113 r = r.shr(f);
114
115 f = if r > U256::from_limbs([4294967295, 0, 0, 0]) { U256_1.shl(U256_5) } else { U256::ZERO };
116 msb = msb.bitor(f);
117 r = r.shr(f);
118
119 f = if r > U256::from_limbs([65535, 0, 0, 0]) { U256_1.shl(U256_4) } else { U256::ZERO };
120 msb = msb.bitor(f);
121 r = r.shr(f);
122
123 f = if r > U256_255 { U256_1.shl(U256_3) } else { U256::ZERO };
124 msb = msb.bitor(f);
125 r = r.shr(f);
126
127 f = if r > U256_15 { U256_1.shl(U256_2) } else { U256::ZERO };
128 msb = msb.bitor(f);
129 r = r.shr(f);
130
131 f = if r > U256_3 { U256_1.shl(U256_1) } else { U256::ZERO };
132 msb = msb.bitor(f);
133 r = r.shr(f);
134
135 f = if r > U256_1 { U256_1 } else { U256::ZERO };
136
137 msb = msb.bitor(f);
138
139 r = if msb >= U256_128 { ratio.shr(msb - U256_127) } else { ratio.shl(U256_127 - msb) };
140
141 let mut log_2: I256 = (I256::from_raw(msb) - I256::from_limbs([128, 0, 0, 0])).shl(64);
142
143 for i in (51..=63).rev() {
144 r = r.overflowing_mul(r).0.shr(U256_127);
145 let f: U256 = r.shr(128);
146 log_2 = log_2.bitor(I256::from_raw(f.shl(i)));
147
148 r = r.shr(f);
149 }
150
151 r = r.overflowing_mul(r).0.shr(U256_127);
152 let f: U256 = r.shr(128);
153 log_2 = log_2.bitor(I256::from_raw(f.shl(50)));
154
155 let log_sqrt10001 = log_2.wrapping_mul(SQRT_10001);
156
157 let tick_low = ((log_sqrt10001 - TICK_LOW) >> 128_u8).low_i32();
158
159 let tick_high = ((log_sqrt10001 + TICK_HIGH) >> 128_u8).low_i32();
160
161 let tick = if tick_low == tick_high {
162 tick_low
163 } else if get_sqrt_ratio_at_tick(tick_high)? <= sqrt_price_x_96 {
164 tick_high
165 } else {
166 tick_low
167 };
168
169 Ok(tick)
170}
171
172#[cfg(test)]
173mod test {
174 use super::*;
175 use std::{ops::Sub, str::FromStr};
176
177 #[test]
178 fn test_get_sqrt_ratio_at_tick_bounds() {
179 if let Err(err) = get_sqrt_ratio_at_tick(MIN_TICK - 1) {
181 assert!(matches!(err, UniswapV3MathError::T));
182 } else {
183 panic!("get_qrt_ratio_at_tick did not respect lower tick bound")
184 }
185 if let Err(err) = get_sqrt_ratio_at_tick(MAX_TICK + 1) {
186 assert!(matches!(err, UniswapV3MathError::T));
187 } else {
188 panic!("get_qrt_ratio_at_tick did not respect upper tick bound")
189 }
190 }
191
192 #[test]
193 fn test_get_sqrt_ratio_at_tick_values() {
194 assert_eq!(get_sqrt_ratio_at_tick(MIN_TICK).unwrap(), U256::from(4295128739u64), "sqrt ratio at min incorrect");
196 assert_eq!(get_sqrt_ratio_at_tick(MIN_TICK + 1).unwrap(), U256::from(4295343490u64), "sqrt ratio at min + 1 incorrect");
197 assert_eq!(
198 get_sqrt_ratio_at_tick(MAX_TICK - 1).unwrap(),
199 U256::from_str("1461373636630004318706518188784493106690254656249").unwrap(),
200 "sqrt ratio at max - 1 incorrect"
201 );
202 assert_eq!(
203 get_sqrt_ratio_at_tick(MAX_TICK).unwrap(),
204 U256::from_str("1461446703485210103287273052203988822378723970342").unwrap(),
205 "sqrt ratio at max incorrect"
206 );
207 assert_eq!(get_sqrt_ratio_at_tick(50).unwrap(), U256::from(79426470787362580746886972461u128), "sqrt ratio at 50 incorrect");
209 assert_eq!(get_sqrt_ratio_at_tick(100).unwrap(), U256::from(79625275426524748796330556128u128), "sqrt ratio at 100 incorrect");
210 assert_eq!(get_sqrt_ratio_at_tick(250).unwrap(), U256::from(80224679980005306637834519095u128), "sqrt ratio at 250 incorrect");
211 assert_eq!(get_sqrt_ratio_at_tick(500).unwrap(), U256::from(81233731461783161732293370115u128), "sqrt ratio at 500 incorrect");
212 assert_eq!(get_sqrt_ratio_at_tick(1000).unwrap(), U256::from(83290069058676223003182343270u128), "sqrt ratio at 1000 incorrect");
213 assert_eq!(get_sqrt_ratio_at_tick(2500).unwrap(), U256::from(89776708723587163891445672585u128), "sqrt ratio at 2500 incorrect");
214 assert_eq!(get_sqrt_ratio_at_tick(3000).unwrap(), U256::from(92049301871182272007977902845u128), "sqrt ratio at 3000 incorrect");
215 assert_eq!(get_sqrt_ratio_at_tick(4000).unwrap(), U256::from(96768528593268422080558758223u128), "sqrt ratio at 4000 incorrect");
216 assert_eq!(get_sqrt_ratio_at_tick(5000).unwrap(), U256::from(101729702841318637793976746270u128), "sqrt ratio at 5000 incorrect");
217 assert_eq!(get_sqrt_ratio_at_tick(50000).unwrap(), U256::from(965075977353221155028623082916u128), "sqrt ratio at 50000 incorrect");
218 assert_eq!(
219 get_sqrt_ratio_at_tick(150000).unwrap(),
220 U256::from(143194173941309278083010301478497u128),
221 "sqrt ratio at 150000 incorrect"
222 );
223 assert_eq!(
224 get_sqrt_ratio_at_tick(250000).unwrap(),
225 U256::from(21246587762933397357449903968194344u128),
226 "sqrt ratio at 250000 incorrect"
227 );
228 assert_eq!(
229 get_sqrt_ratio_at_tick(500000).unwrap(),
230 U256::from_str("5697689776495288729098254600827762987878").unwrap(),
231 "sqrt ratio at 500000 incorrect"
232 );
233 assert_eq!(
234 get_sqrt_ratio_at_tick(738203).unwrap(),
235 U256::from_str("847134979253254120489401328389043031315994541").unwrap(),
236 "sqrt ratio at 738203 incorrect"
237 );
238 }
239
240 #[test]
241 pub fn test_get_tick_at_sqrt_ratio() {
242 let result = get_tick_at_sqrt_ratio(MIN_SQRT_RATIO.sub(U256_1));
244 assert_eq!(
245 result.unwrap_err().to_string(),
246 "Second inequality must be < because the price can never reach the price at the max tick"
247 );
248
249 let result = get_tick_at_sqrt_ratio(MAX_SQRT_RATIO);
251 assert_eq!(
252 result.unwrap_err().to_string(),
253 "Second inequality must be < because the price can never reach the price at the max tick"
254 );
255
256 let result = get_tick_at_sqrt_ratio(MIN_SQRT_RATIO).unwrap();
258 assert_eq!(result, MIN_TICK);
259
260 let result = get_tick_at_sqrt_ratio(U256::from_str("4295343490").unwrap()).unwrap();
262 assert_eq!(result, MIN_TICK + 1);
263 }
264}