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 decrease_acceptable_swap_amount: u64,
161 pub increase_min_swap_output_amount: u64,
163 pub estimated_amount: u64,
165 pub protocol_fee_a: u64,
167 pub protocol_fee_b: u64,
169 pub price_impact: f64,
171}
172
173#[cfg_attr(feature = "wasm", wasm_expose)]
192pub fn get_decrease_spot_position_quote(
193 decrease_amount: u64,
194 collateral_token: u8,
195 leverage: f64,
196 reduce_only: bool,
197 slippage_tolerance_bps: Option<u16>,
198 position_token: u8,
199 position_amount: u64,
200 position_debt: u64,
201 protocol_fee_rate: u32,
202 protocol_fee_rate_on_collateral: u32,
203 fusion_pool: FusionPoolFacade,
204 tick_arrays: TickArrays,
205) -> Result<DecreaseSpotPositionQuoteResult, CoreError> {
206 if collateral_token > TOKEN_B || position_token > TOKEN_B {
207 return Err(INVALID_ARGUMENTS.into());
208 }
209
210 if leverage < 1.0 {
211 return Err(INVALID_ARGUMENTS.into());
212 }
213
214 if protocol_fee_rate >= HUNDRED_PERCENT {
215 return Err(INVALID_ARGUMENTS.into());
216 }
217
218 if protocol_fee_rate_on_collateral >= HUNDRED_PERCENT {
219 return Err(INVALID_ARGUMENTS.into());
220 }
221
222 let price = sqrt_price_to_price(fusion_pool.sqrt_price.into(), 1, 1);
223 let position_to_borrowed_token_price = if position_token == TOKEN_A { price } else { 1.0 / price };
224 let borrowed_token = if position_token == TOKEN_A { TOKEN_B } else { TOKEN_A };
225 let slippage_tolerance_bps = slippage_tolerance_bps.unwrap_or(DEFAULT_SLIPPAGE_TOLERANCE_BPS);
226
227 let mut collateral = 0;
228 let mut borrow = 0;
229 let estimated_amount: u64;
230 let mut next_sqrt_price = fusion_pool.sqrt_price;
231 let mut new_position_token = position_token;
232 let decrease_percent: u32;
233 let mut increase_swap_output_amount: u64 = 0;
234 let mut decrease_acceptable_swap_amount: u64 = 0;
235
236 let mut decrease_amount_in_position_token = if collateral_token == position_token {
237 decrease_amount
238 } else {
239 round(decrease_amount as f64 / position_to_borrowed_token_price) as u64
240 };
241
242 if reduce_only && decrease_amount_in_position_token > position_amount {
243 decrease_amount_in_position_token = position_amount;
244 }
245
246 decrease_percent = ((decrease_amount_in_position_token * HUNDRED_PERCENT as u64 / position_amount) as u32).min(HUNDRED_PERCENT);
247
248 if decrease_amount_in_position_token <= position_amount {
249 estimated_amount = position_amount * (HUNDRED_PERCENT - decrease_percent) as u64 / HUNDRED_PERCENT as u64;
250
251 if collateral_token == position_token {
252 if position_debt > 0 {
253 let amount_out = position_debt * decrease_percent as u64 / HUNDRED_PERCENT as u64;
254 let swap = swap_quote_by_output_token(amount_out, borrowed_token == TOKEN_A, 0, fusion_pool, tick_arrays, None, None)?;
255 next_sqrt_price = swap.next_sqrt_price;
256 decrease_acceptable_swap_amount = try_get_max_amount_with_slippage_tolerance(swap.token_est_in, slippage_tolerance_bps)?;
257 }
258 } else {
259 let amount_in = position_amount - estimated_amount;
260 let swap = swap_quote_by_input_token(amount_in, position_token == TOKEN_A, 0, fusion_pool, tick_arrays, None, None)?;
261 next_sqrt_price = swap.next_sqrt_price;
262 decrease_acceptable_swap_amount = try_get_min_amount_with_slippage_tolerance(swap.token_est_out, slippage_tolerance_bps)?;
263 }
264 } else {
265 new_position_token = if position_token == TOKEN_A { TOKEN_B } else { TOKEN_A };
266 let increase_amount = decrease_amount_in_position_token - position_amount;
267
268 if position_token == collateral_token {
269 borrow = ((increase_amount as f64 * (leverage - 1.0)) / leverage) as u64;
278 let borrow_with_fees_applied = apply_swap_fee(apply_tuna_protocol_fee(borrow, protocol_fee_rate, false)?, fusion_pool.fee_rate, false)?;
279
280 collateral = increase_amount - borrow_with_fees_applied;
281
282 let mut amount_in = 0;
284 if position_debt > 0 {
285 let swap =
286 swap_quote_by_output_token(position_debt, borrowed_token == TOKEN_A, 0, fusion_pool, tick_arrays.clone().into(), None, None)?;
287 amount_in = swap.token_est_in;
288 decrease_acceptable_swap_amount = try_get_max_amount_with_slippage_tolerance(amount_in, slippage_tolerance_bps)?;
289 }
290
291 amount_in += collateral + apply_tuna_protocol_fee(borrow, protocol_fee_rate, false)?;
293 if amount_in > 0 {
294 let swap = swap_quote_by_input_token(amount_in, position_token == TOKEN_A, 0, fusion_pool, tick_arrays, None, None)?;
295 next_sqrt_price = swap.next_sqrt_price;
296 increase_swap_output_amount = swap.token_est_out.saturating_sub(position_debt);
297 }
298
299 estimated_amount = increase_swap_output_amount;
301 } else {
302 let swap = swap_quote_by_input_token(position_amount, position_token == TOKEN_A, 0, fusion_pool, tick_arrays.clone().into(), None, None)?;
310 let decrease_amount_out = swap.token_est_out;
311 decrease_acceptable_swap_amount = try_get_min_amount_with_slippage_tolerance(swap.token_est_out, slippage_tolerance_bps)?;
312
313 borrow = ((increase_amount as f64 * (leverage - 1.0)) / leverage) as u64;
314 let borrow_with_fees_applied = apply_swap_fee(apply_tuna_protocol_fee(borrow, protocol_fee_rate, false)?, fusion_pool.fee_rate, false)?;
315
316 collateral = increase_amount - borrow_with_fees_applied;
317 collateral = ceil(collateral as f64 * position_to_borrowed_token_price) as u64;
318
319 let amount_in = position_amount + apply_tuna_protocol_fee(borrow, protocol_fee_rate, false)?;
321 if amount_in > 0 {
322 let swap = swap_quote_by_input_token(amount_in, position_token == TOKEN_A, 0, fusion_pool, tick_arrays, None, None)?;
323 next_sqrt_price = swap.next_sqrt_price;
324 increase_swap_output_amount = swap.token_est_out.saturating_sub(decrease_amount_out);
325 }
326
327 estimated_amount = increase_swap_output_amount + collateral;
329 }
330
331 collateral = reverse_apply_tuna_protocol_fee(collateral, protocol_fee_rate_on_collateral, false)?;
332 }
333
334 let (protocol_fee_a, protocol_fee_b) =
335 compute_tuna_protocol_fee(collateral_token, borrowed_token, collateral, borrow, protocol_fee_rate_on_collateral, protocol_fee_rate);
336
337 let new_price = sqrt_price_to_price(next_sqrt_price.into(), 1, 1);
338 let price_impact = (new_price / price - 1.0).abs() * 100.0;
339
340 let increase_min_swap_output_amount = try_get_min_amount_with_slippage_tolerance(increase_swap_output_amount, slippage_tolerance_bps)?;
341
342 Ok(DecreaseSpotPositionQuoteResult {
343 decrease_percent,
344 collateral_token,
345 position_token: new_position_token,
346 collateral,
347 borrow,
348 decrease_acceptable_swap_amount,
349 increase_min_swap_output_amount,
350 estimated_amount,
351 protocol_fee_a,
352 protocol_fee_b,
353 price_impact,
354 })
355}
356
357#[cfg_attr(feature = "wasm", wasm_expose)]
368pub fn get_liquidation_price(position_token: u8, amount: f64, debt: f64, liquidation_threshold: f64) -> Result<f64, CoreError> {
369 if debt < 0.0 || amount < 0.0 {
370 return Err(INVALID_ARGUMENTS);
371 }
372
373 if liquidation_threshold <= 0.0 || liquidation_threshold >= 1.0 {
374 return Err(INVALID_ARGUMENTS);
375 }
376
377 if debt == 0.0 || amount == 0.0 {
378 return Ok(0.0);
379 }
380
381 if position_token == TOKEN_A {
382 Ok(debt / (amount * liquidation_threshold))
383 } else {
384 Ok((amount * liquidation_threshold) / debt)
385 }
386}
387
388#[cfg_attr(feature = "wasm", wasm_expose)]
407pub fn get_tradable_amount(
408 collateral_token: u8,
409 available_balance: u64,
410 leverage: f64,
411 new_position_token: u8,
412 reduce_only: bool,
413 position_token: u8,
414 position_amount: u64,
415 position_debt: u64,
416 protocol_fee_rate: u32,
417 protocol_fee_rate_on_collateral: u32,
418 fusion_pool: FusionPoolFacade,
419 tick_arrays: TickArrays,
420) -> Result<u64, CoreError> {
421 if collateral_token > TOKEN_B || position_token > TOKEN_B {
422 return Err(INVALID_ARGUMENTS.into());
423 }
424
425 if leverage < 1.0 {
426 return Err(INVALID_ARGUMENTS.into());
427 }
428
429 if protocol_fee_rate >= HUNDRED_PERCENT {
430 return Err(INVALID_ARGUMENTS.into());
431 }
432
433 if protocol_fee_rate_on_collateral >= HUNDRED_PERCENT {
434 return Err(INVALID_ARGUMENTS.into());
435 }
436
437 if position_amount == 0 && new_position_token != position_token {
438 return Err(INVALID_ARGUMENTS.into());
439 }
440
441 let add_leverage = |collateral: u64| -> Result<u64, CoreError> {
445 let mut collateral = apply_tuna_protocol_fee(collateral, protocol_fee_rate_on_collateral, false)?;
446 if collateral_token != new_position_token {
447 collateral = apply_swap_fee(collateral, fusion_pool.fee_rate, false)?;
448 }
449
450 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);
451 let total = (collateral as f64 / (1.0 - (fee_multiplier * (leverage - 1.0)) / leverage)) as u64;
452 Ok(total)
453 };
454
455 let available_to_trade = if new_position_token == position_token {
456 add_leverage(available_balance)?
457 } else {
458 let price = sqrt_price_to_price(fusion_pool.sqrt_price.into(), 1, 1);
459 let position_to_opposite_token_price = if position_token == TOKEN_A { price } else { 1.0 / price };
460
461 if reduce_only {
462 if collateral_token == position_token {
463 position_amount
464 } else {
465 round(position_amount as f64 * position_to_opposite_token_price) as u64
466 }
467 } else {
468 let position_amount_in_collateral_token = if collateral_token == position_token {
469 position_amount
470 } else {
471 round(position_amount as f64 * position_to_opposite_token_price) as u64
472 };
473
474 let position_collateral = if collateral_token == position_token {
475 let swap_in = if position_debt > 0 {
476 swap_quote_by_output_token(position_debt, position_token == TOKEN_B, 0, fusion_pool, tick_arrays, None, None)?.token_est_in
477 } else {
478 0
479 };
480 position_amount - swap_in
481 } else {
482 if position_amount > 0 {
483 let swap_quote = swap_quote_by_input_token(position_amount, position_token == TOKEN_A, 0, fusion_pool, tick_arrays, None, None)?;
484 swap_quote.token_est_out - position_debt
485 } else {
486 0
487 }
488 };
489
490 position_amount_in_collateral_token + add_leverage(available_balance + position_collateral)?
492 }
493 };
494
495 Ok(available_to_trade)
496}
497
498pub fn apply_tuna_protocol_fee(amount: u64, protocol_fee_rate: u32, round_up: bool) -> Result<u64, CoreError> {
499 try_mul_div(amount, HUNDRED_PERCENT as u128 - protocol_fee_rate as u128, HUNDRED_PERCENT as u128, round_up)
500}
501
502pub fn reverse_apply_tuna_protocol_fee(amount: u64, protocol_fee_rate: u32, round_up: bool) -> Result<u64, CoreError> {
503 try_mul_div(amount, HUNDRED_PERCENT as u128, HUNDRED_PERCENT as u128 - protocol_fee_rate as u128, round_up)
504}
505
506pub fn apply_swap_fee(amount: u64, fee_rate: u16, round_up: bool) -> Result<u64, CoreError> {
507 try_mul_div(amount, 1_000_000 - fee_rate as u128, 1_000_000, round_up)
508}
509
510pub fn reverse_apply_swap_fee(amount: u64, fee_rate: u16, round_up: bool) -> Result<u64, CoreError> {
511 try_mul_div(amount, 1_000_000, 1_000_000 - fee_rate as u128, round_up)
512}
513
514pub fn compute_tuna_protocol_fee(
515 collateral_token: u8,
516 borrowed_token: u8,
517 collateral: u64,
518 borrow: u64,
519 protocol_fee_rate_on_collateral: u32,
520 protocol_fee_rate: u32,
521) -> (u64, u64) {
522 let collateral_a = if collateral_token == TOKEN_A { collateral } else { 0 };
523 let collateral_b = if collateral_token == TOKEN_B { collateral } else { 0 };
524 let borrow_a = if borrowed_token == TOKEN_A { borrow } else { 0 };
525 let borrow_b = if borrowed_token == TOKEN_B { borrow } else { 0 };
526
527 let protocol_fee_a = ((collateral_a as u128 * protocol_fee_rate_on_collateral as u128 + borrow_a as u128 * protocol_fee_rate as u128)
528 / HUNDRED_PERCENT as u128) as u64;
529 let protocol_fee_b = ((collateral_b as u128 * protocol_fee_rate_on_collateral as u128 + borrow_b as u128 * protocol_fee_rate as u128)
530 / HUNDRED_PERCENT as u128) as u64;
531
532 (protocol_fee_a, protocol_fee_b)
533}
534
535#[cfg(all(test, not(feature = "wasm")))]
536mod tests {
537 use super::*;
538 use crate::assert_approx_eq;
539 use fusionamm_core::{
540 get_tick_array_start_tick_index, price_to_sqrt_price, sqrt_price_to_tick_index, TickArrayFacade, TickFacade, TICK_ARRAY_SIZE,
541 };
542
543 fn test_fusion_pool(sqrt_price: u128) -> FusionPoolFacade {
544 let tick_current_index = sqrt_price_to_tick_index(sqrt_price);
545 FusionPoolFacade {
546 tick_current_index,
547 fee_rate: 3000,
548 liquidity: 10000000000000,
549 sqrt_price,
550 tick_spacing: 2,
551 ..FusionPoolFacade::default()
552 }
553 }
554
555 fn test_tick(liquidity_net: i128) -> TickFacade {
556 TickFacade {
557 initialized: true,
558 liquidity_net,
559 ..TickFacade::default()
560 }
561 }
562
563 fn test_tick_array(start_tick_index: i32) -> TickArrayFacade {
564 TickArrayFacade {
565 start_tick_index,
566 ticks: [test_tick(0); TICK_ARRAY_SIZE],
567 }
568 }
569
570 fn test_tick_arrays(fusion_pool: FusionPoolFacade) -> TickArrays {
571 let tick_spacing = fusion_pool.tick_spacing;
572 let tick_current_index = sqrt_price_to_tick_index(fusion_pool.sqrt_price);
573 let tick_array_start_index = get_tick_array_start_tick_index(tick_current_index, tick_spacing);
574
575 [
576 test_tick_array(tick_array_start_index),
577 test_tick_array(tick_array_start_index + TICK_ARRAY_SIZE as i32 * tick_spacing as i32),
578 test_tick_array(tick_array_start_index + TICK_ARRAY_SIZE as i32 * tick_spacing as i32 * 2),
579 test_tick_array(tick_array_start_index - TICK_ARRAY_SIZE as i32 * tick_spacing as i32),
580 test_tick_array(tick_array_start_index - TICK_ARRAY_SIZE as i32 * tick_spacing as i32 * 2),
581 ]
582 .into()
583 }
584
585 #[test]
586 fn test_get_liquidation_price() {
587 assert_eq!(get_liquidation_price(TOKEN_A, 5.0, 0.0, 0.85), Ok(0.0));
588 assert_eq!(get_liquidation_price(TOKEN_A, 0.0, 5.0, 0.85), Ok(0.0));
589 assert_eq!(get_liquidation_price(TOKEN_A, 5.0, 800.0, 0.85), Ok(188.23529411764707));
590 assert_eq!(get_liquidation_price(TOKEN_B, 1000.0, 4.0, 0.85), Ok(212.5));
591 }
592
593 #[test]
594 fn increase_long_position_providing_token_a() {
595 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
596 let fusion_pool = test_fusion_pool(sqrt_price);
597
598 let quote = get_increase_spot_position_quote(
599 5_000_000_000,
600 TOKEN_A,
601 TOKEN_A,
602 5.0,
603 Some(0),
604 HUNDRED_PERCENT / 100,
605 HUNDRED_PERCENT / 200,
606 fusion_pool,
607 test_tick_arrays(fusion_pool),
608 )
609 .unwrap();
610
611 assert_eq!(quote.collateral, 1057165829);
612 assert_eq!(quote.borrow, 800000000);
613 assert_eq!(quote.min_swap_output_amount, 3_947_423_011);
614 assert_eq!(quote.estimated_amount, 4_999_303_011);
615 assert_eq!(quote.protocol_fee_a, 5285829);
616 assert_eq!(quote.protocol_fee_b, 8000000);
617 assert_eq!(quote.price_impact, 0.03531617625702754);
618 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);
619 }
620
621 #[test]
622 fn increase_long_position_providing_token_b() {
623 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
624 let fusion_pool = test_fusion_pool(sqrt_price);
625
626 let quote = get_increase_spot_position_quote(
627 5000_000_000,
628 TOKEN_B,
629 TOKEN_A,
630 5.0,
631 Some(0),
632 HUNDRED_PERCENT / 100,
633 HUNDRED_PERCENT / 200,
634 fusion_pool,
635 test_tick_arrays(fusion_pool),
636 )
637 .unwrap();
638
639 assert_eq!(quote.collateral, 1060346869);
640 assert_eq!(quote.borrow, 4000000000);
641 assert_eq!(quote.estimated_amount, 24_972_080_293);
642 assert_eq!(quote.protocol_fee_a, 0);
643 assert_eq!(quote.protocol_fee_b, 45301734);
644 assert_eq!(quote.price_impact, 0.22373179716579372);
645 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);
646 }
647
648 #[test]
649 fn increase_short_position_providing_a() {
650 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
651 let fusion_pool = test_fusion_pool(sqrt_price);
652
653 let quote = get_increase_spot_position_quote(
654 5_000_000_000,
655 TOKEN_A,
656 TOKEN_B,
657 5.0,
658 Some(0),
659 HUNDRED_PERCENT / 100,
660 HUNDRED_PERCENT / 200,
661 fusion_pool,
662 test_tick_arrays(fusion_pool),
663 )
664 .unwrap();
665
666 assert_eq!(quote.collateral, 1060346869);
667 assert_eq!(quote.borrow, 4000000000);
668 assert_eq!(quote.estimated_amount, 999_776_441);
669 assert_eq!(quote.protocol_fee_a, 45301734);
670 assert_eq!(quote.protocol_fee_b, 0);
671 assert_eq!(quote.price_impact, 0.04470636400017991);
672 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);
673 }
674
675 #[test]
676 fn increase_short_position_providing_b() {
677 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
678 let fusion_pool = test_fusion_pool(sqrt_price);
679
680 let quote = get_increase_spot_position_quote(
681 5000_000_000,
682 TOKEN_B,
683 TOKEN_B,
684 5.0,
685 Some(0),
686 HUNDRED_PERCENT / 100,
687 HUNDRED_PERCENT / 200,
688 fusion_pool,
689 test_tick_arrays(fusion_pool),
690 )
691 .unwrap();
692
693 assert_eq!(quote.collateral, 1057165829);
694 assert_eq!(quote.borrow, 20000000000);
695 assert_eq!(quote.estimated_amount, 4996_517_564);
696 assert_eq!(quote.protocol_fee_a, 200000000);
697 assert_eq!(quote.protocol_fee_b, 5285829);
698 assert_eq!(quote.price_impact, 0.17633175413067637);
699 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);
700 }
701
702 #[test]
703 fn increase_quote_with_slippage() {
704 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
705 let fusion_pool = test_fusion_pool(sqrt_price);
706
707 let quote =
709 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();
710 assert_eq!(quote.min_swap_output_amount, 899_994);
711
712 let quote =
714 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();
715 assert_eq!(quote.min_swap_output_amount, 999_994);
716 }
717
718 #[test]
719 fn decrease_non_leveraged_long_position_providing_a_reduce_only() {
720 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
721 let fusion_pool = test_fusion_pool(sqrt_price);
722
723 let quote = get_decrease_spot_position_quote(
724 1_000_000_000,
725 TOKEN_A,
726 1.0,
727 true,
728 Some(0),
729 TOKEN_A,
730 5_000_000_000, 0, HUNDRED_PERCENT / 100,
733 HUNDRED_PERCENT / 200,
734 fusion_pool,
735 test_tick_arrays(fusion_pool),
736 )
737 .unwrap();
738
739 assert_eq!(quote.decrease_percent, 200000);
740 assert_eq!(quote.collateral_token, TOKEN_A);
741 assert_eq!(quote.position_token, TOKEN_A);
742 assert_eq!(quote.estimated_amount, 4_000_000_000);
743 assert_eq!(quote.protocol_fee_a, 0);
744 assert_eq!(quote.protocol_fee_b, 0);
745 assert_eq!(quote.price_impact, 0.0);
746 assert_eq!(quote.estimated_amount as f64 / (quote.estimated_amount as f64 - (quote.borrow as f64 / 1000.0) * 200.0), 1.0);
747 }
748
749 #[test]
750 fn decrease_non_leveraged_long_position_providing_b_reduce_only() {
751 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
752 let fusion_pool = test_fusion_pool(sqrt_price);
753
754 let quote = get_decrease_spot_position_quote(
755 200_000_000,
756 TOKEN_B,
757 1.0,
758 true,
759 Some(0),
760 TOKEN_A,
761 5_000_000_000, 0, HUNDRED_PERCENT / 100,
764 HUNDRED_PERCENT / 200,
765 fusion_pool,
766 test_tick_arrays(fusion_pool),
767 )
768 .unwrap();
769
770 assert_eq!(quote.decrease_percent, 200000);
771 assert_eq!(quote.collateral_token, TOKEN_B);
772 assert_eq!(quote.position_token, TOKEN_A);
773 assert_eq!(quote.estimated_amount, 4_000_000_000);
774 assert_eq!(quote.protocol_fee_a, 0);
775 assert_eq!(quote.protocol_fee_b, 0);
776 assert_eq!(quote.price_impact, 0.008916842709072448);
777 assert_eq!(quote.estimated_amount as f64 / (quote.estimated_amount as f64 - (quote.borrow as f64 / 1000.0) * 200.0), 1.0);
778 }
779
780 #[test]
781 fn decrease_long_position_providing_a_reduce_only() {
782 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
783 let fusion_pool = test_fusion_pool(sqrt_price);
784
785 let quote = get_decrease_spot_position_quote(
786 1_000_000_000,
787 TOKEN_A,
788 5.0,
789 true,
790 Some(0),
791 TOKEN_A,
792 5_000_000_000, 800_000_000, HUNDRED_PERCENT / 100,
795 HUNDRED_PERCENT / 200,
796 fusion_pool,
797 test_tick_arrays(fusion_pool),
798 )
799 .unwrap();
800
801 assert_eq!(quote.decrease_percent, 200000);
802 assert_eq!(quote.collateral_token, TOKEN_A);
803 assert_eq!(quote.position_token, TOKEN_A);
804 assert_eq!(quote.collateral, 0);
805 assert_eq!(quote.borrow, 0);
806 assert_eq!(quote.estimated_amount, 4_000_000_000);
807 assert_eq!(quote.protocol_fee_a, 0);
808 assert_eq!(quote.protocol_fee_b, 0);
809 assert_eq!(quote.price_impact, 0.007155289528004705);
810
811 let quote = get_decrease_spot_position_quote(
812 6_000_000_000,
813 TOKEN_A,
814 5.0,
815 true,
816 Some(0),
817 TOKEN_A,
818 5_000_000_000, 800_000_000, HUNDRED_PERCENT / 100,
821 HUNDRED_PERCENT / 200,
822 fusion_pool,
823 test_tick_arrays(fusion_pool),
824 )
825 .unwrap();
826
827 assert_eq!(quote.decrease_percent, HUNDRED_PERCENT);
828 assert_eq!(quote.collateral, 0);
829 assert_eq!(quote.borrow, 0);
830 assert_eq!(quote.estimated_amount, 0);
831 }
832
833 #[test]
834 fn decrease_long_position_providing_b_reduce_only() {
835 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
836 let fusion_pool = test_fusion_pool(sqrt_price);
837
838 let quote = get_decrease_spot_position_quote(
839 200_000_000,
840 TOKEN_B,
841 5.0,
842 true,
843 Some(0),
844 TOKEN_A,
845 5_000_000_000, 800_000_000, HUNDRED_PERCENT / 100,
848 HUNDRED_PERCENT / 200,
849 fusion_pool,
850 test_tick_arrays(fusion_pool),
851 )
852 .unwrap();
853
854 assert_eq!(quote.decrease_percent, 200000);
855 assert_eq!(quote.collateral_token, TOKEN_B);
856 assert_eq!(quote.position_token, TOKEN_A);
857 assert_eq!(quote.collateral, 0);
858 assert_eq!(quote.borrow, 0);
859 assert_eq!(quote.estimated_amount, 4000000000);
860 assert_eq!(quote.protocol_fee_a, 0);
861 assert_eq!(quote.protocol_fee_b, 0);
862 assert_eq!(quote.price_impact, 0.008916842709072448);
863
864 let quote = get_decrease_spot_position_quote(
865 1200_000_000,
866 TOKEN_B,
867 5.0,
868 true,
869 Some(0),
870 TOKEN_A,
871 5_000_000_000, 800_000_000, HUNDRED_PERCENT / 100,
874 HUNDRED_PERCENT / 200,
875 fusion_pool,
876 test_tick_arrays(fusion_pool),
877 )
878 .unwrap();
879
880 assert_eq!(quote.decrease_percent, HUNDRED_PERCENT);
881 assert_eq!(quote.collateral, 0);
882 assert_eq!(quote.borrow, 0);
883 assert_eq!(quote.estimated_amount, 0);
884 }
885
886 #[test]
887 fn decrease_long_position_providing_a_without_leverage() {
888 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
889 let fusion_pool = test_fusion_pool(sqrt_price);
890
891 let quote = get_decrease_spot_position_quote(
892 6_000_000_000,
893 TOKEN_A,
894 1.0,
895 false,
896 Some(100),
897 TOKEN_A,
898 5_000_000_000, 800_000_000, HUNDRED_PERCENT / 100,
901 HUNDRED_PERCENT / 200,
902 fusion_pool,
903 test_tick_arrays(fusion_pool),
904 )
905 .unwrap();
906
907 assert_eq!(quote.decrease_percent, HUNDRED_PERCENT);
908 assert_eq!(quote.collateral_token, TOKEN_A);
909 assert_eq!(quote.position_token, TOKEN_B);
910 assert_eq!(quote.collateral, 1005025125);
911 assert_eq!(quote.borrow, 0);
912 assert_eq!(quote.estimated_amount, 199_319_781);
913 assert_eq!(quote.decrease_acceptable_swap_amount, 4_052_881_482);
914 assert_eq!(quote.increase_min_swap_output_amount, 197_326_583);
915 assert_eq!(quote.protocol_fee_a, 5025125);
916 assert_eq!(quote.protocol_fee_b, 0);
917 assert_eq!(quote.price_impact, 0.044685946117650754);
918 }
919
920 #[test]
921 fn decrease_long_position_providing_a() {
922 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
923 let fusion_pool = test_fusion_pool(sqrt_price);
924
925 let quote = get_decrease_spot_position_quote(
926 6_000_000_000,
927 TOKEN_A,
928 5.0,
929 false,
930 Some(100),
931 TOKEN_A,
932 5_000_000_000, 800_000_000, HUNDRED_PERCENT / 100,
935 HUNDRED_PERCENT / 200,
936 fusion_pool,
937 test_tick_arrays(fusion_pool),
938 )
939 .unwrap();
940
941 assert_eq!(quote.decrease_percent, HUNDRED_PERCENT);
942 assert_eq!(quote.collateral_token, TOKEN_A);
943 assert_eq!(quote.position_token, TOKEN_B);
944 assert_eq!(quote.collateral, 211433165);
945 assert_eq!(quote.borrow, 800000000);
946 assert_eq!(quote.estimated_amount, 199_793_344);
947 assert_eq!(quote.decrease_acceptable_swap_amount, 4_052_881_482);
948 assert_eq!(quote.increase_min_swap_output_amount, 197_795_410);
949 assert_eq!(quote.protocol_fee_a, 1057165);
950 assert_eq!(quote.protocol_fee_b, 8000000);
951 assert_eq!(quote.price_impact, 0.04470711974918773);
952 }
953
954 #[test]
955 fn decrease_long_position_providing_b() {
956 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
957 let fusion_pool = test_fusion_pool(sqrt_price);
958
959 let quote = get_decrease_spot_position_quote(
960 1200_000_000,
961 TOKEN_B,
962 5.0,
963 false,
964 Some(0),
965 TOKEN_A,
966 5_000_000_000, 800_000_000, HUNDRED_PERCENT / 100,
969 HUNDRED_PERCENT / 200,
970 fusion_pool,
971 test_tick_arrays(fusion_pool),
972 )
973 .unwrap();
974
975 assert_eq!(quote.decrease_percent, HUNDRED_PERCENT);
976 assert_eq!(quote.collateral_token, TOKEN_B);
977 assert_eq!(quote.position_token, TOKEN_B);
978 assert_eq!(quote.collateral, 42286633);
979 assert_eq!(quote.borrow, 800_000_000);
980 assert_eq!(quote.estimated_amount, 199_924_036);
981 assert_eq!(quote.decrease_acceptable_swap_amount, 996_777_780);
982 assert_eq!(quote.increase_min_swap_output_amount, 157_848_836);
983 assert_eq!(quote.protocol_fee_a, 0);
984 assert_eq!(quote.protocol_fee_b, 8211433);
985 assert_eq!(quote.price_impact, 0.05162980632488212);
986 }
987
988 #[test]
989 fn decrease_non_leveraged_short_position_providing_a() {
990 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
991 let fusion_pool = test_fusion_pool(sqrt_price);
992
993 let quote = get_decrease_spot_position_quote(
994 6_000_000_000,
995 TOKEN_A,
996 1.0,
997 false,
998 Some(0),
999 TOKEN_B,
1000 1000_000_000, 0, HUNDRED_PERCENT / 100,
1003 HUNDRED_PERCENT / 200,
1004 fusion_pool,
1005 test_tick_arrays(fusion_pool),
1006 )
1007 .unwrap();
1008
1009 assert_eq!(quote.decrease_percent, HUNDRED_PERCENT);
1010 assert_eq!(quote.collateral_token, TOKEN_A);
1011 assert_eq!(quote.position_token, TOKEN_A);
1012 assert_eq!(quote.collateral, 1005025125);
1013 assert_eq!(quote.borrow, 0);
1014 assert_eq!(quote.estimated_amount, 1_000_000_000);
1015 assert_eq!(quote.protocol_fee_a, 5025125);
1016 assert_eq!(quote.protocol_fee_b, 0);
1017 assert_eq!(quote.price_impact, 0.044592165429757635);
1018 }
1019
1020 #[test]
1021 fn decrease_short_position_providing_a() {
1022 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
1023 let fusion_pool = test_fusion_pool(sqrt_price);
1024
1025 let quote = get_decrease_spot_position_quote(
1026 6_000_000_000,
1027 TOKEN_A,
1028 5.0,
1029 false,
1030 Some(0),
1031 TOKEN_B,
1032 1000_000_000, 4_000_000_000, HUNDRED_PERCENT / 100,
1035 HUNDRED_PERCENT / 200,
1036 fusion_pool,
1037 test_tick_arrays(fusion_pool),
1038 )
1039 .unwrap();
1040
1041 assert_eq!(quote.decrease_percent, HUNDRED_PERCENT);
1042 assert_eq!(quote.collateral_token, TOKEN_A);
1043 assert_eq!(quote.position_token, TOKEN_A);
1044 assert_eq!(quote.collateral, 211433165);
1045 assert_eq!(quote.borrow, 160000000);
1046 assert_eq!(quote.estimated_amount, 999_620_182);
1047 assert_eq!(quote.decrease_acceptable_swap_amount, 4_983_888_901);
1048 assert_eq!(quote.increase_min_swap_output_amount, 789_244_182);
1049 assert_eq!(quote.protocol_fee_a, 2657165);
1050 assert_eq!(quote.protocol_fee_b, 0);
1051 assert_eq!(quote.price_impact, 0.051656476403882934);
1052 }
1053
1054 #[test]
1055 fn decrease_short_position_providing_b() {
1056 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
1057 let fusion_pool = test_fusion_pool(sqrt_price);
1058
1059 let quote = get_decrease_spot_position_quote(
1060 1_200_000_000,
1061 TOKEN_B,
1062 5.0,
1063 false,
1064 Some(0),
1065 TOKEN_B,
1066 1000_000_000, 4_000_000_000, HUNDRED_PERCENT / 100,
1069 HUNDRED_PERCENT / 200,
1070 fusion_pool,
1071 test_tick_arrays(fusion_pool),
1072 )
1073 .unwrap();
1074
1075 assert_eq!(quote.decrease_percent, HUNDRED_PERCENT);
1076 assert_eq!(quote.collateral_token, TOKEN_B);
1077 assert_eq!(quote.position_token, TOKEN_A);
1078 assert_eq!(quote.collateral, 42_286_633);
1079 assert_eq!(quote.borrow, 160000000);
1080 assert_eq!(quote.estimated_amount, 998_966_727);
1081 assert_eq!(quote.protocol_fee_a, 1600000);
1082 assert_eq!(quote.protocol_fee_b, 211433);
1083 assert_eq!(quote.price_impact, 0.04472711596017476);
1084 }
1085
1086 #[test]
1087 fn tradable_amount_for_1x_long_position_providing_b() {
1088 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
1089 let fusion_pool = test_fusion_pool(sqrt_price);
1090 let tick_arrays = test_tick_arrays(fusion_pool);
1091
1092 let collateral_token = TOKEN_B;
1093 let position_token = TOKEN_A;
1094 let leverage = 1.0;
1095 let protocol_fee_rate = HUNDRED_PERCENT / 100;
1096 let protocol_fee_rate_on_collateral = HUNDRED_PERCENT / 200;
1097 let available_balance = 200_000_000;
1098
1099 let tradable_amount = get_tradable_amount(
1100 collateral_token,
1101 available_balance,
1102 leverage,
1103 position_token,
1104 false,
1105 position_token,
1106 0,
1107 0,
1108 protocol_fee_rate,
1109 protocol_fee_rate_on_collateral,
1110 fusion_pool,
1111 tick_arrays.clone(),
1112 )
1113 .unwrap();
1114 assert_eq!(tradable_amount, 198403000);
1115
1116 let quote = get_increase_spot_position_quote(
1117 tradable_amount,
1118 collateral_token,
1119 position_token,
1120 leverage,
1121 Some(0),
1122 protocol_fee_rate,
1123 protocol_fee_rate_on_collateral,
1124 fusion_pool,
1125 tick_arrays,
1126 )
1127 .unwrap();
1128 assert_eq!(quote.collateral, available_balance);
1129 }
1130
1131 #[test]
1132 fn tradable_amount_for_5x_long_position_providing_b() {
1133 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
1134 let fusion_pool = test_fusion_pool(sqrt_price);
1135 let tick_arrays = test_tick_arrays(fusion_pool);
1136
1137 let collateral_token = TOKEN_B;
1138 let position_token = TOKEN_A;
1139 let leverage = 5.0;
1140 let protocol_fee_rate = HUNDRED_PERCENT / 100;
1141 let protocol_fee_rate_on_collateral = HUNDRED_PERCENT / 200;
1142 let available_balance = 10_000_000;
1143
1144 let tradable_amount = get_tradable_amount(
1145 collateral_token,
1146 available_balance,
1147 leverage,
1148 position_token,
1149 false,
1150 position_token,
1151 0,
1152 0,
1153 protocol_fee_rate,
1154 protocol_fee_rate_on_collateral,
1155 fusion_pool,
1156 tick_arrays.clone(),
1157 )
1158 .unwrap();
1159 assert_eq!(tradable_amount, 47154380);
1160
1161 let quote = get_increase_spot_position_quote(
1162 tradable_amount,
1163 collateral_token,
1164 position_token,
1165 leverage,
1166 Some(0),
1167 protocol_fee_rate,
1168 protocol_fee_rate_on_collateral,
1169 fusion_pool,
1170 tick_arrays,
1171 )
1172 .unwrap();
1173 assert_eq!(quote.collateral, available_balance + 1);
1175 }
1176
1177 #[test]
1178 fn tradable_amount_for_5x_long_position_providing_a() {
1179 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
1180 let fusion_pool = test_fusion_pool(sqrt_price);
1181 let tick_arrays = test_tick_arrays(fusion_pool);
1182
1183 let collateral_token = TOKEN_A;
1184 let position_token = TOKEN_A;
1185 let leverage = 5.0;
1186 let protocol_fee_rate = HUNDRED_PERCENT / 100;
1187 let protocol_fee_rate_on_collateral = HUNDRED_PERCENT / 200;
1188 let available_balance = 1_000_000_000;
1189
1190 let tradable_amount = get_tradable_amount(
1191 collateral_token,
1192 available_balance,
1193 leverage,
1194 position_token,
1195 false,
1196 position_token,
1197 0,
1198 0,
1199 protocol_fee_rate,
1200 protocol_fee_rate_on_collateral,
1201 fusion_pool,
1202 tick_arrays.clone(),
1203 )
1204 .unwrap();
1205 assert_eq!(tradable_amount, 4729626953);
1206
1207 let quote = get_increase_spot_position_quote(
1208 tradable_amount,
1209 collateral_token,
1210 position_token,
1211 leverage,
1212 Some(0),
1213 protocol_fee_rate,
1214 protocol_fee_rate_on_collateral,
1215 fusion_pool,
1216 tick_arrays,
1217 )
1218 .unwrap();
1219 assert_eq!(quote.collateral, available_balance);
1220 }
1222
1223 #[test]
1224 fn tradable_amount_for_5x_short_position_providing_b() {
1225 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
1226 let fusion_pool = test_fusion_pool(sqrt_price);
1227 let tick_arrays = test_tick_arrays(fusion_pool);
1228
1229 let collateral_token = TOKEN_B;
1230 let position_token = TOKEN_B;
1231 let leverage = 5.0;
1232 let protocol_fee_rate = HUNDRED_PERCENT / 100;
1233 let protocol_fee_rate_on_collateral = HUNDRED_PERCENT / 200;
1234 let available_balance = 200_000_000;
1235
1236 let tradable_amount = get_tradable_amount(
1237 collateral_token,
1238 available_balance,
1239 leverage,
1240 position_token,
1241 false,
1242 position_token,
1243 0,
1244 0,
1245 protocol_fee_rate,
1246 protocol_fee_rate_on_collateral,
1247 fusion_pool,
1248 tick_arrays.clone(),
1249 )
1250 .unwrap();
1251 assert_eq!(tradable_amount, 945925390);
1252
1253 let quote = get_increase_spot_position_quote(
1254 tradable_amount,
1255 collateral_token,
1256 position_token,
1257 leverage,
1258 Some(0),
1259 protocol_fee_rate,
1260 protocol_fee_rate_on_collateral,
1261 fusion_pool,
1262 tick_arrays,
1263 )
1264 .unwrap();
1265 assert_eq!(quote.collateral, available_balance + 1);
1267 }
1269
1270 #[test]
1271 fn tradable_amount_for_reducing_existing_long_position() {
1272 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
1273 let fusion_pool = test_fusion_pool(sqrt_price);
1274 let tick_arrays = test_tick_arrays(fusion_pool);
1275
1276 for i in 0..2 {
1277 let collateral_token = if i == 0 { TOKEN_A } else { TOKEN_B };
1278 let position_token = if i == 0 { TOKEN_A } else { TOKEN_B };
1279 let leverage = 5.0;
1280 let reduce_only = true;
1281 let position_amount = 5_000_000_000;
1282 let position_debt = 800_000_000;
1283 let protocol_fee_rate = HUNDRED_PERCENT / 100;
1284 let protocol_fee_rate_on_collateral = HUNDRED_PERCENT / 200;
1285 let available_balance = 50_000_000_000;
1286
1287 let tradable_amount = get_tradable_amount(
1288 collateral_token,
1289 available_balance,
1290 leverage,
1291 if position_token == TOKEN_A { TOKEN_B } else { TOKEN_A },
1292 reduce_only,
1293 position_token,
1294 position_amount,
1295 position_debt,
1296 protocol_fee_rate,
1297 protocol_fee_rate_on_collateral,
1298 fusion_pool,
1299 tick_arrays.clone(),
1300 )
1301 .unwrap();
1302 assert_eq!(tradable_amount, 5_000_000_000);
1303
1304 let quote = get_decrease_spot_position_quote(
1305 tradable_amount,
1306 collateral_token,
1307 5.0,
1308 reduce_only,
1309 Some(0),
1310 position_token,
1311 position_amount,
1312 position_debt,
1313 protocol_fee_rate,
1314 protocol_fee_rate_on_collateral,
1315 fusion_pool,
1316 tick_arrays.clone(),
1317 )
1318 .unwrap();
1319
1320 assert_eq!(quote.decrease_percent, HUNDRED_PERCENT);
1321 }
1322 }
1323
1324 #[test]
1325 fn tradable_amount_when_inverting_long_position_providing_a() {
1326 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
1327 let fusion_pool = test_fusion_pool(sqrt_price);
1328 let tick_arrays = test_tick_arrays(fusion_pool);
1329
1330 let collateral_token = TOKEN_A;
1331 let position_token = TOKEN_A;
1332 let leverage = 5.0;
1333 let reduce_only = false;
1334 let position_amount = 5_000_000_000;
1335 let position_debt = 800_000_000;
1336 let protocol_fee_rate = HUNDRED_PERCENT / 100;
1337 let protocol_fee_rate_on_collateral = HUNDRED_PERCENT / 200;
1338 let available_balance = 2_000_000_000;
1339
1340 let tradable_amount = get_tradable_amount(
1341 collateral_token,
1342 available_balance,
1343 leverage,
1344 if position_token == TOKEN_A { TOKEN_B } else { TOKEN_A },
1345 reduce_only,
1346 position_token,
1347 position_amount,
1348 position_debt,
1349 protocol_fee_rate,
1350 protocol_fee_rate_on_collateral,
1351 fusion_pool,
1352 tick_arrays.clone(),
1353 )
1354 .unwrap();
1355 assert_eq!(tradable_amount, 19_086_173_788);
1356
1357 let quote = get_decrease_spot_position_quote(
1358 tradable_amount,
1359 collateral_token,
1360 5.0,
1361 reduce_only,
1362 Some(0),
1363 position_token,
1364 position_amount,
1365 position_debt,
1366 protocol_fee_rate,
1367 protocol_fee_rate_on_collateral,
1368 fusion_pool,
1369 tick_arrays.clone(),
1370 )
1371 .unwrap();
1372
1373 assert_eq!(quote.decrease_percent, HUNDRED_PERCENT);
1374 assert_eq!(quote.collateral, 2_978_284_319);
1375 }
1376
1377 #[test]
1378 fn tradable_amount_when_inverting_long_position_providing_b() {
1379 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
1380 let fusion_pool = test_fusion_pool(sqrt_price);
1381 let tick_arrays = test_tick_arrays(fusion_pool);
1382
1383 let collateral_token = TOKEN_B;
1384 let position_token = TOKEN_A;
1385 let leverage = 5.0;
1386 let reduce_only = false;
1387 let position_amount = 5_000_000_000;
1388 let position_debt = 800_000_000;
1389 let protocol_fee_rate = HUNDRED_PERCENT / 100;
1390 let protocol_fee_rate_on_collateral = HUNDRED_PERCENT / 200;
1391 let available_balance = 500_000_000;
1392
1393 let tradable_amount = get_tradable_amount(
1394 collateral_token,
1395 available_balance,
1396 leverage,
1397 if position_token == TOKEN_A { TOKEN_B } else { TOKEN_A },
1398 reduce_only,
1399 position_token,
1400 position_amount,
1401 position_debt,
1402 protocol_fee_rate,
1403 protocol_fee_rate_on_collateral,
1404 fusion_pool,
1405 tick_arrays.clone(),
1406 )
1407 .unwrap();
1408 assert_eq!(tradable_amount, 4_295_498_968);
1409
1410 let quote = get_decrease_spot_position_quote(
1411 tradable_amount,
1412 collateral_token,
1413 5.0,
1414 reduce_only,
1415 Some(0),
1416 position_token,
1417 position_amount,
1418 position_debt,
1419 protocol_fee_rate,
1420 protocol_fee_rate_on_collateral,
1421 fusion_pool,
1422 tick_arrays.clone(),
1423 )
1424 .unwrap();
1425
1426 assert_eq!(quote.decrease_percent, HUNDRED_PERCENT);
1427 assert_eq!(quote.collateral, 696_777_779);
1428 }
1429}