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