Skip to main content

kvault_interface/helpers/
withdraw.rs

1use solana_instruction::Instruction;
2use solana_pubkey::Pubkey;
3
4use super::common::{derive_vault_pdas, refresh_remaining_accounts};
5use super::info::{ReserveInfo, VaultInfo};
6use crate::instructions;
7use crate::{pda, KLEND_PROGRAM_ID, KVAULT_PROGRAM_ID, SYSVAR_INSTRUCTIONS_ID, TOKEN_PROGRAM_ID};
8
9/// Build a `withdraw` instruction (from available + from invested via a single reserve).
10///
11/// Burns `shares_amount` vault shares and returns the underlying tokens.
12/// If available liquidity is insufficient, the program automatically
13/// disinvests from the specified `reserve`. PDAs and remaining accounts
14/// are derived automatically.
15pub fn withdraw(
16    vault: &VaultInfo,
17    user: Pubkey,
18    user_token_ata: Pubkey,
19    user_shares_ata: Pubkey,
20    reserve: &ReserveInfo,
21    shares_amount: u64,
22) -> Instruction {
23    let pdas = derive_vault_pdas(&vault.address);
24    let (global_config, _) = pda::global_config(&KVAULT_PROGRAM_ID);
25    let (ctoken_vault, _) = pda::ctoken_vault(&KVAULT_PROGRAM_ID, &vault.address, &reserve.address);
26    let (lending_market_authority, _) =
27        klend_interface::pda::lending_market_authority(&KLEND_PROGRAM_ID, &reserve.lending_market);
28    let remaining = refresh_remaining_accounts(vault);
29
30    instructions::withdraw::withdraw(
31        instructions::withdraw::WithdrawAccounts {
32            user,
33            vault_state: vault.address,
34            global_config,
35            token_vault: pdas.token_vault,
36            base_vault_authority: pdas.base_vault_authority,
37            user_token_ata,
38            token_mint: vault.token_mint,
39            user_shares_ata,
40            shares_mint: pdas.shares_mint,
41            token_program: vault.token_program,
42            shares_token_program: TOKEN_PROGRAM_ID,
43            klend_program: KLEND_PROGRAM_ID,
44            invested_vault_state: vault.address,
45            reserve: reserve.address,
46            ctoken_vault,
47            lending_market: reserve.lending_market,
48            lending_market_authority,
49            reserve_liquidity_supply: reserve.liquidity_supply_vault,
50            reserve_collateral_mint: reserve.collateral_mint,
51            reserve_collateral_token_program: TOKEN_PROGRAM_ID,
52            instruction_sysvar_account: SYSVAR_INSTRUCTIONS_ID,
53        },
54        shares_amount,
55        remaining,
56    )
57}
58
59/// Build a `sell` instruction.
60///
61/// Same accounts and semantics as [`withdraw`], but uses the `sell`
62/// discriminator. The `shares_amount` is interpreted as shares to sell.
63pub fn sell(
64    vault: &VaultInfo,
65    user: Pubkey,
66    user_token_ata: Pubkey,
67    user_shares_ata: Pubkey,
68    reserve: &ReserveInfo,
69    shares_amount: u64,
70) -> Instruction {
71    let pdas = derive_vault_pdas(&vault.address);
72    let (global_config, _) = pda::global_config(&KVAULT_PROGRAM_ID);
73    let (ctoken_vault, _) = pda::ctoken_vault(&KVAULT_PROGRAM_ID, &vault.address, &reserve.address);
74    let (lending_market_authority, _) =
75        klend_interface::pda::lending_market_authority(&KLEND_PROGRAM_ID, &reserve.lending_market);
76    let remaining = refresh_remaining_accounts(vault);
77
78    instructions::withdraw::sell(
79        instructions::withdraw::WithdrawAccounts {
80            user,
81            vault_state: vault.address,
82            global_config,
83            token_vault: pdas.token_vault,
84            base_vault_authority: pdas.base_vault_authority,
85            user_token_ata,
86            token_mint: vault.token_mint,
87            user_shares_ata,
88            shares_mint: pdas.shares_mint,
89            token_program: vault.token_program,
90            shares_token_program: TOKEN_PROGRAM_ID,
91            klend_program: KLEND_PROGRAM_ID,
92            invested_vault_state: vault.address,
93            reserve: reserve.address,
94            ctoken_vault,
95            lending_market: reserve.lending_market,
96            lending_market_authority,
97            reserve_liquidity_supply: reserve.liquidity_supply_vault,
98            reserve_collateral_mint: reserve.collateral_mint,
99            reserve_collateral_token_program: TOKEN_PROGRAM_ID,
100            instruction_sysvar_account: SYSVAR_INSTRUCTIONS_ID,
101        },
102        shares_amount,
103        remaining,
104    )
105}
106
107/// Build a `withdraw_from_available` instruction.
108///
109/// Burns `shares_amount` vault shares and returns tokens exclusively from
110/// the vault's available (uninvested) balance. No reserve disinvestment
111/// occurs — fails if available liquidity is insufficient. PDAs and
112/// remaining accounts are derived automatically.
113pub fn withdraw_from_available(
114    vault: &VaultInfo,
115    user: Pubkey,
116    user_token_ata: Pubkey,
117    user_shares_ata: Pubkey,
118    shares_amount: u64,
119) -> Instruction {
120    let pdas = derive_vault_pdas(&vault.address);
121    let (global_config, _) = pda::global_config(&KVAULT_PROGRAM_ID);
122    let remaining = refresh_remaining_accounts(vault);
123
124    instructions::withdraw::withdraw_from_available(
125        instructions::withdraw::WithdrawFromAvailableAccounts {
126            user,
127            vault_state: vault.address,
128            global_config,
129            token_vault: pdas.token_vault,
130            base_vault_authority: pdas.base_vault_authority,
131            user_token_ata,
132            token_mint: vault.token_mint,
133            user_shares_ata,
134            shares_mint: pdas.shares_mint,
135            token_program: vault.token_program,
136            shares_token_program: TOKEN_PROGRAM_ID,
137            klend_program: KLEND_PROGRAM_ID,
138        },
139        shares_amount,
140        remaining,
141    )
142}