#[cfg(test)]
mod tests {
use crate::accounts::{fetch_all_vault, fetch_tuna_config, fetch_tuna_lp_position};
use crate::instructions::CreateMarketInstructionArgs;
use crate::tests::fusion::swap_exact_in;
use crate::tests::*;
use crate::types::MarketMaker;
use crate::utils::fusion::get_swap_tick_arrays;
use crate::{
get_tuna_config_address, get_tuna_liquidity_position_address, liquidate_tuna_lp_position_fusion_jupiter_instructions,
open_and_increase_tuna_lp_position_fusion_instructions, OpenAndIncreaseTunaLpPositionArgs, HUNDRED_PERCENT, LEVERAGE_ONE,
};
use fusionamm_client::{fetch_fusion_pool, FusionPool};
use jupiter_solana_client::instructions::{Route, RouteInstructionArgs};
use jupiter_solana_client::types::{DefiTunaAccountsType, DefiTunaRemainingAccountsInfo, DefiTunaRemainingAccountsSlice, RoutePlanStep, Swap};
use serial_test::serial;
use solana_instruction::AccountMeta;
use solana_keypair::Keypair;
use solana_program_test::tokio;
use solana_pubkey::Pubkey;
use solana_signer::Signer;
use spl_associated_token_account::get_associated_token_address_with_program_id;
fn test_market_args() -> CreateMarketInstructionArgs {
CreateMarketInstructionArgs {
address_lookup_table: Default::default(),
max_leverage: (LEVERAGE_ONE * 1020) / 100,
protocol_fee: 1000, protocol_fee_on_collateral: 1000, liquidation_fee: 10000, liquidation_threshold: 920000, oracle_price_deviation_threshold: HUNDRED_PERCENT / 2, disabled: false,
borrow_limit_a: 0,
borrow_limit_b: 0,
max_swap_slippage: 0,
rebalance_protocol_fee: 0,
spot_position_size_limit_a: 1000_000_000_000,
spot_position_size_limit_b: 100000_000_000,
}
}
fn get_route_accounts(
market: &TestMarket,
fusion_pool: &FusionPool,
fusion_pool_address: &Pubkey,
tuna_position_address: &Pubkey,
) -> [AccountMeta; 17] {
let swap_ticks_arrays = get_swap_tick_arrays(fusion_pool.tick_current_index, fusion_pool.tick_spacing, &fusion_pool_address);
let tuna_position_ata_a =
get_associated_token_address_with_program_id(tuna_position_address, &market.mint_a_address, &market.token_program_a);
let tuna_position_ata_b =
get_associated_token_address_with_program_id(tuna_position_address, &market.mint_b_address, &market.token_program_b);
[
AccountMeta::new(fusionamm_client::ID, false),
AccountMeta::new(market.token_program_a, false),
AccountMeta::new(market.token_program_b, false),
AccountMeta::new(spl_memo::ID, false),
AccountMeta::new(*tuna_position_address, false),
AccountMeta::new(*fusion_pool_address, false),
AccountMeta::new(market.mint_a_address, false),
AccountMeta::new(market.mint_b_address, false),
AccountMeta::new(tuna_position_ata_a, false),
AccountMeta::new(tuna_position_ata_b, false),
AccountMeta::new(fusion_pool.token_vault_a, false),
AccountMeta::new(fusion_pool.token_vault_b, false),
AccountMeta::new(swap_ticks_arrays[0], false),
AccountMeta::new(swap_ticks_arrays[1], false),
AccountMeta::new(swap_ticks_arrays[2], false),
AccountMeta::new(swap_ticks_arrays[3], false),
AccountMeta::new(swap_ticks_arrays[4], false),
]
}
#[test]
#[serial]
fn test_liquidate_position() {
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
let signer = Keypair::new();
let ctx = RpcContext::new(&signer, fusion::get_fusion_pool_config_accounts(&signer.pubkey())).await;
let test_market = setup_test_market(
&ctx,
test_market_args(),
MarketMaker::Fusion,
TestMarketArgs {
permissionless: false,
..TestMarketArgs::default()
},
)
.await
.unwrap();
let pool = fetch_fusion_pool(&ctx.rpc, &test_market.pool).unwrap();
let tuna_config = fetch_tuna_config(&ctx.rpc, &get_tuna_config_address().0).unwrap();
let actual_tick_index = pool.data.tick_current_index - (pool.data.tick_current_index % pool.data.tick_spacing as i32);
let ix = open_and_increase_tuna_lp_position_fusion_instructions(
&ctx.rpc,
&ctx.signer.pubkey(),
&test_market.pool,
OpenAndIncreaseTunaLpPositionArgs {
tick_lower_index: actual_tick_index - pool.data.tick_spacing as i32 * 3,
tick_upper_index: actual_tick_index + pool.data.tick_spacing as i32 * 3,
lower_limit_order_sqrt_price: 0,
upper_limit_order_sqrt_price: 0,
flags: 0,
collateral_a: 1_000_000_000,
collateral_b: 0,
borrow_a: 4_000_000_000,
borrow_b: 0,
min_added_amount_a: 0,
min_added_amount_b: 0,
max_swap_slippage: 0,
},
)
.unwrap();
ctx.send_transaction_with_signers(ix.instructions, ix.additional_signers.iter().collect())
.unwrap();
swap_exact_in(&ctx, &pool.address, 50000000000, &pool.data.token_mint_b, None)
.await
.unwrap();
let tuna_position = fetch_tuna_lp_position(&ctx.rpc, &get_tuna_liquidity_position_address(&ix.position_mint).0).unwrap();
let vaults = fetch_all_vault(&ctx.rpc, &[test_market.vault_a, test_market.vault_b]).unwrap();
let route_accounts = get_route_accounts(&test_market, &pool.data, &pool.address, &tuna_position.address);
let route_ix_builder = Route {
token_program: Default::default(), user_transfer_authority: Default::default(), user_source_token_account: Default::default(), user_destination_token_account: Default::default(), destination_token_account: None, destination_mint: Default::default(), platform_fee_account: None, event_authority: Default::default(), program: Default::default(), };
let a_to_b = false;
let in_amount = 1005231667 - 10052316;
let route_ix = route_ix_builder.instruction(RouteInstructionArgs {
route_plan: vec![RoutePlanStep {
swap: Swap::DefiTuna {
a_to_b,
remaining_accounts_info: Some(DefiTunaRemainingAccountsInfo {
slices: vec![DefiTunaRemainingAccountsSlice {
accounts_type: DefiTunaAccountsType::SupplementalTickArrays,
length: 2,
}],
}),
},
percent: 100,
input_index: 0,
output_index: 1,
}],
in_amount,
quoted_out_amount: 0,
slippage_bps: 0,
platform_fee_bps: 0,
});
ctx.send_transaction(liquidate_tuna_lp_position_fusion_jupiter_instructions(
&ctx.signer.pubkey(),
&tuna_position.data,
&tuna_config.data,
&vaults[0].address,
&vaults[0].data,
&vaults[1].address,
&vaults[1].data,
&pool.data,
&test_market.token_program_a,
&test_market.token_program_b,
None,
route_ix.data,
a_to_b,
&route_accounts,
))
.unwrap();
});
}
}