1#![allow(clippy::collapsible_else_if)]
2#![allow(clippy::too_many_arguments)]
3
4use crate::{calculate_tuna_protocol_fee, CoreError, COMPUTED_AMOUNT, HUNDRED_PERCENT, INVALID_ARGUMENTS};
5use fusionamm_core::{
6 position_ratio_x64, tick_index_to_sqrt_price, try_apply_swap_fee, try_get_amount_delta_a, try_get_amount_delta_b, try_get_liquidity_from_a,
7 try_get_liquidity_from_amounts, try_get_liquidity_from_b, try_get_token_a_from_liquidity, try_get_token_b_from_liquidity, Q64_RESOLUTION,
8};
9
10#[cfg(feature = "wasm")]
11use fusionamm_macros::wasm_expose;
12
13#[derive(Debug, Copy, Clone, PartialEq)]
14#[cfg_attr(feature = "wasm", wasm_expose)]
15pub struct LiquidationPrices {
16 pub lower: f64,
17 pub upper: f64,
18}
19
20fn compute_liquidation_prices_inside(
52 lower_sqrt_price: u128,
53 upper_sqrt_price: u128,
54 liquidity: u128,
55 leftovers_a: u64,
56 leftovers_b: u64,
57 debt_a: u64,
58 debt_b: u64,
59 liquidation_threshold: u32,
60) -> Result<LiquidationPrices, CoreError> {
61 let liquidation_threshold_f = liquidation_threshold as f64 / HUNDRED_PERCENT as f64;
62 let liquidity_f = liquidity as f64;
63 let lower_sqrt_price_f = lower_sqrt_price as f64 / Q64_RESOLUTION;
64 let upper_sqrt_price_f = upper_sqrt_price as f64 / Q64_RESOLUTION;
65
66 let a = debt_a as f64 + liquidation_threshold_f * (liquidity_f / upper_sqrt_price_f - leftovers_a as f64);
67 let b = -2.0 * liquidation_threshold_f * liquidity_f;
68 let c = debt_b as f64 + liquidation_threshold_f * (liquidity_f * lower_sqrt_price_f - leftovers_b as f64);
69 let d = b * b - 4.0 * a * c;
70
71 let mut lower_liquidation_sqrt_price = 0.0;
72 let mut upper_liquidation_sqrt_price = 0.0;
73
74 if d >= 0.0 {
75 lower_liquidation_sqrt_price = (-b - d.sqrt()) / (2.0 * a);
76 upper_liquidation_sqrt_price = (-b + d.sqrt()) / (2.0 * a);
77 if lower_liquidation_sqrt_price < 0.0 || lower_liquidation_sqrt_price < lower_sqrt_price_f {
78 lower_liquidation_sqrt_price = 0.0;
79 }
80 if upper_liquidation_sqrt_price < 0.0 || upper_liquidation_sqrt_price > upper_sqrt_price_f {
81 upper_liquidation_sqrt_price = 0.0;
82 }
83 }
84
85 Ok(LiquidationPrices {
86 lower: lower_liquidation_sqrt_price * lower_liquidation_sqrt_price,
87 upper: upper_liquidation_sqrt_price * upper_liquidation_sqrt_price,
88 })
89}
90
91fn calculate_liquidation_outside(
122 amount_a: u64,
123 amount_b: u64,
124 leftovers_a: u64,
125 leftovers_b: u64,
126 debt_a: u64,
127 debt_b: u64,
128 liquidation_threshold: u32,
129) -> Result<f64, CoreError> {
130 let liquidation_threshold_f = liquidation_threshold as f64 / HUNDRED_PERCENT as f64;
131
132 if amount_a == 0 && amount_b == 0 {
133 Ok(0.0)
134 } else if amount_a > 0 && amount_b == 0 {
135 let numerator = debt_b as f64 - liquidation_threshold_f * leftovers_b as f64;
137 let denominator = liquidation_threshold_f * (amount_a + leftovers_a) as f64 - debt_a as f64;
138 Ok(numerator / denominator)
139 } else if amount_a == 0 && amount_b > 0 {
140 let numerator = liquidation_threshold_f * (amount_b + leftovers_b) as f64 - debt_b as f64;
142 let denominator = debt_a as f64 - liquidation_threshold_f * leftovers_a as f64;
143 if denominator == 0.0 {
144 return Ok(0.0);
145 }
146 Ok(numerator / denominator)
147 } else {
148 Err(INVALID_ARGUMENTS)
149 }
150}
151
152fn compute_liquidation_prices_outside(
167 lower_sqrt_price: u128,
168 upper_sqrt_price: u128,
169 liquidity: u128,
170 leftovers_a: u64,
171 leftovers_b: u64,
172 debt_a: u64,
173 debt_b: u64,
174 liquidation_threshold: u32,
175) -> Result<LiquidationPrices, CoreError> {
176 let amount_a = try_get_amount_delta_a(lower_sqrt_price.into(), upper_sqrt_price.into(), liquidity.into(), false)?;
177 let amount_b = try_get_amount_delta_b(lower_sqrt_price.into(), upper_sqrt_price.into(), liquidity.into(), false)?;
178
179 let mut liquidation_price_for_a = calculate_liquidation_outside(amount_a, 0, leftovers_a, leftovers_b, debt_a, debt_b, liquidation_threshold)?;
180 let mut liquidation_price_for_b = calculate_liquidation_outside(0, amount_b, leftovers_a, leftovers_b, debt_a, debt_b, liquidation_threshold)?;
181
182 if liquidation_price_for_a < 0.0 || liquidation_price_for_a > (lower_sqrt_price as f64 / Q64_RESOLUTION).powf(2.0) {
183 liquidation_price_for_a = 0.0;
184 }
185 if liquidation_price_for_b < 0.0 || liquidation_price_for_b < (upper_sqrt_price as f64 / Q64_RESOLUTION).powf(2.0) {
186 liquidation_price_for_b = 0.0;
187 }
188
189 Ok(LiquidationPrices {
190 lower: liquidation_price_for_a,
191 upper: liquidation_price_for_b,
192 })
193}
194
195#[cfg_attr(feature = "wasm", wasm_expose)]
210pub fn get_lp_position_liquidation_prices(
211 tick_lower_index: i32,
212 tick_upper_index: i32,
213 liquidity: u128,
214 leftovers_a: u64,
215 leftovers_b: u64,
216 debt_a: u64,
217 debt_b: u64,
218 liquidation_threshold: u32,
219) -> Result<LiquidationPrices, CoreError> {
220 if tick_lower_index >= tick_upper_index {
221 return Err(INVALID_ARGUMENTS);
222 }
223
224 if liquidation_threshold >= HUNDRED_PERCENT {
225 return Err(INVALID_ARGUMENTS);
226 }
227
228 let lower_sqrt_price = tick_index_to_sqrt_price(tick_lower_index);
229 let upper_sqrt_price = tick_index_to_sqrt_price(tick_upper_index);
230
231 let liquidation_price_inside = compute_liquidation_prices_inside(
232 lower_sqrt_price,
233 upper_sqrt_price,
234 liquidity,
235 leftovers_a,
236 leftovers_b,
237 debt_a,
238 debt_b,
239 liquidation_threshold,
240 )?;
241
242 let liquidation_price_outside = compute_liquidation_prices_outside(
243 lower_sqrt_price,
244 upper_sqrt_price,
245 liquidity,
246 leftovers_a,
247 leftovers_b,
248 debt_a,
249 debt_b,
250 liquidation_threshold,
251 )?;
252
253 let lower_liquidation_price = if liquidation_price_inside.lower > 0.0 {
254 liquidation_price_inside.lower
255 } else {
256 liquidation_price_outside.lower
257 };
258
259 let upper_liquidation_price = if liquidation_price_inside.upper > 0.0 {
260 liquidation_price_inside.upper
261 } else {
262 liquidation_price_outside.upper
263 };
264
265 Ok(LiquidationPrices {
266 lower: lower_liquidation_price,
267 upper: upper_liquidation_price,
268 })
269}
270
271#[derive(Debug, Copy, Clone, PartialEq)]
272#[cfg_attr(feature = "wasm", wasm_expose)]
273pub struct IncreaseLpPositionQuoteArgs {
274 pub collateral_a: u64,
276 pub collateral_b: u64,
278 pub borrow_a: u64,
280 pub borrow_b: u64,
282 pub protocol_fee_rate: u16,
284 pub protocol_fee_rate_on_collateral: u16,
286 pub swap_fee_rate: u16,
288 pub sqrt_price: u128,
290 pub tick_lower_index: i32,
292 pub tick_upper_index: i32,
294 pub max_amount_slippage: u32,
296 pub liquidation_threshold: u32,
298}
299
300#[derive(Debug, Copy, Clone, PartialEq)]
301#[cfg_attr(feature = "wasm", wasm_expose)]
302pub struct IncreaseLpPositionQuoteResult {
303 pub collateral_a: u64,
304 pub collateral_b: u64,
305 pub max_collateral_a: u64,
306 pub max_collateral_b: u64,
307 pub borrow_a: u64,
308 pub borrow_b: u64,
309 pub total_a: u64,
310 pub total_b: u64,
311 pub min_total_a: u64,
312 pub min_total_b: u64,
313 pub swap_input: u64,
314 pub swap_output: u64,
315 pub swap_a_to_b: bool,
316 pub protocol_fee_a: u64,
317 pub protocol_fee_b: u64,
318 pub liquidation_lower_price: f64,
319 pub liquidation_upper_price: f64,
320}
321
322#[cfg_attr(feature = "wasm", wasm_expose)]
323pub fn get_increase_lp_position_quote(args: IncreaseLpPositionQuoteArgs) -> Result<IncreaseLpPositionQuoteResult, CoreError> {
324 let mut collateral_a = args.collateral_a;
325 let mut collateral_b = args.collateral_b;
326 let mut borrow_a = args.borrow_a;
327 let mut borrow_b = args.borrow_b;
328 let sqrt_price = args.sqrt_price;
329
330 if args.tick_lower_index > args.tick_upper_index {
331 return Err("Incorrect position tick index order: the lower tick must be less or equal the upper tick.");
332 }
333
334 if args.max_amount_slippage > HUNDRED_PERCENT {
335 return Err("max_amount_slippage must be in range [0; HUNDRED_PERCENT]");
336 }
337
338 if collateral_a == COMPUTED_AMOUNT && collateral_b == COMPUTED_AMOUNT {
339 return Err("Both collateral amounts can't be set to COMPUTED_AMOUNT");
340 }
341
342 let max_amount_slippage = args.max_amount_slippage;
343
344 let mut max_collateral_a = collateral_a;
345 let mut max_collateral_b = collateral_b;
346
347 let lower_sqrt_price = tick_index_to_sqrt_price(args.tick_lower_index);
348 let upper_sqrt_price = tick_index_to_sqrt_price(args.tick_upper_index);
349
350 if collateral_a == COMPUTED_AMOUNT {
351 if sqrt_price <= lower_sqrt_price {
352 return Err("sqrtPrice must be greater than lower_sqrt_price if collateral A is computed.");
353 } else if sqrt_price < upper_sqrt_price {
354 let liquidity = try_get_liquidity_from_b(collateral_b + borrow_b, lower_sqrt_price, sqrt_price)?;
355 let amount_a = try_get_token_a_from_liquidity(liquidity, sqrt_price, upper_sqrt_price, false)?;
356 collateral_a = (amount_a * collateral_b) / (collateral_b + borrow_b);
357 borrow_a = amount_a - collateral_a;
358 max_collateral_a = collateral_a + ((collateral_a as u128 * max_amount_slippage as u128) / HUNDRED_PERCENT as u128) as u64;
359 } else {
360 collateral_a = 0;
361 max_collateral_a = 0;
362 borrow_a = 0;
363 }
364 } else if collateral_b == COMPUTED_AMOUNT {
365 if sqrt_price <= lower_sqrt_price {
366 collateral_b = 0;
367 max_collateral_b = 0;
368 borrow_b = 0;
369 } else if sqrt_price < upper_sqrt_price {
370 let liquidity = try_get_liquidity_from_a(collateral_a + borrow_a, sqrt_price, upper_sqrt_price)?;
371 let amount_b = try_get_token_b_from_liquidity(liquidity, lower_sqrt_price, sqrt_price, false)?;
372 collateral_b = (amount_b * collateral_a) / (collateral_a + borrow_a);
373 borrow_b = amount_b - collateral_b;
374 max_collateral_b = collateral_b + ((collateral_b as u128 * max_amount_slippage as u128) / HUNDRED_PERCENT as u128) as u64;
375 } else {
376 return Err("sqrtPrice must be less than upper_sqrt_price if collateral B is computed.");
377 }
378 }
379
380 let protocol_fee_a = calculate_tuna_protocol_fee(collateral_a, borrow_a, args.protocol_fee_rate_on_collateral, args.protocol_fee_rate);
381 let provided_a = collateral_a + borrow_a - protocol_fee_a;
382
383 let protocol_fee_b = calculate_tuna_protocol_fee(collateral_b, borrow_b, args.protocol_fee_rate_on_collateral, args.protocol_fee_rate);
384 let provided_b = collateral_b + borrow_b - protocol_fee_b;
385
386 let mut swap_input = 0;
387 let mut swap_output = 0;
388 let mut swap_a_to_b = false;
389 let mut total_a = provided_a;
390 let mut total_b = provided_b;
391
392 if args.collateral_a != COMPUTED_AMOUNT && args.collateral_b != COMPUTED_AMOUNT {
393 let position_ratio = position_ratio_x64(sqrt_price.into(), args.tick_lower_index, args.tick_upper_index);
394 let ratio_a = position_ratio.ratio_a as f64 / Q64_RESOLUTION;
395 let ratio_b = position_ratio.ratio_b as f64 / Q64_RESOLUTION;
396
397 let price = (sqrt_price as f64 / Q64_RESOLUTION).powf(2.0);
398
399 let mut total = (provided_a as f64 * price + provided_b as f64) as u64;
401 total_a = (total as f64 * ratio_a / price) as u64;
402 total_b = (total as f64 * ratio_b) as u64;
403
404 let mut fee_a = 0;
405 let mut fee_b = 0;
406
407 if total_a < provided_a {
408 swap_input = provided_a - total_a;
409 fee_a = swap_input - try_apply_swap_fee(swap_input, args.swap_fee_rate)?;
410 swap_output = ((swap_input - fee_a) as f64 * price) as u64;
411 swap_a_to_b = true;
412 } else if total_b < provided_b {
413 swap_input = provided_b - total_b;
414 fee_b = swap_input - try_apply_swap_fee(swap_input, args.swap_fee_rate)?;
415 swap_output = ((swap_input - fee_b) as f64 / price) as u64;
416 swap_a_to_b = false;
417 }
418
419 total = ((provided_a - fee_a) as f64 * price) as u64 + provided_b - fee_b;
421 total_a = ((total as f64 * ratio_a) / price) as u64;
422 total_b = (total as f64 * ratio_b) as u64;
423 }
424
425 let min_total_a = total_a - ((total_a as u128 * max_amount_slippage as u128) / HUNDRED_PERCENT as u128) as u64;
426 let min_total_b = total_b - ((total_b as u128 * max_amount_slippage as u128) / HUNDRED_PERCENT as u128) as u64;
427
428 let liquidity = try_get_liquidity_from_amounts(sqrt_price, lower_sqrt_price, upper_sqrt_price, total_a, total_b)?;
429 let liquidation_prices = get_lp_position_liquidation_prices(
430 args.tick_lower_index,
431 args.tick_upper_index,
432 liquidity,
433 0,
434 0,
435 borrow_a,
436 borrow_b,
437 args.liquidation_threshold,
438 )?;
439
440 Ok(IncreaseLpPositionQuoteResult {
441 collateral_a,
442 collateral_b,
443 max_collateral_a,
444 max_collateral_b,
445 borrow_a,
446 borrow_b,
447 total_a,
448 total_b,
449 min_total_a,
450 min_total_b,
451 swap_input,
452 swap_output,
453 swap_a_to_b,
454 protocol_fee_a,
455 protocol_fee_b,
456 liquidation_lower_price: liquidation_prices.lower,
457 liquidation_upper_price: liquidation_prices.upper,
458 })
459}
460
461#[cfg(all(test, not(feature = "wasm")))]
462mod tests {
463 use crate::{
464 get_increase_lp_position_quote, get_lp_position_liquidation_prices, IncreaseLpPositionQuoteArgs, IncreaseLpPositionQuoteResult,
465 LiquidationPrices, COMPUTED_AMOUNT, HUNDRED_PERCENT,
466 };
467 use fusionamm_core::{price_to_sqrt_price, price_to_tick_index, tick_index_to_sqrt_price, try_get_liquidity_from_b};
468 use once_cell::sync::Lazy;
469
470 pub static TICK_LOWER_INDEX: Lazy<i32> = Lazy::new(|| price_to_tick_index(180.736, 6, 6));
471 pub static TICK_UPPER_INDEX: Lazy<i32> = Lazy::new(|| price_to_tick_index(225.66, 6, 6));
472 pub static SQRT_PRICE: Lazy<u128> = Lazy::new(|| price_to_sqrt_price(213.41, 6, 6));
473 pub static LIQUIDITY: Lazy<u128> =
474 Lazy::new(|| try_get_liquidity_from_b(10000_000_000, tick_index_to_sqrt_price(*TICK_LOWER_INDEX), *SQRT_PRICE).unwrap());
475
476 #[test]
477 fn test_liquidation_price_outside_range_lower() {
478 assert_eq!(
479 get_lp_position_liquidation_prices(
480 *TICK_LOWER_INDEX,
481 *TICK_UPPER_INDEX,
482 *LIQUIDITY,
483 0, 0, 0, 9807_000_000, HUNDRED_PERCENT * 83 / 100 ),
489 Ok(LiquidationPrices {
490 lower: 176.12815046153585,
491 upper: 0.0
492 })
493 );
494 }
495
496 #[test]
497 fn test_liquidation_price_outside_range_upper() {
498 assert_eq!(
499 get_lp_position_liquidation_prices(
500 *TICK_LOWER_INDEX,
501 *TICK_UPPER_INDEX,
502 *LIQUIDITY,
503 0, 0, 20_000_000, 0, HUNDRED_PERCENT * 83 / 100 ),
509 Ok(LiquidationPrices {
510 lower: 0.0,
511 upper: 562.219410388
512 })
513 );
514 }
515
516 #[test]
517 fn test_liquidation_price_outside_range_lower_and_upper() {
518 assert_eq!(
519 get_lp_position_liquidation_prices(
520 *TICK_LOWER_INDEX,
521 *TICK_UPPER_INDEX,
522 *LIQUIDITY,
523 0, 0, 10_000_000, 5000_000_000, HUNDRED_PERCENT * 83 / 100 ),
529 Ok(LiquidationPrices {
530 lower: 109.45458168998225,
531 upper: 624.4388207760001
532 })
533 );
534 }
535
536 #[test]
537 fn test_liquidation_price_inside_range_lower() {
538 assert_eq!(
539 get_lp_position_liquidation_prices(
540 *TICK_LOWER_INDEX,
541 *TICK_UPPER_INDEX,
542 *LIQUIDITY,
543 0, 0, 0, 11000_000_000, HUNDRED_PERCENT * 83 / 100 ),
549 Ok(LiquidationPrices {
550 lower: 204.60489065334323,
551 upper: 0.0
552 })
553 );
554 }
555
556 #[test]
557 fn test_liquidation_price_inside_range_upper() {
558 assert_eq!(
559 get_lp_position_liquidation_prices(
560 *TICK_LOWER_INDEX,
561 *TICK_UPPER_INDEX,
562 *LIQUIDITY,
563 0, 0, 51_000_000, 0, HUNDRED_PERCENT * 83 / 100 ),
569 Ok(LiquidationPrices {
570 lower: 0.0,
571 upper: 220.16318077637644
572 })
573 );
574 }
575
576 #[test]
577 fn test_liquidation_price_inside_range_lower_and_upper() {
578 assert_eq!(
579 get_lp_position_liquidation_prices(
580 *TICK_LOWER_INDEX,
581 *TICK_UPPER_INDEX,
582 *LIQUIDITY,
583 0, 0, 11_500_000, 8700_000_000, HUNDRED_PERCENT * 83 / 100 ),
589 Ok(LiquidationPrices {
590 lower: 210.75514596082337,
591 upper: 219.48595430071575
592 })
593 );
594 }
595
596 #[test]
597 fn test_lp_increase_quote_collateral_a_and_b_provided() {
598 assert_eq!(
599 get_increase_lp_position_quote(IncreaseLpPositionQuoteArgs {
600 collateral_a: 1000000,
601 collateral_b: 1000000,
602 borrow_a: 2000000,
603 borrow_b: 2000000,
604 tick_lower_index: price_to_tick_index(1.0, 1, 1),
605 sqrt_price: price_to_sqrt_price(2.0, 1, 1),
606 tick_upper_index: price_to_tick_index(4.0, 1, 1),
607 protocol_fee_rate: (HUNDRED_PERCENT / 100) as u16,
608 protocol_fee_rate_on_collateral: (HUNDRED_PERCENT / 100) as u16,
609 swap_fee_rate: 10000, max_amount_slippage: HUNDRED_PERCENT / 10,
611 liquidation_threshold: HUNDRED_PERCENT * 83 / 100,
612 }),
613 Ok(IncreaseLpPositionQuoteResult {
614 collateral_a: 1000000,
615 collateral_b: 1000000,
616 max_collateral_a: 1000000,
617 max_collateral_b: 1000000,
618 borrow_a: 2000000,
619 borrow_b: 2000000,
620 total_a: 2223701,
621 total_b: 4447744,
622 min_total_a: 2001331,
623 min_total_b: 4002970,
624 swap_input: 742586,
625 swap_output: 1470320,
626 swap_a_to_b: true,
627 protocol_fee_a: 30000,
628 protocol_fee_b: 30000,
629 liquidation_lower_price: 0.8143170288470588,
630 liquidation_upper_price: 3.4020457909456225,
631 })
632 );
633 }
634
635 #[test]
636 fn test_lp_increase_quote_collateral_a_provided() {
637 assert_eq!(
638 get_increase_lp_position_quote(IncreaseLpPositionQuoteArgs {
639 collateral_a: 10000000,
640 collateral_b: 0,
641 borrow_a: 0,
642 borrow_b: 0,
643 tick_lower_index: price_to_tick_index(0.25, 6, 9),
644 sqrt_price: price_to_sqrt_price(0.5, 6, 9),
645 tick_upper_index: price_to_tick_index(1.0, 6, 9),
646 protocol_fee_rate: (HUNDRED_PERCENT / 100) as u16,
647 protocol_fee_rate_on_collateral: (HUNDRED_PERCENT / 100) as u16,
648 swap_fee_rate: 10000, max_amount_slippage: HUNDRED_PERCENT / 10,
650 liquidation_threshold: HUNDRED_PERCENT * 83 / 100,
651 }),
652 Ok(IncreaseLpPositionQuoteResult {
653 collateral_a: 10000000,
654 collateral_b: 0,
655 max_collateral_a: 10000000,
656 max_collateral_b: 0,
657 borrow_a: 0,
658 borrow_b: 0,
659 total_a: 4925137,
660 total_b: 2462680451,
661 min_total_a: 4432624,
662 min_total_b: 2216412406,
663 swap_input: 4950113,
664 swap_output: 2450305500,
665 swap_a_to_b: true,
666 protocol_fee_a: 100000,
667 protocol_fee_b: 0,
668 liquidation_lower_price: 0.0,
669 liquidation_upper_price: 0.0,
670 })
671 );
672 }
673
674 #[test]
675 fn test_lp_increase_quote_collateral_a_provided_b_computed() {
676 assert_eq!(
677 get_increase_lp_position_quote(IncreaseLpPositionQuoteArgs {
678 collateral_a: 10000000,
679 collateral_b: COMPUTED_AMOUNT,
680 borrow_a: 2000000,
681 borrow_b: COMPUTED_AMOUNT,
682 tick_lower_index: price_to_tick_index(1.0, 1, 1),
683 sqrt_price: price_to_sqrt_price(3.0, 1, 1),
684 tick_upper_index: price_to_tick_index(4.0, 1, 1),
685 protocol_fee_rate: 0,
686 protocol_fee_rate_on_collateral: 0,
687 swap_fee_rate: 0,
688 max_amount_slippage: HUNDRED_PERCENT / 10,
689 liquidation_threshold: HUNDRED_PERCENT * 83 / 100,
690 }),
691 Ok(IncreaseLpPositionQuoteResult {
692 collateral_a: 10000000,
693 collateral_b: 94660495,
694 max_collateral_a: 10000000,
695 max_collateral_b: 104126544,
696 borrow_a: 2000000,
697 borrow_b: 18932100,
698 total_a: 12000000,
699 total_b: 113592595,
700 min_total_a: 10800000,
701 min_total_b: 102233336,
702 swap_input: 0,
703 swap_output: 0,
704 swap_a_to_b: false,
705 protocol_fee_a: 0,
706 protocol_fee_b: 0,
707 liquidation_lower_price: 0.30342990360419136,
708 liquidation_upper_price: 54.925553349999994
709 })
710 );
711 }
712
713 #[test]
714 fn test_lp_increase_quote_collateral_a_computed_b_provided() {
715 assert_eq!(
716 get_increase_lp_position_quote(IncreaseLpPositionQuoteArgs {
717 collateral_a: COMPUTED_AMOUNT,
718 collateral_b: 1000000,
719 borrow_a: COMPUTED_AMOUNT,
720 borrow_b: 2000000,
721 tick_lower_index: price_to_tick_index(1.0, 1, 1),
722 sqrt_price: price_to_sqrt_price(3.0, 1, 1),
723 tick_upper_index: price_to_tick_index(4.0, 1, 1),
724 protocol_fee_rate: 0,
725 protocol_fee_rate_on_collateral: 0,
726 swap_fee_rate: 0,
727 max_amount_slippage: HUNDRED_PERCENT / 10,
728 liquidation_threshold: HUNDRED_PERCENT * 83 / 100,
729 }),
730 Ok(IncreaseLpPositionQuoteResult {
731 collateral_a: 105640,
732 collateral_b: 1000000,
733 max_collateral_a: 116204,
734 max_collateral_b: 1000000,
735 borrow_a: 211282,
736 borrow_b: 2000000,
737 total_a: 316922,
738 total_b: 3000000,
739 min_total_a: 285230,
740 min_total_b: 2700000,
741 swap_input: 0,
742 swap_output: 0,
743 swap_a_to_b: false,
744 protocol_fee_a: 0,
745 protocol_fee_b: 0,
746 liquidation_lower_price: 1.4306915613018771,
747 liquidation_upper_price: 6.63182675287057
748 })
749 );
750 }
751
752 #[test]
753 fn test_lp_increase_quote_one_sided_collateral_a_provided_b_computed() {
754 assert_eq!(
755 get_increase_lp_position_quote(IncreaseLpPositionQuoteArgs {
756 collateral_a: 10000000,
757 collateral_b: COMPUTED_AMOUNT,
758 borrow_a: 2000000,
759 borrow_b: COMPUTED_AMOUNT,
760 tick_lower_index: price_to_tick_index(1.0, 1, 1),
761 sqrt_price: price_to_sqrt_price(0.5, 1, 1),
762 tick_upper_index: price_to_tick_index(4.0, 1, 1),
763 protocol_fee_rate: 0,
764 protocol_fee_rate_on_collateral: 0,
765 swap_fee_rate: 0,
766 max_amount_slippage: HUNDRED_PERCENT / 10,
767 liquidation_threshold: HUNDRED_PERCENT * 83 / 100,
768 }),
769 Ok(IncreaseLpPositionQuoteResult {
770 collateral_a: 10000000,
771 collateral_b: 0,
772 max_collateral_a: 10000000,
773 max_collateral_b: 0,
774 borrow_a: 2000000,
775 borrow_b: 0,
776 total_a: 12000000,
777 total_b: 0,
778 min_total_a: 10800000,
779 min_total_b: 0,
780 swap_input: 0,
781 swap_output: 0,
782 swap_a_to_b: false,
783 protocol_fee_a: 0,
784 protocol_fee_b: 0,
785 liquidation_lower_price: 0.0,
786 liquidation_upper_price: 9.959682525
787 })
788 );
789 }
790
791 #[test]
792 fn test_lp_increase_quote_one_sided_collateral_a_computed_b_provided() {
793 assert_eq!(
794 get_increase_lp_position_quote(IncreaseLpPositionQuoteArgs {
795 collateral_a: COMPUTED_AMOUNT,
796 collateral_b: 1000000,
797 borrow_a: COMPUTED_AMOUNT,
798 borrow_b: 2000000,
799 tick_lower_index: price_to_tick_index(1.0, 1, 1),
800 sqrt_price: price_to_sqrt_price(5.0, 1, 1),
801 tick_upper_index: price_to_tick_index(4.0, 1, 1),
802 protocol_fee_rate: 0,
803 protocol_fee_rate_on_collateral: 0,
804 swap_fee_rate: 0,
805 max_amount_slippage: HUNDRED_PERCENT / 10,
806 liquidation_threshold: HUNDRED_PERCENT * 83 / 100,
807 }),
808 Ok(IncreaseLpPositionQuoteResult {
809 collateral_a: 0,
810 collateral_b: 1000000,
811 max_collateral_a: 0,
812 max_collateral_b: 1000000,
813 borrow_a: 0,
814 borrow_b: 2000000,
815 total_a: 0,
816 total_b: 3000000,
817 min_total_a: 0,
818 min_total_b: 2700000,
819 swap_input: 0,
820 swap_output: 0,
821 swap_a_to_b: false,
822 protocol_fee_a: 0,
823 protocol_fee_b: 0,
824 liquidation_lower_price: 1.8840617719481452,
825 liquidation_upper_price: 0.0
826 })
827 );
828 }
829
830 #[test]
831 fn test_lp_increase_quote_verify_liquidation_prices() {
832 let quote = get_increase_lp_position_quote(IncreaseLpPositionQuoteArgs {
833 collateral_a: 0,
834 collateral_b: 1000_000_000,
835 borrow_a: 3_000_000,
836 borrow_b: 100_000_000,
837 tick_lower_index: price_to_tick_index(180.736, 6, 6),
838 sqrt_price: price_to_sqrt_price(213.41, 6, 6),
839 tick_upper_index: price_to_tick_index(225.66, 6, 6),
840 protocol_fee_rate: 500,
841 protocol_fee_rate_on_collateral: 500,
842 swap_fee_rate: 40,
843 max_amount_slippage: HUNDRED_PERCENT / 10,
844 liquidation_threshold: HUNDRED_PERCENT * 83 / 100,
845 })
846 .unwrap();
847
848 assert_eq!(quote.liquidation_lower_price, 23.805241869982023);
849 assert_eq!(quote.liquidation_upper_price, 451.38033819333333);
850 }
851}