Skip to main content

riptide_amm/math/oracle/
amm.rs

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; // ~1e-13
14const MAX_SQRT_PRICE: u128 = 60257519765924248467716150; // ~2e13
15
16#[cfg(feature = "wasm")]
17use riptide_amm_macros::wasm_expose;
18
19const MAX_POSITIONS: usize = 16;
20
21// A liquidity position here works slightly differently than a regular AMM.
22// Since the liquidity is discretized into 32 levels, we need to calculate the price for each level.
23// A normal AMM would have a continuous price function across the range.
24// A position here is set up in such a way that the first price is always
25// the mid price (sans spread) and the last price is always the outer edge of the position's price
26// range.
27
28#[derive(Debug, Clone, Copy, Eq, PartialEq)]
29#[cfg_attr(true, derive(BorshDeserialize, BorshSerialize))]
30#[cfg_attr(feature = "wasm", wasm_expose)]
31pub enum LiquidityType {
32    // CP shouldn't be used since it is wildly inaccurate if spread across 32 liquidity levels
33    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
107// AMM oracle: current price P is implied by reserves and positions (no stored price).
108// We solve for P using the side being built only: bid => Y(s) = reserves_b, ask => X(s) =
109// reserves_a. Then L_total is scaled so that virtual reserve matches the reserve for that side.
110
111pub(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    // Calculate the price for each liquidity level
191    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 each position, check if it overlaps with the current bin
195        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    // Add the amounts to the liquidity levels that each position contributes to
207    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    // Normalize the total liquidity to reserves
223    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    // Calculate the virtual amounts for each position
302    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    // Calculate the actual amounts for each position
320    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    // Normalize sum(actual_a_amounts) to reserves_a so that we're using all of the reserves
330    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 one reserve is zero, the sqrt price is the highest or lowest price.
364    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    // Binary search for P where virtual_b(P)/virtual_a(P) = reserves_b/reserves_a.
371    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
453// Take the first price in the bin and apply the spread. If we wanted to be more true to an actual
454// AMM, we would take the geometric mean. The problem if we do that is that the first bin's price
455// would not be equal to the mid price meaning you would get an implicit spread which becomes larger
456// the larger the lp range.
457fn 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        // Prices starts ~ 0.99 and ends ~ 9e-14
645        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        // Prices starts ~ 0.99 and ends ~ 0.2475
700        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        // Prices starts ~ 1.02 and ends ~ 1e13
752        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        // Prices starts ~ 1.02 and ends ~ 4.08
807        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}