1#![allow(clippy::collapsible_else_if)]
2#![allow(clippy::too_many_arguments)]
3
4use crate::{calculate_tuna_protocol_fee, sqrt_price_x64_to_price_x64, COMPUTED_AMOUNT, HUNDRED_PERCENT, INVALID_ARGUMENTS};
5use fixed::types::U64F64;
6use fusionamm_core::{
7 get_amount_a_from_liquidity, get_amount_b_from_liquidity, get_amounts_from_liquidity, get_liquidity_from_amount_a, get_liquidity_from_amount_b,
8 get_liquidity_from_amounts, position_ratio_x64, tick_index_to_sqrt_price, try_apply_swap_fee, CoreError, ARITHMETIC_OVERFLOW, Q64_RESOLUTION,
9};
10
11#[cfg(feature = "wasm")]
12use fusionamm_macros::wasm_expose;
13
14#[derive(Debug, Copy, Clone, PartialEq)]
15#[cfg_attr(feature = "wasm", wasm_expose)]
16pub struct LiquidationPrices {
17 pub lower: f64,
18 pub upper: f64,
19}
20
21fn compute_liquidation_prices_inside(
53 lower_sqrt_price: u128,
54 upper_sqrt_price: u128,
55 liquidity: u128,
56 leftovers_a: u64,
57 leftovers_b: u64,
58 debt_a: u64,
59 debt_b: u64,
60 liquidation_threshold: u32,
61) -> Result<LiquidationPrices, CoreError> {
62 let liquidation_threshold_f = liquidation_threshold as f64 / HUNDRED_PERCENT as f64;
63 let liquidity_f = liquidity as f64;
64 let lower_sqrt_price_f = lower_sqrt_price as f64 / Q64_RESOLUTION;
65 let upper_sqrt_price_f = upper_sqrt_price as f64 / Q64_RESOLUTION;
66
67 let a = debt_a as f64 + liquidation_threshold_f * (liquidity_f / upper_sqrt_price_f - leftovers_a as f64);
68 let b = -2.0 * liquidation_threshold_f * liquidity_f;
69 let c = debt_b as f64 + liquidation_threshold_f * (liquidity_f * lower_sqrt_price_f - leftovers_b as f64);
70 let d = b * b - 4.0 * a * c;
71
72 let mut lower_liquidation_sqrt_price = 0.0;
73 let mut upper_liquidation_sqrt_price = 0.0;
74
75 if d >= 0.0 && a > 0.0 {
76 lower_liquidation_sqrt_price = (-b - d.sqrt()) / (2.0 * a);
77 upper_liquidation_sqrt_price = (-b + d.sqrt()) / (2.0 * a);
78 if lower_liquidation_sqrt_price < 0.0 || lower_liquidation_sqrt_price < lower_sqrt_price_f {
79 lower_liquidation_sqrt_price = 0.0;
80 }
81 if upper_liquidation_sqrt_price < 0.0 || upper_liquidation_sqrt_price > upper_sqrt_price_f {
82 upper_liquidation_sqrt_price = 0.0;
83 }
84 }
85
86 Ok(LiquidationPrices {
87 lower: lower_liquidation_sqrt_price * lower_liquidation_sqrt_price,
88 upper: upper_liquidation_sqrt_price * upper_liquidation_sqrt_price,
89 })
90}
91
92fn calculate_liquidation_outside(
123 amount_a: u64,
124 amount_b: u64,
125 leftovers_a: u64,
126 leftovers_b: u64,
127 debt_a: u64,
128 debt_b: u64,
129 liquidation_threshold: u32,
130) -> Result<f64, CoreError> {
131 let liquidation_threshold_f = liquidation_threshold as f64 / HUNDRED_PERCENT as f64;
132
133 if amount_a == 0 && amount_b == 0 {
134 Ok(0.0)
135 } else if amount_a > 0 && amount_b == 0 {
136 let numerator = debt_b as f64 - liquidation_threshold_f * leftovers_b as f64;
138 let denominator = liquidation_threshold_f * (amount_a as f64 + leftovers_a as f64) - debt_a as f64;
139 Ok(numerator / denominator)
140 } else if amount_a == 0 && amount_b > 0 {
141 let numerator = liquidation_threshold_f * (amount_b as f64 + leftovers_b as f64) - debt_b as f64;
143 let denominator = debt_a as f64 - liquidation_threshold_f * leftovers_a as f64;
144 if denominator == 0.0 {
145 return Ok(0.0);
146 }
147 Ok(numerator / denominator)
148 } else {
149 Err(INVALID_ARGUMENTS)
150 }
151}
152
153fn compute_liquidation_prices_outside(
168 lower_sqrt_price: u128,
169 upper_sqrt_price: u128,
170 liquidity: u128,
171 leftovers_a: u64,
172 leftovers_b: u64,
173 debt_a: u64,
174 debt_b: u64,
175 liquidation_threshold: u32,
176) -> Result<LiquidationPrices, CoreError> {
177 let amount_a = get_amount_a_from_liquidity(liquidity, lower_sqrt_price, upper_sqrt_price, false).unwrap_or(u64::MAX);
178 let amount_b = get_amount_b_from_liquidity(liquidity, lower_sqrt_price, upper_sqrt_price, false).unwrap_or(u64::MAX);
179
180 let mut liquidation_price_for_a = calculate_liquidation_outside(amount_a, 0, leftovers_a, leftovers_b, debt_a, debt_b, liquidation_threshold)?;
181 let mut liquidation_price_for_b = calculate_liquidation_outside(0, amount_b, leftovers_a, leftovers_b, debt_a, debt_b, liquidation_threshold)?;
182
183 if liquidation_price_for_a < 0.0 || liquidation_price_for_a > (lower_sqrt_price as f64 / Q64_RESOLUTION).powf(2.0) {
184 liquidation_price_for_a = 0.0;
185 }
186 if liquidation_price_for_b < 0.0 || liquidation_price_for_b < (upper_sqrt_price as f64 / Q64_RESOLUTION).powf(2.0) {
187 liquidation_price_for_b = 0.0;
188 }
189
190 Ok(LiquidationPrices {
191 lower: liquidation_price_for_a,
192 upper: liquidation_price_for_b,
193 })
194}
195
196#[cfg_attr(feature = "wasm", wasm_expose)]
211pub fn get_lp_position_liquidation_prices(
212 tick_lower_index: i32,
213 tick_upper_index: i32,
214 liquidity: u128,
215 leftovers_a: u64,
216 leftovers_b: u64,
217 debt_a: u64,
218 debt_b: u64,
219 liquidation_threshold: u32,
220) -> Result<LiquidationPrices, CoreError> {
221 if tick_lower_index >= tick_upper_index {
222 return Err("Incorrect position tick index order: the lower tick must be less then the upper tick.");
223 }
224
225 if liquidation_threshold >= HUNDRED_PERCENT {
226 return Err("Incorrect liquidation_threshold value.");
227 }
228
229 let lower_sqrt_price = tick_index_to_sqrt_price(tick_lower_index);
230 let upper_sqrt_price = tick_index_to_sqrt_price(tick_upper_index);
231
232 let liquidation_price_inside = compute_liquidation_prices_inside(
233 lower_sqrt_price,
234 upper_sqrt_price,
235 liquidity,
236 leftovers_a,
237 leftovers_b,
238 debt_a,
239 debt_b,
240 liquidation_threshold,
241 )?;
242
243 let liquidation_price_outside = compute_liquidation_prices_outside(
244 lower_sqrt_price,
245 upper_sqrt_price,
246 liquidity,
247 leftovers_a,
248 leftovers_b,
249 debt_a,
250 debt_b,
251 liquidation_threshold,
252 )?;
253
254 let lower_liquidation_price = if liquidation_price_inside.lower > 0.0 {
255 liquidation_price_inside.lower
256 } else {
257 liquidation_price_outside.lower
258 };
259
260 let upper_liquidation_price = if liquidation_price_inside.upper > 0.0 {
261 liquidation_price_inside.upper
262 } else {
263 liquidation_price_outside.upper
264 };
265
266 Ok(LiquidationPrices {
267 lower: lower_liquidation_price,
268 upper: upper_liquidation_price,
269 })
270}
271
272#[derive(Debug, Copy, Clone, PartialEq)]
273#[cfg_attr(feature = "wasm", wasm_expose)]
274pub struct IncreaseLpPositionQuoteArgs {
275 pub collateral_a: u64,
277 pub collateral_b: u64,
279 pub borrow_a: u64,
281 pub borrow_b: u64,
283 pub protocol_fee_rate: u16,
285 pub protocol_fee_rate_on_collateral: u16,
287 pub swap_fee_rate: u16,
289 pub sqrt_price: u128,
291 pub tick_lower_index: i32,
293 pub tick_upper_index: i32,
295 pub liquidation_threshold: u32,
297}
298
299#[derive(Debug, Copy, Clone, PartialEq)]
300#[cfg_attr(feature = "wasm", wasm_expose)]
301pub struct IncreaseLpPositionQuoteResult {
302 pub collateral_a: u64,
303 pub collateral_b: u64,
304 pub borrow_a: u64,
305 pub borrow_b: u64,
306 pub total_a: u64,
307 pub total_b: u64,
308 pub swap_input: u64,
309 pub swap_output: u64,
310 pub swap_a_to_b: bool,
311 pub protocol_fee_a: u64,
312 pub protocol_fee_b: u64,
313 pub liquidity: u128,
314 pub leverage: f64,
315 pub liquidation_lower_price: f64,
316 pub liquidation_upper_price: f64,
317}
318
319#[cfg_attr(feature = "wasm", wasm_expose)]
320pub fn get_increase_lp_position_quote(args: IncreaseLpPositionQuoteArgs) -> Result<IncreaseLpPositionQuoteResult, CoreError> {
321 let mut collateral_a = args.collateral_a;
322 let mut collateral_b = args.collateral_b;
323 let mut borrow_a = args.borrow_a;
324 let mut borrow_b = args.borrow_b;
325 let sqrt_price = args.sqrt_price;
326
327 if args.tick_lower_index >= args.tick_upper_index {
328 return Err("Incorrect position tick index order: the lower tick must be less than the upper tick.");
329 }
330
331 if collateral_a == COMPUTED_AMOUNT && collateral_b == COMPUTED_AMOUNT {
332 return Err("Both collateral amounts can't be set to COMPUTED_AMOUNT");
333 }
334
335 let lower_sqrt_price = tick_index_to_sqrt_price(args.tick_lower_index);
336 let upper_sqrt_price = tick_index_to_sqrt_price(args.tick_upper_index);
337
338 if collateral_a == COMPUTED_AMOUNT {
339 if sqrt_price <= lower_sqrt_price {
340 return Err("sqrtPrice must be greater than lower_sqrt_price if collateral A is computed.");
341 } else if sqrt_price < upper_sqrt_price {
342 let liquidity = get_liquidity_from_amount_b(collateral_b + borrow_b, lower_sqrt_price, sqrt_price)?;
343 let amount_a = get_amount_a_from_liquidity(liquidity, sqrt_price, upper_sqrt_price, false)?;
344 collateral_a = ((amount_a as u128 * collateral_b as u128) / (collateral_b as u128 + borrow_b as u128)) as u64;
345 borrow_a = amount_a - collateral_a;
346 } else {
347 collateral_a = 0;
348 borrow_a = 0;
349 }
350 } else if collateral_b == COMPUTED_AMOUNT {
351 if sqrt_price <= lower_sqrt_price {
352 collateral_b = 0;
353 borrow_b = 0;
354 } else if sqrt_price < upper_sqrt_price {
355 let liquidity = get_liquidity_from_amount_a(collateral_a + borrow_a, sqrt_price, upper_sqrt_price)?;
356 let amount_b = get_amount_b_from_liquidity(liquidity, lower_sqrt_price, sqrt_price, false)?;
357 collateral_b = ((amount_b as u128 * collateral_a as u128) / (collateral_a as u128 + borrow_a as u128)) as u64;
358 borrow_b = amount_b - collateral_b;
359 } else {
360 return Err("sqrtPrice must be less than upper_sqrt_price if collateral B is computed.");
361 }
362 }
363
364 let protocol_fee_a = calculate_tuna_protocol_fee(collateral_a, borrow_a, args.protocol_fee_rate_on_collateral, args.protocol_fee_rate);
365 let provided_a = collateral_a + borrow_a - protocol_fee_a;
366
367 let protocol_fee_b = calculate_tuna_protocol_fee(collateral_b, borrow_b, args.protocol_fee_rate_on_collateral, args.protocol_fee_rate);
368 let provided_b = collateral_b + borrow_b - protocol_fee_b;
369
370 let mut swap_input = 0;
371 let mut swap_output = 0;
372 let mut swap_a_to_b = false;
373 let mut total_a = provided_a;
374 let mut total_b = provided_b;
375
376 if args.collateral_a != COMPUTED_AMOUNT && args.collateral_b != COMPUTED_AMOUNT {
377 let position_ratio = position_ratio_x64(sqrt_price.into(), args.tick_lower_index, args.tick_upper_index);
378 let ratio_a = position_ratio.ratio_a as f64 / Q64_RESOLUTION;
379 let ratio_b = position_ratio.ratio_b as f64 / Q64_RESOLUTION;
380
381 let price = (sqrt_price as f64 / Q64_RESOLUTION).powf(2.0);
382
383 let mut total = (provided_a as f64 * price + provided_b as f64) as u64;
385 total_a = (total as f64 * ratio_a / price) as u64;
386 total_b = (total as f64 * ratio_b) as u64;
387
388 let mut fee_a = 0;
389 let mut fee_b = 0;
390
391 if total_a < provided_a {
392 swap_input = provided_a - total_a;
393 fee_a = swap_input - try_apply_swap_fee(swap_input, args.swap_fee_rate)?;
394 swap_output = ((swap_input - fee_a) as f64 * price) as u64;
395 swap_a_to_b = true;
396 } else if total_b < provided_b {
397 swap_input = provided_b - total_b;
398 fee_b = swap_input - try_apply_swap_fee(swap_input, args.swap_fee_rate)?;
399 swap_output = ((swap_input - fee_b) as f64 / price) as u64;
400 swap_a_to_b = false;
401 }
402
403 total = ((provided_a - fee_a) as f64 * price) as u64 + provided_b - fee_b;
405 total_a = ((total as f64 * ratio_a) / price) as u64;
406 total_b = (total as f64 * ratio_b) as u64;
407 }
408
409 let liquidity = get_liquidity_from_amounts(sqrt_price, lower_sqrt_price, upper_sqrt_price, total_a, total_b)?;
410 let liquidation_prices = get_lp_position_liquidation_prices(
411 args.tick_lower_index,
412 args.tick_upper_index,
413 liquidity,
414 0,
415 0,
416 borrow_a,
417 borrow_b,
418 args.liquidation_threshold,
419 )?;
420
421 let leverage = compute_leverage(total_a, total_b, borrow_a, borrow_b, sqrt_price)?;
422
423 Ok(IncreaseLpPositionQuoteResult {
424 collateral_a,
425 collateral_b,
426 borrow_a,
427 borrow_b,
428 total_a,
429 total_b,
430 swap_input,
431 swap_output,
432 swap_a_to_b,
433 protocol_fee_a,
434 protocol_fee_b,
435 liquidity,
436 leverage,
437 liquidation_lower_price: liquidation_prices.lower,
438 liquidation_upper_price: liquidation_prices.upper,
439 })
440}
441
442#[derive(Debug, Copy, Clone, PartialEq)]
443#[cfg_attr(feature = "wasm", wasm_expose)]
444pub struct RepayLpPositionDebtQuoteArgs {
445 pub liquidity: u128,
447 pub debt_a: u64,
449 pub debt_b: u64,
451 pub leftovers_a: u64,
453 pub leftovers_b: u64,
455 pub tick_lower_index: i32,
457 pub tick_upper_index: i32,
459 pub repay_a: u64,
461 pub repay_b: u64,
463 pub sqrt_price: u128,
465 pub liquidation_threshold: u32,
467}
468
469#[derive(Debug, Copy, Clone, PartialEq)]
470#[cfg_attr(feature = "wasm", wasm_expose)]
471pub struct RepayLpPositionDebtQuoteResult {
472 pub debt_a: u64,
473 pub debt_b: u64,
474 pub leverage: f64,
475 pub liquidation_lower_price: f64,
476 pub liquidation_upper_price: f64,
477}
478
479#[cfg_attr(feature = "wasm", wasm_expose)]
480pub fn get_repay_lp_position_debt_quote(args: RepayLpPositionDebtQuoteArgs) -> Result<RepayLpPositionDebtQuoteResult, CoreError> {
481 let mut debt_a = args.debt_a;
482 let mut debt_b = args.debt_b;
483 let repay_a = args.repay_a;
484 let repay_b = args.repay_b;
485
486 if args.liquidity == 0 {
487 return Err("Position liquidity can't be zero.");
488 }
489
490 if debt_a < repay_a {
491 return Err("Position debt A is less than the repaid amount.");
492 }
493
494 if debt_b < repay_b {
495 return Err("Position debt b is less than the repaid amount.");
496 }
497
498 debt_a -= repay_a;
499 debt_b -= repay_b;
500
501 let liquidation_prices = get_lp_position_liquidation_prices(
502 args.tick_lower_index,
503 args.tick_upper_index,
504 args.liquidity,
505 args.leftovers_a,
506 args.leftovers_b,
507 debt_a,
508 debt_b,
509 args.liquidation_threshold,
510 )?;
511
512 let lower_sqrt_price = tick_index_to_sqrt_price(args.tick_lower_index);
513 let upper_sqrt_price = tick_index_to_sqrt_price(args.tick_upper_index);
514
515 let total = get_amounts_from_liquidity(args.liquidity, args.sqrt_price, lower_sqrt_price, upper_sqrt_price, false)?;
516 let leverage = compute_leverage(total.a + args.leftovers_a, total.b + args.leftovers_b, debt_a, debt_b, args.sqrt_price)?;
517
518 Ok(RepayLpPositionDebtQuoteResult {
519 debt_a,
520 debt_b,
521 leverage,
522 liquidation_lower_price: liquidation_prices.lower,
523 liquidation_upper_price: liquidation_prices.upper,
524 })
525}
526
527#[cfg_attr(feature = "wasm", wasm_expose)]
528pub fn compute_leverage(total_a: u64, total_b: u64, debt_a: u64, debt_b: u64, sqrt_price: u128) -> Result<f64, CoreError> {
529 let price = sqrt_price_x64_to_price_x64(sqrt_price)?;
530
531 let total = U64F64::from(total_a)
532 .checked_mul(price)
533 .ok_or(ARITHMETIC_OVERFLOW)?
534 .to_num::<u64>()
535 .checked_add(total_b)
536 .ok_or(ARITHMETIC_OVERFLOW)?;
537
538 let debt = U64F64::from(debt_a)
539 .checked_mul(price)
540 .ok_or(ARITHMETIC_OVERFLOW)?
541 .to_num::<u64>()
542 .checked_add(debt_b)
543 .ok_or(ARITHMETIC_OVERFLOW)?;
544
545 if total == 0 {
547 return Ok(1.0);
548 }
549
550 if debt >= total {
551 return Err("The debt is greater than the total size");
552 }
553
554 let leverage = total as f64 / (total - debt) as f64;
555 Ok(leverage)
556}
557
558#[cfg(all(test, not(feature = "wasm")))]
559mod tests {
560 use crate::{
561 get_increase_lp_position_quote, get_lp_position_liquidation_prices, get_repay_lp_position_debt_quote, IncreaseLpPositionQuoteArgs,
562 IncreaseLpPositionQuoteResult, LiquidationPrices, RepayLpPositionDebtQuoteArgs, COMPUTED_AMOUNT, HUNDRED_PERCENT,
563 };
564 use fusionamm_core::{
565 get_liquidity_from_amount_b, price_to_sqrt_price, price_to_tick_index, tick_index_to_sqrt_price, MAX_TICK_INDEX, MIN_TICK_INDEX,
566 };
567 use once_cell::sync::Lazy;
568
569 pub static TICK_LOWER_INDEX: Lazy<i32> = Lazy::new(|| price_to_tick_index(180.736, 6, 6));
570 pub static TICK_UPPER_INDEX: Lazy<i32> = Lazy::new(|| price_to_tick_index(225.66, 6, 6));
571 pub static SQRT_PRICE: Lazy<u128> = Lazy::new(|| price_to_sqrt_price(213.41, 6, 6));
572 pub static LIQUIDITY: Lazy<u128> =
573 Lazy::new(|| get_liquidity_from_amount_b(10000_000_000, tick_index_to_sqrt_price(*TICK_LOWER_INDEX), *SQRT_PRICE).unwrap());
574
575 #[test]
576 fn test_liquidation_price_outside_range_lower() {
577 assert_eq!(
578 get_lp_position_liquidation_prices(
579 *TICK_LOWER_INDEX,
580 *TICK_UPPER_INDEX,
581 *LIQUIDITY,
582 0, 0, 0, 9807_000_000, HUNDRED_PERCENT * 83 / 100 ),
588 Ok(LiquidationPrices {
589 lower: 176.12815046153585,
590 upper: 0.0
591 })
592 );
593 }
594
595 #[test]
596 fn test_liquidation_price_outside_range_upper() {
597 assert_eq!(
598 get_lp_position_liquidation_prices(
599 *TICK_LOWER_INDEX,
600 *TICK_UPPER_INDEX,
601 *LIQUIDITY,
602 0, 0, 20_000_000, 0, HUNDRED_PERCENT * 83 / 100 ),
608 Ok(LiquidationPrices {
609 lower: 0.0,
610 upper: 562.219410388
611 })
612 );
613 }
614
615 #[test]
616 fn test_liquidation_price_outside_range_lower_and_upper() {
617 assert_eq!(
618 get_lp_position_liquidation_prices(
619 *TICK_LOWER_INDEX,
620 *TICK_UPPER_INDEX,
621 *LIQUIDITY,
622 0, 0, 10_000_000, 5000_000_000, HUNDRED_PERCENT * 83 / 100 ),
628 Ok(LiquidationPrices {
629 lower: 109.45458168998225,
630 upper: 624.4388207760001
631 })
632 );
633 }
634
635 #[test]
636 fn test_liquidation_price_inside_range_lower() {
637 assert_eq!(
638 get_lp_position_liquidation_prices(
639 *TICK_LOWER_INDEX,
640 *TICK_UPPER_INDEX,
641 *LIQUIDITY,
642 0, 0, 0, 11000_000_000, HUNDRED_PERCENT * 83 / 100 ),
648 Ok(LiquidationPrices {
649 lower: 204.60489065334323,
650 upper: 0.0
651 })
652 );
653 }
654
655 #[test]
656 fn test_liquidation_price_inside_range_upper() {
657 assert_eq!(
658 get_lp_position_liquidation_prices(
659 *TICK_LOWER_INDEX,
660 *TICK_UPPER_INDEX,
661 *LIQUIDITY,
662 0, 0, 51_000_000, 0, HUNDRED_PERCENT * 83 / 100 ),
668 Ok(LiquidationPrices {
669 lower: 0.0,
670 upper: 220.16318077637644
671 })
672 );
673 }
674
675 #[test]
676 fn test_liquidation_price_inside_range_lower_and_upper() {
677 assert_eq!(
678 get_lp_position_liquidation_prices(
679 *TICK_LOWER_INDEX,
680 *TICK_UPPER_INDEX,
681 *LIQUIDITY,
682 0, 0, 11_500_000, 8700_000_000, HUNDRED_PERCENT * 83 / 100 ),
688 Ok(LiquidationPrices {
689 lower: 210.75514596082337,
690 upper: 219.48595430071575
691 })
692 );
693 }
694
695 #[test]
696 fn test_lp_increase_quote_collateral_a_and_b_provided() {
697 assert_eq!(
698 get_increase_lp_position_quote(IncreaseLpPositionQuoteArgs {
699 collateral_a: 1000000,
700 collateral_b: 1000000,
701 borrow_a: 2000000,
702 borrow_b: 2000000,
703 tick_lower_index: price_to_tick_index(1.0, 1, 1),
704 sqrt_price: price_to_sqrt_price(2.0, 1, 1),
705 tick_upper_index: price_to_tick_index(4.0, 1, 1),
706 protocol_fee_rate: (HUNDRED_PERCENT / 100) as u16,
707 protocol_fee_rate_on_collateral: (HUNDRED_PERCENT / 100) as u16,
708 swap_fee_rate: 10000, liquidation_threshold: HUNDRED_PERCENT * 83 / 100,
710 }),
711 Ok(IncreaseLpPositionQuoteResult {
712 collateral_a: 1000000,
713 collateral_b: 1000000,
714 borrow_a: 2000000,
715 borrow_b: 2000000,
716 total_a: 2223701,
717 total_b: 4447744,
718 swap_input: 742586,
719 swap_output: 1470320,
720 swap_a_to_b: true,
721 protocol_fee_a: 30000,
722 protocol_fee_b: 30000,
723 liquidity: 10737803,
724 leverage: 3.0724343435529677,
725 liquidation_lower_price: 0.8143170288470588,
726 liquidation_upper_price: 3.4020457909456225,
727 })
728 );
729 }
730
731 #[test]
732 fn test_lp_increase_quote_collateral_a_provided() {
733 assert_eq!(
734 get_increase_lp_position_quote(IncreaseLpPositionQuoteArgs {
735 collateral_a: 10000000,
736 collateral_b: 0,
737 borrow_a: 0,
738 borrow_b: 0,
739 tick_lower_index: price_to_tick_index(0.25, 6, 9),
740 sqrt_price: price_to_sqrt_price(0.5, 6, 9),
741 tick_upper_index: price_to_tick_index(1.0, 6, 9),
742 protocol_fee_rate: (HUNDRED_PERCENT / 100) as u16,
743 protocol_fee_rate_on_collateral: (HUNDRED_PERCENT / 100) as u16,
744 swap_fee_rate: 10000, liquidation_threshold: HUNDRED_PERCENT * 83 / 100,
746 }),
747 Ok(IncreaseLpPositionQuoteResult {
748 collateral_a: 10000000,
749 collateral_b: 0,
750 borrow_a: 0,
751 borrow_b: 0,
752 total_a: 4925137,
753 total_b: 2462680451,
754 swap_input: 4950113,
755 swap_output: 2450305500,
756 swap_a_to_b: true,
757 protocol_fee_a: 100000,
758 protocol_fee_b: 0,
759 liquidity: 376005629,
760 leverage: 1.0,
761 liquidation_lower_price: 0.0,
762 liquidation_upper_price: 0.0,
763 })
764 );
765 }
766
767 #[test]
768 fn test_lp_increase_quote_collateral_a_provided_b_computed() {
769 assert_eq!(
770 get_increase_lp_position_quote(IncreaseLpPositionQuoteArgs {
771 collateral_a: 10000000,
772 collateral_b: COMPUTED_AMOUNT,
773 borrow_a: 2000000,
774 borrow_b: COMPUTED_AMOUNT,
775 tick_lower_index: price_to_tick_index(1.0, 1, 1),
776 sqrt_price: price_to_sqrt_price(3.0, 1, 1),
777 tick_upper_index: price_to_tick_index(4.0, 1, 1),
778 protocol_fee_rate: 0,
779 protocol_fee_rate_on_collateral: 0,
780 swap_fee_rate: 0,
781 liquidation_threshold: HUNDRED_PERCENT * 83 / 100,
782 }),
783 Ok(IncreaseLpPositionQuoteResult {
784 collateral_a: 10000000,
785 collateral_b: 94660495,
786 borrow_a: 2000000,
787 borrow_b: 18932100,
788 total_a: 12000000,
789 total_b: 113592595,
790 swap_input: 0,
791 swap_output: 0,
792 swap_a_to_b: false,
793 protocol_fee_a: 0,
794 protocol_fee_b: 0,
795 liquidity: 155170370,
796 leverage: 1.2,
797 liquidation_lower_price: 0.30342990360419136,
798 liquidation_upper_price: 54.925553349999994
799 })
800 );
801 }
802
803 #[test]
804 fn test_lp_increase_quote_collateral_a_provided_b_computed_no_leverage() {
805 assert_eq!(
806 get_increase_lp_position_quote(IncreaseLpPositionQuoteArgs {
807 collateral_a: 30000000000,
808 collateral_b: COMPUTED_AMOUNT,
809 borrow_a: 0,
810 borrow_b: 0,
811 tick_lower_index: price_to_tick_index(1.0, 1, 1),
812 sqrt_price: price_to_sqrt_price(3.0, 1, 1),
813 tick_upper_index: price_to_tick_index(4.0, 1, 1),
814 protocol_fee_rate: 0,
815 protocol_fee_rate_on_collateral: 0,
816 swap_fee_rate: 0,
817 liquidation_threshold: HUNDRED_PERCENT * 83 / 100,
818 }),
819 Ok(IncreaseLpPositionQuoteResult {
820 collateral_a: 30000000000,
821 collateral_b: 283981489799,
822 borrow_a: 0,
823 borrow_b: 0,
824 total_a: 30000000000,
825 total_b: 283981489799,
826 swap_input: 0,
827 swap_output: 0,
828 swap_a_to_b: false,
829 protocol_fee_a: 0,
830 protocol_fee_b: 0,
831 liquidity: 387925929269,
832 leverage: 1.0,
833 liquidation_lower_price: 0.0,
834 liquidation_upper_price: 0.0
835 })
836 );
837 }
838
839 #[test]
840 fn test_lp_increase_quote_collateral_a_computed_b_provided() {
841 assert_eq!(
842 get_increase_lp_position_quote(IncreaseLpPositionQuoteArgs {
843 collateral_a: COMPUTED_AMOUNT,
844 collateral_b: 1000000,
845 borrow_a: COMPUTED_AMOUNT,
846 borrow_b: 2000000,
847 tick_lower_index: price_to_tick_index(1.0, 1, 1),
848 sqrt_price: price_to_sqrt_price(3.0, 1, 1),
849 tick_upper_index: price_to_tick_index(4.0, 1, 1),
850 protocol_fee_rate: 0,
851 protocol_fee_rate_on_collateral: 0,
852 swap_fee_rate: 0,
853 liquidation_threshold: HUNDRED_PERCENT * 83 / 100,
854 }),
855 Ok(IncreaseLpPositionQuoteResult {
856 collateral_a: 105640,
857 collateral_b: 1000000,
858 borrow_a: 211282,
859 borrow_b: 2000000,
860 total_a: 316922,
861 total_b: 3000000,
862 swap_input: 0,
863 swap_output: 0,
864 swap_a_to_b: false,
865 protocol_fee_a: 0,
866 protocol_fee_b: 0,
867 liquidity: 4098075,
868 leverage: 3.000003796737843,
869 liquidation_lower_price: 1.4306915613018771,
870 liquidation_upper_price: 6.63182675287057
871 })
872 );
873 }
874
875 #[test]
876 fn test_lp_increase_quote_collateral_a_computed_b_provided_no_leverage() {
877 assert_eq!(
878 get_increase_lp_position_quote(IncreaseLpPositionQuoteArgs {
879 collateral_a: COMPUTED_AMOUNT,
880 collateral_b: 30000000000,
881 borrow_a: 0,
882 borrow_b: 0,
883 tick_lower_index: price_to_tick_index(1.0, 1, 1),
884 sqrt_price: price_to_sqrt_price(3.0, 1, 1),
885 tick_upper_index: price_to_tick_index(4.0, 1, 1),
886 protocol_fee_rate: 0,
887 protocol_fee_rate_on_collateral: 0,
888 swap_fee_rate: 0,
889 liquidation_threshold: HUNDRED_PERCENT * 83 / 100,
890 }),
891 Ok(IncreaseLpPositionQuoteResult {
892 collateral_a: 3169220644,
893 collateral_b: 30000000000,
894 borrow_a: 0,
895 borrow_b: 0,
896 total_a: 3169220644,
897 total_b: 30000000000,
898 swap_input: 0,
899 swap_output: 0,
900 swap_a_to_b: false,
901 protocol_fee_a: 0,
902 protocol_fee_b: 0,
903 liquidity: 40980762112,
904 leverage: 1.0,
905 liquidation_lower_price: 0.0,
906 liquidation_upper_price: 0.0
907 })
908 );
909 }
910
911 #[test]
912 fn test_lp_increase_quote_collateral_a_computed_b_provided_no_leverage_full_range() {
913 assert_eq!(
914 get_increase_lp_position_quote(IncreaseLpPositionQuoteArgs {
915 collateral_a: COMPUTED_AMOUNT,
916 collateral_b: 30000000000,
917 borrow_a: 0,
918 borrow_b: 0,
919 tick_lower_index: MIN_TICK_INDEX,
920 sqrt_price: price_to_sqrt_price(3.0, 1, 1),
921 tick_upper_index: MAX_TICK_INDEX,
922 protocol_fee_rate: 0,
923 protocol_fee_rate_on_collateral: 0,
924 swap_fee_rate: 0,
925 liquidation_threshold: HUNDRED_PERCENT * 83 / 100,
926 }),
927 Ok(IncreaseLpPositionQuoteResult {
928 collateral_a: 9999999997,
929 collateral_b: 30000000000,
930 borrow_a: 0,
931 borrow_b: 0,
932 total_a: 9999999997,
933 total_b: 30000000000,
934 swap_input: 0,
935 swap_output: 0,
936 swap_a_to_b: false,
937 protocol_fee_a: 0,
938 protocol_fee_b: 0,
939 liquidity: 17320508077,
940 leverage: 1.0,
941 liquidation_lower_price: 0.0,
942 liquidation_upper_price: 0.0
943 })
944 );
945 }
946
947 #[test]
948 fn test_lp_increase_quote_one_sided_collateral_a_provided_b_computed() {
949 assert_eq!(
950 get_increase_lp_position_quote(IncreaseLpPositionQuoteArgs {
951 collateral_a: 10000000,
952 collateral_b: COMPUTED_AMOUNT,
953 borrow_a: 2000000,
954 borrow_b: COMPUTED_AMOUNT,
955 tick_lower_index: price_to_tick_index(1.0, 1, 1),
956 sqrt_price: price_to_sqrt_price(0.5, 1, 1),
957 tick_upper_index: price_to_tick_index(4.0, 1, 1),
958 protocol_fee_rate: 0,
959 protocol_fee_rate_on_collateral: 0,
960 swap_fee_rate: 0,
961 liquidation_threshold: HUNDRED_PERCENT * 83 / 100,
962 }),
963 Ok(IncreaseLpPositionQuoteResult {
964 collateral_a: 10000000,
965 collateral_b: 0,
966 borrow_a: 2000000,
967 borrow_b: 0,
968 total_a: 12000000,
969 total_b: 0,
970 swap_input: 0,
971 swap_output: 0,
972 swap_a_to_b: false,
973 protocol_fee_a: 0,
974 protocol_fee_b: 0,
975 liquidity: 24000764,
976 leverage: 1.2,
977 liquidation_lower_price: 0.0,
978 liquidation_upper_price: 9.959682525
979 })
980 );
981 }
982
983 #[test]
984 fn test_lp_increase_quote_one_sided_collateral_a_computed_b_provided() {
985 assert_eq!(
986 get_increase_lp_position_quote(IncreaseLpPositionQuoteArgs {
987 collateral_a: COMPUTED_AMOUNT,
988 collateral_b: 1000000,
989 borrow_a: COMPUTED_AMOUNT,
990 borrow_b: 2000000,
991 tick_lower_index: price_to_tick_index(1.0, 1, 1),
992 sqrt_price: price_to_sqrt_price(5.0, 1, 1),
993 tick_upper_index: price_to_tick_index(4.0, 1, 1),
994 protocol_fee_rate: 0,
995 protocol_fee_rate_on_collateral: 0,
996 swap_fee_rate: 0,
997 liquidation_threshold: HUNDRED_PERCENT * 83 / 100,
998 }),
999 Ok(IncreaseLpPositionQuoteResult {
1000 collateral_a: 0,
1001 collateral_b: 1000000,
1002 borrow_a: 0,
1003 borrow_b: 2000000,
1004 total_a: 0,
1005 total_b: 3000000,
1006 swap_input: 0,
1007 swap_output: 0,
1008 swap_a_to_b: false,
1009 protocol_fee_a: 0,
1010 protocol_fee_b: 0,
1011 liquidity: 3000191,
1012 leverage: 3.0,
1013 liquidation_lower_price: 1.8840617719481452,
1014 liquidation_upper_price: 0.0
1015 })
1016 );
1017 }
1018
1019 #[test]
1020 fn test_lp_increase_quote_verify_liquidation_prices() {
1021 let quote = get_increase_lp_position_quote(IncreaseLpPositionQuoteArgs {
1022 collateral_a: 0,
1023 collateral_b: 1000_000_000,
1024 borrow_a: 3_000_000,
1025 borrow_b: 100_000_000,
1026 tick_lower_index: price_to_tick_index(180.736, 6, 6),
1027 sqrt_price: price_to_sqrt_price(213.41, 6, 6),
1028 tick_upper_index: price_to_tick_index(225.66, 6, 6),
1029 protocol_fee_rate: 500,
1030 protocol_fee_rate_on_collateral: 500,
1031 swap_fee_rate: 40,
1032 liquidation_threshold: HUNDRED_PERCENT * 83 / 100,
1033 })
1034 .unwrap();
1035
1036 assert_eq!(quote.liquidation_lower_price, 23.805241869982023);
1037 assert_eq!(quote.liquidation_upper_price, 451.38033819333333);
1038 }
1039
1040 #[test]
1041 fn test_repay_debt_quote() {
1042 let quote = get_repay_lp_position_debt_quote(RepayLpPositionDebtQuoteArgs {
1043 repay_a: 1_000_000,
1044 repay_b: 30_000_000,
1045 liquidity: 1109671058,
1046 debt_a: 3_000_000,
1047 debt_b: 100_000_000,
1048 leftovers_a: 2,
1049 leftovers_b: 15,
1050 tick_lower_index: price_to_tick_index(180.736, 6, 6),
1051 sqrt_price: price_to_sqrt_price(213.41, 6, 6),
1052 tick_upper_index: price_to_tick_index(225.66, 6, 6),
1053 liquidation_threshold: HUNDRED_PERCENT * 83 / 100,
1054 })
1055 .unwrap();
1056
1057 assert_eq!(quote.debt_a, 2_000_000);
1058 assert_eq!(quote.debt_b, 70_000_000);
1059 assert_eq!(quote.liquidation_lower_price, 13.459576327110664);
1060 assert_eq!(quote.liquidation_upper_price, 692.0710879340029);
1061 }
1062}