Skip to main content

kvault_interface/instructions/
redeem.rs

1use borsh::BorshSerialize;
2use solana_instruction::{AccountMeta, Instruction};
3use solana_pubkey::Pubkey;
4
5use crate::{discriminators, pda, util::*, KVAULT_PROGRAM_ID};
6
7// ---------------------------------------------------------------------------
8// redeem_in_kind
9// ---------------------------------------------------------------------------
10
11/// Account addresses required by the `redeem_in_kind` instruction.
12pub struct RedeemInKindAccounts {
13    pub user: Pubkey,
14    pub vault_state: Pubkey,
15    pub global_config: Pubkey,
16    pub base_vault_authority: Pubkey,
17    pub reserve: Pubkey,
18    pub ctoken_vault: Pubkey,
19    pub user_ctoken_ta: Pubkey,
20    pub ctoken_mint: Pubkey,
21    pub user_shares_ta: Pubkey,
22    pub shares_mint: Pubkey,
23    pub reserve_collateral_token_program: Pubkey,
24    pub shares_token_program: Pubkey,
25    pub klend_program: Pubkey,
26}
27
28/// Build a raw `redeem_in_kind` instruction.
29///
30/// Burns `shares_amount` vault shares and transfers the proportional
31/// cTokens directly to the user instead of redeeming them for the
32/// underlying token. Pass `u64::MAX` to burn the caller's entire share
33/// balance — the program clamps `shares_amount` to the balance actually held.
34/// `remaining_accounts` must contain the refresh list: writable reserve metas
35/// followed by readonly lending-market metas, in allocation-slot order.
36///
37/// # Farms
38///
39/// If the vault has a share farm (`VaultState::vault_farm` is set), the
40/// caller's shares are staked in the farm and must be unstaked from it (a
41/// separate Kamino Farms instruction, prepended to the transaction) before
42/// they can be burned here.
43pub fn redeem_in_kind(
44    accounts: RedeemInKindAccounts,
45    shares_amount: u64,
46    remaining_accounts: Vec<AccountMeta>,
47) -> Instruction {
48    #[derive(BorshSerialize)]
49    struct Args {
50        shares_amount: u64,
51    }
52
53    let args = Args { shares_amount };
54    let mut data = discriminators::REDEEM_IN_KIND.to_vec();
55    args.serialize(&mut data).unwrap();
56
57    let (evt_auth, _) = pda::event_authority(&KVAULT_PROGRAM_ID);
58    let mut account_metas = vec![
59        signer(accounts.user),
60        writable(accounts.vault_state),
61        readonly(accounts.global_config),
62        readonly(accounts.base_vault_authority),
63        writable(accounts.reserve),
64        writable(accounts.ctoken_vault),
65        writable(accounts.user_ctoken_ta),
66        writable(accounts.ctoken_mint),
67        writable(accounts.user_shares_ta),
68        writable(accounts.shares_mint),
69        readonly(accounts.reserve_collateral_token_program),
70        readonly(accounts.shares_token_program),
71        readonly(accounts.klend_program),
72        // event_cpi accounts
73        readonly(evt_auth),
74        readonly(KVAULT_PROGRAM_ID),
75    ];
76    account_metas.extend(remaining_accounts);
77
78    Instruction {
79        program_id: KVAULT_PROGRAM_ID,
80        accounts: account_metas,
81        data,
82    }
83}