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