1use borsh::{BorshDeserialize, BorshSerialize};
2
3use ethnum::U256;
4
5use super::{
6 super::error::{
7 CoreError, AMOUNT_EXCEEDS_MAX_U128, AMOUNT_EXCEEDS_MAX_U64, ARITHMETIC_OVERFLOW,
8 INVALID_ORACLE_DATA,
9 },
10 QuoteType, SingleSideLiquidity, LIQUIDITY_LEVELS, PER_M_DENOMINATOR,
11};
12
13const MIN_SQRT_PRICE: u128 = 5647135299341; const MAX_SQRT_PRICE: u128 = 60257519765924248467716150; #[cfg(feature = "wasm")]
17use riptide_amm_macros::wasm_expose;
18
19const MAX_POSITIONS: usize = 16;
20
21const MIN_SQRT_PRICE_RANGE: u128 = 18_446_744_073_710;
25
26#[derive(Debug, Clone, Copy, Eq, PartialEq)]
34#[cfg_attr(true, derive(BorshDeserialize, BorshSerialize))]
35#[cfg_attr(feature = "wasm", wasm_expose)]
36pub enum LiquidityType {
37 ConstantProduct,
39 ConcentratedLiquidity {
40 lower_sqrt_price: u128,
41 upper_sqrt_price: u128,
42 },
43}
44
45impl LiquidityType {
46 fn positions(&self) -> Result<[Position; MAX_POSITIONS], CoreError> {
47 let mut positions = [Position::default(); MAX_POSITIONS];
48 match self {
49 LiquidityType::ConstantProduct => {
50 positions[0] = Position::full_range(PER_M_DENOMINATOR as u32);
51 }
52 LiquidityType::ConcentratedLiquidity {
53 lower_sqrt_price,
54 upper_sqrt_price,
55 } => {
56 positions[0] = Position::new(
57 *lower_sqrt_price,
58 *upper_sqrt_price,
59 PER_M_DENOMINATOR as u32,
60 )?;
61 }
62 };
63 Ok(positions)
64 }
65}
66
67#[derive(Debug, Clone, Copy)]
68struct Position {
69 lower_sqrt_price: u128,
70 upper_sqrt_price: u128,
71 liquidity_share_per_m: u32,
72}
73
74impl Default for Position {
75 fn default() -> Self {
76 Self {
77 lower_sqrt_price: MIN_SQRT_PRICE,
78 upper_sqrt_price: MAX_SQRT_PRICE,
79 liquidity_share_per_m: 0,
80 }
81 }
82}
83
84impl Position {
85 fn full_range(liquidity_share_per_m: u32) -> Self {
86 Self {
87 lower_sqrt_price: MIN_SQRT_PRICE,
88 upper_sqrt_price: MAX_SQRT_PRICE,
89 liquidity_share_per_m,
90 }
91 }
92
93 fn new(
94 lower_sqrt_price: u128,
95 upper_sqrt_price: u128,
96 liquidity_share_per_m: u32,
97 ) -> Result<Self, CoreError> {
98 if lower_sqrt_price > upper_sqrt_price
99 || lower_sqrt_price < MIN_SQRT_PRICE
100 || upper_sqrt_price > MAX_SQRT_PRICE
101 || upper_sqrt_price - lower_sqrt_price < MIN_SQRT_PRICE_RANGE
102 {
103 return Err(INVALID_ORACLE_DATA);
104 }
105 Ok(Self {
106 lower_sqrt_price,
107 upper_sqrt_price,
108 liquidity_share_per_m,
109 })
110 }
111}
112
113pub(crate) fn amm_price(
118 liquidity_type: LiquidityType,
119 reserves_a: u64,
120 reserves_b: u64,
121) -> Result<u128, CoreError> {
122 if reserves_a == 0 && reserves_b == 0 {
123 return Ok(0);
124 }
125
126 let positions = liquidity_type.positions()?;
127 let sqrt_price = implied_sqrt_price(&positions, reserves_a, reserves_b)?;
128
129 U256::from(sqrt_price)
130 .checked_mul(U256::from(sqrt_price))
131 .ok_or(ARITHMETIC_OVERFLOW)?
132 .checked_shr(64)
133 .ok_or(ARITHMETIC_OVERFLOW)?
134 .try_into()
135 .map_err(|_| AMOUNT_EXCEEDS_MAX_U128)
136}
137
138pub(crate) fn amm_liquidity(
139 liquidity_type: LiquidityType,
140 bid_spread_per_m: i32,
141 ask_spread_per_m: i32,
142 quote_type: QuoteType,
143 reserves_a: u64,
144 reserves_b: u64,
145) -> Result<SingleSideLiquidity, CoreError> {
146 if bid_spread_per_m <= -PER_M_DENOMINATOR
147 || bid_spread_per_m >= PER_M_DENOMINATOR
148 || ask_spread_per_m <= -PER_M_DENOMINATOR
149 {
150 return Err(INVALID_ORACLE_DATA);
151 }
152 if reserves_a == 0 && reserves_b == 0 {
153 return Ok(SingleSideLiquidity::new());
154 }
155
156 let positions = liquidity_type.positions()?;
157 let total_liquidity_share_per_m = positions
158 .iter()
159 .map(|p| u128::from(p.liquidity_share_per_m))
160 .sum::<u128>();
161 if total_liquidity_share_per_m == 0 || total_liquidity_share_per_m > PER_M_DENOMINATOR as u128 {
162 return Err(INVALID_ORACLE_DATA);
163 }
164
165 let mid_sqrt_price = implied_sqrt_price(&positions, reserves_a, reserves_b)?;
166
167 let reserves = if quote_type.a_to_b() {
168 reserves_b
169 } else {
170 reserves_a
171 };
172
173 let spread = if quote_type.a_to_b() {
174 PER_M_DENOMINATOR - bid_spread_per_m
175 } else {
176 PER_M_DENOMINATOR + ask_spread_per_m
177 };
178
179 let end_sqrt_price = if quote_type.a_to_b() {
180 positions
181 .iter()
182 .filter(|p| p.liquidity_share_per_m > 0)
183 .map(|p| p.lower_sqrt_price)
184 .min()
185 .unwrap_or(MIN_SQRT_PRICE)
186 } else {
187 positions
188 .iter()
189 .filter(|p| p.liquidity_share_per_m > 0)
190 .map(|p| p.upper_sqrt_price)
191 .max()
192 .unwrap_or(MAX_SQRT_PRICE)
193 };
194
195 let mut liquidity_bins: [(u128, u64); LIQUIDITY_LEVELS] = [(0, 0); LIQUIDITY_LEVELS];
196 let mut position_bin_indexes: [[bool; LIQUIDITY_LEVELS]; MAX_POSITIONS] =
197 [[false; LIQUIDITY_LEVELS]; MAX_POSITIONS];
198
199 for i in 0..LIQUIDITY_LEVELS {
201 let (start_sqrt_price, end_sqrt_price) = bin_boundaries(i, mid_sqrt_price, end_sqrt_price)?;
202
203 for j in 0..MAX_POSITIONS {
207 position_bin_indexes[j][i] = if start_sqrt_price < end_sqrt_price {
208 positions[j].lower_sqrt_price <= start_sqrt_price
209 && end_sqrt_price <= positions[j].upper_sqrt_price
210 } else {
211 positions[j].lower_sqrt_price <= end_sqrt_price
212 && start_sqrt_price <= positions[j].upper_sqrt_price
213 };
214 }
215
216 liquidity_bins[i].0 = bin_price(i, start_sqrt_price, end_sqrt_price, spread)?;
217 }
218
219 let position_amounts =
220 position_reserves(&positions, mid_sqrt_price, reserves, quote_type.a_to_b())?;
221
222 for i in 0..MAX_POSITIONS {
225 let num_bins = position_bin_indexes[i].iter().filter(|&b| *b).count();
226 if num_bins == 0 {
227 continue;
228 }
229 if positions[i].liquidity_share_per_m == 0 {
230 continue;
231 }
232
233 let mut total_overlap: u128 = 0;
235 for j in 0..LIQUIDITY_LEVELS {
236 if position_bin_indexes[i][j] {
237 total_overlap = total_overlap
238 .checked_add(bin_position_overlap(
239 j,
240 mid_sqrt_price,
241 end_sqrt_price,
242 &positions[i],
243 )?)
244 .ok_or(ARITHMETIC_OVERFLOW)?;
245 }
246 }
247
248 if total_overlap == 0 {
249 continue;
250 }
251
252 for j in 0..LIQUIDITY_LEVELS {
254 if position_bin_indexes[i][j] {
255 let width = bin_position_overlap(j, mid_sqrt_price, end_sqrt_price, &positions[i])?;
256 if width > 0 {
257 let amount = U256::from(position_amounts[i])
258 .checked_mul(U256::from(width))
259 .ok_or(ARITHMETIC_OVERFLOW)?
260 .checked_div(U256::from(total_overlap))
261 .ok_or(ARITHMETIC_OVERFLOW)?;
262 let amount: u64 = amount.try_into().map_err(|_| AMOUNT_EXCEEDS_MAX_U64)?;
263 liquidity_bins[j].1 = liquidity_bins[j]
264 .1
265 .checked_add(amount)
266 .ok_or(ARITHMETIC_OVERFLOW)?;
267 }
268 }
269 }
270 }
271
272 let total_amount = liquidity_bins
274 .iter()
275 .map(|b: &(u128, u64)| b.1)
276 .sum::<u64>();
277
278 let mut remaining = reserves - total_amount;
279 while remaining > 0 {
280 for i in (0..LIQUIDITY_LEVELS).rev() {
281 if remaining == 0 {
282 break;
283 }
284 if liquidity_bins[i].1 > 0 {
285 liquidity_bins[i].1 = liquidity_bins[i]
286 .1
287 .checked_add(1)
288 .ok_or(ARITHMETIC_OVERFLOW)?;
289 remaining -= 1;
290 }
291 }
292 }
293
294 Ok(SingleSideLiquidity::from_slice(&liquidity_bins))
295}
296
297fn virtual_position_reserves(
298 sqrt_price: u128,
299 position: &Position,
300) -> Result<(u64, u64), CoreError> {
301 let liquidity = U256::from(position.liquidity_share_per_m);
302 let sqrt_price = sqrt_price.clamp(position.lower_sqrt_price, position.upper_sqrt_price);
303
304 let diff_a = U256::from(position.upper_sqrt_price).saturating_sub(U256::from(sqrt_price));
305 let virtual_a = if diff_a == 0 {
306 0u64
307 } else {
308 let numerator = liquidity
309 .checked_mul(diff_a)
310 .ok_or(ARITHMETIC_OVERFLOW)?
311 .checked_shl(64)
312 .ok_or(ARITHMETIC_OVERFLOW)?;
313 let denominator = U256::from(sqrt_price)
314 .checked_mul(U256::from(position.upper_sqrt_price))
315 .ok_or(ARITHMETIC_OVERFLOW)?;
316
317 numerator
318 .checked_div(denominator)
319 .ok_or(ARITHMETIC_OVERFLOW)?
320 .try_into()
321 .map_err(|_| AMOUNT_EXCEEDS_MAX_U64)?
322 };
323
324 let diff_b = U256::from(sqrt_price).saturating_sub(U256::from(position.lower_sqrt_price));
325 let virtual_b = if diff_b == 0 {
326 0u64
327 } else {
328 liquidity
329 .checked_mul(diff_b)
330 .ok_or(ARITHMETIC_OVERFLOW)?
331 .checked_shr(64)
332 .ok_or(ARITHMETIC_OVERFLOW)?
333 .try_into()
334 .map_err(|_| AMOUNT_EXCEEDS_MAX_U64)?
335 };
336
337 Ok((virtual_a, virtual_b))
338}
339
340fn virtual_reserves(positions: &[Position], sqrt_price: u128) -> Result<(u64, u64), CoreError> {
341 let mut virtual_a = 0u64;
342 let mut virtual_b = 0u64;
343 for position in positions {
344 if position.liquidity_share_per_m == 0 {
345 continue;
346 }
347 let (a_amount, b_amount) = virtual_position_reserves(sqrt_price, position)?;
348 virtual_a = virtual_a.checked_add(a_amount).ok_or(ARITHMETIC_OVERFLOW)?;
349 virtual_b = virtual_b.checked_add(b_amount).ok_or(ARITHMETIC_OVERFLOW)?;
350 }
351 Ok((virtual_a, virtual_b))
352}
353
354fn position_reserves(
355 positions: &[Position],
356 sqrt_price: u128,
357 reserves: u64,
358 a_to_b: bool,
359) -> Result<[u64; MAX_POSITIONS], CoreError> {
360 let mut total_virtual_amount: u64 = 0;
362 let mut virtual_amounts: [u64; MAX_POSITIONS] = [0; MAX_POSITIONS];
363 for i in 0..MAX_POSITIONS {
364 let (virtual_a, virtual_b) = virtual_position_reserves(sqrt_price, &positions[i])?;
365 if a_to_b {
366 virtual_amounts[i] = virtual_b;
367 total_virtual_amount = total_virtual_amount
368 .checked_add(virtual_b)
369 .ok_or(ARITHMETIC_OVERFLOW)?;
370 } else {
371 virtual_amounts[i] = virtual_a;
372 total_virtual_amount = total_virtual_amount
373 .checked_add(virtual_a)
374 .ok_or(ARITHMETIC_OVERFLOW)?;
375 }
376 }
377
378 let mut actual_amounts: [u64; MAX_POSITIONS] = [0; MAX_POSITIONS];
380 for i in 0..MAX_POSITIONS {
381 actual_amounts[i] = reserves
382 .checked_mul(virtual_amounts[i])
383 .ok_or(ARITHMETIC_OVERFLOW)?
384 .checked_div(total_virtual_amount)
385 .ok_or(ARITHMETIC_OVERFLOW)?;
386 }
387
388 let total_actual_amount = actual_amounts.iter().sum::<u64>();
390 let mut remaining = reserves - total_actual_amount;
391 while remaining > 0 {
392 for i in 0..MAX_POSITIONS {
393 if remaining == 0 {
394 break;
395 }
396 if actual_amounts[i] > 0 {
397 actual_amounts[i] = actual_amounts[i]
398 .checked_add(1)
399 .ok_or(ARITHMETIC_OVERFLOW)?;
400 remaining -= 1;
401 }
402 }
403 }
404
405 Ok(actual_amounts)
406}
407
408fn implied_sqrt_price(
409 positions: &[Position],
410 reserves_a: u64,
411 reserves_b: u64,
412) -> Result<u128, CoreError> {
413 if positions.is_empty() {
414 return Err(INVALID_ORACLE_DATA);
415 }
416
417 let mut lowest_sqrt_price = positions
418 .iter()
419 .filter(|p| p.liquidity_share_per_m > 0)
420 .map(|p| p.lower_sqrt_price)
421 .min()
422 .unwrap_or(0);
423 let mut highest_sqrt_price = positions
424 .iter()
425 .filter(|p| p.liquidity_share_per_m > 0)
426 .map(|p| p.upper_sqrt_price)
427 .max()
428 .unwrap_or(u128::MAX);
429
430 if reserves_a == 0 {
432 return Ok(highest_sqrt_price);
433 } else if reserves_b == 0 {
434 return Ok(lowest_sqrt_price);
435 }
436
437 for _ in 0..128 {
439 if highest_sqrt_price <= lowest_sqrt_price + 1 {
440 break;
441 }
442 let diff = highest_sqrt_price.abs_diff(lowest_sqrt_price) >> 1;
443 let mid = lowest_sqrt_price
444 .checked_add(diff)
445 .ok_or(ARITHMETIC_OVERFLOW)?;
446 let (virtual_a, virtual_b) = virtual_reserves(positions, mid)?;
447
448 let lhs = u128::from(virtual_b)
449 .checked_mul(reserves_a as u128)
450 .ok_or(ARITHMETIC_OVERFLOW)?;
451 let rhs = u128::from(virtual_a)
452 .checked_mul(reserves_b as u128)
453 .ok_or(ARITHMETIC_OVERFLOW)?;
454
455 if lhs < rhs {
456 lowest_sqrt_price = mid;
457 } else {
458 highest_sqrt_price = mid;
459 }
460 }
461
462 let diff = highest_sqrt_price.abs_diff(lowest_sqrt_price) >> 1;
463 let sqrt_price = lowest_sqrt_price
464 .checked_add(diff)
465 .ok_or(ARITHMETIC_OVERFLOW)?;
466
467 Ok(sqrt_price)
468}
469
470#[inline(always)]
471fn bin_position_overlap(
472 bin_index: usize,
473 mid_sqrt_price: u128,
474 end_sqrt_price: u128,
475 position: &Position,
476) -> Result<u128, CoreError> {
477 let (bin_start, bin_end) = bin_boundaries(bin_index, mid_sqrt_price, end_sqrt_price)?;
478 let (lo, hi) = if bin_start <= bin_end {
479 (bin_start, bin_end)
480 } else {
481 (bin_end, bin_start)
482 };
483 let overlap_start = lo.max(position.lower_sqrt_price);
484 let overlap_end = hi.min(position.upper_sqrt_price);
485 if overlap_end > overlap_start {
486 Ok(overlap_end - overlap_start)
487 } else {
488 Ok(0)
489 }
490}
491
492fn bin_boundaries(
493 i: usize,
494 sqrt_price: u128,
495 end_sqrt_price: u128,
496) -> Result<(u128, u128), CoreError> {
497 let sqrt_price_diff = end_sqrt_price.abs_diff(sqrt_price);
498
499 let start_sqrt_price_delta = U256::from(sqrt_price_diff)
500 .checked_mul(U256::from(i as u128))
501 .ok_or(ARITHMETIC_OVERFLOW)?
502 .checked_div(U256::from(LIQUIDITY_LEVELS as u128))
503 .ok_or(ARITHMETIC_OVERFLOW)?;
504 let end_sqrt_price_delta = U256::from(sqrt_price_diff)
505 .checked_mul(U256::from(i as u128 + 1))
506 .ok_or(ARITHMETIC_OVERFLOW)?
507 .checked_div(U256::from(LIQUIDITY_LEVELS as u128))
508 .ok_or(ARITHMETIC_OVERFLOW)?;
509
510 let (start_sqrt_price, end_sqrt_price) = if end_sqrt_price > sqrt_price {
511 let start = U256::from(sqrt_price)
512 .checked_add(start_sqrt_price_delta)
513 .ok_or(ARITHMETIC_OVERFLOW)?;
514
515 let end = U256::from(sqrt_price)
516 .checked_add(end_sqrt_price_delta)
517 .ok_or(ARITHMETIC_OVERFLOW)?;
518
519 (start, end)
520 } else {
521 let start = U256::from(sqrt_price)
522 .checked_sub(start_sqrt_price_delta)
523 .ok_or(ARITHMETIC_OVERFLOW)?;
524
525 let end = U256::from(sqrt_price)
526 .checked_sub(end_sqrt_price_delta)
527 .ok_or(ARITHMETIC_OVERFLOW)?;
528
529 (start, end)
530 };
531
532 Ok((
533 start_sqrt_price
534 .try_into()
535 .map_err(|_| AMOUNT_EXCEEDS_MAX_U128)?,
536 end_sqrt_price
537 .try_into()
538 .map_err(|_| AMOUNT_EXCEEDS_MAX_U128)?,
539 ))
540}
541
542fn bin_price(
547 i: usize,
548 start_sqrt_price: u128,
549 end_sqrt_price: u128,
550 spread: i32,
551) -> Result<u128, CoreError> {
552 let sqrt_price_diff = end_sqrt_price.abs_diff(start_sqrt_price);
553 let sqrt_price_delta = U256::from(sqrt_price_diff)
554 .checked_mul(U256::from(i as u128))
555 .ok_or(ARITHMETIC_OVERFLOW)?
556 .checked_div(U256::from(LIQUIDITY_LEVELS as u128 - 1))
557 .ok_or(ARITHMETIC_OVERFLOW)?;
558
559 let sqrt_price = if end_sqrt_price > start_sqrt_price {
560 U256::from(start_sqrt_price)
561 .checked_add(sqrt_price_delta)
562 .ok_or(ARITHMETIC_OVERFLOW)?
563 } else {
564 U256::from(start_sqrt_price)
565 .checked_sub(sqrt_price_delta)
566 .ok_or(ARITHMETIC_OVERFLOW)?
567 };
568
569 let product = sqrt_price
570 .checked_mul(sqrt_price)
571 .ok_or(ARITHMETIC_OVERFLOW)?;
572
573 let quotient = product.checked_shr(64).ok_or(ARITHMETIC_OVERFLOW)?;
574 let remainder = product & u128::MAX;
575
576 let base_price: u128 = if end_sqrt_price > start_sqrt_price && remainder > 0 {
577 (quotient + 1)
578 .try_into()
579 .map_err(|_| AMOUNT_EXCEEDS_MAX_U128)?
580 } else {
581 quotient.try_into().map_err(|_| AMOUNT_EXCEEDS_MAX_U128)?
582 };
583
584 let product = U256::from(base_price)
585 .checked_mul(U256::from(spread as u128))
586 .ok_or(ARITHMETIC_OVERFLOW)?;
587 let quotient = product
588 .checked_div(U256::from(PER_M_DENOMINATOR as u128))
589 .ok_or(ARITHMETIC_OVERFLOW)?;
590 let remainder = product
591 .checked_rem(U256::from(PER_M_DENOMINATOR as u128))
592 .ok_or(ARITHMETIC_OVERFLOW)?;
593
594 let price = if end_sqrt_price > start_sqrt_price && remainder > 0 {
595 (quotient + 1)
596 .try_into()
597 .map_err(|_| AMOUNT_EXCEEDS_MAX_U128)?
598 } else {
599 quotient.try_into().map_err(|_| AMOUNT_EXCEEDS_MAX_U128)?
600 };
601
602 Ok(price)
603}
604
605#[cfg(test)]
606mod tests {
607 use super::*;
608 use rstest::rstest;
609
610 #[rstest]
611 #[case(Position::full_range(PER_M_DENOMINATOR as u32), 100, 100, 18446738426575981041)]
612 #[case(Position::full_range(PER_M_DENOMINATOR as u32), 150, 50, 10650233338091508966)]
613 #[case(Position::full_range(PER_M_DENOMINATOR as u32), 50, 150, 31950688720003928217)]
614 #[case(Position::full_range(PER_M_DENOMINATOR as u32), 200, 0, MIN_SQRT_PRICE)]
615 #[case(Position::full_range(PER_M_DENOMINATOR as u32), 0, 200, MAX_SQRT_PRICE)]
616 #[case(Position::new((1 << 64) / 2, (1 << 64) * 2, PER_M_DENOMINATOR as u32).unwrap(), 100, 100, 18446744073709551615)]
617 #[case(Position::new((1 << 64) / 3, (1 << 64) * 2, PER_M_DENOMINATOR as u32).unwrap(), 100, 100, 16973448724429105277)]
618 #[case(Position::new((1 << 64) / 2, (1 << 64) * 3, PER_M_DENOMINATOR as u32).unwrap(), 100, 100, 20047903282541897858)]
619 #[case(Position::new((1 << 64) / 2, (1 << 64) * 2, PER_M_DENOMINATOR as u32).unwrap(), 150, 50, 14159573177026862144)]
620 #[case(Position::new((1 << 64) / 2, (1 << 64) * 2, PER_M_DENOMINATOR as u32).unwrap(), 50, 150, 24031964994045732128)]
621 #[case(Position::new((1 << 64) / 2, (1 << 64) * 2, PER_M_DENOMINATOR as u32).unwrap(), 200, 0, (1 << 64) / 2)]
622 #[case(Position::new((1 << 64) / 2, (1 << 64) * 2, PER_M_DENOMINATOR as u32).unwrap(), 0, 200, (1 << 64) * 2)]
623 fn test_implied_sqrt_price_single_position(
624 #[case] position: Position,
625 #[case] reserves_a: u64,
626 #[case] reserves_b: u64,
627 #[case] expected: u128,
628 ) {
629 let sqrt_price = implied_sqrt_price(&[position], reserves_a, reserves_b).unwrap();
630 assert_eq!(sqrt_price, expected);
631 }
632
633 #[rstest]
634 #[case(100, 100, 18446744073709551615)]
635 #[case(150, 50, 13516113850247725564)]
636 #[case(50, 150, 25176050652658693911)]
637 #[case(200, 0, 6148914691236517205)]
638 #[case(0, 200, 55340232221128654848)]
639 fn test_implied_sqrt_price_multiple_positions(
640 #[case] reserves_a: u64,
641 #[case] reserves_b: u64,
642 #[case] expected: u128,
643 ) {
644 let positions = vec![
645 Position::new((1 << 64) / 2, (1 << 64) * 2, 300_000).unwrap(),
646 Position::new((1 << 64) / 2, (1 << 64) * 3, 200_000).unwrap(),
647 Position::new((1 << 64) / 3, (1 << 64) * 2, 200_000).unwrap(),
648 Position::new((1 << 64) / 3, (1 << 64) * 3, 300_000).unwrap(),
649 ];
650 let sqrt_price = implied_sqrt_price(&positions, reserves_a, reserves_b).unwrap();
651 assert_eq!(sqrt_price, expected);
652 }
653
654 #[rstest]
655 #[case(LiquidityType::ConstantProduct, 100, 100, Ok(18446732779444139232))]
656 #[case(LiquidityType::ConstantProduct, 150, 50, Ok(6148915478122426543))]
657 #[case(LiquidityType::ConstantProduct, 50, 150, Ok(55340200178605227858))]
658 #[case(LiquidityType::ConstantProduct, 200, 0, Ok(1728767))]
659 #[case(
660 LiquidityType::ConstantProduct,
661 0,
662 200,
663 Ok(196835207006294262292126795817913)
664 )]
665 #[case(LiquidityType::ConcentratedLiquidity { lower_sqrt_price: (1 << 64) / 2, upper_sqrt_price: (1 << 64) * 2 }, 100, 100, Ok(18446744073709551614))]
666 #[case(LiquidityType::ConcentratedLiquidity { lower_sqrt_price: (1 << 64) / 2, upper_sqrt_price: (1 << 64) * 2 }, 150, 50, Ok(10868775094100403166))]
667 #[case(LiquidityType::ConcentratedLiquidity { lower_sqrt_price: (1 << 64) / 2, upper_sqrt_price: (1 << 64) * 2 }, 50, 150, Ok(31308253595719773170))]
668 #[case(LiquidityType::ConcentratedLiquidity { lower_sqrt_price: (1 << 64) / 2, upper_sqrt_price: (1 << 64) * 2 }, 200, 0, Ok(4611686018427387904))]
669 #[case(LiquidityType::ConcentratedLiquidity { lower_sqrt_price: (1 << 64) / 2, upper_sqrt_price: (1 << 64) * 2 }, 0, 200, Ok(73786976294838206464))]
670 fn test_amm_price(
671 #[case] liquidity_type: LiquidityType,
672 #[case] reserves_a: u64,
673 #[case] reserves_b: u64,
674 #[case] expected: Result<u128, CoreError>,
675 ) {
676 let price = amm_price(liquidity_type, reserves_a, reserves_b);
677 assert_eq!(price, expected);
678 }
679
680 #[rstest]
681 #[case(0, MIN_SQRT_PRICE, Ok((18446744073709551616, 17870283497879106233)))]
682 #[case(1, MIN_SQRT_PRICE, Ok((17870283497879106233, 17293822922048660849)))]
683 #[case(0, MAX_SQRT_PRICE, Ok((18446744073709551616, 1883065362968454170744257)))]
684 #[case(1, MAX_SQRT_PRICE, Ok((1883065362968454170744257, 3766112279192834631936899)))]
685 fn test_bin_boundaries(
686 #[case] i: usize,
687 #[case] end_sqrt_price: u128,
688 #[case] expected: Result<(u128, u128), CoreError>,
689 ) {
690 let result = bin_boundaries(i, 1 << 64, end_sqrt_price);
691 assert_eq!(result, expected);
692 }
693
694 #[rstest]
695 #[case(0, 1 << 64, (1 << 64) / 2, 999_000, Ok(18428297329635842064))]
696 #[case(31, 1 << 64, (1 << 64) / 2, 999_000, Ok(4607074332408960516))]
697 #[case(0, 1 << 64, (1 << 64) / 2, 998_000, Ok(18409850585562132512))]
698 #[case(31, 1 << 64, (1 << 64) / 2, 998_000, Ok(4602462646390533128))]
699 #[case(0, 1 << 64, (1 << 64) / 2, 1_001_000, Ok(18465190817783261167))]
700 #[case(31, 1 << 64, (1 << 64) / 2, 1_002_000, Ok(4620909390464242679))]
701 #[case(0, 1 << 64, (1 << 64) * 2, 999_000, Ok(18428297329635842065))]
702 #[case(31, 1 << 64, (1 << 64) * 2, 999_000, Ok(73713189318543368258))]
703 #[case(0, 1 << 64, (1 << 64) * 2, 998_000, Ok(18409850585562132513))]
704 #[case(31, 1 << 64, (1 << 64) * 2, 998_000, Ok(73639402342248530052))]
705 #[case(0, 1 << 64, (1 << 64) * 2, 1_001_000, Ok(18465190817783261168))]
706 #[case(31, 1 << 64, (1 << 64) * 2, 1_001_000, Ok(73860763271133044671))]
707 #[case(0, 1 << 64, (1 << 64) * 2, 1_002_000, Ok(18483637561856970720))]
708 #[case(31, 1 << 64, (1 << 64) * 2, 1_002_000, Ok(73934550247427882877))]
709 fn test_bin_price(
710 #[case] i: usize,
711 #[case] start_sqrt_price: u128,
712 #[case] end_sqrt_price: u128,
713 #[case] spread: i32,
714 #[case] expected: Result<u128, CoreError>,
715 ) {
716 let result = bin_price(i, start_sqrt_price, end_sqrt_price, spread);
717 assert_eq!(result, expected);
718 }
719
720 #[test]
721 fn test_amm_liquidity_a_to_b() {
722 let liquidity = amm_liquidity(
723 LiquidityType::ConstantProduct,
724 10000,
725 20000,
726 QuoteType::TokenAExactIn,
727 1000,
728 1000,
729 )
730 .unwrap()
731 .as_slice()
732 .to_vec();
733 let expected = vec![
735 (18262265451649697839, 31),
736 (17103058524375092466, 31),
737 (15981858369775465113, 31),
738 (14898664987850815784, 31),
739 (13853478378601144476, 31),
740 (12846298542026451190, 31),
741 (11877125478126735926, 31),
742 (10945959186901998683, 31),
743 (10052799668352239462, 31),
744 (9197646922477458264, 31),
745 (8380500949277655088, 31),
746 (7601361748752829935, 31),
747 (6860229320902982803, 31),
748 (6157103665728113694, 31),
749 (5491984783228222606, 31),
750 (4864872673403309539, 31),
751 (4275767336253374494, 31),
752 (3724668771778417472, 31),
753 (3211576979978438473, 31),
754 (2736491960853437495, 31),
755 (2299413714403414540, 31),
756 (1900342240628369606, 31),
757 (1539277539528302694, 31),
758 (1216219611103213804, 31),
759 (931168455353102936, 32),
760 (684124072277970090, 32),
761 (475086461877815267, 32),
762 (304055624152638466, 32),
763 (171031559102439686, 32),
764 (76014266727218928, 32),
765 (19003747026976192, 32),
766 (1711479, 32),
767 ];
768
769 assert_eq!(liquidity, expected);
770 }
771
772 #[test]
773 fn test_amm_liquidity_concentrated_a_to_b() {
774 let liquidity = amm_liquidity(
775 LiquidityType::ConcentratedLiquidity {
776 lower_sqrt_price: (1 << 64) / 2,
777 upper_sqrt_price: (1 << 64) * 2,
778 },
779 10000,
780 20000,
781 QuoteType::TokenAExactIn,
782 1000,
783 1000,
784 )
785 .unwrap()
786 .as_slice()
787 .to_vec();
788 let expected = vec![
790 (18262276632972456097, 31),
791 (17677921787536552847, 31),
792 (17103068646904485422, 31),
793 (16537717211076253820, 31),
794 (15981867480051858042, 31),
795 (15435519453831298092, 31),
796 (14898673132414573967, 31),
797 (14371328515801685667, 31),
798 (13853485603992633191, 31),
799 (13345144396987416541, 31),
800 (12846304894786035717, 31),
801 (12356967097388490717, 31),
802 (11877131004794781542, 31),
803 (11406796617004908193, 31),
804 (10945963934018870670, 31),
805 (10494632955836668971, 31),
806 (10052803682458303097, 31),
807 (9620476113883773049, 31),
808 (9197650250113078826, 31),
809 (8784326091146220429, 31),
810 (8380503636983197855, 31),
811 (7986182887624011109, 31),
812 (7601363843068660187, 31),
813 (7226046503317145091, 31),
814 (6860230868369465819, 32),
815 (6503916938225622372, 32),
816 (6157104712885614751, 32),
817 (5819794192349442955, 32),
818 (5491985376617106984, 32),
819 (5173678265688606840, 32),
820 (4864872859563942520, 32),
821 (4565569158243114024, 32),
822 ];
823
824 assert_eq!(liquidity, expected);
825 }
826
827 #[test]
828 fn test_amm_liquidity_b_to_a() {
829 let liquidity = amm_liquidity(
830 LiquidityType::ConstantProduct,
831 10000,
832 20000,
833 QuoteType::TokenBExactIn,
834 1000,
835 1000,
836 )
837 .unwrap()
838 .as_slice()
839 .to_vec();
840 let expected = vec![
842 (18815667435033022018, 31),
843 (208923620106592073258895578064, 31),
844 (835686549707659367878445172487, 31),
845 (1880288788822017551293681805289, 31),
846 (3342730337449666623504606336313, 31),
847 (5223011195590606584511217260829, 31),
848 (7521131363244837434313515223724, 31),
849 (10237090840412359172911501729725, 31),
850 (13370889627093171800305175704025, 31),
851 (16922527723287275316494535211973, 31),
852 (20892005128994669721479581758299, 31),
853 (25279321844215355015260315343002, 31),
854 (30084477868949331197836738545617, 31),
855 (35307473203196598269208849216532, 31),
856 (40948307846957156229376644346290, 31),
857 (47006981800231005078340126514425, 31),
858 (53483495063018144816099299160316, 31),
859 (60377847635318575442654155620167, 31),
860 (67690039517132296958004699118395, 31),
861 (75420070708459309362150933739263, 31),
862 (83567941209299612655092855828431, 31),
863 (92133651019653206836830460871714, 31),
864 (101117200139520091907363752953373, 31),
865 (110518588568900267866692732073410, 31),
866 (120337816307793734714817403390893, 32),
867 (130574883356200492451737762176676, 32),
868 (141229789714120541077453802841767, 32),
869 (152302535381553880591965530545236, 32),
870 (163793120358500510995272951305994, 32),
871 (175701544644960432287376053301180, 32),
872 (188027808240933644468274842334742, 32),
873 (200771911146420147537969331734273, 32),
874 ];
875
876 assert_eq!(liquidity, expected);
877 }
878
879 #[test]
880 fn test_amm_liquidity_concentrated_b_to_a() {
881 let liquidity = amm_liquidity(
882 LiquidityType::ConcentratedLiquidity {
883 lower_sqrt_price: (1 << 64) / 2,
884 upper_sqrt_price: (1 << 64) * 2,
885 },
886 10000,
887 20000,
888 QuoteType::TokenBExactIn,
889 1000,
890 1000,
891 )
892 .unwrap()
893 .as_slice()
894 .to_vec();
895 let expected = vec![
897 (18815678955183742648, 31),
898 (20049172996990793413, 31),
899 (21321825579807591824, 31),
900 (22633636703634137876, 31),
901 (23984606368470431575, 31),
902 (25374734574316472913, 31),
903 (26804021321172261898, 31),
904 (28272466609037798524, 31),
905 (29780070437913082795, 31),
906 (31326832807798114708, 31),
907 (32912753718692894266, 31),
908 (34537833170597421466, 31),
909 (36202071163511696311, 31),
910 (37905467697435718797, 31),
911 (39648022772369488929, 31),
912 (41429736388313006701, 31),
913 (43250608545266272120, 31),
914 (45110639243229285179, 31),
915 (47009828482202045885, 31),
916 (48948176262184554231, 31),
917 (50925682583176810224, 31),
918 (52942347445178813857, 31),
919 (54998170848190565136, 31),
920 (57093152792212064055, 31),
921 (59227293277243310621, 32),
922 (61400592303284304828, 32),
923 (63613049870335046681, 32),
924 (65864665978395536173, 32),
925 (68155440627465773314, 32),
926 (70485373817545758093, 32),
927 (72854465548635490520, 32),
928 (75262715820734970594, 32),
929 ];
930
931 assert_eq!(liquidity, expected);
932 }
933
934 #[test]
935 fn test_narrow_range_rejected() {
936 let result = amm_liquidity(
938 LiquidityType::ConcentratedLiquidity {
939 lower_sqrt_price: 1 << 64,
940 upper_sqrt_price: ((1 << 64) * 1_000_001) / 1_000_000,
941 },
942 0,
943 0,
944 QuoteType::TokenAExactIn,
945 1_000_000,
946 1_000_000,
947 );
948 assert_eq!(result, Err(INVALID_ORACLE_DATA));
949 }
950
951 #[test]
952 fn test_min_valid_range_accepted() {
953 let result = Position::new(
955 1 << 64,
956 (1 << 64) + MIN_SQRT_PRICE_RANGE,
957 PER_M_DENOMINATOR as u32,
958 );
959 assert!(result.is_ok());
960 }
961
962 #[test]
963 fn test_below_min_range_rejected() {
964 let result = Position::new(
966 1 << 64,
967 (1 << 64) + MIN_SQRT_PRICE_RANGE - 1,
968 PER_M_DENOMINATOR as u32,
969 );
970 assert!(result.is_err());
971 }
972}