defituna_client/txbuilder/
decrease_tuna_lp_position_orca.rs

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