defituna_client/txbuilder/
decrease_tuna_lp_position_fusion.rs1use crate::accounts::{fetch_all_vault, fetch_tuna_lp_position, TunaLpPosition, Vault};
2use crate::instructions::{DecreaseTunaLpPositionFusion, DecreaseTunaLpPositionFusionInstructionArgs};
3use crate::types::{AccountsType, PoolToken, RemainingAccountsInfo, RemainingAccountsSlice};
4use crate::utils::fusion::get_swap_tick_arrays;
5use crate::utils::get_create_ata_instructions;
6use crate::{get_market_address, get_tuna_config_address, get_tuna_liquidity_position_address, get_vault_address, HUNDRED_PERCENT};
7use anyhow::{anyhow, Result};
8use fusionamm_client::{fetch_fusion_pool, get_position_address, get_tick_array_address, FusionPool};
9use fusionamm_core::get_tick_array_start_tick_index;
10use solana_client::rpc_client::RpcClient;
11use solana_instruction::{AccountMeta, Instruction};
12use solana_pubkey::Pubkey;
13use spl_associated_token_account::get_associated_token_address_with_program_id;
14
15pub struct DecreaseTunaLpPositionArgs {
16 pub decrease_percent: u32,
17 pub swap_to_token: Option<PoolToken>,
18 pub min_removed_amount_a: u64,
19 pub min_removed_amount_b: u64,
20 pub max_swap_slippage: u32,
21}
22
23impl Default for DecreaseTunaLpPositionArgs {
24 fn default() -> Self {
25 Self {
26 decrease_percent: HUNDRED_PERCENT,
27 swap_to_token: None,
28 min_removed_amount_a: 0,
29 min_removed_amount_b: 0,
30 max_swap_slippage: 0,
31 }
32 }
33}
34
35pub fn decrease_tuna_lp_position_fusion_instructions(
36 rpc: &RpcClient,
37 authority: &Pubkey,
38 position_mint: &Pubkey,
39 args: DecreaseTunaLpPositionArgs,
40) -> Result<Vec<Instruction>> {
41 let tuna_position = fetch_tuna_lp_position(&rpc, &get_tuna_liquidity_position_address(&position_mint).0)?;
42
43 let fusion_pool = fetch_fusion_pool(rpc, &tuna_position.data.pool)?;
44 let mint_a_address = fusion_pool.data.token_mint_a;
45 let mint_b_address = fusion_pool.data.token_mint_b;
46
47 let vaults = fetch_all_vault(&rpc, &[get_vault_address(&mint_a_address).0, get_vault_address(&mint_b_address).0])?;
48 let (vault_a, vault_b) = (&vaults[0], &vaults[1]);
49
50 let all_mint_addresses = vec![mint_a_address, mint_b_address];
51 let mint_accounts = rpc.get_multiple_accounts(all_mint_addresses[0..all_mint_addresses.len()].into())?;
52 let mint_a_account = mint_accounts[0].as_ref().ok_or(anyhow!("Token A mint account not found"))?;
53 let mint_b_account = mint_accounts[1].as_ref().ok_or(anyhow!("Token B mint account not found"))?;
54
55 let authority_ata_a_instructions = get_create_ata_instructions(&mint_a_address, authority, authority, &mint_a_account.owner, 0);
56 let authority_ata_b_instructions = get_create_ata_instructions(&mint_b_address, authority, authority, &mint_b_account.owner, 0);
57
58 let mut instructions = vec![];
59 instructions.extend(authority_ata_a_instructions.create);
60 instructions.extend(authority_ata_b_instructions.create);
61
62 instructions.push(decrease_tuna_lp_position_fusion_instruction(
63 authority,
64 &tuna_position.data,
65 &vault_a.data,
66 &vault_b.data,
67 &fusion_pool.data,
68 &mint_a_account.owner,
69 &mint_b_account.owner,
70 args,
71 ));
72
73 instructions.extend(authority_ata_a_instructions.cleanup);
74 instructions.extend(authority_ata_b_instructions.cleanup);
75
76 Ok(instructions)
77}
78
79pub fn decrease_tuna_lp_position_fusion_instruction(
80 authority: &Pubkey,
81 tuna_position: &TunaLpPosition,
82 vault_a: &Vault,
83 vault_b: &Vault,
84 fusion_pool: &FusionPool,
85 token_program_a: &Pubkey,
86 token_program_b: &Pubkey,
87 args: DecreaseTunaLpPositionArgs,
88) -> Instruction {
89 let mint_a = fusion_pool.token_mint_a;
90 let mint_b = fusion_pool.token_mint_b;
91
92 assert_eq!(vault_a.mint, mint_a);
93 assert_eq!(vault_b.mint, mint_b);
94 assert_eq!(tuna_position.mint_a, mint_a);
95 assert_eq!(tuna_position.mint_b, mint_b);
96
97 let tuna_config_address = get_tuna_config_address().0;
98 let market_address = get_market_address(&tuna_position.pool).0;
99 let tuna_position_address = get_tuna_liquidity_position_address(&tuna_position.position_mint).0;
100
101 let tuna_position_owner_ata_a = get_associated_token_address_with_program_id(&authority, &mint_a, token_program_a);
102 let tuna_position_owner_ata_b = get_associated_token_address_with_program_id(&authority, &mint_b, token_program_b);
103
104 let vault_a_address = get_vault_address(&mint_a).0;
105 let vault_b_address = get_vault_address(&mint_b).0;
106 let fusion_pool_address = tuna_position.pool;
107
108 let tick_array_lower_start_tick_index = get_tick_array_start_tick_index(tuna_position.tick_lower_index, fusion_pool.tick_spacing);
109 let tick_array_lower_address = get_tick_array_address(&fusion_pool_address, tick_array_lower_start_tick_index).unwrap().0;
110
111 let tick_array_upper_start_tick_index = get_tick_array_start_tick_index(tuna_position.tick_upper_index, fusion_pool.tick_spacing);
112 let tick_array_upper_address = get_tick_array_address(&fusion_pool_address, tick_array_upper_start_tick_index).unwrap().0;
113
114 let swap_ticks_arrays = get_swap_tick_arrays(fusion_pool.tick_current_index, fusion_pool.tick_spacing, &fusion_pool_address);
115
116 let ix_builder = DecreaseTunaLpPositionFusion {
117 authority: *authority,
118 tuna_config: tuna_config_address,
119 mint_a: tuna_position.mint_a,
120 mint_b: tuna_position.mint_b,
121 market: market_address,
122 vault_a: vault_a_address,
123 vault_b: vault_b_address,
124 vault_a_ata: get_associated_token_address_with_program_id(&vault_a_address, &tuna_position.mint_a, token_program_a),
125 vault_b_ata: get_associated_token_address_with_program_id(&vault_b_address, &tuna_position.mint_b, token_program_b),
126 tuna_position: tuna_position_address,
127 tuna_position_ata: get_associated_token_address_with_program_id(&tuna_position_address, &tuna_position.position_mint, &spl_token_2022::ID),
128 tuna_position_ata_a: get_associated_token_address_with_program_id(&tuna_position_address, &tuna_position.mint_a, token_program_a),
129 tuna_position_ata_b: get_associated_token_address_with_program_id(&tuna_position_address, &tuna_position.mint_b, token_program_b),
130 tuna_position_owner_ata_a,
131 tuna_position_owner_ata_b,
132 pyth_oracle_price_feed_a: vault_a.pyth_oracle_price_update,
133 pyth_oracle_price_feed_b: vault_b.pyth_oracle_price_update,
134 fusionamm_program: fusionamm_client::ID,
135 fusion_pool: fusion_pool_address,
136 fusion_position: get_position_address(&tuna_position.position_mint).unwrap().0,
137 token_program_a: *token_program_a,
138 token_program_b: *token_program_b,
139 memo_program: spl_memo::ID,
140 };
141
142 ix_builder.instruction_with_remaining_accounts(
143 DecreaseTunaLpPositionFusionInstructionArgs {
144 decrease_percent: args.decrease_percent,
145 swap_to_token: args.swap_to_token,
146 min_removed_amount_a: args.min_removed_amount_a,
147 min_removed_amount_b: args.min_removed_amount_b,
148 max_swap_slippage: args.max_swap_slippage,
149 remaining_accounts_info: RemainingAccountsInfo {
150 slices: vec![
151 RemainingAccountsSlice {
152 accounts_type: AccountsType::SwapTickArrays,
153 length: 5,
154 },
155 RemainingAccountsSlice {
156 accounts_type: AccountsType::TickArrayLower,
157 length: 1,
158 },
159 RemainingAccountsSlice {
160 accounts_type: AccountsType::TickArrayUpper,
161 length: 1,
162 },
163 RemainingAccountsSlice {
164 accounts_type: AccountsType::PoolVaultTokenA,
165 length: 1,
166 },
167 RemainingAccountsSlice {
168 accounts_type: AccountsType::PoolVaultTokenB,
169 length: 1,
170 },
171 ],
172 },
173 },
174 &[
175 AccountMeta::new(swap_ticks_arrays[0], false),
176 AccountMeta::new(swap_ticks_arrays[1], false),
177 AccountMeta::new(swap_ticks_arrays[2], false),
178 AccountMeta::new(swap_ticks_arrays[3], false),
179 AccountMeta::new(swap_ticks_arrays[4], false),
180 AccountMeta::new(tick_array_lower_address, false),
181 AccountMeta::new(tick_array_upper_address, false),
182 AccountMeta::new(fusion_pool.token_vault_a, false),
183 AccountMeta::new(fusion_pool.token_vault_b, false),
184 ],
185 )
186}