Skip to main content

defituna_client/txbuilder/
decrease_tuna_lp_position_fusion.rs

1use crate::accounts::{fetch_all_vault, fetch_market, 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, 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 market_address = get_market_address(&tuna_position.data.pool).0;
48    let market = fetch_market(&rpc, &market_address)?;
49
50    let vaults = fetch_all_vault(&rpc, &[market.data.vault_a, market.data.vault_b])?;
51    let (vault_a, vault_b) = (&vaults[0], &vaults[1]);
52
53    let all_mint_addresses = vec![mint_a_address, mint_b_address];
54    let mint_accounts = rpc.get_multiple_accounts(all_mint_addresses[0..all_mint_addresses.len()].into())?;
55    let mint_a_account = mint_accounts[0].as_ref().ok_or(anyhow!("Token A mint account not found"))?;
56    let mint_b_account = mint_accounts[1].as_ref().ok_or(anyhow!("Token B mint account not found"))?;
57
58    let authority_ata_a_instructions = get_create_ata_instructions(&mint_a_address, authority, authority, &mint_a_account.owner, 0);
59    let authority_ata_b_instructions = get_create_ata_instructions(&mint_b_address, authority, authority, &mint_b_account.owner, 0);
60
61    let mut instructions = vec![];
62    instructions.extend(authority_ata_a_instructions.create);
63    instructions.extend(authority_ata_b_instructions.create);
64
65    instructions.push(decrease_tuna_lp_position_fusion_instruction(
66        authority,
67        &tuna_position.data,
68        &vault_a.address,
69        &vault_a.data,
70        &vault_b.address,
71        &vault_b.data,
72        &fusion_pool.data,
73        &mint_a_account.owner,
74        &mint_b_account.owner,
75        args,
76    ));
77
78    instructions.extend(authority_ata_a_instructions.cleanup);
79    instructions.extend(authority_ata_b_instructions.cleanup);
80
81    Ok(instructions)
82}
83
84pub fn decrease_tuna_lp_position_fusion_instruction(
85    authority: &Pubkey,
86    tuna_position: &TunaLpPosition,
87    vault_a_address: &Pubkey,
88    vault_a: &Vault,
89    vault_b_address: &Pubkey,
90    vault_b: &Vault,
91    fusion_pool: &FusionPool,
92    token_program_a: &Pubkey,
93    token_program_b: &Pubkey,
94    args: DecreaseTunaLpPositionArgs,
95) -> Instruction {
96    let mint_a = fusion_pool.token_mint_a;
97    let mint_b = fusion_pool.token_mint_b;
98
99    assert_eq!(vault_a.mint, mint_a);
100    assert_eq!(vault_b.mint, mint_b);
101    assert_eq!(tuna_position.mint_a, mint_a);
102    assert_eq!(tuna_position.mint_b, mint_b);
103
104    let tuna_config_address = get_tuna_config_address().0;
105    let market_address = get_market_address(&tuna_position.pool).0;
106    let tuna_position_address = get_tuna_liquidity_position_address(&tuna_position.position_mint).0;
107
108    let tuna_position_owner_ata_a = get_associated_token_address_with_program_id(&authority, &mint_a, token_program_a);
109    let tuna_position_owner_ata_b = get_associated_token_address_with_program_id(&authority, &mint_b, token_program_b);
110
111    let fusion_pool_address = tuna_position.pool;
112
113    let tick_array_lower_start_tick_index = get_tick_array_start_tick_index(tuna_position.tick_lower_index, fusion_pool.tick_spacing);
114    let tick_array_lower_address = get_tick_array_address(&fusion_pool_address, tick_array_lower_start_tick_index).unwrap().0;
115
116    let tick_array_upper_start_tick_index = get_tick_array_start_tick_index(tuna_position.tick_upper_index, fusion_pool.tick_spacing);
117    let tick_array_upper_address = get_tick_array_address(&fusion_pool_address, tick_array_upper_start_tick_index).unwrap().0;
118
119    let swap_ticks_arrays = get_swap_tick_arrays(fusion_pool.tick_current_index, fusion_pool.tick_spacing, &fusion_pool_address);
120
121    let ix_builder = DecreaseTunaLpPositionFusion {
122        authority: *authority,
123        tuna_config: tuna_config_address,
124        mint_a: tuna_position.mint_a,
125        mint_b: tuna_position.mint_b,
126        market: market_address,
127        vault_a: *vault_a_address,
128        vault_b: *vault_b_address,
129        vault_a_ata: get_associated_token_address_with_program_id(vault_a_address, &tuna_position.mint_a, token_program_a),
130        vault_b_ata: get_associated_token_address_with_program_id(vault_b_address, &tuna_position.mint_b, token_program_b),
131        tuna_position: tuna_position_address,
132        tuna_position_ata: get_associated_token_address_with_program_id(&tuna_position_address, &tuna_position.position_mint, &spl_token_2022::ID),
133        tuna_position_ata_a: get_associated_token_address_with_program_id(&tuna_position_address, &tuna_position.mint_a, token_program_a),
134        tuna_position_ata_b: get_associated_token_address_with_program_id(&tuna_position_address, &tuna_position.mint_b, token_program_b),
135        tuna_position_owner_ata_a,
136        tuna_position_owner_ata_b,
137        oracle_price_update_a: vault_a.oracle_price_update,
138        oracle_price_update_b: vault_b.oracle_price_update,
139        fusionamm_program: fusionamm_client::ID,
140        fusion_pool: fusion_pool_address,
141        fusion_position: get_position_address(&tuna_position.position_mint).unwrap().0,
142        token_program_a: *token_program_a,
143        token_program_b: *token_program_b,
144        memo_program: spl_memo::ID,
145    };
146
147    ix_builder.instruction_with_remaining_accounts(
148        DecreaseTunaLpPositionFusionInstructionArgs {
149            decrease_percent: args.decrease_percent,
150            swap_to_token: args.swap_to_token,
151            min_removed_amount_a: args.min_removed_amount_a,
152            min_removed_amount_b: args.min_removed_amount_b,
153            max_swap_slippage: args.max_swap_slippage,
154            remaining_accounts_info: RemainingAccountsInfo {
155                slices: vec![
156                    RemainingAccountsSlice {
157                        accounts_type: AccountsType::SwapTickArrays,
158                        length: 5,
159                    },
160                    RemainingAccountsSlice {
161                        accounts_type: AccountsType::TickArrayLower,
162                        length: 1,
163                    },
164                    RemainingAccountsSlice {
165                        accounts_type: AccountsType::TickArrayUpper,
166                        length: 1,
167                    },
168                    RemainingAccountsSlice {
169                        accounts_type: AccountsType::PoolVaultTokenA,
170                        length: 1,
171                    },
172                    RemainingAccountsSlice {
173                        accounts_type: AccountsType::PoolVaultTokenB,
174                        length: 1,
175                    },
176                ],
177            },
178        },
179        &[
180            AccountMeta::new(swap_ticks_arrays[0], false),
181            AccountMeta::new(swap_ticks_arrays[1], false),
182            AccountMeta::new(swap_ticks_arrays[2], false),
183            AccountMeta::new(swap_ticks_arrays[3], false),
184            AccountMeta::new(swap_ticks_arrays[4], false),
185            AccountMeta::new(tick_array_lower_address, false),
186            AccountMeta::new(tick_array_upper_address, false),
187            AccountMeta::new(fusion_pool.token_vault_a, false),
188            AccountMeta::new(fusion_pool.token_vault_b, false),
189        ],
190    )
191}