1use alloy_primitives::{U160, U256};
17
18use super::full_math::FullMath;
19use crate::{
20 defi::tick_map::tick_math::get_sqrt_ratio_at_tick,
21 types::{PRICE_RAW_MAX, PRICE_RAW_MIN, Price, fixed::FIXED_PRECISION},
22};
23
24pub fn encode_sqrt_ratio_x96(amount0: u128, amount1: u128) -> U160 {
36 let amount0_u256 = U256::from(amount0);
37 let amount1_u256 = U256::from(amount1);
38
39 if amount1_u256.is_zero() {
40 panic!("Division by zero");
41 }
42 if amount0_u256.is_zero() {
43 return U160::ZERO;
44 }
45
46 let q192 = U256::from(1u128) << 192;
52
53 if amount0_u256 > U256::MAX / q192 {
55 let sqrt_amount0 = FullMath::sqrt(amount0_u256);
58 let sqrt_amount1 = FullMath::sqrt(amount1_u256);
59
60 if sqrt_amount1.is_zero() {
61 panic!("Division by zero in sqrt");
62 }
63
64 let q96 = U256::from(1u128) << 96;
65
66 let result = FullMath::mul_div(sqrt_amount0, q96, sqrt_amount1).expect("mul_div overflow");
68
69 return if result > U256::from(U160::MAX) {
71 U160::MAX
72 } else {
73 U160::from(result)
74 };
75 }
76
77 let ratio_q192 = FullMath::mul_div(amount0_u256, q192, amount1_u256).expect("mul_div overflow");
79
80 let sqrt_result = FullMath::sqrt(ratio_q192);
82
83 if sqrt_result > U256::from(U160::MAX) {
85 U160::MAX
86 } else {
87 U160::from(sqrt_result)
88 }
89}
90
91fn get_next_sqrt_price_from_amount0_rounding_up(
93 sqrt_price_x96: U160,
94 liquidity: u128,
95 amount: U256,
96 add: bool,
97) -> U160 {
98 if amount.is_zero() {
99 return sqrt_price_x96;
100 }
101 let numerator = U256::from(liquidity) << 96;
102 let sqrt_price_x96 = U256::from(sqrt_price_x96);
103 let product = amount * sqrt_price_x96;
104
105 if add {
106 if product / amount == sqrt_price_x96 {
107 let denominator = numerator + product;
108 if denominator >= numerator {
109 let result = FullMath::mul_div_rounding_up(numerator, sqrt_price_x96, denominator)
111 .expect("mul_div_rounding_up failed");
112 return U160::from(result);
113 }
114 }
115
116 let fallback_denominator = (numerator / sqrt_price_x96) + amount;
118 let result = FullMath::div_rounding_up(numerator, fallback_denominator)
119 .expect("div_rounding_up failed");
120
121 if result > U256::from(U160::MAX) {
123 panic!("Result overflows U160");
124 }
125 U160::from(result)
126 } else {
127 if !((product / amount) == sqrt_price_x96 && numerator > product) {
129 panic!("Invalid conditions for amount0 removal: overflow or underflow detected")
130 }
131
132 let denominator = numerator - product;
133 let result = FullMath::mul_div_rounding_up(numerator, sqrt_price_x96, denominator)
134 .expect("mul_div_rounding_up failed");
135 U160::from(result)
136 }
137}
138
139fn get_next_sqrt_price_from_amount1_rounding_down(
141 sqrt_price_x96: U160,
142 liquidity: u128,
143 amount: U256,
144 add: bool,
145) -> U160 {
146 if add {
149 let quotient = if amount <= U256::from(U160::MAX) {
150 (amount << 96) / U256::from(liquidity)
152 } else {
153 FullMath::mul_div(amount, U256::from(1u128) << 96, U256::from(liquidity))
155 .unwrap_or(U256::ZERO)
156 };
157
158 U160::from(U256::from(sqrt_price_x96) + quotient)
160 } else {
161 let quotient = if amount <= U256::from(U160::MAX) {
162 FullMath::div_rounding_up(amount << 96, U256::from(liquidity)).unwrap_or(U256::ZERO)
164 } else {
165 FullMath::mul_div_rounding_up(amount, U256::from(1u128) << 96, U256::from(liquidity))
167 .unwrap_or(U256::ZERO)
168 };
169
170 if U256::from(sqrt_price_x96) <= quotient {
172 panic!("sqrt_price_x96 must be greater than quotient");
173 }
174
175 U160::from(U256::from(sqrt_price_x96) - quotient)
177 }
178}
179
180pub fn get_next_sqrt_price_from_input(
185 sqrt_price_x96: U160,
186 liquidity: u128,
187 amount_in: U256,
188 zero_for_one: bool,
189) -> U160 {
190 assert!(
191 sqrt_price_x96 > U160::ZERO,
192 "sqrt_price_x96 must be greater than zero"
193 );
194 assert!(liquidity > 0, "Liquidity must be greater than zero");
195
196 if zero_for_one {
197 get_next_sqrt_price_from_amount0_rounding_up(sqrt_price_x96, liquidity, amount_in, true)
198 } else {
199 get_next_sqrt_price_from_amount1_rounding_down(sqrt_price_x96, liquidity, amount_in, true)
200 }
201}
202
203pub fn get_next_sqrt_price_from_output(
208 sqrt_price_x96: U160,
209 liquidity: u128,
210 amount_out: U256,
211 zero_for_one: bool,
212) -> U160 {
213 assert!(
214 sqrt_price_x96 > U160::ZERO,
215 "sqrt_price_x96 must be greater than zero"
216 );
217 assert!(liquidity > 0, "Liquidity must be greater than zero");
218
219 if zero_for_one {
220 get_next_sqrt_price_from_amount1_rounding_down(sqrt_price_x96, liquidity, amount_out, false)
221 } else {
222 get_next_sqrt_price_from_amount0_rounding_up(sqrt_price_x96, liquidity, amount_out, false)
223 }
224}
225
226#[must_use]
228pub fn get_amount0_delta(
229 sqrt_ratio_ax96: U160,
230 sqrt_ratio_bx96: U160,
231 liquidity: u128,
232 round_up: bool,
233) -> U256 {
234 let (sqrt_ratio_a, sqrt_ratio_b) = if sqrt_ratio_ax96 > sqrt_ratio_bx96 {
235 (sqrt_ratio_bx96, sqrt_ratio_ax96)
236 } else {
237 (sqrt_ratio_ax96, sqrt_ratio_bx96)
238 };
239
240 let numerator1 = U256::from(liquidity) << 96;
241 let numerator2 = U256::from(sqrt_ratio_b - sqrt_ratio_a);
242
243 if round_up {
244 let result =
246 FullMath::mul_div_rounding_up(numerator1, numerator2, U256::from(sqrt_ratio_b))
247 .unwrap_or(U256::ZERO);
248
249 FullMath::div_rounding_up(result, U256::from(sqrt_ratio_a)).unwrap_or(U256::ZERO)
251 } else {
252 let result = FullMath::mul_div(numerator1, numerator2, U256::from(sqrt_ratio_b))
253 .unwrap_or(U256::ZERO);
254 result / U256::from(sqrt_ratio_a)
255 }
256}
257#[must_use]
259pub fn get_amount1_delta(
260 sqrt_ratio_ax96: U160,
261 sqrt_ratio_bx96: U160,
262 liquidity: u128,
263 round_up: bool,
264) -> U256 {
265 let (sqrt_ratio_a, sqrt_ratio_b) = if sqrt_ratio_ax96 > sqrt_ratio_bx96 {
266 (sqrt_ratio_bx96, sqrt_ratio_ax96)
267 } else {
268 (sqrt_ratio_ax96, sqrt_ratio_bx96)
269 };
270
271 let liquidity_u256 = U256::from(liquidity);
272 let sqrt_ratio_diff = U256::from(sqrt_ratio_b - sqrt_ratio_a);
273 let q96 = U256::from(1u128) << 96;
274
275 if round_up {
276 FullMath::mul_div_rounding_up(liquidity_u256, sqrt_ratio_diff, q96).unwrap_or(U256::ZERO)
277 } else {
278 FullMath::mul_div(liquidity_u256, sqrt_ratio_diff, q96).unwrap_or(U256::ZERO)
279 }
280}
281
282#[must_use]
284pub fn get_amounts_for_liquidity(
285 sqrt_ratio_x96: U160,
286 tick_lower: i32,
287 tick_upper: i32,
288 liquidity: u128,
289 round_up: bool,
290) -> (U256, U256) {
291 let sqrt_ratio_lower_x96 = get_sqrt_ratio_at_tick(tick_lower);
292 let sqrt_ratio_upper_x96 = get_sqrt_ratio_at_tick(tick_upper);
293
294 let (sqrt_ratio_a, sqrt_ratio_b) = if sqrt_ratio_lower_x96 > sqrt_ratio_upper_x96 {
296 (sqrt_ratio_upper_x96, sqrt_ratio_lower_x96)
297 } else {
298 (sqrt_ratio_lower_x96, sqrt_ratio_upper_x96)
299 };
300
301 let amount0 = if sqrt_ratio_x96 <= sqrt_ratio_a {
302 get_amount0_delta(sqrt_ratio_a, sqrt_ratio_b, liquidity, round_up)
304 } else if sqrt_ratio_x96 < sqrt_ratio_b {
305 get_amount0_delta(sqrt_ratio_x96, sqrt_ratio_b, liquidity, round_up)
307 } else {
308 U256::ZERO
310 };
311
312 let amount1 = if sqrt_ratio_x96 < sqrt_ratio_a {
313 U256::ZERO
315 } else if sqrt_ratio_x96 < sqrt_ratio_b {
316 get_amount1_delta(sqrt_ratio_a, sqrt_ratio_x96, liquidity, round_up)
318 } else {
319 get_amount1_delta(sqrt_ratio_a, sqrt_ratio_b, liquidity, round_up)
321 };
322
323 (amount0, amount1)
324}
325
326pub fn expand_to_18_decimals(amount: u64) -> u128 {
328 amount as u128 * 10u128.pow(18)
329}
330
331pub fn decode_sqrt_price_x96_to_price(sqrt_price_x96: U160) -> anyhow::Result<Price> {
340 let sqrt_price = U256::from(sqrt_price_x96);
341 let price_x192 = sqrt_price * sqrt_price;
342
343 let fixed_scalar = U256::from(10u128.pow(FIXED_PRECISION as u32));
344 let divisor = U256::from(1u128) << 192;
345 let price_raw_u256 = FullMath::mul_div(price_x192, fixed_scalar, divisor)?;
346
347 let price_raw = price_raw_u256
348 .try_into()
349 .map_err(|_| anyhow::anyhow!("Price overflow: {price_raw_u256} exceeds PriceRaw range"))?;
350
351 Ok(Price::from_raw(price_raw, FIXED_PRECISION))
352}
353
354pub fn decode_sqrt_price_x96_to_price_tokens_adjusted(
369 sqrt_price_x96: U160,
370 token0_decimals: u8,
371 token1_decimals: u8,
372 invert: bool,
373) -> anyhow::Result<Price> {
374 let sqrt_price = U256::from(sqrt_price_x96);
375 let price_x192 = sqrt_price * sqrt_price;
376
377 let decimal_diff = token0_decimals as i32 - token1_decimals as i32;
378 let fixed_scalar = U256::from(10u128.pow(FIXED_PRECISION as u32));
379 let divisor_base = U256::from(1u128) << 192;
380
381 let numerator = if invert {
382 if decimal_diff >= 0 {
383 let decimal_adjustment = U256::from(10u128.pow(decimal_diff.unsigned_abs()));
384 let denominator = FullMath::mul_div(price_x192, decimal_adjustment, U256::from(1))?;
385 FullMath::mul_div(divisor_base, fixed_scalar, denominator)?
386 } else {
387 let decimal_adjustment = U256::from(10u128.pow(decimal_diff.unsigned_abs()));
388 let numerator_adjusted =
389 FullMath::mul_div(divisor_base, decimal_adjustment, U256::from(1))?;
390 FullMath::mul_div(numerator_adjusted, fixed_scalar, price_x192)?
391 }
392 } else if decimal_diff >= 0 {
393 let decimal_adjustment = U256::from(10u128.pow(decimal_diff.unsigned_abs()));
394 let temp = FullMath::mul_div(price_x192, decimal_adjustment, U256::from(1))?;
395 FullMath::mul_div(temp, fixed_scalar, divisor_base)?
396 } else {
397 let decimal_adjustment = U256::from(10u128.pow(decimal_diff.unsigned_abs()));
398 let divisor_adjusted = divisor_base * decimal_adjustment;
399 FullMath::mul_div(price_x192, fixed_scalar, divisor_adjusted)?
400 };
401
402 let price_raw: i128 = numerator
403 .try_into()
404 .map_err(|_| anyhow::anyhow!("Price overflow: {numerator} exceeds PriceRaw range"))?;
405
406 if price_raw > PRICE_RAW_MAX {
408 anyhow::bail!("Price {price_raw} exceeds maximum valid price {PRICE_RAW_MAX}");
409 }
410 if price_raw < PRICE_RAW_MIN {
411 anyhow::bail!("Price {price_raw} is below minimum valid price {PRICE_RAW_MIN}");
412 }
413
414 Ok(Price::from_raw(price_raw, FIXED_PRECISION))
415}
416
417#[cfg(test)]
418mod tests {
419 use rstest::*;
421
422 use super::*;
423 use crate::defi::tick_map::full_math::Q96_U160;
424
425 #[rstest]
426 #[should_panic(expected = "sqrt_price_x96 must be greater than zero")]
427 fn test_if_get_next_sqrt_price_from_input_panic_if_price_zero() {
428 let _ = get_next_sqrt_price_from_input(U160::ZERO, 1, U256::ZERO, true);
429 }
430
431 #[rstest]
432 #[should_panic(expected = "Liquidity must be greater than zero")]
433 fn test_if_get_next_sqrt_price_from_input_panic_if_liquidity_zero() {
434 let _ = get_next_sqrt_price_from_input(U160::from(1), 0, U256::ZERO, true);
435 }
436
437 #[rstest]
438 #[should_panic(expected = "Uint conversion error: Value is too large for Uint<160>")]
439 fn test_if_get_next_sqrt_price_from_input_panics_from_big_price() {
440 let price = U160::MAX - U160::from(1);
441 let _ = get_next_sqrt_price_from_input(price, 1024, U256::from(1024), false);
442 }
443
444 #[rstest]
445 fn test_any_input_amount_cannot_underflow_the_price() {
446 let price = U160::from(1);
449 let liquidity = 1;
450 let amount_in = U256::from(2).pow(U256::from(255));
451 let result = get_next_sqrt_price_from_input(price, liquidity, amount_in, true);
452 assert_eq!(result, U160::from(1));
453 }
454
455 #[rstest]
456 fn test_returns_input_price_if_amount_in_is_zero_and_zero_for_one_true() {
457 let price = encode_sqrt_ratio_x96(1, 1);
458 let liquidity = expand_to_18_decimals(1) / 10;
459 let result = get_next_sqrt_price_from_input(price, liquidity, U256::ZERO, true);
460 assert_eq!(result, price);
461 }
462
463 #[rstest]
464 fn test_returns_input_price_if_amount_in_is_zero_and_zero_for_one_false() {
465 let price = encode_sqrt_ratio_x96(1, 1);
466 let liquidity = expand_to_18_decimals(1) / 10;
467 let result = get_next_sqrt_price_from_input(price, liquidity, U256::ZERO, false);
468 assert_eq!(result, price);
469 }
470
471 #[rstest]
472 fn test_returns_the_minimum_price_for_max_inputs() {
473 let sqrt_p = U160::MAX;
474 let liquidity = u128::MAX;
475 let max_amount_no_overflow = U256::MAX - (U256::from(liquidity) << 96) / U256::from(sqrt_p);
476 let result =
477 get_next_sqrt_price_from_input(sqrt_p, liquidity, max_amount_no_overflow, true);
478 assert_eq!(result, U160::from(1));
479 }
480
481 #[rstest]
482 fn test_input_amount_of_0_1_token1() {
483 let sqrt_q = get_next_sqrt_price_from_input(
484 encode_sqrt_ratio_x96(1, 1),
485 expand_to_18_decimals(1),
486 U256::from(expand_to_18_decimals(1)) / U256::from(10),
487 false,
488 );
489 assert_eq!(
490 sqrt_q,
491 U160::from_str_radix("87150978765690771352898345369", 10).unwrap()
492 );
493 }
494
495 #[rstest]
496 fn test_input_amount_of_0_1_token0() {
497 let sqrt_q = get_next_sqrt_price_from_input(
498 encode_sqrt_ratio_x96(1, 1),
499 expand_to_18_decimals(1),
500 U256::from(expand_to_18_decimals(1)) / U256::from(10),
501 true,
502 );
503 assert_eq!(
504 sqrt_q,
505 U160::from_str_radix("72025602285694852357767227579", 10).unwrap()
506 );
507 }
508
509 #[rstest]
510 fn test_amount_in_greater_than_uint96_max_and_zero_for_one_true() {
511 let result = get_next_sqrt_price_from_input(
512 encode_sqrt_ratio_x96(1, 1),
513 expand_to_18_decimals(10),
514 U256::from(2).pow(U256::from(100)),
515 true,
516 );
517 assert_eq!(
518 result,
519 U160::from_str_radix("624999999995069620", 10).unwrap()
520 );
521 }
522
523 #[rstest]
524 fn test_can_return_1_with_enough_amount_in_and_zero_for_one_true() {
525 let result = get_next_sqrt_price_from_input(
526 encode_sqrt_ratio_x96(1, 1),
527 1,
528 U256::MAX / U256::from(2),
529 true,
530 );
531 assert_eq!(result, U160::from(1));
532 }
533
534 #[rstest]
535 #[should_panic(
536 expected = "Invalid conditions for amount0 removal: overflow or underflow detected"
537 )]
538 fn test_fails_if_output_amount_is_exactly_virtual_reserves_of_token0() {
539 let price = U160::from_str_radix("20282409603651670423947251286016", 10).unwrap();
540 let liquidity = 1024;
541 let amount_out = U256::from(4);
542 let _ = get_next_sqrt_price_from_output(price, liquidity, amount_out, false);
543 }
544
545 #[rstest]
546 #[should_panic(
547 expected = "Invalid conditions for amount0 removal: overflow or underflow detected"
548 )]
549 fn test_fails_if_output_amount_is_greater_than_virtual_reserves_of_token0() {
550 let price = U160::from_str_radix("20282409603651670423947251286016", 10).unwrap();
551 let liquidity = 1024;
552 let amount_out = U256::from(5);
553 let _ = get_next_sqrt_price_from_output(price, liquidity, amount_out, false);
554 }
555
556 #[rstest]
557 #[should_panic(expected = "sqrt_price_x96 must be greater than quotient")]
558 fn test_fails_if_output_amount_is_greater_than_virtual_reserves_of_token1() {
559 let price = U160::from_str_radix("20282409603651670423947251286016", 10).unwrap();
560 let liquidity = 1024;
561 let amount_out = U256::from(262145);
562 let _ = get_next_sqrt_price_from_output(price, liquidity, amount_out, true);
563 }
564
565 #[rstest]
566 #[should_panic(expected = "sqrt_price_x96 must be greater than quotient")]
567 fn test_fails_if_output_amount_is_exactly_virtual_reserves_of_token1() {
568 let price = U160::from_str_radix("20282409603651670423947251286016", 10).unwrap();
569 let liquidity = 1024;
570 let amount_out = U256::from(262144);
571 let _ = get_next_sqrt_price_from_output(price, liquidity, amount_out, true);
572 }
573
574 #[rstest]
575 fn test_succeeds_if_output_amount_is_just_less_than_virtual_reserves_of_token1() {
576 let price = U160::from_str_radix("20282409603651670423947251286016", 10).unwrap();
577 let liquidity = 1024;
578 let amount_out = U256::from(262143);
579 let result = get_next_sqrt_price_from_output(price, liquidity, amount_out, true);
580 assert_eq!(
581 result,
582 U160::from_str_radix("77371252455336267181195264", 10).unwrap()
583 );
584 }
585
586 #[rstest]
587 fn test_returns_input_price_if_amount_out_is_zero_and_zero_for_one_true() {
588 let price = encode_sqrt_ratio_x96(1, 1);
589 let liquidity = expand_to_18_decimals(1) / 10;
590 let result = get_next_sqrt_price_from_output(price, liquidity, U256::ZERO, true);
591 assert_eq!(result, price);
592 }
593
594 #[rstest]
595 fn test_returns_input_price_if_amount_out_is_zero_and_zero_for_one_false() {
596 let price = encode_sqrt_ratio_x96(1, 1);
597 let liquidity = expand_to_18_decimals(1) / 10;
598 let result = get_next_sqrt_price_from_output(price, liquidity, U256::ZERO, false);
599 assert_eq!(result, price);
600 }
601
602 #[rstest]
603 fn test_output_amount_of_0_1_token1_zero_for_one_false() {
604 let sqrt_q = get_next_sqrt_price_from_output(
605 encode_sqrt_ratio_x96(1, 1),
606 expand_to_18_decimals(1),
607 U256::from(expand_to_18_decimals(1)) / U256::from(10),
608 false,
609 );
610 assert_eq!(
611 sqrt_q,
612 U160::from_str_radix("88031291682515930659493278152", 10).unwrap()
613 );
614 }
615
616 #[rstest]
617 fn test_output_amount_of_0_1_token1_zero_for_one_true() {
618 let sqrt_q = get_next_sqrt_price_from_output(
619 encode_sqrt_ratio_x96(1, 1),
620 expand_to_18_decimals(1),
621 U256::from(expand_to_18_decimals(1)) / U256::from(10),
622 true,
623 );
624 assert_eq!(
625 sqrt_q,
626 U160::from_str_radix("71305346262837903834189555302", 10).unwrap()
627 );
628 }
629
630 #[rstest]
631 #[should_panic(expected = "sqrt_price_x96 must be greater than zero")]
632 fn test_if_get_next_sqrt_price_from_output_panic_if_price_zero() {
633 let _ = get_next_sqrt_price_from_output(U160::ZERO, 1, U256::ZERO, true);
634 }
635
636 #[rstest]
637 #[should_panic(expected = "Liquidity must be greater than zero")]
638 fn test_if_get_next_sqrt_price_from_output_panic_if_liquidity_zero() {
639 let _ = get_next_sqrt_price_from_output(U160::from(1), 0, U256::ZERO, true);
640 }
641
642 #[rstest]
643 fn test_encode_sqrt_ratio_x98_some_values() {
644 assert_eq!(encode_sqrt_ratio_x96(1, 1), Q96_U160);
645 assert_eq!(
646 encode_sqrt_ratio_x96(100, 1),
647 U160::from(792281625142643375935439503360_u128)
648 );
649 assert_eq!(
650 encode_sqrt_ratio_x96(1, 100),
651 U160::from(7922816251426433759354395033_u128)
652 );
653 assert_eq!(
654 encode_sqrt_ratio_x96(111, 333),
655 U160::from(45742400955009932534161870629_u128)
656 );
657 assert_eq!(
658 encode_sqrt_ratio_x96(333, 111),
659 U160::from(137227202865029797602485611888_u128)
660 );
661 }
662
663 #[rstest]
664 fn test_get_amount0_delta_returns_0_if_liquidity_is_0() {
665 let amount0 = get_amount0_delta(
666 encode_sqrt_ratio_x96(1, 1),
667 encode_sqrt_ratio_x96(2, 1),
668 0,
669 true,
670 );
671 assert_eq!(amount0, U256::ZERO);
672 }
673
674 #[rstest]
675 fn test_get_amount0_delta_returns_0_if_prices_are_equal() {
676 let amount0 = get_amount0_delta(
677 encode_sqrt_ratio_x96(1, 1),
678 encode_sqrt_ratio_x96(1, 1),
679 0,
680 true,
681 );
682 assert_eq!(amount0, U256::ZERO);
683 }
684
685 #[rstest]
686 fn test_get_amount0_delta_returns_0_1_amount1_for_price_of_1_to_1_21() {
687 let amount0 = get_amount0_delta(
688 encode_sqrt_ratio_x96(1, 1),
689 encode_sqrt_ratio_x96(121, 100),
690 expand_to_18_decimals(1),
691 true,
692 );
693 assert_eq!(
694 amount0,
695 U256::from_str_radix("90909090909090910", 10).unwrap()
696 );
697
698 let amount0_rounded_down = get_amount0_delta(
699 encode_sqrt_ratio_x96(1, 1),
700 encode_sqrt_ratio_x96(121, 100),
701 expand_to_18_decimals(1),
702 false,
703 );
704
705 assert_eq!(amount0_rounded_down, amount0 - U256::from(1));
706 }
707
708 #[rstest]
709 fn test_get_amount0_delta_works_for_prices_that_overflow() {
710 let price_low =
712 encode_sqrt_ratio_x96(U256::from(2).pow(U256::from(90)).try_into().unwrap(), 1);
713 let price_high =
714 encode_sqrt_ratio_x96(U256::from(2).pow(U256::from(96)).try_into().unwrap(), 1);
715
716 let amount0_up = get_amount0_delta(price_low, price_high, expand_to_18_decimals(1), true);
717
718 let amount0_down =
719 get_amount0_delta(price_low, price_high, expand_to_18_decimals(1), false);
720
721 assert_eq!(amount0_up, amount0_down + U256::from(1));
722 }
723
724 #[rstest]
725 fn test_get_amount1_delta_returns_0_if_liquidity_is_0() {
726 let amount1 = get_amount1_delta(
727 encode_sqrt_ratio_x96(1, 1),
728 encode_sqrt_ratio_x96(2, 1),
729 0,
730 true,
731 );
732 assert_eq!(amount1, U256::ZERO);
733 }
734
735 #[rstest]
736 fn test_get_amount1_delta_returns_0_if_prices_are_equal() {
737 let amount1 = get_amount1_delta(
738 encode_sqrt_ratio_x96(1, 1),
739 encode_sqrt_ratio_x96(1, 1),
740 0,
741 true,
742 );
743 assert_eq!(amount1, U256::ZERO);
744 }
745
746 #[rstest]
747 fn test_get_amount1_delta_returns_0_1_amount1_for_price_of_1_to_1_21() {
748 let amount1 = get_amount1_delta(
749 encode_sqrt_ratio_x96(1, 1),
750 encode_sqrt_ratio_x96(121, 100),
751 expand_to_18_decimals(1),
752 true,
753 );
754 assert_eq!(
755 amount1,
756 U256::from_str_radix("100000000000000000", 10).unwrap()
757 );
758
759 let amount1_rounded_down = get_amount1_delta(
760 encode_sqrt_ratio_x96(1, 1),
761 encode_sqrt_ratio_x96(121, 100),
762 expand_to_18_decimals(1),
763 false,
764 );
765
766 assert_eq!(amount1_rounded_down, amount1 - U256::from(1));
767 }
768
769 #[rstest]
770 fn test_decode_sqrt_price_x96_to_price_and_decimal_adjustments() {
771 let sqrt_price_x96 =
773 U160::from_str_radix("2018382873588440326581633304624437", 10).unwrap();
774
775 let raw_price = decode_sqrt_price_x96_to_price(sqrt_price_x96).unwrap();
776 assert_eq!(raw_price.as_f64(), 649004842.70137);
777
778 let adjusted_price =
780 decode_sqrt_price_x96_to_price_tokens_adjusted(sqrt_price_x96, 6, 18, true).unwrap();
781 assert_eq!(adjusted_price.as_f64(), 1540.8205520280458);
782 }
783}