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, TokenPair,
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 =
107 calculate_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: protocol_fee.a,
132 protocol_fee_b: 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 =
319 calculate_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: protocol_fee.a,
336 protocol_fee_b: 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
490#[cfg_attr(feature = "wasm", wasm_expose)]
491pub fn calculate_tuna_protocol_fee(
492 collateral_token: u8,
493 borrowed_token: u8,
494 collateral: u64,
495 borrow: u64,
496 protocol_fee_rate_on_collateral: u16,
497 protocol_fee_rate: u16,
498) -> TokenPair {
499 let collateral_a = if collateral_token == TOKEN_A { collateral } else { 0 };
500 let collateral_b = if collateral_token == TOKEN_B { collateral } else { 0 };
501 let borrow_a = if borrowed_token == TOKEN_A { borrow } else { 0 };
502 let borrow_b = if borrowed_token == TOKEN_B { borrow } else { 0 };
503
504 let protocol_fee_a = ((collateral_a as u128 * protocol_fee_rate_on_collateral as u128 + borrow_a as u128 * protocol_fee_rate as u128)
505 / HUNDRED_PERCENT as u128) as u64;
506 let protocol_fee_b = ((collateral_b as u128 * protocol_fee_rate_on_collateral as u128 + borrow_b as u128 * protocol_fee_rate as u128)
507 / HUNDRED_PERCENT as u128) as u64;
508
509 TokenPair {
510 a: protocol_fee_a,
511 b: protocol_fee_b,
512 }
513}
514
515#[cfg(all(test, not(feature = "wasm")))]
516mod tests {
517 use super::*;
518 use crate::assert_approx_eq;
519 use fusionamm_core::{
520 get_tick_array_start_tick_index, price_to_sqrt_price, sqrt_price_to_tick_index, TickArrayFacade, TickFacade, TICK_ARRAY_SIZE,
521 };
522
523 fn test_fusion_pool(sqrt_price: u128) -> FusionPoolFacade {
524 let tick_current_index = sqrt_price_to_tick_index(sqrt_price);
525 FusionPoolFacade {
526 tick_current_index,
527 fee_rate: 3000,
528 liquidity: 10000000000000,
529 sqrt_price,
530 tick_spacing: 2,
531 ..FusionPoolFacade::default()
532 }
533 }
534
535 fn test_tick(liquidity_net: i128) -> TickFacade {
536 TickFacade {
537 initialized: true,
538 liquidity_net,
539 ..TickFacade::default()
540 }
541 }
542
543 fn test_tick_array(start_tick_index: i32) -> TickArrayFacade {
544 TickArrayFacade {
545 start_tick_index,
546 ticks: [test_tick(0); TICK_ARRAY_SIZE],
547 }
548 }
549
550 fn test_tick_arrays(fusion_pool: FusionPoolFacade) -> TickArrays {
551 let tick_spacing = fusion_pool.tick_spacing;
552 let tick_current_index = sqrt_price_to_tick_index(fusion_pool.sqrt_price);
553 let tick_array_start_index = get_tick_array_start_tick_index(tick_current_index, tick_spacing);
554
555 [
556 test_tick_array(tick_array_start_index),
557 test_tick_array(tick_array_start_index + TICK_ARRAY_SIZE as i32 * tick_spacing as i32),
558 test_tick_array(tick_array_start_index + TICK_ARRAY_SIZE as i32 * tick_spacing as i32 * 2),
559 test_tick_array(tick_array_start_index - TICK_ARRAY_SIZE as i32 * tick_spacing as i32),
560 test_tick_array(tick_array_start_index - TICK_ARRAY_SIZE as i32 * tick_spacing as i32 * 2),
561 ]
562 .into()
563 }
564
565 #[test]
566 fn test_get_liquidation_price() {
567 assert_eq!(get_liquidation_price(TOKEN_A, 5.0, 0.0, 0.85), Ok(0.0));
568 assert_eq!(get_liquidation_price(TOKEN_A, 0.0, 5.0, 0.85), Ok(0.0));
569 assert_eq!(get_liquidation_price(TOKEN_A, 5.0, 800.0, 0.85), Ok(188.23529411764707));
570 assert_eq!(get_liquidation_price(TOKEN_B, 1000.0, 4.0, 0.85), Ok(212.5));
571 }
572
573 #[test]
574 fn increase_long_position_providing_token_a() {
575 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
576 let fusion_pool = test_fusion_pool(sqrt_price);
577
578 let quote = get_increase_spot_position_quote(
579 5_000_000_000,
580 TOKEN_A,
581 TOKEN_A,
582 5.0,
583 Some(0),
584 (HUNDRED_PERCENT / 100) as u16,
585 (HUNDRED_PERCENT / 200) as u16,
586 fusion_pool,
587 test_tick_arrays(fusion_pool),
588 )
589 .unwrap();
590
591 assert_eq!(quote.collateral, 1057165829);
592 assert_eq!(quote.borrow, 800000000);
593 assert_eq!(quote.min_swap_output_amount, 3_947_423_011);
594 assert_eq!(quote.estimated_amount, 4_999_303_011);
595 assert_eq!(quote.protocol_fee_a, 5285829);
596 assert_eq!(quote.protocol_fee_b, 8000000);
597 assert_eq!(quote.price_impact, 0.03531617625702754);
598 assert_approx_eq!(quote.estimated_amount as f64 / (quote.estimated_amount as f64 - (quote.borrow as f64 * 1000.0) / 200.0), 5.0, 0.1);
599 }
600
601 #[test]
602 fn increase_long_position_providing_token_b() {
603 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
604 let fusion_pool = test_fusion_pool(sqrt_price);
605
606 let quote = get_increase_spot_position_quote(
607 5000_000_000,
608 TOKEN_B,
609 TOKEN_A,
610 5.0,
611 Some(0),
612 (HUNDRED_PERCENT / 100) as u16,
613 (HUNDRED_PERCENT / 200) as u16,
614 fusion_pool,
615 test_tick_arrays(fusion_pool),
616 )
617 .unwrap();
618
619 assert_eq!(quote.collateral, 1060346869);
620 assert_eq!(quote.borrow, 4000000000);
621 assert_eq!(quote.estimated_amount, 24_972_080_293);
622 assert_eq!(quote.protocol_fee_a, 0);
623 assert_eq!(quote.protocol_fee_b, 45301734);
624 assert_eq!(quote.price_impact, 0.22373179716579372);
625 assert_approx_eq!(quote.estimated_amount as f64 / (quote.estimated_amount as f64 - (quote.borrow as f64 * 1000.0) / 200.0), 5.0, 0.1);
626 }
627
628 #[test]
629 fn increase_short_position_providing_a() {
630 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
631 let fusion_pool = test_fusion_pool(sqrt_price);
632
633 let quote = get_increase_spot_position_quote(
634 5_000_000_000,
635 TOKEN_A,
636 TOKEN_B,
637 5.0,
638 Some(0),
639 (HUNDRED_PERCENT / 100) as u16,
640 (HUNDRED_PERCENT / 200) as u16,
641 fusion_pool,
642 test_tick_arrays(fusion_pool),
643 )
644 .unwrap();
645
646 assert_eq!(quote.collateral, 1060346869);
647 assert_eq!(quote.borrow, 4000000000);
648 assert_eq!(quote.estimated_amount, 999_776_441);
649 assert_eq!(quote.protocol_fee_a, 45301734);
650 assert_eq!(quote.protocol_fee_b, 0);
651 assert_eq!(quote.price_impact, 0.04470636400017991);
652 assert_approx_eq!(quote.estimated_amount as f64 / (quote.estimated_amount as f64 - (quote.borrow as f64 / 1000.0) * 200.0), 5.0, 0.1);
653 }
654
655 #[test]
656 fn increase_short_position_providing_b() {
657 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
658 let fusion_pool = test_fusion_pool(sqrt_price);
659
660 let quote = get_increase_spot_position_quote(
661 5000_000_000,
662 TOKEN_B,
663 TOKEN_B,
664 5.0,
665 Some(0),
666 (HUNDRED_PERCENT / 100) as u16,
667 (HUNDRED_PERCENT / 200) as u16,
668 fusion_pool,
669 test_tick_arrays(fusion_pool),
670 )
671 .unwrap();
672
673 assert_eq!(quote.collateral, 1057165829);
674 assert_eq!(quote.borrow, 20000000000);
675 assert_eq!(quote.estimated_amount, 4996_517_564);
676 assert_eq!(quote.protocol_fee_a, 200000000);
677 assert_eq!(quote.protocol_fee_b, 5285829);
678 assert_eq!(quote.price_impact, 0.17633175413067637);
679 assert_approx_eq!(quote.estimated_amount as f64 / (quote.estimated_amount as f64 - (quote.borrow as f64 / 1000.0) * 200.0), 5.0, 0.1);
680 }
681
682 #[test]
683 fn increase_quote_with_slippage() {
684 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
685 let fusion_pool = test_fusion_pool(sqrt_price);
686
687 let quote =
689 get_increase_spot_position_quote(200_000, TOKEN_B, TOKEN_A, 5.0, Some(1000), 0, 0, fusion_pool, test_tick_arrays(fusion_pool)).unwrap();
690 assert_eq!(quote.min_swap_output_amount, 899_994);
691
692 let quote =
694 get_increase_spot_position_quote(200_000, TOKEN_B, TOKEN_A, 5.0, Some(0), 0, 0, fusion_pool, test_tick_arrays(fusion_pool)).unwrap();
695 assert_eq!(quote.min_swap_output_amount, 999_994);
696 }
697
698 #[test]
699 fn decrease_non_leveraged_long_position_providing_a_reduce_only() {
700 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
701 let fusion_pool = test_fusion_pool(sqrt_price);
702
703 let quote = get_decrease_spot_position_quote(
704 1_000_000_000,
705 TOKEN_A,
706 1.0,
707 true,
708 Some(0),
709 TOKEN_A,
710 5_000_000_000, 0, (HUNDRED_PERCENT / 100) as u16,
713 (HUNDRED_PERCENT / 200) as u16,
714 fusion_pool,
715 test_tick_arrays(fusion_pool),
716 )
717 .unwrap();
718
719 assert_eq!(quote.decrease_percent, 200000);
720 assert_eq!(quote.collateral_token, TOKEN_A);
721 assert_eq!(quote.position_token, TOKEN_A);
722 assert_eq!(quote.estimated_amount, 4_000_000_000);
723 assert_eq!(quote.protocol_fee_a, 0);
724 assert_eq!(quote.protocol_fee_b, 0);
725 assert_eq!(quote.price_impact, 0.0);
726 assert_eq!(quote.estimated_amount as f64 / (quote.estimated_amount as f64 - (quote.borrow as f64 / 1000.0) * 200.0), 1.0);
727 }
728
729 #[test]
730 fn decrease_non_leveraged_long_position_providing_b_reduce_only() {
731 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
732 let fusion_pool = test_fusion_pool(sqrt_price);
733
734 let quote = get_decrease_spot_position_quote(
735 200_000_000,
736 TOKEN_B,
737 1.0,
738 true,
739 Some(0),
740 TOKEN_A,
741 5_000_000_000, 0, (HUNDRED_PERCENT / 100) as u16,
744 (HUNDRED_PERCENT / 200) as u16,
745 fusion_pool,
746 test_tick_arrays(fusion_pool),
747 )
748 .unwrap();
749
750 assert_eq!(quote.decrease_percent, 200000);
751 assert_eq!(quote.collateral_token, TOKEN_B);
752 assert_eq!(quote.position_token, TOKEN_A);
753 assert_eq!(quote.estimated_amount, 4_000_000_000);
754 assert_eq!(quote.protocol_fee_a, 0);
755 assert_eq!(quote.protocol_fee_b, 0);
756 assert_eq!(quote.price_impact, 0.008916842709072448);
757 assert_eq!(quote.estimated_amount as f64 / (quote.estimated_amount as f64 - (quote.borrow as f64 / 1000.0) * 200.0), 1.0);
758 }
759
760 #[test]
761 fn decrease_long_position_providing_a_reduce_only() {
762 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
763 let fusion_pool = test_fusion_pool(sqrt_price);
764
765 let quote = get_decrease_spot_position_quote(
766 1_000_000_000,
767 TOKEN_A,
768 5.0,
769 true,
770 Some(0),
771 TOKEN_A,
772 5_000_000_000, 800_000_000, (HUNDRED_PERCENT / 100) as u16,
775 (HUNDRED_PERCENT / 200) as u16,
776 fusion_pool,
777 test_tick_arrays(fusion_pool),
778 )
779 .unwrap();
780
781 assert_eq!(quote.decrease_percent, 200000);
782 assert_eq!(quote.collateral_token, TOKEN_A);
783 assert_eq!(quote.position_token, TOKEN_A);
784 assert_eq!(quote.collateral, 0);
785 assert_eq!(quote.borrow, 0);
786 assert_eq!(quote.estimated_amount, 4_000_000_000);
787 assert_eq!(quote.protocol_fee_a, 0);
788 assert_eq!(quote.protocol_fee_b, 0);
789 assert_eq!(quote.price_impact, 0.007155289528004705);
790
791 let quote = get_decrease_spot_position_quote(
792 6_000_000_000,
793 TOKEN_A,
794 5.0,
795 true,
796 Some(0),
797 TOKEN_A,
798 5_000_000_000, 800_000_000, (HUNDRED_PERCENT / 100) as u16,
801 (HUNDRED_PERCENT / 200) as u16,
802 fusion_pool,
803 test_tick_arrays(fusion_pool),
804 )
805 .unwrap();
806
807 assert_eq!(quote.decrease_percent, HUNDRED_PERCENT);
808 assert_eq!(quote.collateral, 0);
809 assert_eq!(quote.borrow, 0);
810 assert_eq!(quote.estimated_amount, 0);
811 }
812
813 #[test]
814 fn decrease_long_position_providing_b_reduce_only() {
815 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
816 let fusion_pool = test_fusion_pool(sqrt_price);
817
818 let quote = get_decrease_spot_position_quote(
819 200_000_000,
820 TOKEN_B,
821 5.0,
822 true,
823 Some(0),
824 TOKEN_A,
825 5_000_000_000, 800_000_000, (HUNDRED_PERCENT / 100) as u16,
828 (HUNDRED_PERCENT / 200) as u16,
829 fusion_pool,
830 test_tick_arrays(fusion_pool),
831 )
832 .unwrap();
833
834 assert_eq!(quote.decrease_percent, 200000);
835 assert_eq!(quote.collateral_token, TOKEN_B);
836 assert_eq!(quote.position_token, TOKEN_A);
837 assert_eq!(quote.collateral, 0);
838 assert_eq!(quote.borrow, 0);
839 assert_eq!(quote.estimated_amount, 4000000000);
840 assert_eq!(quote.protocol_fee_a, 0);
841 assert_eq!(quote.protocol_fee_b, 0);
842 assert_eq!(quote.price_impact, 0.008916842709072448);
843
844 let quote = get_decrease_spot_position_quote(
845 1200_000_000,
846 TOKEN_B,
847 5.0,
848 true,
849 Some(0),
850 TOKEN_A,
851 5_000_000_000, 800_000_000, (HUNDRED_PERCENT / 100) as u16,
854 (HUNDRED_PERCENT / 200) as u16,
855 fusion_pool,
856 test_tick_arrays(fusion_pool),
857 )
858 .unwrap();
859
860 assert_eq!(quote.decrease_percent, HUNDRED_PERCENT);
861 assert_eq!(quote.collateral, 0);
862 assert_eq!(quote.borrow, 0);
863 assert_eq!(quote.estimated_amount, 0);
864 }
865
866 #[test]
867 fn decrease_long_position_providing_a_without_leverage() {
868 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
869 let fusion_pool = test_fusion_pool(sqrt_price);
870
871 let quote = get_decrease_spot_position_quote(
872 6_000_000_000,
873 TOKEN_A,
874 1.0,
875 false,
876 Some(100),
877 TOKEN_A,
878 5_000_000_000, 800_000_000, (HUNDRED_PERCENT / 100) as u16,
881 (HUNDRED_PERCENT / 200) as u16,
882 fusion_pool,
883 test_tick_arrays(fusion_pool),
884 )
885 .unwrap();
886
887 assert_eq!(quote.decrease_percent, HUNDRED_PERCENT);
888 assert_eq!(quote.collateral_token, TOKEN_A);
889 assert_eq!(quote.position_token, TOKEN_B);
890 assert_eq!(quote.collateral, 1005025125);
891 assert_eq!(quote.borrow, 0);
892 assert_eq!(quote.estimated_amount, 199_319_781);
893 assert_eq!(quote.decrease_acceptable_swap_amount, 4_052_881_482);
894 assert_eq!(quote.increase_min_swap_output_amount, 197_326_583);
895 assert_eq!(quote.protocol_fee_a, 5025125);
896 assert_eq!(quote.protocol_fee_b, 0);
897 assert_eq!(quote.price_impact, 0.044685946117650754);
898 }
899
900 #[test]
901 fn decrease_long_position_providing_a() {
902 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
903 let fusion_pool = test_fusion_pool(sqrt_price);
904
905 let quote = get_decrease_spot_position_quote(
906 6_000_000_000,
907 TOKEN_A,
908 5.0,
909 false,
910 Some(100),
911 TOKEN_A,
912 5_000_000_000, 800_000_000, (HUNDRED_PERCENT / 100) as u16,
915 (HUNDRED_PERCENT / 200) as u16,
916 fusion_pool,
917 test_tick_arrays(fusion_pool),
918 )
919 .unwrap();
920
921 assert_eq!(quote.decrease_percent, HUNDRED_PERCENT);
922 assert_eq!(quote.collateral_token, TOKEN_A);
923 assert_eq!(quote.position_token, TOKEN_B);
924 assert_eq!(quote.collateral, 211433165);
925 assert_eq!(quote.borrow, 800000000);
926 assert_eq!(quote.estimated_amount, 199_793_344);
927 assert_eq!(quote.decrease_acceptable_swap_amount, 4_052_881_482);
928 assert_eq!(quote.increase_min_swap_output_amount, 197_795_410);
929 assert_eq!(quote.protocol_fee_a, 1057165);
930 assert_eq!(quote.protocol_fee_b, 8000000);
931 assert_eq!(quote.price_impact, 0.04470711974918773);
932 }
933
934 #[test]
935 fn decrease_long_position_providing_b() {
936 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
937 let fusion_pool = test_fusion_pool(sqrt_price);
938
939 let quote = get_decrease_spot_position_quote(
940 1200_000_000,
941 TOKEN_B,
942 5.0,
943 false,
944 Some(0),
945 TOKEN_A,
946 5_000_000_000, 800_000_000, (HUNDRED_PERCENT / 100) as u16,
949 (HUNDRED_PERCENT / 200) as u16,
950 fusion_pool,
951 test_tick_arrays(fusion_pool),
952 )
953 .unwrap();
954
955 assert_eq!(quote.decrease_percent, HUNDRED_PERCENT);
956 assert_eq!(quote.collateral_token, TOKEN_B);
957 assert_eq!(quote.position_token, TOKEN_B);
958 assert_eq!(quote.collateral, 42286633);
959 assert_eq!(quote.borrow, 800_000_000);
960 assert_eq!(quote.estimated_amount, 199_924_036);
961 assert_eq!(quote.decrease_acceptable_swap_amount, 996_777_780);
962 assert_eq!(quote.increase_min_swap_output_amount, 157_848_836);
963 assert_eq!(quote.protocol_fee_a, 0);
964 assert_eq!(quote.protocol_fee_b, 8211433);
965 assert_eq!(quote.price_impact, 0.05162980632488212);
966 }
967
968 #[test]
969 fn decrease_non_leveraged_short_position_providing_a() {
970 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
971 let fusion_pool = test_fusion_pool(sqrt_price);
972
973 let quote = get_decrease_spot_position_quote(
974 6_000_000_000,
975 TOKEN_A,
976 1.0,
977 false,
978 Some(0),
979 TOKEN_B,
980 1000_000_000, 0, (HUNDRED_PERCENT / 100) as u16,
983 (HUNDRED_PERCENT / 200) as u16,
984 fusion_pool,
985 test_tick_arrays(fusion_pool),
986 )
987 .unwrap();
988
989 assert_eq!(quote.decrease_percent, HUNDRED_PERCENT);
990 assert_eq!(quote.collateral_token, TOKEN_A);
991 assert_eq!(quote.position_token, TOKEN_A);
992 assert_eq!(quote.collateral, 1005025125);
993 assert_eq!(quote.borrow, 0);
994 assert_eq!(quote.estimated_amount, 1_000_000_000);
995 assert_eq!(quote.protocol_fee_a, 5025125);
996 assert_eq!(quote.protocol_fee_b, 0);
997 assert_eq!(quote.price_impact, 0.044592165429757635);
998 }
999
1000 #[test]
1001 fn decrease_short_position_providing_a() {
1002 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
1003 let fusion_pool = test_fusion_pool(sqrt_price);
1004
1005 let quote = get_decrease_spot_position_quote(
1006 6_000_000_000,
1007 TOKEN_A,
1008 5.0,
1009 false,
1010 Some(0),
1011 TOKEN_B,
1012 1000_000_000, 4_000_000_000, (HUNDRED_PERCENT / 100) as u16,
1015 (HUNDRED_PERCENT / 200) as u16,
1016 fusion_pool,
1017 test_tick_arrays(fusion_pool),
1018 )
1019 .unwrap();
1020
1021 assert_eq!(quote.decrease_percent, HUNDRED_PERCENT);
1022 assert_eq!(quote.collateral_token, TOKEN_A);
1023 assert_eq!(quote.position_token, TOKEN_A);
1024 assert_eq!(quote.collateral, 211433165);
1025 assert_eq!(quote.borrow, 160000000);
1026 assert_eq!(quote.estimated_amount, 999_620_182);
1027 assert_eq!(quote.decrease_acceptable_swap_amount, 4_983_888_901);
1028 assert_eq!(quote.increase_min_swap_output_amount, 789_244_182);
1029 assert_eq!(quote.protocol_fee_a, 2657165);
1030 assert_eq!(quote.protocol_fee_b, 0);
1031 assert_eq!(quote.price_impact, 0.051656476403882934);
1032 }
1033
1034 #[test]
1035 fn decrease_short_position_providing_b() {
1036 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
1037 let fusion_pool = test_fusion_pool(sqrt_price);
1038
1039 let quote = get_decrease_spot_position_quote(
1040 1_200_000_000,
1041 TOKEN_B,
1042 5.0,
1043 false,
1044 Some(0),
1045 TOKEN_B,
1046 1000_000_000, 4_000_000_000, (HUNDRED_PERCENT / 100) as u16,
1049 (HUNDRED_PERCENT / 200) as u16,
1050 fusion_pool,
1051 test_tick_arrays(fusion_pool),
1052 )
1053 .unwrap();
1054
1055 assert_eq!(quote.decrease_percent, HUNDRED_PERCENT);
1056 assert_eq!(quote.collateral_token, TOKEN_B);
1057 assert_eq!(quote.position_token, TOKEN_A);
1058 assert_eq!(quote.collateral, 42_286_633);
1059 assert_eq!(quote.borrow, 160000000);
1060 assert_eq!(quote.estimated_amount, 998_966_727);
1061 assert_eq!(quote.protocol_fee_a, 1600000);
1062 assert_eq!(quote.protocol_fee_b, 211433);
1063 assert_eq!(quote.price_impact, 0.04472711596017476);
1064 }
1065
1066 #[test]
1067 fn tradable_amount_for_1x_long_position_providing_b() {
1068 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
1069 let fusion_pool = test_fusion_pool(sqrt_price);
1070 let tick_arrays = test_tick_arrays(fusion_pool);
1071
1072 let collateral_token = TOKEN_B;
1073 let position_token = TOKEN_A;
1074 let leverage = 1.0;
1075 let protocol_fee_rate = (HUNDRED_PERCENT / 100) as u16;
1076 let protocol_fee_rate_on_collateral = (HUNDRED_PERCENT / 200) as u16;
1077 let available_balance = 200_000_000;
1078
1079 let tradable_amount = get_tradable_amount(
1080 collateral_token,
1081 available_balance,
1082 leverage,
1083 position_token,
1084 false,
1085 position_token,
1086 0,
1087 0,
1088 protocol_fee_rate,
1089 protocol_fee_rate_on_collateral,
1090 fusion_pool,
1091 tick_arrays.clone(),
1092 )
1093 .unwrap();
1094 assert_eq!(tradable_amount, 198403000);
1095
1096 let quote = get_increase_spot_position_quote(
1097 tradable_amount,
1098 collateral_token,
1099 position_token,
1100 leverage,
1101 Some(0),
1102 protocol_fee_rate,
1103 protocol_fee_rate_on_collateral,
1104 fusion_pool,
1105 tick_arrays,
1106 )
1107 .unwrap();
1108 assert_eq!(quote.collateral, available_balance);
1109 }
1110
1111 #[test]
1112 fn tradable_amount_for_5x_long_position_providing_b() {
1113 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
1114 let fusion_pool = test_fusion_pool(sqrt_price);
1115 let tick_arrays = test_tick_arrays(fusion_pool);
1116
1117 let collateral_token = TOKEN_B;
1118 let position_token = TOKEN_A;
1119 let leverage = 5.0;
1120 let protocol_fee_rate = (HUNDRED_PERCENT / 100) as u16;
1121 let protocol_fee_rate_on_collateral = (HUNDRED_PERCENT / 200) as u16;
1122 let available_balance = 10_000_000;
1123
1124 let tradable_amount = get_tradable_amount(
1125 collateral_token,
1126 available_balance,
1127 leverage,
1128 position_token,
1129 false,
1130 position_token,
1131 0,
1132 0,
1133 protocol_fee_rate,
1134 protocol_fee_rate_on_collateral,
1135 fusion_pool,
1136 tick_arrays.clone(),
1137 )
1138 .unwrap();
1139 assert_eq!(tradable_amount, 47154380);
1140
1141 let quote = get_increase_spot_position_quote(
1142 tradable_amount,
1143 collateral_token,
1144 position_token,
1145 leverage,
1146 Some(0),
1147 protocol_fee_rate,
1148 protocol_fee_rate_on_collateral,
1149 fusion_pool,
1150 tick_arrays,
1151 )
1152 .unwrap();
1153 assert_eq!(quote.collateral, available_balance + 1);
1155 }
1156
1157 #[test]
1158 fn tradable_amount_for_5x_long_position_providing_a() {
1159 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
1160 let fusion_pool = test_fusion_pool(sqrt_price);
1161 let tick_arrays = test_tick_arrays(fusion_pool);
1162
1163 let collateral_token = TOKEN_A;
1164 let position_token = TOKEN_A;
1165 let leverage = 5.0;
1166 let protocol_fee_rate = (HUNDRED_PERCENT / 100) as u16;
1167 let protocol_fee_rate_on_collateral = (HUNDRED_PERCENT / 200) as u16;
1168 let available_balance = 1_000_000_000;
1169
1170 let tradable_amount = get_tradable_amount(
1171 collateral_token,
1172 available_balance,
1173 leverage,
1174 position_token,
1175 false,
1176 position_token,
1177 0,
1178 0,
1179 protocol_fee_rate,
1180 protocol_fee_rate_on_collateral,
1181 fusion_pool,
1182 tick_arrays.clone(),
1183 )
1184 .unwrap();
1185 assert_eq!(tradable_amount, 4729626953);
1186
1187 let quote = get_increase_spot_position_quote(
1188 tradable_amount,
1189 collateral_token,
1190 position_token,
1191 leverage,
1192 Some(0),
1193 protocol_fee_rate,
1194 protocol_fee_rate_on_collateral,
1195 fusion_pool,
1196 tick_arrays,
1197 )
1198 .unwrap();
1199 assert_eq!(quote.collateral, available_balance);
1200 }
1202
1203 #[test]
1204 fn tradable_amount_for_5x_short_position_providing_b() {
1205 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
1206 let fusion_pool = test_fusion_pool(sqrt_price);
1207 let tick_arrays = test_tick_arrays(fusion_pool);
1208
1209 let collateral_token = TOKEN_B;
1210 let position_token = TOKEN_B;
1211 let leverage = 5.0;
1212 let protocol_fee_rate = (HUNDRED_PERCENT / 100) as u16;
1213 let protocol_fee_rate_on_collateral = (HUNDRED_PERCENT / 200) as u16;
1214 let available_balance = 200_000_000;
1215
1216 let tradable_amount = get_tradable_amount(
1217 collateral_token,
1218 available_balance,
1219 leverage,
1220 position_token,
1221 false,
1222 position_token,
1223 0,
1224 0,
1225 protocol_fee_rate,
1226 protocol_fee_rate_on_collateral,
1227 fusion_pool,
1228 tick_arrays.clone(),
1229 )
1230 .unwrap();
1231 assert_eq!(tradable_amount, 945925390);
1232
1233 let quote = get_increase_spot_position_quote(
1234 tradable_amount,
1235 collateral_token,
1236 position_token,
1237 leverage,
1238 Some(0),
1239 protocol_fee_rate,
1240 protocol_fee_rate_on_collateral,
1241 fusion_pool,
1242 tick_arrays,
1243 )
1244 .unwrap();
1245 assert_eq!(quote.collateral, available_balance + 1);
1247 }
1249
1250 #[test]
1251 fn tradable_amount_for_reducing_existing_long_position() {
1252 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
1253 let fusion_pool = test_fusion_pool(sqrt_price);
1254 let tick_arrays = test_tick_arrays(fusion_pool);
1255
1256 for i in 0..2 {
1257 let collateral_token = if i == 0 { TOKEN_A } else { TOKEN_B };
1258 let position_token = if i == 0 { TOKEN_A } else { TOKEN_B };
1259 let leverage = 5.0;
1260 let reduce_only = true;
1261 let position_amount = 5_000_000_000;
1262 let position_debt = 800_000_000;
1263 let protocol_fee_rate = (HUNDRED_PERCENT / 100) as u16;
1264 let protocol_fee_rate_on_collateral = (HUNDRED_PERCENT / 200) as u16;
1265 let available_balance = 50_000_000_000;
1266
1267 let tradable_amount = get_tradable_amount(
1268 collateral_token,
1269 available_balance,
1270 leverage,
1271 if position_token == TOKEN_A { TOKEN_B } else { TOKEN_A },
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, 5_000_000_000);
1283
1284 let quote = get_decrease_spot_position_quote(
1285 tradable_amount,
1286 collateral_token,
1287 5.0,
1288 reduce_only,
1289 Some(0),
1290 position_token,
1291 position_amount,
1292 position_debt,
1293 protocol_fee_rate,
1294 protocol_fee_rate_on_collateral,
1295 fusion_pool,
1296 tick_arrays.clone(),
1297 )
1298 .unwrap();
1299
1300 assert_eq!(quote.decrease_percent, HUNDRED_PERCENT);
1301 }
1302 }
1303
1304 #[test]
1305 fn tradable_amount_when_inverting_long_position_providing_a() {
1306 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
1307 let fusion_pool = test_fusion_pool(sqrt_price);
1308 let tick_arrays = test_tick_arrays(fusion_pool);
1309
1310 let collateral_token = TOKEN_A;
1311 let position_token = TOKEN_A;
1312 let leverage = 5.0;
1313 let reduce_only = false;
1314 let position_amount = 5_000_000_000;
1315 let position_debt = 800_000_000;
1316 let protocol_fee_rate = (HUNDRED_PERCENT / 100) as u16;
1317 let protocol_fee_rate_on_collateral = (HUNDRED_PERCENT / 200) as u16;
1318 let available_balance = 2_000_000_000;
1319
1320 let tradable_amount = get_tradable_amount(
1321 collateral_token,
1322 available_balance,
1323 leverage,
1324 if position_token == TOKEN_A { TOKEN_B } else { TOKEN_A },
1325 reduce_only,
1326 position_token,
1327 position_amount,
1328 position_debt,
1329 protocol_fee_rate,
1330 protocol_fee_rate_on_collateral,
1331 fusion_pool,
1332 tick_arrays.clone(),
1333 )
1334 .unwrap();
1335 assert_eq!(tradable_amount, 19_086_173_788);
1336
1337 let quote = get_decrease_spot_position_quote(
1338 tradable_amount,
1339 collateral_token,
1340 5.0,
1341 reduce_only,
1342 Some(0),
1343 position_token,
1344 position_amount,
1345 position_debt,
1346 protocol_fee_rate,
1347 protocol_fee_rate_on_collateral,
1348 fusion_pool,
1349 tick_arrays.clone(),
1350 )
1351 .unwrap();
1352
1353 assert_eq!(quote.decrease_percent, HUNDRED_PERCENT);
1354 assert_eq!(quote.collateral, 2_978_284_319);
1355 }
1356
1357 #[test]
1358 fn tradable_amount_when_inverting_long_position_providing_b() {
1359 let sqrt_price = price_to_sqrt_price(200.0, 9, 6);
1360 let fusion_pool = test_fusion_pool(sqrt_price);
1361 let tick_arrays = test_tick_arrays(fusion_pool);
1362
1363 let collateral_token = TOKEN_B;
1364 let position_token = TOKEN_A;
1365 let leverage = 5.0;
1366 let reduce_only = false;
1367 let position_amount = 5_000_000_000;
1368 let position_debt = 800_000_000;
1369 let protocol_fee_rate = (HUNDRED_PERCENT / 100) as u16;
1370 let protocol_fee_rate_on_collateral = (HUNDRED_PERCENT / 200) as u16;
1371 let available_balance = 500_000_000;
1372
1373 let tradable_amount = get_tradable_amount(
1374 collateral_token,
1375 available_balance,
1376 leverage,
1377 if position_token == TOKEN_A { TOKEN_B } else { TOKEN_A },
1378 reduce_only,
1379 position_token,
1380 position_amount,
1381 position_debt,
1382 protocol_fee_rate,
1383 protocol_fee_rate_on_collateral,
1384 fusion_pool,
1385 tick_arrays.clone(),
1386 )
1387 .unwrap();
1388 assert_eq!(tradable_amount, 4_295_498_968);
1389
1390 let quote = get_decrease_spot_position_quote(
1391 tradable_amount,
1392 collateral_token,
1393 5.0,
1394 reduce_only,
1395 Some(0),
1396 position_token,
1397 position_amount,
1398 position_debt,
1399 protocol_fee_rate,
1400 protocol_fee_rate_on_collateral,
1401 fusion_pool,
1402 tick_arrays.clone(),
1403 )
1404 .unwrap();
1405
1406 assert_eq!(quote.decrease_percent, HUNDRED_PERCENT);
1407 assert_eq!(quote.collateral, 696_777_779);
1408 }
1409}