Skip to main content

fusionamm_core/quote/
liquidity.rs

1//
2// Copyright (c) Cryptic Dot
3//
4// Modification based on Orca Whirlpools (https://github.com/orca-so/whirlpools),
5// originally licensed under the Apache License, Version 2.0, prior to February 26, 2025.
6//
7// Modifications licensed under FusionAMM SDK Source-Available License v1.0
8// See the LICENSE file in the project root for license information.
9//
10
11#[cfg(feature = "wasm")]
12use fusionamm_macros::wasm_expose;
13
14use crate::{
15    get_amounts_from_liquidity, get_liquidity_from_amount_a, get_liquidity_from_amount_b, order_tick_indexes, position_status,
16    tick_index_to_sqrt_price, try_apply_transfer_fee, try_get_max_amount_with_slippage_tolerance, try_get_min_amount_with_slippage_tolerance,
17    try_reverse_apply_transfer_fee, CoreError, DecreaseLiquidityQuote, IncreaseLiquidityQuote, PositionStatus, TransferFee,
18};
19
20/// Calculate the quote for decreasing liquidity
21///
22/// # Parameters
23/// - `liquidity_delta` - The amount of liquidity to decrease
24/// - `slippage_tolerance` - The slippage tolerance in bps
25/// - `current_sqrt_price` - The current sqrt price of the pool
26/// - `tick_index_1` - The first tick index of the position
27/// - `tick_index_2` - The second tick index of the position
28/// - `transfer_fee_a` - The transfer fee for token A in bps
29/// - `transfer_fee_b` - The transfer fee for token B in bps
30///
31/// # Returns
32/// - A DecreaseLiquidityQuote struct containing the estimated token amounts
33#[cfg_attr(feature = "wasm", wasm_expose)]
34pub fn decrease_liquidity_quote(
35    liquidity_delta: u128,
36    slippage_tolerance_bps: u16,
37    current_sqrt_price: u128,
38    tick_index_1: i32,
39    tick_index_2: i32,
40    transfer_fee_a: Option<TransferFee>,
41    transfer_fee_b: Option<TransferFee>,
42) -> Result<DecreaseLiquidityQuote, CoreError> {
43    let liquidity_delta: u128 = liquidity_delta.into();
44    if liquidity_delta == 0 {
45        return Ok(DecreaseLiquidityQuote::default());
46    }
47
48    let tick_range = order_tick_indexes(tick_index_1, tick_index_2);
49    let current_sqrt_price: u128 = current_sqrt_price.into();
50
51    let sqrt_price_lower = tick_index_to_sqrt_price(tick_range.tick_lower_index);
52    let sqrt_price_upper = tick_index_to_sqrt_price(tick_range.tick_upper_index);
53
54    let token_est = get_amounts_from_liquidity(liquidity_delta, current_sqrt_price, sqrt_price_lower, sqrt_price_upper, false)?;
55
56    let token_est_a = try_apply_transfer_fee(token_est.a, transfer_fee_a.unwrap_or_default())?;
57    let token_est_b = try_apply_transfer_fee(token_est.b, transfer_fee_b.unwrap_or_default())?;
58
59    let token_min_a = try_get_min_amount_with_slippage_tolerance(token_est_a, slippage_tolerance_bps)?;
60    let token_min_b = try_get_min_amount_with_slippage_tolerance(token_est_b, slippage_tolerance_bps)?;
61
62    Ok(DecreaseLiquidityQuote {
63        liquidity_delta,
64        token_est_a,
65        token_est_b,
66        token_min_a,
67        token_min_b,
68    })
69}
70
71/// Calculate the quote for decreasing liquidity given a token a amount
72///
73/// # Parameters
74/// - `token_amount_a` - The amount of token a to decrease
75/// - `slippage_tolerance` - The slippage tolerance in bps
76/// - `current_sqrt_price` - The current sqrt price of the pool
77/// - `tick_index_1` - The first tick index of the position
78/// - `tick_index_2` - The second tick index of the position
79/// - `transfer_fee_a` - The transfer fee for token A in bps
80/// - `transfer_fee_b` - The transfer fee for token B in bps
81///
82/// # Returns
83/// - A DecreaseLiquidityQuote struct containing the estimated token amounts
84#[cfg_attr(feature = "wasm", wasm_expose)]
85pub fn decrease_liquidity_quote_a(
86    token_amount_a: u64,
87    slippage_tolerance_bps: u16,
88    current_sqrt_price: u128,
89    tick_index_1: i32,
90    tick_index_2: i32,
91    transfer_fee_a: Option<TransferFee>,
92    transfer_fee_b: Option<TransferFee>,
93) -> Result<DecreaseLiquidityQuote, CoreError> {
94    let tick_range = order_tick_indexes(tick_index_1, tick_index_2);
95    let token_delta_a = try_reverse_apply_transfer_fee(token_amount_a, transfer_fee_a.unwrap_or_default())?;
96
97    if token_delta_a == 0 {
98        return Ok(DecreaseLiquidityQuote::default());
99    }
100
101    let current_sqrt_price: u128 = current_sqrt_price.into();
102    let sqrt_price_lower: u128 = tick_index_to_sqrt_price(tick_range.tick_lower_index).into();
103    let sqrt_price_upper: u128 = tick_index_to_sqrt_price(tick_range.tick_upper_index).into();
104
105    let position_status = position_status(current_sqrt_price.into(), tick_range.tick_lower_index, tick_range.tick_upper_index);
106
107    let liquidity: u128 = match position_status {
108        PositionStatus::PriceBelowRange => get_liquidity_from_amount_a(token_delta_a, sqrt_price_lower, sqrt_price_upper)?,
109        PositionStatus::Invalid | PositionStatus::PriceAboveRange => 0,
110        PositionStatus::PriceInRange => get_liquidity_from_amount_a(token_delta_a, current_sqrt_price, sqrt_price_upper)?,
111    };
112
113    decrease_liquidity_quote(
114        liquidity.into(),
115        slippage_tolerance_bps,
116        current_sqrt_price.into(),
117        tick_index_1,
118        tick_index_2,
119        transfer_fee_a,
120        transfer_fee_b,
121    )
122}
123
124/// Calculate the quote for decreasing liquidity given a token b amount
125///
126/// # Parameters
127/// - `token_amount_b` - The amount of token b to decrease
128/// - `slippage_tolerance` - The slippage tolerance in bps
129/// - `current_sqrt_price` - The current sqrt price of the pool
130/// - `tick_index_1` - The first tick index of the position
131/// - `tick_index_2` - The second tick index of the position
132/// - `transfer_fee_a` - The transfer fee for token A in bps
133/// - `transfer_fee_b` - The transfer fee for token B in bps
134///
135/// # Returns
136/// - A DecreaseLiquidityQuote struct containing the estimated token amounts
137#[cfg_attr(feature = "wasm", wasm_expose)]
138pub fn decrease_liquidity_quote_b(
139    token_amount_b: u64,
140    slippage_tolerance_bps: u16,
141    current_sqrt_price: u128,
142    tick_index_1: i32,
143    tick_index_2: i32,
144    transfer_fee_a: Option<TransferFee>,
145    transfer_fee_b: Option<TransferFee>,
146) -> Result<DecreaseLiquidityQuote, CoreError> {
147    let tick_range = order_tick_indexes(tick_index_1, tick_index_2);
148    let token_delta_b = try_reverse_apply_transfer_fee(token_amount_b, transfer_fee_b.unwrap_or_default())?;
149
150    if token_delta_b == 0 {
151        return Ok(DecreaseLiquidityQuote::default());
152    }
153
154    let current_sqrt_price: u128 = current_sqrt_price.into();
155    let sqrt_price_lower: u128 = tick_index_to_sqrt_price(tick_range.tick_lower_index).into();
156    let sqrt_price_upper: u128 = tick_index_to_sqrt_price(tick_range.tick_upper_index).into();
157
158    let position_status = position_status(current_sqrt_price.into(), tick_range.tick_lower_index, tick_range.tick_upper_index);
159
160    let liquidity: u128 = match position_status {
161        PositionStatus::Invalid | PositionStatus::PriceBelowRange => 0,
162        PositionStatus::PriceAboveRange => get_liquidity_from_amount_b(token_delta_b, sqrt_price_lower, sqrt_price_upper)?,
163        PositionStatus::PriceInRange => get_liquidity_from_amount_b(token_delta_b, sqrt_price_lower, current_sqrt_price)?,
164    };
165
166    decrease_liquidity_quote(
167        liquidity.into(),
168        slippage_tolerance_bps,
169        current_sqrt_price.into(),
170        tick_index_1,
171        tick_index_2,
172        transfer_fee_a,
173        transfer_fee_b,
174    )
175}
176
177/// Calculate the quote for increasing liquidity
178///
179/// # Parameters
180/// - `liquidity_delta` - The amount of liquidity to increase
181/// - `slippage_tolerance` - The slippage tolerance in bps
182/// - `current_sqrt_price` - The current sqrt price of the pool
183/// - `tick_index_1` - The first tick index of the position
184/// - `tick_index_2` - The second tick index of the position
185/// - `transfer_fee_a` - The transfer fee for token A in bps
186/// - `transfer_fee_b` - The transfer fee for token B in bps
187///
188/// # Returns
189/// - An IncreaseLiquidityQuote struct containing the estimated token amounts
190#[cfg_attr(feature = "wasm", wasm_expose)]
191pub fn increase_liquidity_quote(
192    liquidity_delta: u128,
193    slippage_tolerance_bps: u16,
194    current_sqrt_price: u128,
195    tick_index_1: i32,
196    tick_index_2: i32,
197    transfer_fee_a: Option<TransferFee>,
198    transfer_fee_b: Option<TransferFee>,
199) -> Result<IncreaseLiquidityQuote, CoreError> {
200    let liquidity_delta: u128 = liquidity_delta.into();
201    if liquidity_delta == 0 {
202        return Ok(IncreaseLiquidityQuote::default());
203    }
204
205    let tick_range = order_tick_indexes(tick_index_1, tick_index_2);
206    let sqrt_price_lower = tick_index_to_sqrt_price(tick_range.tick_lower_index);
207    let sqrt_price_upper = tick_index_to_sqrt_price(tick_range.tick_upper_index);
208
209    let token_est = get_amounts_from_liquidity(liquidity_delta, current_sqrt_price, sqrt_price_lower, sqrt_price_upper, true)?;
210
211    let token_est_a = try_reverse_apply_transfer_fee(token_est.a, transfer_fee_a.unwrap_or_default())?;
212    let token_est_b = try_reverse_apply_transfer_fee(token_est.b, transfer_fee_b.unwrap_or_default())?;
213
214    let token_max_a = try_get_max_amount_with_slippage_tolerance(token_est_a, slippage_tolerance_bps)?;
215    let token_max_b = try_get_max_amount_with_slippage_tolerance(token_est_b, slippage_tolerance_bps)?;
216
217    Ok(IncreaseLiquidityQuote {
218        liquidity_delta,
219        token_est_a,
220        token_est_b,
221        token_max_a,
222        token_max_b,
223    })
224}
225
226/// Calculate the quote for increasing liquidity given a token a amount
227///
228/// # Parameters
229/// - `token_amount_a` - The amount of token a to increase
230/// - `slippage_tolerance` - The slippage tolerance in bps
231/// - `current_sqrt_price` - The current sqrt price of the pool
232/// - `tick_index_1` - The first tick index of the position
233/// - `tick_index_2` - The second tick index of the position
234/// - `transfer_fee_a` - The transfer fee for token A in bps
235/// - `transfer_fee_b` - The transfer fee for token B in bps
236///
237/// # Returns
238/// - An IncreaseLiquidityQuote struct containing the estimated token amounts
239#[cfg_attr(feature = "wasm", wasm_expose)]
240pub fn increase_liquidity_quote_a(
241    token_amount_a: u64,
242    slippage_tolerance_bps: u16,
243    current_sqrt_price: u128,
244    tick_index_1: i32,
245    tick_index_2: i32,
246    transfer_fee_a: Option<TransferFee>,
247    transfer_fee_b: Option<TransferFee>,
248) -> Result<IncreaseLiquidityQuote, CoreError> {
249    let tick_range = order_tick_indexes(tick_index_1, tick_index_2);
250    let token_delta_a = try_apply_transfer_fee(token_amount_a, transfer_fee_a.unwrap_or_default())?;
251
252    if token_delta_a == 0 {
253        return Ok(IncreaseLiquidityQuote::default());
254    }
255
256    let sqrt_price_lower = tick_index_to_sqrt_price(tick_range.tick_lower_index);
257    let sqrt_price_upper = tick_index_to_sqrt_price(tick_range.tick_upper_index);
258
259    let position_status = position_status(current_sqrt_price, tick_index_1, tick_index_2);
260
261    let liquidity: u128 = match position_status {
262        PositionStatus::PriceBelowRange => get_liquidity_from_amount_a(token_delta_a, sqrt_price_lower, sqrt_price_upper)?,
263        PositionStatus::Invalid | PositionStatus::PriceAboveRange => 0,
264        PositionStatus::PriceInRange => get_liquidity_from_amount_a(token_delta_a, current_sqrt_price, sqrt_price_upper)?,
265    };
266
267    increase_liquidity_quote(
268        liquidity.into(),
269        slippage_tolerance_bps,
270        current_sqrt_price.into(),
271        tick_index_1,
272        tick_index_2,
273        transfer_fee_a,
274        transfer_fee_b,
275    )
276}
277
278/// Calculate the quote for increasing liquidity given a token b amount
279///
280/// # Parameters
281/// - `token_amount_b` - The amount of token b to increase
282/// - `slippage_tolerance` - The slippage tolerance in bps
283/// - `current_sqrt_price` - The current sqrt price of the pool
284/// - `tick_index_1` - The first tick index of the position
285/// - `tick_index_2` - The second tick index of the position
286/// - `transfer_fee_a` - The transfer fee for token A in bps
287/// - `transfer_fee_b` - The transfer fee for token B in bps
288///
289/// # Returns
290/// - An IncreaseLiquidityQuote struct containing the estimated token amounts
291#[cfg_attr(feature = "wasm", wasm_expose)]
292pub fn increase_liquidity_quote_b(
293    token_amount_b: u64,
294    slippage_tolerance_bps: u16,
295    current_sqrt_price: u128,
296    tick_index_1: i32,
297    tick_index_2: i32,
298    transfer_fee_a: Option<TransferFee>,
299    transfer_fee_b: Option<TransferFee>,
300) -> Result<IncreaseLiquidityQuote, CoreError> {
301    let tick_range = order_tick_indexes(tick_index_1, tick_index_2);
302    let token_delta_b = try_apply_transfer_fee(token_amount_b, transfer_fee_b.unwrap_or_default())?;
303
304    if token_delta_b == 0 {
305        return Ok(IncreaseLiquidityQuote::default());
306    }
307
308    let sqrt_price_lower = tick_index_to_sqrt_price(tick_range.tick_lower_index);
309    let sqrt_price_upper = tick_index_to_sqrt_price(tick_range.tick_upper_index);
310
311    let position_status = position_status(current_sqrt_price, tick_index_1, tick_index_2);
312
313    let liquidity: u128 = match position_status {
314        PositionStatus::Invalid | PositionStatus::PriceBelowRange => 0,
315        PositionStatus::PriceAboveRange => get_liquidity_from_amount_b(token_delta_b, sqrt_price_lower, sqrt_price_upper)?,
316        PositionStatus::PriceInRange => get_liquidity_from_amount_b(token_delta_b, sqrt_price_lower, current_sqrt_price)?,
317    };
318
319    increase_liquidity_quote(liquidity, slippage_tolerance_bps, current_sqrt_price, tick_index_1, tick_index_2, transfer_fee_a, transfer_fee_b)
320}
321
322#[cfg(all(test, not(feature = "wasm")))]
323mod tests {
324    use super::*;
325
326    #[test]
327    fn test_decrease_liquidity_quote() {
328        // Below range
329        let result = decrease_liquidity_quote(1000000, 100, 18354745142194483561, -10, 10, None, None).unwrap();
330        assert_eq!(result.liquidity_delta, 1000000);
331        assert_eq!(result.token_est_a, 999);
332        assert_eq!(result.token_est_b, 0);
333        assert_eq!(result.token_min_a, 989);
334        assert_eq!(result.token_min_b, 0);
335
336        // in range
337        let result = decrease_liquidity_quote(1000000, 100, 18446744073709551616, -10, 10, None, None).unwrap();
338        assert_eq!(result.liquidity_delta, 1000000);
339        assert_eq!(result.token_est_a, 499);
340        assert_eq!(result.token_est_b, 499);
341        assert_eq!(result.token_min_a, 494);
342        assert_eq!(result.token_min_b, 494);
343
344        // Above range
345        let result = decrease_liquidity_quote(1000000, 100, 18539204128674405812, -10, 10, None, None).unwrap();
346        assert_eq!(result.liquidity_delta, 1000000);
347        assert_eq!(result.token_est_a, 0);
348        assert_eq!(result.token_est_b, 999);
349        assert_eq!(result.token_min_a, 0);
350        assert_eq!(result.token_min_b, 989);
351
352        // zero liquidity
353        let result = decrease_liquidity_quote(0, 100, 18446744073709551616, -10, 10, None, None).unwrap();
354        assert_eq!(result.liquidity_delta, 0);
355        assert_eq!(result.token_est_a, 0);
356        assert_eq!(result.token_est_b, 0);
357        assert_eq!(result.token_min_a, 0);
358        assert_eq!(result.token_min_b, 0);
359    }
360
361    #[test]
362    fn test_decrease_liquidity_quote_a() {
363        // Below range
364        let result = decrease_liquidity_quote_a(1000, 100, 18354745142194483561, -10, 10, None, None).unwrap();
365        assert_eq!(result.liquidity_delta, 1000049);
366        assert_eq!(result.token_est_a, 999);
367        assert_eq!(result.token_est_b, 0);
368        assert_eq!(result.token_min_a, 989);
369        assert_eq!(result.token_min_b, 0);
370
371        // in range
372        let result = decrease_liquidity_quote_a(500, 100, 18446744073709551616, -10, 10, None, None).unwrap();
373        assert_eq!(result.liquidity_delta, 1000300);
374        assert_eq!(result.token_est_a, 499);
375        assert_eq!(result.token_est_b, 499);
376        assert_eq!(result.token_min_a, 494);
377        assert_eq!(result.token_min_b, 494);
378
379        // Above range
380        let result = decrease_liquidity_quote_a(1000, 100, 18539204128674405812, -10, 10, None, None).unwrap();
381        assert_eq!(result.liquidity_delta, 0);
382        assert_eq!(result.token_est_a, 0);
383        assert_eq!(result.token_est_b, 0);
384        assert_eq!(result.token_min_a, 0);
385        assert_eq!(result.token_min_b, 0);
386
387        // zero liquidity
388        let result = decrease_liquidity_quote_a(0, 100, 18446744073709551616, -10, 10, None, None).unwrap();
389        assert_eq!(result.liquidity_delta, 0);
390        assert_eq!(result.token_est_a, 0);
391        assert_eq!(result.token_est_b, 0);
392        assert_eq!(result.token_min_a, 0);
393        assert_eq!(result.token_min_b, 0);
394    }
395
396    #[test]
397    fn test_decrease_liquidity_quote_b() {
398        // Below range
399        let result = decrease_liquidity_quote_b(1000, 100, 18354745142194483561, -10, 10, None, None).unwrap();
400        assert_eq!(result.liquidity_delta, 0);
401        assert_eq!(result.token_est_a, 0);
402        assert_eq!(result.token_est_b, 0);
403        assert_eq!(result.token_min_a, 0);
404        assert_eq!(result.token_min_b, 0);
405
406        // in range
407        let result = decrease_liquidity_quote_b(500, 100, 18446744073709551616, -10, 10, None, None).unwrap();
408        assert_eq!(result.liquidity_delta, 1000300);
409        assert_eq!(result.token_est_a, 499);
410        assert_eq!(result.token_est_b, 499);
411        assert_eq!(result.token_min_a, 494);
412        assert_eq!(result.token_min_b, 494);
413
414        // Above range
415        let result = decrease_liquidity_quote_b(1000, 100, 18539204128674405812, -10, 10, None, None).unwrap();
416        assert_eq!(result.liquidity_delta, 1000049);
417        assert_eq!(result.token_est_a, 0);
418        assert_eq!(result.token_est_b, 999);
419        assert_eq!(result.token_min_a, 0);
420        assert_eq!(result.token_min_b, 989);
421
422        // zero liquidity
423        let result = decrease_liquidity_quote_b(0, 100, 18446744073709551616, -10, 10, None, None).unwrap();
424        assert_eq!(result.liquidity_delta, 0);
425        assert_eq!(result.token_est_a, 0);
426        assert_eq!(result.token_est_b, 0);
427        assert_eq!(result.token_min_a, 0);
428        assert_eq!(result.token_min_b, 0);
429    }
430
431    #[test]
432    fn test_increase_liquidity_quote() {
433        // Below range
434        let result = increase_liquidity_quote(1000000, 100, 18354745142194483561, -10, 10, None, None).unwrap();
435        assert_eq!(result.liquidity_delta, 1000000);
436        assert_eq!(result.token_est_a, 1000);
437        assert_eq!(result.token_est_b, 0);
438        assert_eq!(result.token_max_a, 1010);
439        assert_eq!(result.token_max_b, 0);
440
441        // in range
442        let result = increase_liquidity_quote(1000000, 100, 18446744073709551616, -10, 10, None, None).unwrap();
443        assert_eq!(result.liquidity_delta, 1000000);
444        assert_eq!(result.token_est_a, 500);
445        assert_eq!(result.token_est_b, 500);
446        assert_eq!(result.token_max_a, 505);
447        assert_eq!(result.token_max_b, 505);
448
449        // Above range
450        let result = increase_liquidity_quote(1000000, 100, 18539204128674405812, -10, 10, None, None).unwrap();
451        assert_eq!(result.liquidity_delta, 1000000);
452        assert_eq!(result.token_est_a, 0);
453        assert_eq!(result.token_est_b, 1000);
454        assert_eq!(result.token_max_a, 0);
455        assert_eq!(result.token_max_b, 1010);
456
457        // zero liquidity
458        let result = increase_liquidity_quote(0, 100, 18446744073709551616, -10, 10, None, None).unwrap();
459        assert_eq!(result.liquidity_delta, 0);
460        assert_eq!(result.token_est_a, 0);
461        assert_eq!(result.token_est_b, 0);
462        assert_eq!(result.token_max_a, 0);
463        assert_eq!(result.token_max_b, 0);
464    }
465
466    #[test]
467    fn test_increase_liquidity_quote_a() {
468        // Below range
469        let result = increase_liquidity_quote_a(1000, 100, 18354745142194483561, -10, 10, None, None).unwrap();
470        assert_eq!(result.liquidity_delta, 1000049);
471        assert_eq!(result.token_est_a, 1000);
472        assert_eq!(result.token_est_b, 0);
473        assert_eq!(result.token_max_a, 1010);
474        assert_eq!(result.token_max_b, 0);
475
476        // in range
477        let result = increase_liquidity_quote_a(500, 100, 18446744073709551616, -10, 10, None, None).unwrap();
478        assert_eq!(result.liquidity_delta, 1000300);
479        assert_eq!(result.token_est_a, 500);
480        assert_eq!(result.token_est_b, 500);
481        assert_eq!(result.token_max_a, 505);
482        assert_eq!(result.token_max_b, 505);
483
484        // Above range
485        let result = increase_liquidity_quote_a(1000, 100, 18539204128674405812, -10, 10, None, None).unwrap();
486        assert_eq!(result.liquidity_delta, 0);
487        assert_eq!(result.token_est_a, 0);
488        assert_eq!(result.token_est_b, 0);
489        assert_eq!(result.token_max_a, 0);
490        assert_eq!(result.token_max_b, 0);
491
492        // zero liquidity
493        let result = increase_liquidity_quote_a(0, 100, 18446744073709551616, -10, 10, None, None).unwrap();
494        assert_eq!(result.liquidity_delta, 0);
495        assert_eq!(result.token_est_a, 0);
496        assert_eq!(result.token_est_b, 0);
497        assert_eq!(result.token_max_a, 0);
498        assert_eq!(result.token_max_b, 0);
499    }
500
501    #[test]
502    fn test_increase_liquidity_quote_b() {
503        // Below range
504        let result = increase_liquidity_quote_b(1000, 100, 18354745142194483561, -10, 10, None, None).unwrap();
505        assert_eq!(result.liquidity_delta, 0);
506        assert_eq!(result.token_est_a, 0);
507        assert_eq!(result.token_est_b, 0);
508        assert_eq!(result.token_max_a, 0);
509        assert_eq!(result.token_max_b, 0);
510
511        // in range
512        let result = increase_liquidity_quote_b(500, 100, 18446744073709551616, -10, 10, None, None).unwrap();
513        assert_eq!(result.liquidity_delta, 1000300);
514        assert_eq!(result.token_est_a, 500);
515        assert_eq!(result.token_est_b, 500);
516        assert_eq!(result.token_max_a, 505);
517        assert_eq!(result.token_max_b, 505);
518
519        // Above range
520        let result = increase_liquidity_quote_b(1000, 100, 18539204128674405812, -10, 10, None, None).unwrap();
521        assert_eq!(result.liquidity_delta, 1000049);
522        assert_eq!(result.token_est_a, 0);
523        assert_eq!(result.token_est_b, 1000);
524        assert_eq!(result.token_max_a, 0);
525        assert_eq!(result.token_max_b, 1010);
526
527        // zero liquidity
528        let result = increase_liquidity_quote_b(0, 100, 18446744073709551616, -10, 10, None, None).unwrap();
529        assert_eq!(result.liquidity_delta, 0);
530        assert_eq!(result.token_est_a, 0);
531        assert_eq!(result.token_est_b, 0);
532        assert_eq!(result.token_max_a, 0);
533        assert_eq!(result.token_max_b, 0);
534    }
535
536    #[test]
537    fn test_decrease_liquidity_quote_with_fee() {
538        // Below range
539        let result =
540            decrease_liquidity_quote(1000000, 100, 18354745142194483561, -10, 10, Some(TransferFee::new(2000)), Some(TransferFee::new(1000)))
541                .unwrap();
542        assert_eq!(result.liquidity_delta, 1000000);
543        assert_eq!(result.token_est_a, 799);
544        assert_eq!(result.token_est_b, 0);
545        assert_eq!(result.token_min_a, 791);
546        assert_eq!(result.token_min_b, 0);
547
548        // in range
549        let result =
550            decrease_liquidity_quote(1000000, 100, 18446744073709551616, -10, 10, Some(TransferFee::new(2000)), Some(TransferFee::new(1000)))
551                .unwrap();
552        assert_eq!(result.liquidity_delta, 1000000);
553        assert_eq!(result.token_est_a, 399);
554        assert_eq!(result.token_est_b, 449);
555        assert_eq!(result.token_min_a, 395);
556        assert_eq!(result.token_min_b, 444);
557
558        // Above range
559        let result =
560            decrease_liquidity_quote(1000000, 100, 18539204128674405812, -10, 10, Some(TransferFee::new(2000)), Some(TransferFee::new(1000)))
561                .unwrap();
562        assert_eq!(result.liquidity_delta, 1000000);
563        assert_eq!(result.token_est_a, 0);
564        assert_eq!(result.token_est_b, 899);
565        assert_eq!(result.token_min_a, 0);
566        assert_eq!(result.token_min_b, 890);
567
568        // zero liquidity
569        let result =
570            decrease_liquidity_quote(0, 100, 18446744073709551616, -10, 10, Some(TransferFee::new(2000)), Some(TransferFee::new(1000))).unwrap();
571        assert_eq!(result.liquidity_delta, 0);
572        assert_eq!(result.token_est_a, 0);
573        assert_eq!(result.token_est_b, 0);
574        assert_eq!(result.token_min_a, 0);
575        assert_eq!(result.token_min_b, 0);
576    }
577
578    #[test]
579    fn test_decrease_liquidity_quote_a_with_fee() {
580        // Below range
581        let result =
582            decrease_liquidity_quote_a(1000, 100, 18354745142194483561, -10, 10, Some(TransferFee::new(2000)), Some(TransferFee::new(1000))).unwrap();
583        assert_eq!(result.liquidity_delta, 1250062);
584        assert_eq!(result.token_est_a, 999);
585        assert_eq!(result.token_est_b, 0);
586        assert_eq!(result.token_min_a, 989);
587        assert_eq!(result.token_min_b, 0);
588
589        // in range
590        let result =
591            decrease_liquidity_quote_a(500, 100, 18446744073709551616, -10, 10, Some(TransferFee::new(2000)), Some(TransferFee::new(1000))).unwrap();
592        assert_eq!(result.liquidity_delta, 1250375);
593        assert_eq!(result.token_est_a, 499);
594        assert_eq!(result.token_est_b, 561);
595        assert_eq!(result.token_min_a, 494);
596        assert_eq!(result.token_min_b, 555);
597
598        // Above range
599        let result =
600            decrease_liquidity_quote_a(1000, 100, 18539204128674405812, -10, 10, Some(TransferFee::new(2000)), Some(TransferFee::new(1000))).unwrap();
601        assert_eq!(result.liquidity_delta, 0);
602        assert_eq!(result.token_est_a, 0);
603        assert_eq!(result.token_est_b, 0);
604        assert_eq!(result.token_min_a, 0);
605        assert_eq!(result.token_min_b, 0);
606
607        // zero liquidity
608        let result =
609            decrease_liquidity_quote_a(0, 100, 18446744073709551616, -10, 10, Some(TransferFee::new(2000)), Some(TransferFee::new(1000))).unwrap();
610        assert_eq!(result.liquidity_delta, 0);
611        assert_eq!(result.token_est_a, 0);
612        assert_eq!(result.token_est_b, 0);
613        assert_eq!(result.token_min_a, 0);
614        assert_eq!(result.token_min_b, 0);
615    }
616
617    #[test]
618    fn test_decrease_liquidity_quote_b_with_fee() {
619        // Below range
620        let result =
621            decrease_liquidity_quote_b(1000, 100, 18354745142194483561, -10, 10, Some(TransferFee::new(2000)), Some(TransferFee::new(1000))).unwrap();
622        assert_eq!(result.liquidity_delta, 0);
623        assert_eq!(result.token_est_a, 0);
624        assert_eq!(result.token_est_b, 0);
625        assert_eq!(result.token_min_a, 0);
626        assert_eq!(result.token_min_b, 0);
627
628        // in range
629        let result =
630            decrease_liquidity_quote_b(500, 100, 18446744073709551616, -10, 10, Some(TransferFee::new(2000)), Some(TransferFee::new(1000))).unwrap();
631        assert_eq!(result.liquidity_delta, 1112333);
632        assert_eq!(result.token_est_a, 444);
633        assert_eq!(result.token_est_b, 499);
634        assert_eq!(result.token_min_a, 439);
635        assert_eq!(result.token_min_b, 494);
636
637        // Above range
638        let result =
639            decrease_liquidity_quote_b(1000, 100, 18539204128674405812, -10, 10, Some(TransferFee::new(2000)), Some(TransferFee::new(1000))).unwrap();
640        assert_eq!(result.liquidity_delta, 1112055);
641        assert_eq!(result.token_est_a, 0);
642        assert_eq!(result.token_est_b, 999);
643        assert_eq!(result.token_min_a, 0);
644        assert_eq!(result.token_min_b, 989);
645
646        // zero liquidity
647        let result =
648            decrease_liquidity_quote_b(0, 100, 18446744073709551616, -10, 10, Some(TransferFee::new(2000)), Some(TransferFee::new(1000))).unwrap();
649        assert_eq!(result.liquidity_delta, 0);
650        assert_eq!(result.token_est_a, 0);
651        assert_eq!(result.token_est_b, 0);
652        assert_eq!(result.token_min_a, 0);
653        assert_eq!(result.token_min_b, 0);
654    }
655
656    #[test]
657    fn test_increase_liquidity_quote_with_fee() {
658        // Below range
659        let result =
660            increase_liquidity_quote(1000000, 100, 18354745142194483561, -10, 10, Some(TransferFee::new(2000)), Some(TransferFee::new(1000)))
661                .unwrap();
662        assert_eq!(result.liquidity_delta, 1000000);
663        assert_eq!(result.token_est_a, 1250);
664        assert_eq!(result.token_est_b, 0);
665        assert_eq!(result.token_max_a, 1263);
666        assert_eq!(result.token_max_b, 0);
667
668        // in range
669        let result =
670            increase_liquidity_quote(1000000, 100, 18446744073709551616, -10, 10, Some(TransferFee::new(2000)), Some(TransferFee::new(1000)))
671                .unwrap();
672        assert_eq!(result.liquidity_delta, 1000000);
673        assert_eq!(result.token_est_a, 625);
674        assert_eq!(result.token_est_b, 556);
675        assert_eq!(result.token_max_a, 632);
676        assert_eq!(result.token_max_b, 562);
677
678        // Above range
679        let result =
680            increase_liquidity_quote(1000000, 100, 18539204128674405812, -10, 10, Some(TransferFee::new(2000)), Some(TransferFee::new(1000)))
681                .unwrap();
682        assert_eq!(result.liquidity_delta, 1000000);
683        assert_eq!(result.token_est_a, 0);
684        assert_eq!(result.token_est_b, 1112);
685        assert_eq!(result.token_max_a, 0);
686        assert_eq!(result.token_max_b, 1124);
687
688        // zero liquidity
689        let result =
690            increase_liquidity_quote(0, 100, 18446744073709551616, -10, 10, Some(TransferFee::new(2000)), Some(TransferFee::new(1000))).unwrap();
691        assert_eq!(result.liquidity_delta, 0);
692        assert_eq!(result.token_est_a, 0);
693        assert_eq!(result.token_est_b, 0);
694        assert_eq!(result.token_max_a, 0);
695        assert_eq!(result.token_max_b, 0);
696    }
697
698    #[test]
699    fn test_increase_liquidity_quote_a_with_fee() {
700        // Below range
701        let result =
702            increase_liquidity_quote_a(1000, 100, 18354745142194483561, -10, 10, Some(TransferFee::new(2000)), Some(TransferFee::new(1000))).unwrap();
703        assert_eq!(result.liquidity_delta, 800039);
704        assert_eq!(result.token_est_a, 1000);
705        assert_eq!(result.token_est_b, 0);
706        assert_eq!(result.token_max_a, 1010);
707        assert_eq!(result.token_max_b, 0);
708
709        // in range
710        let result =
711            increase_liquidity_quote_a(500, 100, 18446744073709551616, -10, 10, Some(TransferFee::new(2000)), Some(TransferFee::new(1000))).unwrap();
712        assert_eq!(result.liquidity_delta, 800240);
713        assert_eq!(result.token_est_a, 500);
714        assert_eq!(result.token_est_b, 445);
715        assert_eq!(result.token_max_a, 505);
716        assert_eq!(result.token_max_b, 450);
717
718        // Above range
719        let result =
720            increase_liquidity_quote_a(1000, 100, 18539204128674405812, -10, 10, Some(TransferFee::new(2000)), Some(TransferFee::new(1000))).unwrap();
721        assert_eq!(result.liquidity_delta, 0);
722        assert_eq!(result.token_est_a, 0);
723        assert_eq!(result.token_est_b, 0);
724        assert_eq!(result.token_max_a, 0);
725        assert_eq!(result.token_max_b, 0);
726
727        // zero liquidity
728        let result =
729            increase_liquidity_quote_a(0, 100, 18446744073709551616, -10, 10, Some(TransferFee::new(2000)), Some(TransferFee::new(1000))).unwrap();
730        assert_eq!(result.liquidity_delta, 0);
731        assert_eq!(result.token_est_a, 0);
732        assert_eq!(result.token_est_b, 0);
733        assert_eq!(result.token_max_a, 0);
734        assert_eq!(result.token_max_b, 0);
735    }
736
737    #[test]
738    fn test_increase_liquidity_quote_b_with_fee() {
739        // Below range
740        let result =
741            increase_liquidity_quote_b(1000, 100, 18354745142194483561, -10, 10, Some(TransferFee::new(2000)), Some(TransferFee::new(1000))).unwrap();
742        assert_eq!(result.liquidity_delta, 0);
743        assert_eq!(result.token_est_a, 0);
744        assert_eq!(result.token_est_b, 0);
745        assert_eq!(result.token_max_a, 0);
746        assert_eq!(result.token_max_b, 0);
747
748        // in range
749        let result =
750            increase_liquidity_quote_b(500, 100, 18446744073709551616, -10, 10, Some(TransferFee::new(2000)), Some(TransferFee::new(1000))).unwrap();
751        assert_eq!(result.liquidity_delta, 900270);
752        assert_eq!(result.token_est_a, 563);
753        assert_eq!(result.token_est_b, 500);
754        assert_eq!(result.token_max_a, 569);
755        assert_eq!(result.token_max_b, 505);
756
757        // Above range
758        let result =
759            increase_liquidity_quote_b(1000, 100, 18539204128674405812, -10, 10, Some(TransferFee::new(2000)), Some(TransferFee::new(1000))).unwrap();
760        assert_eq!(result.liquidity_delta, 900044);
761        assert_eq!(result.token_est_a, 0);
762        assert_eq!(result.token_est_b, 1000);
763        assert_eq!(result.token_max_a, 0);
764        assert_eq!(result.token_max_b, 1010);
765
766        // zero liquidity
767        let result =
768            increase_liquidity_quote_b(0, 100, 18446744073709551616, -10, 10, Some(TransferFee::new(2000)), Some(TransferFee::new(1000))).unwrap();
769        assert_eq!(result.liquidity_delta, 0);
770        assert_eq!(result.token_est_a, 0);
771        assert_eq!(result.token_est_b, 0);
772        assert_eq!(result.token_max_a, 0);
773        assert_eq!(result.token_max_b, 0);
774    }
775}