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