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