1#![allow(clippy::collapsible_else_if)]
2#![allow(clippy::too_many_arguments)]
3
4use crate::{CoreError, HUNDRED_PERCENT, INVALID_ARGUMENTS, TOKEN_A, TOKEN_B};
5use fusionamm_core::{
6 sqrt_price_to_price, swap_quote_by_input_token, swap_quote_by_output_token, try_get_max_amount_with_slippage_tolerance,
7 try_get_min_amount_with_slippage_tolerance, try_mul_div, FusionPoolFacade, TickArrays,
8};
9use libm::{ceil, round};
10
11pub const DEFAULT_SLIPPAGE_TOLERANCE_BPS: u16 = 100;
12
13#[cfg(feature = "wasm")]
14use fusionamm_macros::wasm_expose;
15
16#[cfg_attr(feature = "wasm", wasm_expose)]
17pub struct IncreaseSpotPositionQuoteResult {
18 pub collateral: u64,
20 pub borrow: u64,
22 pub estimated_amount: u64,
24 pub swap_input_amount: u64,
26 pub min_swap_output_amount: u64,
28 pub protocol_fee_a: u64,
30 pub protocol_fee_b: u64,
32 pub price_impact: f64,
34}
35
36#[cfg_attr(feature = "wasm", wasm_expose)]
52pub fn get_increase_spot_position_quote(
53 increase_amount: u64,
54 collateral_token: u8,
55 position_token: u8,
56 leverage: f64,
57 slippage_tolerance_bps: Option<u16>,
58 protocol_fee_rate: u32,
59 protocol_fee_rate_on_collateral: u32,
60 fusion_pool: FusionPoolFacade,
61 tick_arrays: TickArrays,
62) -> Result<IncreaseSpotPositionQuoteResult, CoreError> {
63 if collateral_token > TOKEN_B || position_token > TOKEN_B {
64 return Err(INVALID_ARGUMENTS.into());
65 }
66
67 if leverage < 1.0 {
68 return Err(INVALID_ARGUMENTS.into());
69 }
70
71 if protocol_fee_rate >= HUNDRED_PERCENT {
72 return Err(INVALID_ARGUMENTS.into());
73 }
74
75 if protocol_fee_rate_on_collateral >= HUNDRED_PERCENT {
76 return Err(INVALID_ARGUMENTS.into());
77 }
78
79 let borrow: u64;
80 let mut collateral: u64;
81 let mut estimated_amount: u64 = 0;
82 let mut swap_input_amount: u64;
83 let mut next_sqrt_price = fusion_pool.sqrt_price;
84 let mut min_swap_output_amount: u64 = 0;
85
86 let price = sqrt_price_to_price(fusion_pool.sqrt_price.into(), 1, 1);
87 let slippage_tolerance_bps = slippage_tolerance_bps.unwrap_or(DEFAULT_SLIPPAGE_TOLERANCE_BPS);
88
89 let borrowed_token = if position_token == TOKEN_A { TOKEN_B } else { TOKEN_A };
90 let swap_input_token_is_a = borrowed_token == TOKEN_A;
91
92 if borrowed_token == collateral_token {
93 borrow = ceil((increase_amount as f64 * (leverage - 1.0)) / leverage) as u64;
94 collateral = increase_amount - apply_swap_fee(apply_tuna_protocol_fee(borrow, protocol_fee_rate, false)?, fusion_pool.fee_rate, false)?;
95 collateral = reverse_apply_swap_fee(collateral, fusion_pool.fee_rate, false)?;
96 collateral = reverse_apply_tuna_protocol_fee(collateral, protocol_fee_rate_on_collateral, false)?;
97
98 swap_input_amount = collateral + borrow;
99 } else {
100 let position_to_borrowed_token_price = if collateral_token == TOKEN_A { price } else { 1.0 / price };
101 let borrow_in_position_token = ceil((increase_amount as f64 * (leverage - 1.0)) / leverage);
102
103 borrow = ceil(borrow_in_position_token * position_to_borrowed_token_price) as u64;
104
105 let borrow_in_position_token_with_fees_applied =
106 apply_swap_fee(apply_tuna_protocol_fee(borrow_in_position_token as u64, protocol_fee_rate, false)?, fusion_pool.fee_rate, false)?;
107
108 collateral = increase_amount - borrow_in_position_token_with_fees_applied;
109 collateral = reverse_apply_tuna_protocol_fee(collateral, protocol_fee_rate_on_collateral, false)?;
110
111 swap_input_amount = borrow;
112 }
113
114 let (protocol_fee_a, protocol_fee_b) =
115 compute_tuna_protocol_fee(collateral_token, borrowed_token, collateral, borrow, protocol_fee_rate_on_collateral, protocol_fee_rate);
116
117 swap_input_amount -= if swap_input_token_is_a { protocol_fee_a } else { protocol_fee_b };
118
119 if position_token == collateral_token {
120 estimated_amount = collateral - if collateral_token == TOKEN_A { protocol_fee_a } else { protocol_fee_b };
121 }
122
123 if swap_input_amount > 0 {
124 let swap = swap_quote_by_input_token(swap_input_amount, swap_input_token_is_a, 0, fusion_pool, tick_arrays, None, None)?;
125 estimated_amount += swap.token_est_out;
126 next_sqrt_price = swap.next_sqrt_price;
127 min_swap_output_amount = try_get_min_amount_with_slippage_tolerance(swap.token_est_out, slippage_tolerance_bps)?;
128 }
129
130 let new_price = sqrt_price_to_price(next_sqrt_price.into(), 1, 1);
131 let price_impact = (new_price / price - 1.0).abs() * 100.0;
132
133 Ok(IncreaseSpotPositionQuoteResult {
134 collateral,
135 borrow,
136 estimated_amount,
137 swap_input_amount,
138 min_swap_output_amount,
139 protocol_fee_a,
140 protocol_fee_b,
141 price_impact,
142 })
143}
144
145#[cfg_attr(feature = "wasm", wasm_expose)]
146pub struct DecreaseSpotPositionQuoteResult {
147 pub decrease_percent: u32,
149 pub collateral_token: u8,
151 pub position_token: u8,
153 pub collateral: u64,
155 pub borrow: u64,
157 pub max_swap_input_amount: u64,
159 pub estimated_amount: u64,
161 pub protocol_fee_a: u64,
163 pub protocol_fee_b: u64,
165 pub price_impact: f64,
167}
168
169#[cfg_attr(feature = "wasm", wasm_expose)]
188pub fn get_decrease_spot_position_quote(
189 decrease_amount: u64,
190 collateral_token: u8,
191 leverage: f64,
192 reduce_only: bool,
193 slippage_tolerance_bps: Option<u16>,
194 position_token: u8,
195 position_amount: u64,
196 position_debt: u64,
197 protocol_fee_rate: u32,
198 protocol_fee_rate_on_collateral: u32,
199 fusion_pool: FusionPoolFacade,
200 tick_arrays: TickArrays,
201) -> Result<DecreaseSpotPositionQuoteResult, CoreError> {
202 if collateral_token > TOKEN_B || position_token > TOKEN_B {
203 return Err(INVALID_ARGUMENTS.into());
204 }
205
206 if leverage < 1.0 {
207 return Err(INVALID_ARGUMENTS.into());
208 }
209
210 if protocol_fee_rate >= HUNDRED_PERCENT {
211 return Err(INVALID_ARGUMENTS.into());
212 }
213
214 if protocol_fee_rate_on_collateral >= HUNDRED_PERCENT {
215 return Err(INVALID_ARGUMENTS.into());
216 }
217
218 let price = sqrt_price_to_price(fusion_pool.sqrt_price.into(), 1, 1);
219 let position_to_borrowed_token_price = if position_token == TOKEN_A { price } else { 1.0 / price };
220 let borrowed_token = if position_token == TOKEN_A { TOKEN_B } else { TOKEN_A };
221 let slippage_tolerance_bps = slippage_tolerance_bps.unwrap_or(DEFAULT_SLIPPAGE_TOLERANCE_BPS);
222
223 let mut collateral = 0;
224 let mut borrow = 0;
225 let mut swap_input_amount = 0;
226 let estimated_amount: u64;
227 let mut next_sqrt_price = fusion_pool.sqrt_price;
228 let mut new_position_token = position_token;
229 let decrease_percent: u32;
230
231 let mut decrease_amount_in_position_token = if collateral_token == position_token {
232 decrease_amount
233 } else {
234 round(decrease_amount as f64 / position_to_borrowed_token_price) as u64
235 };
236
237 if reduce_only && decrease_amount_in_position_token > position_amount {
238 decrease_amount_in_position_token = position_amount;
239 }
240
241 decrease_percent = ((decrease_amount_in_position_token * HUNDRED_PERCENT as u64 / position_amount) as u32).min(HUNDRED_PERCENT);
242
243 if decrease_amount_in_position_token <= position_amount {
244 estimated_amount = position_amount * (HUNDRED_PERCENT - decrease_percent) as u64 / HUNDRED_PERCENT as u64;
245
246 if collateral_token == position_token {
247 if position_debt > 0 {
248 let swap_out = position_debt * decrease_percent as u64 / HUNDRED_PERCENT as u64;
249 let swap = swap_quote_by_output_token(swap_out, borrowed_token == TOKEN_A, 0, fusion_pool, tick_arrays, None, None)?;
250 next_sqrt_price = swap.next_sqrt_price;
251 swap_input_amount = swap.token_est_in;
252 }
253 } else {
254 swap_input_amount = position_amount - estimated_amount;
255 let swap = swap_quote_by_input_token(swap_input_amount, position_token == TOKEN_A, 0, fusion_pool, tick_arrays, None, None)?;
256 next_sqrt_price = swap.next_sqrt_price;
257 }
258 } else {
259 new_position_token = if position_token == TOKEN_A { TOKEN_B } else { TOKEN_A };
260 let increase_amount = decrease_amount_in_position_token - position_amount;
261
262 if position_token == collateral_token {
263 estimated_amount = round(increase_amount as f64 * position_to_borrowed_token_price) as u64;
272
273 borrow = round((increase_amount as f64 * (leverage - 1.0)) / leverage) as u64;
275 let borrow_with_fees_applied = apply_swap_fee(apply_tuna_protocol_fee(borrow, protocol_fee_rate, false)?, fusion_pool.fee_rate, false)?;
276
277 collateral = increase_amount - borrow_with_fees_applied;
278
279 if position_debt > 0 {
281 let swap =
282 swap_quote_by_output_token(position_debt, position_token != TOKEN_A, 0, fusion_pool, tick_arrays.clone().into(), None, None)?;
283 swap_input_amount = swap.token_est_in;
284 }
285
286 swap_input_amount += collateral + apply_tuna_protocol_fee(borrow, protocol_fee_rate, false)?;
287 let swap = swap_quote_by_input_token(swap_input_amount, position_token == TOKEN_A, 0, fusion_pool, tick_arrays, None, None)?;
288 next_sqrt_price = swap.next_sqrt_price;
289
290 collateral = reverse_apply_tuna_protocol_fee(collateral, protocol_fee_rate_on_collateral, false)?;
291 } else {
292 estimated_amount = round(increase_amount as f64 * position_to_borrowed_token_price) as u64;
300
301 borrow = round((increase_amount as f64 * (leverage - 1.0)) / leverage) as u64;
302 let borrow_with_fees_applied = apply_swap_fee(apply_tuna_protocol_fee(borrow, protocol_fee_rate, false)?, fusion_pool.fee_rate, false)?;
303
304 collateral = increase_amount - borrow_with_fees_applied;
305 collateral = round(collateral as f64 * position_to_borrowed_token_price) as u64;
306 collateral = reverse_apply_tuna_protocol_fee(collateral, protocol_fee_rate_on_collateral, false)?;
307
308 swap_input_amount = position_amount + apply_tuna_protocol_fee(borrow, protocol_fee_rate, false)?;
310 let swap_quote = swap_quote_by_input_token(swap_input_amount, position_token == TOKEN_A, 0, fusion_pool, tick_arrays, None, None)?;
311 next_sqrt_price = swap_quote.next_sqrt_price;
312 }
313 }
314
315 let (protocol_fee_a, protocol_fee_b) =
316 compute_tuna_protocol_fee(collateral_token, borrowed_token, collateral, borrow, protocol_fee_rate_on_collateral, protocol_fee_rate);
317
318 let new_price = sqrt_price_to_price(next_sqrt_price.into(), 1, 1);
319 let price_impact = (new_price / price - 1.0).abs() * 100.0;
320
321 let max_swap_input_amount = try_get_max_amount_with_slippage_tolerance(swap_input_amount, slippage_tolerance_bps)?;
322
323 Ok(DecreaseSpotPositionQuoteResult {
324 decrease_percent,
325 collateral_token,
326 position_token: new_position_token,
327 collateral,
328 borrow,
329 max_swap_input_amount,
330 estimated_amount,
331 protocol_fee_a,
332 protocol_fee_b,
333 price_impact,
334 })
335}
336
337#[cfg_attr(feature = "wasm", wasm_expose)]
348pub fn get_liquidation_price(position_token: u8, amount: f64, debt: f64, liquidation_threshold: f64) -> Result<f64, CoreError> {
349 if debt < 0.0 || amount < 0.0 {
350 return Err(INVALID_ARGUMENTS);
351 }
352
353 if liquidation_threshold <= 0.0 || liquidation_threshold >= 1.0 {
354 return Err(INVALID_ARGUMENTS);
355 }
356
357 if debt == 0.0 || amount == 0.0 {
358 return Ok(0.0);
359 }
360
361 if position_token == TOKEN_A {
362 Ok(debt / (amount * liquidation_threshold))
363 } else {
364 Ok((amount * liquidation_threshold) / debt)
365 }
366}
367
368#[cfg_attr(feature = "wasm", wasm_expose)]
387pub fn get_tradable_amount(
388 collateral_token: u8,
389 available_balance: u64,
390 leverage: f64,
391 new_position_token: u8,
392 reduce_only: bool,
393 position_token: u8,
394 position_amount: u64,
395 position_debt: u64,
396 protocol_fee_rate: u32,
397 protocol_fee_rate_on_collateral: u32,
398 fusion_pool: FusionPoolFacade,
399 tick_arrays: TickArrays,
400) -> Result<u64, CoreError> {
401 if collateral_token > TOKEN_B || position_token > TOKEN_B {
402 return Err(INVALID_ARGUMENTS.into());
403 }
404
405 if leverage < 1.0 {
406 return Err(INVALID_ARGUMENTS.into());
407 }
408
409 if protocol_fee_rate >= HUNDRED_PERCENT {
410 return Err(INVALID_ARGUMENTS.into());
411 }
412
413 if protocol_fee_rate_on_collateral >= HUNDRED_PERCENT {
414 return Err(INVALID_ARGUMENTS.into());
415 }
416
417 if position_amount == 0 && new_position_token != position_token {
418 return Err(INVALID_ARGUMENTS.into());
419 }
420
421 let add_leverage = |collateral: u64| -> Result<u64, CoreError> {
425 let mut collateral = apply_tuna_protocol_fee(collateral, protocol_fee_rate_on_collateral, false)?;
426 if collateral_token != new_position_token {
427 collateral = apply_swap_fee(collateral, fusion_pool.fee_rate, false)?;
428 }
429
430 let fee_multiplier = (1.0 - protocol_fee_rate as f64 / HUNDRED_PERCENT as f64) * (1.0 - fusion_pool.fee_rate as f64 / 1_000_000.0);
431 let total = (collateral as f64 / (1.0 - (fee_multiplier * (leverage - 1.0)) / leverage)) as u64;
432 Ok(total)
433 };
434
435 let available_to_trade = if new_position_token == position_token {
436 add_leverage(available_balance)?
437 } else {
438 let price = sqrt_price_to_price(fusion_pool.sqrt_price.into(), 1, 1);
439 let position_to_opposite_token_price = if position_token == TOKEN_A { price } else { 1.0 / price };
440
441 if reduce_only {
442 if collateral_token == position_token {
443 position_amount
444 } else {
445 round(position_amount as f64 * position_to_opposite_token_price) as u64
446 }
447 } else {
448 let position_amount_in_collateral_token = if collateral_token == position_token {
449 position_amount
450 } else {
451 round(position_amount as f64 * position_to_opposite_token_price) as u64
452 };
453
454 let position_collateral = if collateral_token == position_token {
455 let swap_in = if position_debt > 0 {
456 swap_quote_by_output_token(position_debt, position_token == TOKEN_B, 0, fusion_pool, tick_arrays, None, None)?.token_est_in
457 } else {
458 0
459 };
460 position_amount - swap_in
461 } else {
462 if position_amount > 0 {
463 let swap_quote = swap_quote_by_input_token(position_amount, position_token == TOKEN_A, 0, fusion_pool, tick_arrays, None, None)?;
464 swap_quote.token_est_out - position_debt
465 } else {
466 0
467 }
468 };
469
470 position_amount_in_collateral_token + add_leverage(available_balance + position_collateral)?
472 }
473 };
474
475 Ok(available_to_trade)
476}
477
478pub fn apply_tuna_protocol_fee(amount: u64, protocol_fee_rate: u32, round_up: bool) -> Result<u64, CoreError> {
479 try_mul_div(amount, HUNDRED_PERCENT as u128 - protocol_fee_rate as u128, HUNDRED_PERCENT as u128, round_up)
480}
481
482pub fn reverse_apply_tuna_protocol_fee(amount: u64, protocol_fee_rate: u32, round_up: bool) -> Result<u64, CoreError> {
483 try_mul_div(amount, HUNDRED_PERCENT as u128, HUNDRED_PERCENT as u128 - protocol_fee_rate as u128, round_up)
484}
485
486pub fn apply_swap_fee(amount: u64, fee_rate: u16, round_up: bool) -> Result<u64, CoreError> {
487 try_mul_div(amount, 1_000_000 - fee_rate as u128, 1_000_000, round_up)
488}
489
490pub fn reverse_apply_swap_fee(amount: u64, fee_rate: u16, round_up: bool) -> Result<u64, CoreError> {
491 try_mul_div(amount, 1_000_000, 1_000_000 - fee_rate as u128, round_up)
492}
493
494pub fn compute_tuna_protocol_fee(
495 collateral_token: u8,
496 borrowed_token: u8,
497 collateral: u64,
498 borrow: u64,
499 protocol_fee_rate_on_collateral: u32,
500 protocol_fee_rate: u32,
501) -> (u64, u64) {
502 let collateral_a = if collateral_token == TOKEN_A { collateral } else { 0 };
503 let collateral_b = if collateral_token == TOKEN_B { collateral } else { 0 };
504 let borrow_a = if borrowed_token == TOKEN_A { borrow } else { 0 };
505 let borrow_b = if borrowed_token == TOKEN_B { borrow } else { 0 };
506
507 let protocol_fee_a = ((collateral_a as u128 * protocol_fee_rate_on_collateral as u128 + borrow_a as u128 * protocol_fee_rate as u128)
508 / HUNDRED_PERCENT as u128) as u64;
509 let protocol_fee_b = ((collateral_b as u128 * protocol_fee_rate_on_collateral as u128 + borrow_b as u128 * protocol_fee_rate as u128)
510 / HUNDRED_PERCENT as u128) as u64;
511
512 (protocol_fee_a, protocol_fee_b)
513}
514
515#[cfg(all(test, not(feature = "wasm")))]
516mod tests {
517 use super::*;
518 use crate::assert_approx_eq;
519 use fusionamm_core::{
520 get_tick_array_start_tick_index, price_to_sqrt_price, sqrt_price_to_tick_index, TickArrayFacade, TickFacade, TICK_ARRAY_SIZE,
521 };
522
523 fn test_fusion_pool(sqrt_price: u128) -> FusionPoolFacade {
524 let tick_current_index = sqrt_price_to_tick_index(sqrt_price);
525 FusionPoolFacade {
526 tick_current_index,
527 fee_rate: 3000,
528 liquidity: 10000000000000,
529 sqrt_price,
530 tick_spacing: 2,
531 ..FusionPoolFacade::default()
532 }
533 }
534
535 fn test_tick(liquidity_net: i128) -> TickFacade {
536 TickFacade {
537 initialized: true,
538 liquidity_net,
539 ..TickFacade::default()
540 }
541 }
542
543 fn test_tick_array(start_tick_index: i32) -> TickArrayFacade {
544 TickArrayFacade {
545 start_tick_index,
546 ticks: [test_tick(0); TICK_ARRAY_SIZE],
547 }
548 }
549
550 fn test_tick_arrays(fusion_pool: FusionPoolFacade) -> TickArrays {
551 let tick_spacing = fusion_pool.tick_spacing;
552 let tick_current_index = sqrt_price_to_tick_index(fusion_pool.sqrt_price);
553 let tick_array_start_index = get_tick_array_start_tick_index(tick_current_index, tick_spacing);
554
555 [
556 test_tick_array(tick_array_start_index),
557 test_tick_array(tick_array_start_index + TICK_ARRAY_SIZE as i32 * tick_spacing as i32),
558 test_tick_array(tick_array_start_index + TICK_ARRAY_SIZE as i32 * tick_spacing as i32 * 2),
559 test_tick_array(tick_array_start_index - TICK_ARRAY_SIZE as i32 * tick_spacing as i32),
560 test_tick_array(tick_array_start_index - TICK_ARRAY_SIZE as i32 * tick_spacing as i32 * 2),
561 ]
562 .into()
563 }
564
565 #[test]
566 fn test_get_liquidation_price() {
567 assert_eq!(get_liquidation_price(TOKEN_A, 5.0, 0.0, 0.85), Ok(0.0));
568 assert_eq!(get_liquidation_price(TOKEN_A, 0.0, 5.0, 0.85), Ok(0.0));
569 assert_eq!(get_liquidation_price(TOKEN_A, 5.0, 800.0, 0.85), Ok(188.23529411764707));
570 assert_eq!(get_liquidation_price(TOKEN_B, 1000.0, 4.0, 0.85), Ok(212.5));
571 }
572
573 #[test]
574 fn increase_long_position_providing_token_a() {
575 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
576 let fusion_pool = test_fusion_pool(sqrt_price);
577
578 let quote = get_increase_spot_position_quote(
579 5_000_000_000,
580 TOKEN_A,
581 TOKEN_A,
582 5.0,
583 Some(0),
584 HUNDRED_PERCENT / 100,
585 HUNDRED_PERCENT / 200,
586 fusion_pool,
587 test_tick_arrays(fusion_pool),
588 )
589 .unwrap();
590
591 assert_eq!(quote.collateral, 1057165829);
592 assert_eq!(quote.borrow, 800000000);
593 assert_eq!(quote.min_swap_output_amount, 3_947_423_011);
594 assert_eq!(quote.estimated_amount, 4_999_303_011);
595 assert_eq!(quote.protocol_fee_a, 5285829);
596 assert_eq!(quote.protocol_fee_b, 8000000);
597 assert_eq!(quote.price_impact, 0.03531617625702754);
598 assert_approx_eq!(quote.estimated_amount as f64 / (quote.estimated_amount as f64 - (quote.borrow as f64 * 1000.0) / 200.0), 5.0, 0.1);
599 }
600
601 #[test]
602 fn increase_long_position_providing_token_b() {
603 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
604 let fusion_pool = test_fusion_pool(sqrt_price);
605
606 let quote = get_increase_spot_position_quote(
607 5000_000_000,
608 TOKEN_B,
609 TOKEN_A,
610 5.0,
611 Some(0),
612 HUNDRED_PERCENT / 100,
613 HUNDRED_PERCENT / 200,
614 fusion_pool,
615 test_tick_arrays(fusion_pool),
616 )
617 .unwrap();
618
619 assert_eq!(quote.collateral, 1060346869);
620 assert_eq!(quote.borrow, 4000000000);
621 assert_eq!(quote.estimated_amount, 24_972_080_293);
622 assert_eq!(quote.protocol_fee_a, 0);
623 assert_eq!(quote.protocol_fee_b, 45301734);
624 assert_eq!(quote.price_impact, 0.22373179716579372);
625 assert_approx_eq!(quote.estimated_amount as f64 / (quote.estimated_amount as f64 - (quote.borrow as f64 * 1000.0) / 200.0), 5.0, 0.1);
626 }
627
628 #[test]
629 fn increase_short_position_providing_a() {
630 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
631 let fusion_pool = test_fusion_pool(sqrt_price);
632
633 let quote = get_increase_spot_position_quote(
634 5_000_000_000,
635 TOKEN_A,
636 TOKEN_B,
637 5.0,
638 Some(0),
639 HUNDRED_PERCENT / 100,
640 HUNDRED_PERCENT / 200,
641 fusion_pool,
642 test_tick_arrays(fusion_pool),
643 )
644 .unwrap();
645
646 assert_eq!(quote.collateral, 1060346869);
647 assert_eq!(quote.borrow, 4000000000);
648 assert_eq!(quote.estimated_amount, 999_776_441);
649 assert_eq!(quote.protocol_fee_a, 45301734);
650 assert_eq!(quote.protocol_fee_b, 0);
651 assert_eq!(quote.price_impact, 0.04470636400017991);
652 assert_approx_eq!(quote.estimated_amount as f64 / (quote.estimated_amount as f64 - (quote.borrow as f64 / 1000.0) * 200.0), 5.0, 0.1);
653 }
654
655 #[test]
656 fn increase_short_position_providing_b() {
657 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
658 let fusion_pool = test_fusion_pool(sqrt_price);
659
660 let quote = get_increase_spot_position_quote(
661 5000_000_000,
662 TOKEN_B,
663 TOKEN_B,
664 5.0,
665 Some(0),
666 HUNDRED_PERCENT / 100,
667 HUNDRED_PERCENT / 200,
668 fusion_pool,
669 test_tick_arrays(fusion_pool),
670 )
671 .unwrap();
672
673 assert_eq!(quote.collateral, 1057165829);
674 assert_eq!(quote.borrow, 20000000000);
675 assert_eq!(quote.estimated_amount, 4996_517_564);
676 assert_eq!(quote.protocol_fee_a, 200000000);
677 assert_eq!(quote.protocol_fee_b, 5285829);
678 assert_eq!(quote.price_impact, 0.17633175413067637);
679 assert_approx_eq!(quote.estimated_amount as f64 / (quote.estimated_amount as f64 - (quote.borrow as f64 / 1000.0) * 200.0), 5.0, 0.1);
680 }
681
682 #[test]
683 fn increase_quote_with_slippage() {
684 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
685 let fusion_pool = test_fusion_pool(sqrt_price);
686
687 let quote =
689 get_increase_spot_position_quote(200_000, TOKEN_B, TOKEN_A, 5.0, Some(1000), 0, 0, fusion_pool, test_tick_arrays(fusion_pool)).unwrap();
690 assert_eq!(quote.min_swap_output_amount, 899_994);
691
692 let quote =
694 get_increase_spot_position_quote(200_000, TOKEN_B, TOKEN_A, 5.0, Some(0), 0, 0, fusion_pool, test_tick_arrays(fusion_pool)).unwrap();
695 assert_eq!(quote.min_swap_output_amount, 999_994);
696 }
697
698 #[test]
699 fn decrease_non_leveraged_long_position_providing_a_reduce_only() {
700 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
701 let fusion_pool = test_fusion_pool(sqrt_price);
702
703 let quote = get_decrease_spot_position_quote(
704 1_000_000_000,
705 TOKEN_A,
706 1.0,
707 true,
708 Some(0),
709 TOKEN_A,
710 5_000_000_000, 0, HUNDRED_PERCENT / 100,
713 HUNDRED_PERCENT / 200,
714 fusion_pool,
715 test_tick_arrays(fusion_pool),
716 )
717 .unwrap();
718
719 assert_eq!(quote.decrease_percent, 200000);
720 assert_eq!(quote.collateral_token, TOKEN_A);
721 assert_eq!(quote.position_token, TOKEN_A);
722 assert_eq!(quote.estimated_amount, 4_000_000_000);
723 assert_eq!(quote.protocol_fee_a, 0);
724 assert_eq!(quote.protocol_fee_b, 0);
725 assert_eq!(quote.price_impact, 0.0);
726 assert_eq!(quote.estimated_amount as f64 / (quote.estimated_amount as f64 - (quote.borrow as f64 / 1000.0) * 200.0), 1.0);
727 }
728
729 #[test]
730 fn decrease_non_leveraged_long_position_providing_b_reduce_only() {
731 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
732 let fusion_pool = test_fusion_pool(sqrt_price);
733
734 let quote = get_decrease_spot_position_quote(
735 200_000_000,
736 TOKEN_B,
737 1.0,
738 true,
739 Some(0),
740 TOKEN_A,
741 5_000_000_000, 0, HUNDRED_PERCENT / 100,
744 HUNDRED_PERCENT / 200,
745 fusion_pool,
746 test_tick_arrays(fusion_pool),
747 )
748 .unwrap();
749
750 assert_eq!(quote.decrease_percent, 200000);
751 assert_eq!(quote.collateral_token, TOKEN_B);
752 assert_eq!(quote.position_token, TOKEN_A);
753 assert_eq!(quote.estimated_amount, 4_000_000_000);
754 assert_eq!(quote.protocol_fee_a, 0);
755 assert_eq!(quote.protocol_fee_b, 0);
756 assert_eq!(quote.price_impact, 0.008916842709072448);
757 assert_eq!(quote.estimated_amount as f64 / (quote.estimated_amount as f64 - (quote.borrow as f64 / 1000.0) * 200.0), 1.0);
758 }
759
760 #[test]
761 fn decrease_long_position_providing_a_reduce_only() {
762 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
763 let fusion_pool = test_fusion_pool(sqrt_price);
764
765 let quote = get_decrease_spot_position_quote(
766 1_000_000_000,
767 TOKEN_A,
768 5.0,
769 true,
770 Some(0),
771 TOKEN_A,
772 5_000_000_000, 800_000_000, HUNDRED_PERCENT / 100,
775 HUNDRED_PERCENT / 200,
776 fusion_pool,
777 test_tick_arrays(fusion_pool),
778 )
779 .unwrap();
780
781 assert_eq!(quote.decrease_percent, 200000);
782 assert_eq!(quote.collateral_token, TOKEN_A);
783 assert_eq!(quote.position_token, TOKEN_A);
784 assert_eq!(quote.collateral, 0);
785 assert_eq!(quote.borrow, 0);
786 assert_eq!(quote.estimated_amount, 4_000_000_000);
787 assert_eq!(quote.protocol_fee_a, 0);
788 assert_eq!(quote.protocol_fee_b, 0);
789 assert_eq!(quote.price_impact, 0.007155289528004705);
790
791 let quote = get_decrease_spot_position_quote(
792 6_000_000_000,
793 TOKEN_A,
794 5.0,
795 true,
796 Some(0),
797 TOKEN_A,
798 5_000_000_000, 800_000_000, HUNDRED_PERCENT / 100,
801 HUNDRED_PERCENT / 200,
802 fusion_pool,
803 test_tick_arrays(fusion_pool),
804 )
805 .unwrap();
806
807 assert_eq!(quote.decrease_percent, HUNDRED_PERCENT);
808 assert_eq!(quote.collateral, 0);
809 assert_eq!(quote.borrow, 0);
810 assert_eq!(quote.estimated_amount, 0);
811 }
812
813 #[test]
814 fn decrease_long_position_providing_b_reduce_only() {
815 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
816 let fusion_pool = test_fusion_pool(sqrt_price);
817
818 let quote = get_decrease_spot_position_quote(
819 200_000_000,
820 TOKEN_B,
821 5.0,
822 true,
823 Some(0),
824 TOKEN_A,
825 5_000_000_000, 800_000_000, HUNDRED_PERCENT / 100,
828 HUNDRED_PERCENT / 200,
829 fusion_pool,
830 test_tick_arrays(fusion_pool),
831 )
832 .unwrap();
833
834 assert_eq!(quote.decrease_percent, 200000);
835 assert_eq!(quote.collateral_token, TOKEN_B);
836 assert_eq!(quote.position_token, TOKEN_A);
837 assert_eq!(quote.collateral, 0);
838 assert_eq!(quote.borrow, 0);
839 assert_eq!(quote.estimated_amount, 4000000000);
840 assert_eq!(quote.protocol_fee_a, 0);
841 assert_eq!(quote.protocol_fee_b, 0);
842 assert_eq!(quote.price_impact, 0.008916842709072448);
843
844 let quote = get_decrease_spot_position_quote(
845 1200_000_000,
846 TOKEN_B,
847 5.0,
848 true,
849 Some(0),
850 TOKEN_A,
851 5_000_000_000, 800_000_000, HUNDRED_PERCENT / 100,
854 HUNDRED_PERCENT / 200,
855 fusion_pool,
856 test_tick_arrays(fusion_pool),
857 )
858 .unwrap();
859
860 assert_eq!(quote.decrease_percent, HUNDRED_PERCENT);
861 assert_eq!(quote.collateral, 0);
862 assert_eq!(quote.borrow, 0);
863 assert_eq!(quote.estimated_amount, 0);
864 }
865
866 #[test]
867 fn decrease_long_position_providing_a_without_leverage() {
868 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
869 let fusion_pool = test_fusion_pool(sqrt_price);
870
871 let quote = get_decrease_spot_position_quote(
872 6_000_000_000,
873 TOKEN_A,
874 1.0,
875 false,
876 Some(0),
877 TOKEN_A,
878 5_000_000_000, 800_000_000, HUNDRED_PERCENT / 100,
881 HUNDRED_PERCENT / 200,
882 fusion_pool,
883 test_tick_arrays(fusion_pool),
884 )
885 .unwrap();
886
887 assert_eq!(quote.decrease_percent, HUNDRED_PERCENT);
888 assert_eq!(quote.collateral_token, TOKEN_A);
889 assert_eq!(quote.position_token, TOKEN_B);
890 assert_eq!(quote.collateral, 1005025125);
891 assert_eq!(quote.borrow, 0);
892 assert_eq!(quote.estimated_amount, 200_000_000);
893 assert_eq!(quote.protocol_fee_a, 5025125);
894 assert_eq!(quote.protocol_fee_b, 0);
895 assert_eq!(quote.price_impact, 0.044685946117650754);
896 }
897
898 #[test]
899 fn decrease_long_position_providing_a() {
900 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
901 let fusion_pool = test_fusion_pool(sqrt_price);
902
903 let quote = get_decrease_spot_position_quote(
904 6_000_000_000,
905 TOKEN_A,
906 5.0,
907 false,
908 Some(0),
909 TOKEN_A,
910 5_000_000_000, 800_000_000, HUNDRED_PERCENT / 100,
913 HUNDRED_PERCENT / 200,
914 fusion_pool,
915 test_tick_arrays(fusion_pool),
916 )
917 .unwrap();
918
919 assert_eq!(quote.decrease_percent, HUNDRED_PERCENT);
920 assert_eq!(quote.collateral_token, TOKEN_A);
921 assert_eq!(quote.position_token, TOKEN_B);
922 assert_eq!(quote.collateral, 211433165);
923 assert_eq!(quote.borrow, 800000000);
924 assert_eq!(quote.estimated_amount, 200_000_000);
925 assert_eq!(quote.protocol_fee_a, 1057165);
926 assert_eq!(quote.protocol_fee_b, 8000000);
927 assert_eq!(quote.price_impact, 0.04470711974918773);
928 }
929
930 #[test]
931 fn decrease_long_position_providing_b() {
932 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
933 let fusion_pool = test_fusion_pool(sqrt_price);
934
935 let quote = get_decrease_spot_position_quote(
936 1_200_000_000,
937 TOKEN_B,
938 5.0,
939 false,
940 Some(0),
941 TOKEN_A,
942 5_000_000_000, 800_000_000, HUNDRED_PERCENT / 100,
945 HUNDRED_PERCENT / 200,
946 fusion_pool,
947 test_tick_arrays(fusion_pool),
948 )
949 .unwrap();
950
951 assert_eq!(quote.decrease_percent, HUNDRED_PERCENT);
952 assert_eq!(quote.collateral_token, TOKEN_B);
953 assert_eq!(quote.position_token, TOKEN_B);
954 assert_eq!(quote.collateral, 42286633);
955 assert_eq!(quote.borrow, 800_000_000);
956 assert_eq!(quote.estimated_amount, 200000000);
957 assert_eq!(quote.protocol_fee_a, 0);
958 assert_eq!(quote.protocol_fee_b, 8211433);
959 assert_eq!(quote.price_impact, 0.05162980632488212);
960 }
961
962 #[test]
963 fn decrease_non_leveraged_short_position_providing_a() {
964 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
965 let fusion_pool = test_fusion_pool(sqrt_price);
966
967 let quote = get_decrease_spot_position_quote(
968 6_000_000_000,
969 TOKEN_A,
970 1.0,
971 false,
972 Some(0),
973 TOKEN_B,
974 1000_000_000, 0, HUNDRED_PERCENT / 100,
977 HUNDRED_PERCENT / 200,
978 fusion_pool,
979 test_tick_arrays(fusion_pool),
980 )
981 .unwrap();
982
983 assert_eq!(quote.decrease_percent, HUNDRED_PERCENT);
984 assert_eq!(quote.collateral_token, TOKEN_A);
985 assert_eq!(quote.position_token, TOKEN_A);
986 assert_eq!(quote.collateral, 1005025125);
987 assert_eq!(quote.borrow, 0);
988 assert_eq!(quote.estimated_amount, 1_000_000_000);
989 assert_eq!(quote.protocol_fee_a, 5025125);
990 assert_eq!(quote.protocol_fee_b, 0);
991 assert_eq!(quote.price_impact, 0.044592165429757635);
992 }
993
994 #[test]
995 fn decrease_short_position_providing_a() {
996 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
997 let fusion_pool = test_fusion_pool(sqrt_price);
998
999 let quote = get_decrease_spot_position_quote(
1000 6_000_000_000,
1001 TOKEN_A,
1002 5.0,
1003 false,
1004 Some(0),
1005 TOKEN_B,
1006 1000_000_000, 4_000_000_000, HUNDRED_PERCENT / 100,
1009 HUNDRED_PERCENT / 200,
1010 fusion_pool,
1011 test_tick_arrays(fusion_pool),
1012 )
1013 .unwrap();
1014
1015 assert_eq!(quote.decrease_percent, HUNDRED_PERCENT);
1016 assert_eq!(quote.collateral_token, TOKEN_A);
1017 assert_eq!(quote.position_token, TOKEN_A);
1018 assert_eq!(quote.collateral, 211433165);
1019 assert_eq!(quote.borrow, 160000000);
1020 assert_eq!(quote.estimated_amount, 1_000_000_000);
1021 assert_eq!(quote.protocol_fee_a, 2657165);
1022 assert_eq!(quote.protocol_fee_b, 0);
1023 assert_eq!(quote.price_impact, 0.051656476403882934);
1024 }
1025
1026 #[test]
1027 fn decrease_short_position_providing_b() {
1028 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
1029 let fusion_pool = test_fusion_pool(sqrt_price);
1030
1031 let quote = get_decrease_spot_position_quote(
1032 1_200_000_000,
1033 TOKEN_B,
1034 5.0,
1035 false,
1036 Some(0),
1037 TOKEN_B,
1038 1000_000_000, 4_000_000_000, HUNDRED_PERCENT / 100,
1041 HUNDRED_PERCENT / 200,
1042 fusion_pool,
1043 test_tick_arrays(fusion_pool),
1044 )
1045 .unwrap();
1046
1047 assert_eq!(quote.decrease_percent, HUNDRED_PERCENT);
1048 assert_eq!(quote.collateral_token, TOKEN_B);
1049 assert_eq!(quote.position_token, TOKEN_A);
1050 assert_eq!(quote.collateral, 42_286_633);
1051 assert_eq!(quote.borrow, 160000000);
1052 assert_eq!(quote.estimated_amount, 1_000_000_000);
1053 assert_eq!(quote.protocol_fee_a, 1600000);
1054 assert_eq!(quote.protocol_fee_b, 211433);
1055 assert_eq!(quote.price_impact, 0.04472711596017476);
1056 }
1057
1058 #[test]
1059 fn tradable_amount_for_1x_long_position_providing_b() {
1060 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
1061 let fusion_pool = test_fusion_pool(sqrt_price);
1062 let tick_arrays = test_tick_arrays(fusion_pool);
1063
1064 let collateral_token = TOKEN_B;
1065 let position_token = TOKEN_A;
1066 let leverage = 1.0;
1067 let protocol_fee_rate = HUNDRED_PERCENT / 100;
1068 let protocol_fee_rate_on_collateral = HUNDRED_PERCENT / 200;
1069 let available_balance = 200_000_000;
1070
1071 let tradable_amount = get_tradable_amount(
1072 collateral_token,
1073 available_balance,
1074 leverage,
1075 position_token,
1076 false,
1077 position_token,
1078 0,
1079 0,
1080 protocol_fee_rate,
1081 protocol_fee_rate_on_collateral,
1082 fusion_pool,
1083 tick_arrays.clone(),
1084 )
1085 .unwrap();
1086 assert_eq!(tradable_amount, 198403000);
1087
1088 let quote = get_increase_spot_position_quote(
1089 tradable_amount,
1090 collateral_token,
1091 position_token,
1092 leverage,
1093 Some(0),
1094 protocol_fee_rate,
1095 protocol_fee_rate_on_collateral,
1096 fusion_pool,
1097 tick_arrays,
1098 )
1099 .unwrap();
1100 assert_eq!(quote.collateral, available_balance);
1101 }
1102
1103 #[test]
1104 fn tradable_amount_for_5x_long_position_providing_b() {
1105 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
1106 let fusion_pool = test_fusion_pool(sqrt_price);
1107 let tick_arrays = test_tick_arrays(fusion_pool);
1108
1109 let collateral_token = TOKEN_B;
1110 let position_token = TOKEN_A;
1111 let leverage = 5.0;
1112 let protocol_fee_rate = HUNDRED_PERCENT / 100;
1113 let protocol_fee_rate_on_collateral = HUNDRED_PERCENT / 200;
1114 let available_balance = 10_000_000;
1115
1116 let tradable_amount = get_tradable_amount(
1117 collateral_token,
1118 available_balance,
1119 leverage,
1120 position_token,
1121 false,
1122 position_token,
1123 0,
1124 0,
1125 protocol_fee_rate,
1126 protocol_fee_rate_on_collateral,
1127 fusion_pool,
1128 tick_arrays.clone(),
1129 )
1130 .unwrap();
1131 assert_eq!(tradable_amount, 47154380);
1132
1133 let quote = get_increase_spot_position_quote(
1134 tradable_amount,
1135 collateral_token,
1136 position_token,
1137 leverage,
1138 Some(0),
1139 protocol_fee_rate,
1140 protocol_fee_rate_on_collateral,
1141 fusion_pool,
1142 tick_arrays,
1143 )
1144 .unwrap();
1145 assert_eq!(quote.collateral, available_balance + 1);
1147 }
1148
1149 #[test]
1150 fn tradable_amount_for_5x_long_position_providing_a() {
1151 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
1152 let fusion_pool = test_fusion_pool(sqrt_price);
1153 let tick_arrays = test_tick_arrays(fusion_pool);
1154
1155 let collateral_token = TOKEN_A;
1156 let position_token = TOKEN_A;
1157 let leverage = 5.0;
1158 let protocol_fee_rate = HUNDRED_PERCENT / 100;
1159 let protocol_fee_rate_on_collateral = HUNDRED_PERCENT / 200;
1160 let available_balance = 1_000_000_000;
1161
1162 let tradable_amount = get_tradable_amount(
1163 collateral_token,
1164 available_balance,
1165 leverage,
1166 position_token,
1167 false,
1168 position_token,
1169 0,
1170 0,
1171 protocol_fee_rate,
1172 protocol_fee_rate_on_collateral,
1173 fusion_pool,
1174 tick_arrays.clone(),
1175 )
1176 .unwrap();
1177 assert_eq!(tradable_amount, 4729626953);
1178
1179 let quote = get_increase_spot_position_quote(
1180 tradable_amount,
1181 collateral_token,
1182 position_token,
1183 leverage,
1184 Some(0),
1185 protocol_fee_rate,
1186 protocol_fee_rate_on_collateral,
1187 fusion_pool,
1188 tick_arrays,
1189 )
1190 .unwrap();
1191 assert_eq!(quote.collateral, available_balance);
1192 }
1194
1195 #[test]
1196 fn tradable_amount_for_5x_short_position_providing_b() {
1197 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
1198 let fusion_pool = test_fusion_pool(sqrt_price);
1199 let tick_arrays = test_tick_arrays(fusion_pool);
1200
1201 let collateral_token = TOKEN_B;
1202 let position_token = TOKEN_B;
1203 let leverage = 5.0;
1204 let protocol_fee_rate = HUNDRED_PERCENT / 100;
1205 let protocol_fee_rate_on_collateral = HUNDRED_PERCENT / 200;
1206 let available_balance = 200_000_000;
1207
1208 let tradable_amount = get_tradable_amount(
1209 collateral_token,
1210 available_balance,
1211 leverage,
1212 position_token,
1213 false,
1214 position_token,
1215 0,
1216 0,
1217 protocol_fee_rate,
1218 protocol_fee_rate_on_collateral,
1219 fusion_pool,
1220 tick_arrays.clone(),
1221 )
1222 .unwrap();
1223 assert_eq!(tradable_amount, 945925390);
1224
1225 let quote = get_increase_spot_position_quote(
1226 tradable_amount,
1227 collateral_token,
1228 position_token,
1229 leverage,
1230 Some(0),
1231 protocol_fee_rate,
1232 protocol_fee_rate_on_collateral,
1233 fusion_pool,
1234 tick_arrays,
1235 )
1236 .unwrap();
1237 assert_eq!(quote.collateral, available_balance + 1);
1239 }
1241
1242 #[test]
1243 fn tradable_amount_for_reducing_existing_long_position() {
1244 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
1245 let fusion_pool = test_fusion_pool(sqrt_price);
1246 let tick_arrays = test_tick_arrays(fusion_pool);
1247
1248 for i in 0..2 {
1249 let collateral_token = if i == 0 { TOKEN_A } else { TOKEN_B };
1250 let position_token = if i == 0 { TOKEN_A } else { TOKEN_B };
1251 let leverage = 5.0;
1252 let reduce_only = true;
1253 let position_amount = 5_000_000_000;
1254 let position_debt = 800_000_000;
1255 let protocol_fee_rate = HUNDRED_PERCENT / 100;
1256 let protocol_fee_rate_on_collateral = HUNDRED_PERCENT / 200;
1257 let available_balance = 50_000_000_000;
1258
1259 let tradable_amount = get_tradable_amount(
1260 collateral_token,
1261 available_balance,
1262 leverage,
1263 if position_token == TOKEN_A { TOKEN_B } else { TOKEN_A },
1264 reduce_only,
1265 position_token,
1266 position_amount,
1267 position_debt,
1268 protocol_fee_rate,
1269 protocol_fee_rate_on_collateral,
1270 fusion_pool,
1271 tick_arrays.clone(),
1272 )
1273 .unwrap();
1274 assert_eq!(tradable_amount, 5_000_000_000);
1275
1276 let quote = get_decrease_spot_position_quote(
1277 tradable_amount,
1278 collateral_token,
1279 5.0,
1280 reduce_only,
1281 Some(0),
1282 position_token,
1283 position_amount,
1284 position_debt,
1285 protocol_fee_rate,
1286 protocol_fee_rate_on_collateral,
1287 fusion_pool,
1288 tick_arrays.clone(),
1289 )
1290 .unwrap();
1291
1292 assert_eq!(quote.decrease_percent, HUNDRED_PERCENT);
1293 }
1294 }
1295
1296 #[test]
1297 fn tradable_amount_when_inverting_long_position_providing_a() {
1298 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
1299 let fusion_pool = test_fusion_pool(sqrt_price);
1300 let tick_arrays = test_tick_arrays(fusion_pool);
1301
1302 let collateral_token = TOKEN_A;
1303 let position_token = TOKEN_A;
1304 let leverage = 5.0;
1305 let reduce_only = false;
1306 let position_amount = 5_000_000_000;
1307 let position_debt = 800_000_000;
1308 let protocol_fee_rate = HUNDRED_PERCENT / 100;
1309 let protocol_fee_rate_on_collateral = HUNDRED_PERCENT / 200;
1310 let available_balance = 2_000_000_000;
1311
1312 let tradable_amount = get_tradable_amount(
1313 collateral_token,
1314 available_balance,
1315 leverage,
1316 if position_token == TOKEN_A { TOKEN_B } else { TOKEN_A },
1317 reduce_only,
1318 position_token,
1319 position_amount,
1320 position_debt,
1321 protocol_fee_rate,
1322 protocol_fee_rate_on_collateral,
1323 fusion_pool,
1324 tick_arrays.clone(),
1325 )
1326 .unwrap();
1327 assert_eq!(tradable_amount, 19_086_173_788);
1328
1329 let quote = get_decrease_spot_position_quote(
1330 tradable_amount,
1331 collateral_token,
1332 5.0,
1333 reduce_only,
1334 Some(0),
1335 position_token,
1336 position_amount,
1337 position_debt,
1338 protocol_fee_rate,
1339 protocol_fee_rate_on_collateral,
1340 fusion_pool,
1341 tick_arrays.clone(),
1342 )
1343 .unwrap();
1344
1345 assert_eq!(quote.decrease_percent, HUNDRED_PERCENT);
1346 assert_eq!(quote.collateral, 2_978_284_319);
1347 }
1348
1349 #[test]
1350 fn tradable_amount_when_inverting_long_position_providing_b() {
1351 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
1352 let fusion_pool = test_fusion_pool(sqrt_price);
1353 let tick_arrays = test_tick_arrays(fusion_pool);
1354
1355 let collateral_token = TOKEN_B;
1356 let position_token = TOKEN_A;
1357 let leverage = 5.0;
1358 let reduce_only = false;
1359 let position_amount = 5_000_000_000;
1360 let position_debt = 800_000_000;
1361 let protocol_fee_rate = HUNDRED_PERCENT / 100;
1362 let protocol_fee_rate_on_collateral = HUNDRED_PERCENT / 200;
1363 let available_balance = 500_000_000;
1364
1365 let tradable_amount = get_tradable_amount(
1366 collateral_token,
1367 available_balance,
1368 leverage,
1369 if position_token == TOKEN_A { TOKEN_B } else { TOKEN_A },
1370 reduce_only,
1371 position_token,
1372 position_amount,
1373 position_debt,
1374 protocol_fee_rate,
1375 protocol_fee_rate_on_collateral,
1376 fusion_pool,
1377 tick_arrays.clone(),
1378 )
1379 .unwrap();
1380 assert_eq!(tradable_amount, 4_295_498_968);
1381
1382 let quote = get_decrease_spot_position_quote(
1383 tradable_amount,
1384 collateral_token,
1385 5.0,
1386 reduce_only,
1387 Some(0),
1388 position_token,
1389 position_amount,
1390 position_debt,
1391 protocol_fee_rate,
1392 protocol_fee_rate_on_collateral,
1393 fusion_pool,
1394 tick_arrays.clone(),
1395 )
1396 .unwrap();
1397
1398 assert_eq!(quote.decrease_percent, HUNDRED_PERCENT);
1399 assert_eq!(quote.collateral, 696_777_779);
1400 }
1401}