> ## Documentation Index
> Fetch the complete documentation index at: https://kamino.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Withdraw

> Withdraw assets from Kamino Earn vaults

Withdraw assets from Kamino Earn vaults to redeem your shares for underlying tokens. The SDK handles transaction building and vault interaction.

## Complete Flow

Implement the full withdrawal flow either in an off-chain TypeScript client or via on-chain Rust CPI within your Anchor program.

<Tabs>
  <Tab title="TypeScript">
    <Steps>
      <Step>
        ### Import Dependencies

        Import the required packages for Solana RPC communication, Kamino SDK operations, and Kit transaction building.

        ```typescript theme={null}
        import {
          createSolanaRpc,
          createSolanaRpcSubscriptions,
          address,
          pipe,
          createTransactionMessage,
          setTransactionMessageFeePayerSigner,
          setTransactionMessageLifetimeUsingBlockhash,
          appendTransactionMessageInstructions,
          signTransactionMessageWithSigners,
          sendAndConfirmTransactionFactory,
          getSignatureFromTransaction,
        } from '@solana/kit';
        import { KaminoVault } from '@kamino-finance/klend-sdk';
        import { parseKeypairFile } from '@kamino-finance/klend-sdk/dist/utils/signer.js';
        import { Decimal } from 'decimal.js';
        ```

        <Note>
          `@solana/kit` provides modern utilities for RPC, transaction building, and signing. `@kamino-finance/klend-sdk` contains vault operation methods.
        </Note>
      </Step>

      <Step>
        ### Load Keypair and Initialize Vault

        Load the keypair from file, initialize RPC connections, and create the vault instance.

        ```typescript theme={null}
        const KEYPAIR_FILE = '/path/to/your/keypair.json';
        const signer = await parseKeypairFile(KEYPAIR_FILE);

        const rpc = createSolanaRpc('https://api.mainnet-beta.solana.com');
        const rpcSubscriptions = createSolanaRpcSubscriptions('wss://api.mainnet-beta.solana.com');

        const vault = new KaminoVault(
          rpc,
          address('HDsayqAsDWy3QvANGqh2yNraqcD8Fnjgh73Mhb3WRS5E') // USDC vault
        );
        ```

        <Note>
          `parseKeypairFile` loads an existing keypair from a JSON file.
        </Note>
      </Step>

      <Step>
        ### Build Withdraw Instructions

        Generate withdraw instructions including optional unstaking instructions.

        ```typescript theme={null}
        const withdrawAmount = new Decimal(1.0);
        const bundle = await vault.withdrawIxs(signer, withdrawAmount);
        const instructions = [...(bundle.unstakeFromFarmIfNeededIxs || []), ...(bundle.withdrawIxs || [])];

        if (!instructions.length) {
          throw new Error('No instructions returned by Kamino SDK');
        }
        ```

        <Info>
          The `withdrawIxs` method returns both unstaking and withdraw instructions. The bundle ensures optimal handling by automatically unstaking deposited assets when needed. The amount represents **vault shares** to redeem.
        </Info>
      </Step>

      <Step>
        ### Build and Send Transaction

        Fetch the latest blockhash and construct the transaction message.

        ```typescript theme={null}
        const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();

        const transactionMessage = pipe(
          createTransactionMessage({ version: 0 }),
          (tx) => setTransactionMessageFeePayerSigner(signer, tx),
          (tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),
          (tx) => appendTransactionMessageInstructions(instructions, tx)
        );
        ```

        <Note>
          Kit's `pipe` function enables functional composition of transaction building steps for cleaner, more maintainable code.
        </Note>

        Sign and send the transaction with built-in confirmation.

        ```typescript theme={null}
        const signedTransaction = await signTransactionMessageWithSigners(transactionMessage);

        const signature = getSignatureFromTransaction(signedTransaction);

        await sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions })(signedTransaction, {
          commitment: 'confirmed',
          skipPreflight: true,
        });

        console.log('Withdraw successful! Signature:', signature);
        ```

        <Check>
          The withdrawal is complete. Your vault shares have been redeemed for the underlying assets.
        </Check>
      </Step>
    </Steps>
  </Tab>

  <Tab title="Rust">
    <Info>
      A standalone off-chain Rust client that calls the Kamino Vault program directly with your wallet keypair. Burns kVUSDC vault shares to redeem the underlying USDC at the current share price. Uses the same `Cargo.toml` and wallet setup as the deposit client; running deposit at least once leaves the kVUSDC shares this client burns.
    </Info>

    <Steps>
      <Step>
        ### Add Dependencies

        ```toml theme={null}
        [dependencies]
        solana-client      = "3"
        solana-instruction = "3"
        solana-keypair     = "3"
        solana-message     = "3"
        solana-pubkey      = "3"
        solana-signer      = "3"
        solana-transaction = "3"
        ```
      </Step>

      <Step>
        ### Set Up RPC Client and Wallet

        Load the Solana CLI wallet from `~/.config/solana/id.json` and create an RPC client.

        ```rust theme={null}
        use solana_client::rpc_client::RpcClient;
        use solana_keypair::read_keypair_file;
        use solana_signer::Signer;

        let rpc = RpcClient::new("https://api.mainnet-beta.solana.com".to_string());

        let home = std::env::var("HOME").unwrap();
        let user = read_keypair_file(format!("{}/.config/solana/id.json", home))
            .expect("Failed to load wallet from ~/.config/solana/id.json");
        ```
      </Step>

      <Step>
        ### Define Program IDs and Vault Constants

        Add the Steakhouse USDC vault state, allocated klend reserves, lending markets, and the withdraw discriminator.

        ```rust theme={null}
        use solana_pubkey::{pubkey, Pubkey};

        const KVAULT_PROGRAM_ID:  Pubkey = pubkey!("KvauGMspG5k6rtzrqqn7WNn3oZdyKqLKwK2XWQ8FLjd");
        const KLEND_PROGRAM_ID:   Pubkey = pubkey!("KLend2g3cP87fffoy8q1mQqGKjrxjC8boSyAYavgmjD");
        const TOKEN_PROGRAM:      Pubkey = pubkey!("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");
        const ATA_PROGRAM:        Pubkey = pubkey!("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL");
        const SYSTEM_PROGRAM:     Pubkey = pubkey!("11111111111111111111111111111111");
        const INSTRUCTIONS_SYSVAR: Pubkey = pubkey!("Sysvar1nstructions1111111111111111111111111");

        const VAULT_STATE:          Pubkey = pubkey!("HDsayqAsDWy3QvANGqh2yNraqcD8Fnjgh73Mhb3WRS5E");
        const TOKEN_VAULT:          Pubkey = pubkey!("CKTEDx5z19CntAB9B66AxuS98S1NuCgMvfpsew7TQwi");
        const TOKEN_MINT:           Pubkey = pubkey!("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v");
        const SHARES_MINT:          Pubkey = pubkey!("7D8C5pDFxug58L9zkwK7bCiDg4kD4AygzbcZUmf5usHS");
        const BASE_VAULT_AUTHORITY: Pubkey = pubkey!("AyY6VCkHfTWdFs7SqBbu6AnCqLUhgzVHBzW3WcJu5Jc8");
        const GLOBAL_CONFIG:        Pubkey = pubkey!("BKyTcUe6daNG8HbgBix2ugdRHbykG2dK9hPBBqhUyoEX");

        const RESERVES: &[Pubkey] = &[
            pubkey!("Ga4rZytCpq1unD4DbEJ5bkHeUz9g3oh9AAFEi6vSauXp"),
            pubkey!("D6q6wuQSrifJKZYpR1M8R4YawnLDtDsMmWM1NbBmgJ59"),
            pubkey!("9FRZvAsjDJ6WM8BJ2S45h9PoDCLAq8DNY9zZDX7MyGzT"),
            pubkey!("Atj6UREVWa7WxbF2EMKNyfmYUY1U1txughe2gjhcPDCo"),
        ];

        const LENDING_MARKETS: &[Pubkey] = &[
            pubkey!("DxXdAyU3kCjnyggvHmY5nAwg5cRbbmdyX3npfDMjjMek"),
            pubkey!("7u3HeHxYDLhnCoErrtycNokbQYbWGzLs6JSDqGAv5PfF"),
            pubkey!("GMqmFygF5iSm5nkckYU6tieggFcR42SyjkkhK5rswFRs"),
            pubkey!("6WEGfej9B9wjxRs6t4BYpb9iCXd8CpTpJ8fVSNzHCC5y"),
        ];

        const REDEEM_RESERVE:        Pubkey = pubkey!("D6q6wuQSrifJKZYpR1M8R4YawnLDtDsMmWM1NbBmgJ59");
        const REDEEM_CTOKEN_VAULT:   Pubkey = pubkey!("CZg8x8oqB7FYUfURq15F5AcjRTymcXsc8ann76CrpJrf");
        const REDEEM_LENDING_MARKET: Pubkey = pubkey!("7u3HeHxYDLhnCoErrtycNokbQYbWGzLs6JSDqGAv5PfF");

        const RESERVE_LIQUIDITY_SUPPLY_OFFSET: usize = 160;
        const RESERVE_COLLATERAL_MINT_OFFSET:  usize = 2560;

        const WITHDRAW_DISCRIMINATOR: [u8; 8] = [183, 18, 70, 156, 148, 109, 161, 34];
        const SHARES_AMOUNT: u64 = 959_664;
        ```

        <Note>
          `WITHDRAW_DISCRIMINATOR` is the 8-byte instruction selector kvault expects on the wire: the first 8 bytes of `sha256("global:withdraw")`. `SHARES_AMOUNT` is in raw kVUSDC units (6 decimals); `959_664` ≈ all the shares from a 1 USDC deposit (returns ≈ 0.9997 USDC after fees).
        </Note>

        <Note>
          `REDEEM_*` are the klend-side accounts kvault uses when `tokenAvailable` is too low to satisfy a withdrawal directly — kvault then redeems cTokens from a klend reserve.
        </Note>
      </Step>

      <Step>
        ### Derive User Token Accounts

        Compute the user's USDC and kVUSDC ATAs and the kvault event-authority PDA.

        ```rust theme={null}
        let user_token_ata  = ata(&user.pubkey(), &TOKEN_MINT);
        let user_shares_ata = ata(&user.pubkey(), &SHARES_MINT);
        let (event_authority, _) =
            Pubkey::find_program_address(&[b"__event_authority"], &KVAULT_PROGRAM_ID);
        ```

        ```rust theme={null}
        fn ata(owner: &Pubkey, mint: &Pubkey) -> Pubkey {
            Pubkey::find_program_address(
                &[owner.as_ref(), TOKEN_PROGRAM.as_ref(), mint.as_ref()],
                &ATA_PROGRAM,
            )
            .0
        }
        ```
      </Step>

      <Step>
        ### Send Setup Transaction

        Make sure the USDC ATA exists. The `create_idempotent` instruction is a no-op if the ATA is already there.

        ```rust theme={null}
        use solana_instruction::{AccountMeta, Instruction};
        use solana_message::{Message, VersionedMessage};
        use solana_transaction::versioned::VersionedTransaction;

        let setup_ix = ata_create_idempotent_ix(&user.pubkey(), &user.pubkey(), &TOKEN_MINT);
        let blockhash = rpc.get_latest_blockhash().unwrap();
        let setup_msg = Message::new_with_blockhash(&[setup_ix], Some(&user.pubkey()), &blockhash);
        let setup_tx = VersionedTransaction::try_new(VersionedMessage::Legacy(setup_msg), &[&user]).unwrap();
        rpc.send_and_confirm_transaction(&setup_tx).unwrap();
        ```

        ```rust theme={null}
        fn ata_create_idempotent_ix(payer: &Pubkey, owner: &Pubkey, mint: &Pubkey) -> Instruction {
            let ata_addr = ata(owner, mint);
            Instruction {
                program_id: ATA_PROGRAM,
                accounts: vec![
                    AccountMeta::new(*payer, true),
                    AccountMeta::new(ata_addr, false),
                    AccountMeta::new_readonly(*owner, false),
                    AccountMeta::new_readonly(*mint, false),
                    AccountMeta::new_readonly(SYSTEM_PROGRAM, false),
                    AccountMeta::new_readonly(TOKEN_PROGRAM, false),
                ],
                data: vec![1],
            }
        }
        ```
      </Step>

      <Step>
        ### Look Up Redemption Reserve Klend Accounts

        Fetch the redemption reserve account, parse `liquidity.supplyVault` and `collateral.mint` at fixed offsets, and derive the `lending_market_authority` PDA from the klend program seeds.

        ```rust theme={null}
        let reserve_acct = rpc.get_account(&REDEEM_RESERVE).unwrap();
        let reserve_liquidity_supply = read_pubkey(&reserve_acct.data, RESERVE_LIQUIDITY_SUPPLY_OFFSET);
        let reserve_collateral_mint  = read_pubkey(&reserve_acct.data, RESERVE_COLLATERAL_MINT_OFFSET);
        let (lending_market_authority, _) = Pubkey::find_program_address(
            &[b"lma", REDEEM_LENDING_MARKET.as_ref()],
            &KLEND_PROGRAM_ID,
        );
        ```

        ```rust theme={null}
        fn read_pubkey(data: &[u8], offset: usize) -> Pubkey {
            Pubkey::new_from_array(data[offset..offset + 32].try_into().unwrap())
        }
        ```
      </Step>

      <Step>
        ### Build the Withdraw Instruction

        Build `kamino_vault::withdraw` with the discriminator, the shares amount, and the full set of account metas — using the dynamically resolved klend accounts in the reserve-group slots.

        ```rust theme={null}
        let withdraw_ix = build_withdraw_ix(
            &user.pubkey(),
            &user_token_ata,
            &user_shares_ata,
            &event_authority,
            &lending_market_authority,
            &reserve_liquidity_supply,
            &reserve_collateral_mint,
            SHARES_AMOUNT,
        );
        ```

        ```rust theme={null}
        fn build_withdraw_ix(
            user: &Pubkey,
            user_token_ata: &Pubkey,
            user_shares_ata: &Pubkey,
            event_authority: &Pubkey,
            lending_market_authority: &Pubkey,
            reserve_liquidity_supply: &Pubkey,
            reserve_collateral_mint: &Pubkey,
            shares_amount: u64,
        ) -> Instruction {
            let mut data = Vec::with_capacity(16);
            data.extend_from_slice(&WITHDRAW_DISCRIMINATOR);
            data.extend_from_slice(&shares_amount.to_le_bytes());

            let mut metas = vec![
                AccountMeta::new(*user, true),
                AccountMeta::new(VAULT_STATE, false),
                AccountMeta::new_readonly(GLOBAL_CONFIG, false),
                AccountMeta::new(TOKEN_VAULT, false),
                AccountMeta::new_readonly(BASE_VAULT_AUTHORITY, false),
                AccountMeta::new(*user_token_ata, false),
                AccountMeta::new(TOKEN_MINT, false),
                AccountMeta::new(*user_shares_ata, false),
                AccountMeta::new(SHARES_MINT, false),
                AccountMeta::new_readonly(TOKEN_PROGRAM, false),
                AccountMeta::new_readonly(TOKEN_PROGRAM, false),
                AccountMeta::new_readonly(KLEND_PROGRAM_ID, false),
                AccountMeta::new_readonly(*event_authority, false),
                AccountMeta::new_readonly(KVAULT_PROGRAM_ID, false),
                AccountMeta::new(VAULT_STATE, false),
                AccountMeta::new(REDEEM_RESERVE, false),
                AccountMeta::new(REDEEM_CTOKEN_VAULT, false),
                AccountMeta::new_readonly(REDEEM_LENDING_MARKET, false),
                AccountMeta::new_readonly(*lending_market_authority, false),
                AccountMeta::new(*reserve_liquidity_supply, false),
                AccountMeta::new(*reserve_collateral_mint, false),
                AccountMeta::new_readonly(TOKEN_PROGRAM, false),
                AccountMeta::new_readonly(INSTRUCTIONS_SYSVAR, false),
                AccountMeta::new_readonly(*event_authority, false),
                AccountMeta::new_readonly(KVAULT_PROGRAM_ID, false),
            ];
            for r in RESERVES {
                metas.push(AccountMeta::new(*r, false));
            }
            for m in LENDING_MARKETS {
                metas.push(AccountMeta::new_readonly(*m, false));
            }

            Instruction {
                program_id: KVAULT_PROGRAM_ID,
                accounts: metas,
                data,
            }
        }
        ```

        <Note>
          Instruction data is `[discriminator (8 bytes) | shares_amount (u64 LE, 8 bytes)]`.
        </Note>
      </Step>

      <Step>
        ### Send the Withdraw Transaction

        Send the withdraw transaction and print the resulting signature.

        ```rust theme={null}
        let blockhash = rpc.get_latest_blockhash().unwrap();
        let msg = Message::new_with_blockhash(&[withdraw_ix], Some(&user.pubkey()), &blockhash);
        let tx = VersionedTransaction::try_new(VersionedMessage::Legacy(msg), &[&user]).unwrap();
        let sig = rpc.send_and_confirm_transaction(&tx).unwrap();
        println!("Withdraw tx: {}", sig);
        ```

        <Check>
          The withdrawal is complete. Your kVUSDC shares have been burned and the underlying USDC has been transferred to your wallet at the current share price.
        </Check>
      </Step>
    </Steps>
  </Tab>

  <Tab title="Rust (CPI)">
    <Info>
      This example shows how an **on-chain Anchor program** can CPI into a Kamino Vault to withdraw assets. A PDA authority owns the user token and shares vaults and signs the CPI via `invoke_signed`.
    </Info>

    <Steps>
      <Step>
        ### Add Dependencies

        ```toml theme={null}
        [dependencies]
        anchor-lang = "0.30"
        anchor-spl = "0.30"
        solana-program = "2.1"
        ```
      </Step>

      <Step>
        ### Define Program IDs and Discriminator

        ```rust theme={null}
        use anchor_lang::prelude::*;
        use anchor_lang::solana_program::{
            instruction::{AccountMeta, Instruction},
            program::invoke_signed,
        };
        use anchor_spl::token::{Token, TokenAccount};

        declare_id!("YourProgram1111111111111111111111111111111111");

        const AUTHORITY_SEED: &[u8] = b"vault_authority";

        pub const KVAULT_PROGRAM_ID: Pubkey = pubkey!("KvauGMspG5k6rtzrqqn7WNn3oZdyKqLKwK2XWQ8FLjd");
        pub const KLEND_PROGRAM_ID: Pubkey = pubkey!("KLend2g3cP87fffoy8q1mQqGKjrxjC8boSyAYavgmjD");

        // sha256("global:withdraw")[..8]
        pub const KVAULT_WITHDRAW_DISCRIMINATOR: [u8; 8] = [183, 18, 70, 156, 148, 109, 161, 34];
        ```

        <AccordionGroup>
          <Accordion title="Mainnet Program IDs">
            <br />

            | Constant            | Address                                       |
            | ------------------- | --------------------------------------------- |
            | `KVAULT_PROGRAM_ID` | `KvauGMspG5k6rtzrqqn7WNn3oZdyKqLKwK2XWQ8FLjd` |
            | `KLEND_PROGRAM_ID`  | `KLend2g3cP87fffoy8q1mQqGKjrxjC8boSyAYavgmjD` |
          </Accordion>

          <Accordion title="Devnet Program IDs">
            <br />

            | Constant            | Address                                       |
            | ------------------- | --------------------------------------------- |
            | `KVAULT_PROGRAM_ID` | `devkRngFnfp4gBc5a3LsadgbQKdPo8MSZ4prFiNSVmY` |
            | `KLEND_PROGRAM_ID`  | `KLend2g3cP87fffoy8q1mQqGKjrxjC8boSyAYavgmjD` |
          </Accordion>
        </AccordionGroup>
      </Step>

      <Step>
        ### Define the Withdraw Handler

        ```rust theme={null}
        #[program]
        pub mod kvault_ops {
            use super::*;

            pub fn withdraw<'info>(ctx: Context<'info, Withdraw<'info>>, shares_amount: u64) -> Result<()> {
                let vault_state_key = ctx.accounts.vault_state.key();
                let authority_seeds: &[&[u8]] = &[
                    AUTHORITY_SEED,
                    vault_state_key.as_ref(),
                    &[ctx.bumps.authority],
                ];
        ```
      </Step>

      <Step>
        ### Build the Withdraw Instruction Data

        Pack the discriminator + `shares_amount` (little-endian u64) as instruction data.

        ```rust theme={null}
                // data = [discriminator (8 bytes) | shares_amount as u64 LE (8 bytes)]
                let mut data = Vec::with_capacity(16);
                data.extend_from_slice(&KVAULT_WITHDRAW_DISCRIMINATOR);
                data.extend_from_slice(&shares_amount.to_le_bytes());
        ```
      </Step>

      <Step>
        ### Assemble Account Metas

        Withdraw flattens two IDL groups into 25 `AccountMeta`s. Some accounts repeat across groups: `vault_state` at indices 1 & 14, and `event_authority` / `kvault_program` at the end of each group.

        ```rust theme={null}
                let metas = vec![
                    // withdrawFromAvailable (14)
                    AccountMeta::new(ctx.accounts.authority.key(), true),
                    AccountMeta::new(ctx.accounts.vault_state.key(), false),
                    AccountMeta::new_readonly(ctx.accounts.global_config.key(), false),
                    AccountMeta::new(ctx.accounts.token_vault.key(), false),
                    AccountMeta::new_readonly(ctx.accounts.base_vault_authority.key(), false),
                    AccountMeta::new(ctx.accounts.user_token_ata.key(), false),
                    AccountMeta::new(ctx.accounts.token_mint.key(), false),
                    AccountMeta::new(ctx.accounts.user_shares_ata.key(), false),
                    AccountMeta::new(ctx.accounts.shares_mint.key(), false),
                    AccountMeta::new_readonly(ctx.accounts.token_program.key(), false),
                    AccountMeta::new_readonly(ctx.accounts.shares_token_program.key(), false),
                    AccountMeta::new_readonly(ctx.accounts.klend_program.key(), false),
                    AccountMeta::new_readonly(ctx.accounts.event_authority.key(), false),
                    AccountMeta::new_readonly(ctx.accounts.kvault_program.key(), false),
                    // withdrawFromReserveAccounts (9) — vault_state repeats
                    AccountMeta::new(ctx.accounts.vault_state.key(), false),
                    AccountMeta::new(ctx.accounts.reserve.key(), false),
                    AccountMeta::new(ctx.accounts.ctoken_vault.key(), false),
                    AccountMeta::new_readonly(ctx.accounts.lending_market.key(), false),
                    AccountMeta::new_readonly(ctx.accounts.lending_market_authority.key(), false),
                    AccountMeta::new(ctx.accounts.reserve_liquidity_supply.key(), false),
                    AccountMeta::new(ctx.accounts.reserve_collateral_mint.key(), false),
                    AccountMeta::new_readonly(ctx.accounts.reserve_collateral_token_program.key(), false),
                    AccountMeta::new_readonly(ctx.accounts.instruction_sysvar_account.key(), false),
                    // trailing — event_authority and kvault_program repeat
                    AccountMeta::new_readonly(ctx.accounts.event_authority.key(), false),
                    AccountMeta::new_readonly(ctx.accounts.kvault_program.key(), false),
                ];

                let ix = Instruction {
                    program_id: KVAULT_PROGRAM_ID,
                    accounts: metas,
                    data,
                };
        ```
      </Step>

      <Step>
        ### Invoke Signed and Sign with the PDA

        The `AccountInfo`s must be supplied in the same order as the `AccountMeta`s above — including the repeated entries.

        ```rust theme={null}
                let account_infos = vec![
                    ctx.accounts.authority.to_account_info(),
                    ctx.accounts.vault_state.to_account_info(),
                    ctx.accounts.global_config.to_account_info(),
                    ctx.accounts.token_vault.to_account_info(),
                    ctx.accounts.base_vault_authority.to_account_info(),
                    ctx.accounts.user_token_ata.to_account_info(),
                    ctx.accounts.token_mint.to_account_info(),
                    ctx.accounts.user_shares_ata.to_account_info(),
                    ctx.accounts.shares_mint.to_account_info(),
                    ctx.accounts.token_program.to_account_info(),
                    ctx.accounts.shares_token_program.to_account_info(),
                    ctx.accounts.klend_program.to_account_info(),
                    ctx.accounts.event_authority.to_account_info(),
                    ctx.accounts.kvault_program.to_account_info(),
                    ctx.accounts.vault_state.to_account_info(),
                    ctx.accounts.reserve.to_account_info(),
                    ctx.accounts.ctoken_vault.to_account_info(),
                    ctx.accounts.lending_market.to_account_info(),
                    ctx.accounts.lending_market_authority.to_account_info(),
                    ctx.accounts.reserve_liquidity_supply.to_account_info(),
                    ctx.accounts.reserve_collateral_mint.to_account_info(),
                    ctx.accounts
                        .reserve_collateral_token_program
                        .to_account_info(),
                    ctx.accounts.instruction_sysvar_account.to_account_info(),
                    ctx.accounts.event_authority.to_account_info(),
                    ctx.accounts.kvault_program.to_account_info(),
                ];

                invoke_signed(&ix, &account_infos, &[authority_seeds])?;
                Ok(())
            }
        }
        ```
      </Step>

      <Step>
        ### Account Validation Struct

        ```rust expandable title="Withdraw accounts" theme={null}
        #[derive(Accounts)]
        pub struct Withdraw<'info> {
            #[account(
                mut,
                seeds = [AUTHORITY_SEED, vault_state.key().as_ref()],
                bump,
            )]
            pub authority: SystemAccount<'info>,

            #[account(mut, token::authority = authority)]
            pub user_token_ata: Account<'info, TokenAccount>,

            #[account(mut, token::authority = authority)]
            pub user_shares_ata: Account<'info, TokenAccount>,

            /// CHECK: Kvault state account.
            #[account(mut)]
            pub vault_state: UncheckedAccount<'info>,
            /// CHECK: Kvault global config account.
            pub global_config: UncheckedAccount<'info>,
            /// CHECK: Token vault owned by the Kvault program.
            #[account(mut)]
            pub token_vault: UncheckedAccount<'info>,
            /// CHECK: Kvault base vault authority PDA.
            pub base_vault_authority: UncheckedAccount<'info>,
            /// CHECK: Underlying token mint.
            #[account(mut)]
            pub token_mint: UncheckedAccount<'info>,
            /// CHECK: Shares mint.
            #[account(mut)]
            pub shares_mint: UncheckedAccount<'info>,
            /// CHECK: Event-emit PDA owned by the Kvault program.
            pub event_authority: UncheckedAccount<'info>,

            /// CHECK: Klend reserve account.
            #[account(mut)]
            pub reserve: UncheckedAccount<'info>,
            /// CHECK: cToken vault owned by the Kvault program.
            #[account(mut)]
            pub ctoken_vault: UncheckedAccount<'info>,
            /// CHECK: Klend lending market.
            pub lending_market: UncheckedAccount<'info>,
            /// CHECK: Klend lending market authority PDA.
            pub lending_market_authority: UncheckedAccount<'info>,
            /// CHECK: Reserve's underlying liquidity supply vault.
            #[account(mut)]
            pub reserve_liquidity_supply: UncheckedAccount<'info>,
            /// CHECK: Reserve's cToken mint.
            #[account(mut)]
            pub reserve_collateral_mint: UncheckedAccount<'info>,
            /// CHECK: Reserve's cToken program.
            pub reserve_collateral_token_program: UncheckedAccount<'info>,
            /// CHECK: Solana instructions sysvar.
            #[account(address = pubkey!("Sysvar1nstructions1111111111111111111111111"))]
            pub instruction_sysvar_account: UncheckedAccount<'info>,

            /// CHECK: The Kvault program.
            #[account(address = KVAULT_PROGRAM_ID)]
            pub kvault_program: UncheckedAccount<'info>,
            /// CHECK: The Klend program.
            #[account(address = KLEND_PROGRAM_ID)]
            pub klend_program: UncheckedAccount<'info>,
            pub token_program: Program<'info, Token>,
            pub shares_token_program: Program<'info, Token>,
        }
        ```
      </Step>
    </Steps>
  </Tab>
</Tabs>

#### Full Code Example

<Tabs>
  <Tab title="TypeScript">
    ```typescript expandable theme={null}
    import {
      createSolanaRpc,
      createSolanaRpcSubscriptions,
      address,
      pipe,
      createTransactionMessage,
      setTransactionMessageFeePayerSigner,
      setTransactionMessageLifetimeUsingBlockhash,
      appendTransactionMessageInstructions,
      signTransactionMessageWithSigners,
      sendAndConfirmTransactionFactory,
      getSignatureFromTransaction,
    } from '@solana/kit';
    import { KaminoVault } from '@kamino-finance/klend-sdk';
    import { parseKeypairFile } from '@kamino-finance/klend-sdk/dist/utils/signer.js';
    import { Decimal } from 'decimal.js';

    // Configuration - UPDATE THESE VALUES
    const KEYPAIR_FILE = '/path/to/your/keypair.json';

    // Load keypair from file
    const signer = await parseKeypairFile(KEYPAIR_FILE);

    // Initialize RPC and RPC Subscriptions
    const rpc = createSolanaRpc('https://api.mainnet-beta.solana.com');
    const rpcSubscriptions = createSolanaRpcSubscriptions('wss://api.mainnet-beta.solana.com');
    const vault = new KaminoVault(
      rpc,
      address('HDsayqAsDWy3QvANGqh2yNraqcD8Fnjgh73Mhb3WRS5E') // USDC vault
    );

    // Build withdraw instructions (includes optional unstaking)
    const withdrawAmount = new Decimal(1.0);
    const bundle = await vault.withdrawIxs(signer, withdrawAmount);
    const instructions = [...(bundle.unstakeFromFarmIfNeededIxs || []), ...(bundle.withdrawIxs || [])];

    if (!instructions.length) {
      throw new Error('No instructions returned by Kamino SDK');
    }

    // Build and sign transaction using functional pipe pattern
    const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();

    const transactionMessage = pipe(
      createTransactionMessage({ version: 0 }),
      (tx) => setTransactionMessageFeePayerSigner(signer, tx),
      (tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),
      (tx) => appendTransactionMessageInstructions(instructions, tx)
    );

    const signedTransaction = await signTransactionMessageWithSigners(transactionMessage);

    // Send and confirm transaction
    const signature = getSignatureFromTransaction(signedTransaction);

    await sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions })(signedTransaction, {
      commitment: 'confirmed',
      skipPreflight: true,
    });

    console.log('Withdraw successful! Signature:', signature);
    ```
  </Tab>

  <Tab title="Rust">
    ```rust expandable theme={null}
    use solana_client::rpc_client::RpcClient;
    use solana_instruction::{AccountMeta, Instruction};
    use solana_keypair::read_keypair_file;
    use solana_message::{Message, VersionedMessage};
    use solana_pubkey::{pubkey, Pubkey};
    use solana_signer::Signer;
    use solana_transaction::versioned::VersionedTransaction;

    const KVAULT_PROGRAM_ID:  Pubkey = pubkey!("KvauGMspG5k6rtzrqqn7WNn3oZdyKqLKwK2XWQ8FLjd");
    const KLEND_PROGRAM_ID:   Pubkey = pubkey!("KLend2g3cP87fffoy8q1mQqGKjrxjC8boSyAYavgmjD");
    const TOKEN_PROGRAM:      Pubkey = pubkey!("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");
    const ATA_PROGRAM:        Pubkey = pubkey!("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL");
    const SYSTEM_PROGRAM:     Pubkey = pubkey!("11111111111111111111111111111111");
    const INSTRUCTIONS_SYSVAR: Pubkey = pubkey!("Sysvar1nstructions1111111111111111111111111");

    const VAULT_STATE:          Pubkey = pubkey!("HDsayqAsDWy3QvANGqh2yNraqcD8Fnjgh73Mhb3WRS5E");
    const TOKEN_VAULT:          Pubkey = pubkey!("CKTEDx5z19CntAB9B66AxuS98S1NuCgMvfpsew7TQwi");
    const TOKEN_MINT:           Pubkey = pubkey!("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v");
    const SHARES_MINT:          Pubkey = pubkey!("7D8C5pDFxug58L9zkwK7bCiDg4kD4AygzbcZUmf5usHS");
    const BASE_VAULT_AUTHORITY: Pubkey = pubkey!("AyY6VCkHfTWdFs7SqBbu6AnCqLUhgzVHBzW3WcJu5Jc8");
    const GLOBAL_CONFIG:        Pubkey = pubkey!("BKyTcUe6daNG8HbgBix2ugdRHbykG2dK9hPBBqhUyoEX");

    const RESERVES: &[Pubkey] = &[
        pubkey!("Ga4rZytCpq1unD4DbEJ5bkHeUz9g3oh9AAFEi6vSauXp"),
        pubkey!("D6q6wuQSrifJKZYpR1M8R4YawnLDtDsMmWM1NbBmgJ59"),
        pubkey!("9FRZvAsjDJ6WM8BJ2S45h9PoDCLAq8DNY9zZDX7MyGzT"),
        pubkey!("Atj6UREVWa7WxbF2EMKNyfmYUY1U1txughe2gjhcPDCo"),
    ];

    const LENDING_MARKETS: &[Pubkey] = &[
        pubkey!("DxXdAyU3kCjnyggvHmY5nAwg5cRbbmdyX3npfDMjjMek"),
        pubkey!("7u3HeHxYDLhnCoErrtycNokbQYbWGzLs6JSDqGAv5PfF"),
        pubkey!("GMqmFygF5iSm5nkckYU6tieggFcR42SyjkkhK5rswFRs"),
        pubkey!("6WEGfej9B9wjxRs6t4BYpb9iCXd8CpTpJ8fVSNzHCC5y"),
    ];

    const REDEEM_RESERVE:        Pubkey = pubkey!("D6q6wuQSrifJKZYpR1M8R4YawnLDtDsMmWM1NbBmgJ59");
    const REDEEM_CTOKEN_VAULT:   Pubkey = pubkey!("CZg8x8oqB7FYUfURq15F5AcjRTymcXsc8ann76CrpJrf");
    const REDEEM_LENDING_MARKET: Pubkey = pubkey!("7u3HeHxYDLhnCoErrtycNokbQYbWGzLs6JSDqGAv5PfF");

    const RESERVE_LIQUIDITY_SUPPLY_OFFSET: usize = 160;
    const RESERVE_COLLATERAL_MINT_OFFSET:  usize = 2560;

    const WITHDRAW_DISCRIMINATOR: [u8; 8] = [183, 18, 70, 156, 148, 109, 161, 34];
    const SHARES_AMOUNT: u64 = 959_664;

    fn main() {
        let rpc = RpcClient::new("https://api.mainnet-beta.solana.com".to_string());
        let home = std::env::var("HOME").unwrap();
        let user = read_keypair_file(format!("{}/.config/solana/id.json", home))
            .expect("Failed to load wallet from ~/.config/solana/id.json");
        println!("User wallet: {}", user.pubkey());

        let user_token_ata  = ata(&user.pubkey(), &TOKEN_MINT);
        let user_shares_ata = ata(&user.pubkey(), &SHARES_MINT);
        let (event_authority, _) =
            Pubkey::find_program_address(&[b"__event_authority"], &KVAULT_PROGRAM_ID);

        let setup_ix = ata_create_idempotent_ix(&user.pubkey(), &user.pubkey(), &TOKEN_MINT);
        let blockhash = rpc.get_latest_blockhash().unwrap();
        let setup_msg = Message::new_with_blockhash(&[setup_ix], Some(&user.pubkey()), &blockhash);
        let setup_tx = VersionedTransaction::try_new(VersionedMessage::Legacy(setup_msg), &[&user]).unwrap();
        rpc.send_and_confirm_transaction(&setup_tx).unwrap();

        let reserve_acct = rpc.get_account(&REDEEM_RESERVE).unwrap();
        let reserve_liquidity_supply = read_pubkey(&reserve_acct.data, RESERVE_LIQUIDITY_SUPPLY_OFFSET);
        let reserve_collateral_mint  = read_pubkey(&reserve_acct.data, RESERVE_COLLATERAL_MINT_OFFSET);
        let (lending_market_authority, _) = Pubkey::find_program_address(
            &[b"lma", REDEEM_LENDING_MARKET.as_ref()],
            &KLEND_PROGRAM_ID,
        );

        let withdraw_ix = build_withdraw_ix(
            &user.pubkey(),
            &user_token_ata,
            &user_shares_ata,
            &event_authority,
            &lending_market_authority,
            &reserve_liquidity_supply,
            &reserve_collateral_mint,
            SHARES_AMOUNT,
        );
        let blockhash = rpc.get_latest_blockhash().unwrap();
        let msg = Message::new_with_blockhash(&[withdraw_ix], Some(&user.pubkey()), &blockhash);
        let tx = VersionedTransaction::try_new(VersionedMessage::Legacy(msg), &[&user]).unwrap();
        let sig = rpc.send_and_confirm_transaction(&tx).unwrap();
        println!("Withdraw tx: {}", sig);
    }

    fn ata(owner: &Pubkey, mint: &Pubkey) -> Pubkey {
        Pubkey::find_program_address(
            &[owner.as_ref(), TOKEN_PROGRAM.as_ref(), mint.as_ref()],
            &ATA_PROGRAM,
        )
        .0
    }

    fn read_pubkey(data: &[u8], offset: usize) -> Pubkey {
        Pubkey::new_from_array(data[offset..offset + 32].try_into().unwrap())
    }

    fn ata_create_idempotent_ix(payer: &Pubkey, owner: &Pubkey, mint: &Pubkey) -> Instruction {
        let ata_addr = ata(owner, mint);
        Instruction {
            program_id: ATA_PROGRAM,
            accounts: vec![
                AccountMeta::new(*payer, true),
                AccountMeta::new(ata_addr, false),
                AccountMeta::new_readonly(*owner, false),
                AccountMeta::new_readonly(*mint, false),
                AccountMeta::new_readonly(SYSTEM_PROGRAM, false),
                AccountMeta::new_readonly(TOKEN_PROGRAM, false),
            ],
            data: vec![1],
        }
    }

    fn build_withdraw_ix(
        user: &Pubkey,
        user_token_ata: &Pubkey,
        user_shares_ata: &Pubkey,
        event_authority: &Pubkey,
        lending_market_authority: &Pubkey,
        reserve_liquidity_supply: &Pubkey,
        reserve_collateral_mint: &Pubkey,
        shares_amount: u64,
    ) -> Instruction {
        let mut data = Vec::with_capacity(16);
        data.extend_from_slice(&WITHDRAW_DISCRIMINATOR);
        data.extend_from_slice(&shares_amount.to_le_bytes());

        let mut metas = vec![
            AccountMeta::new(*user, true),
            AccountMeta::new(VAULT_STATE, false),
            AccountMeta::new_readonly(GLOBAL_CONFIG, false),
            AccountMeta::new(TOKEN_VAULT, false),
            AccountMeta::new_readonly(BASE_VAULT_AUTHORITY, false),
            AccountMeta::new(*user_token_ata, false),
            AccountMeta::new(TOKEN_MINT, false),
            AccountMeta::new(*user_shares_ata, false),
            AccountMeta::new(SHARES_MINT, false),
            AccountMeta::new_readonly(TOKEN_PROGRAM, false),
            AccountMeta::new_readonly(TOKEN_PROGRAM, false),
            AccountMeta::new_readonly(KLEND_PROGRAM_ID, false),
            AccountMeta::new_readonly(*event_authority, false),
            AccountMeta::new_readonly(KVAULT_PROGRAM_ID, false),
            AccountMeta::new(VAULT_STATE, false),
            AccountMeta::new(REDEEM_RESERVE, false),
            AccountMeta::new(REDEEM_CTOKEN_VAULT, false),
            AccountMeta::new_readonly(REDEEM_LENDING_MARKET, false),
            AccountMeta::new_readonly(*lending_market_authority, false),
            AccountMeta::new(*reserve_liquidity_supply, false),
            AccountMeta::new(*reserve_collateral_mint, false),
            AccountMeta::new_readonly(TOKEN_PROGRAM, false),
            AccountMeta::new_readonly(INSTRUCTIONS_SYSVAR, false),
            AccountMeta::new_readonly(*event_authority, false),
            AccountMeta::new_readonly(KVAULT_PROGRAM_ID, false),
        ];
        for r in RESERVES {
            metas.push(AccountMeta::new(*r, false));
        }
        for m in LENDING_MARKETS {
            metas.push(AccountMeta::new_readonly(*m, false));
        }

        Instruction {
            program_id: KVAULT_PROGRAM_ID,
            accounts: metas,
            data,
        }
    }
    ```
  </Tab>

  <Tab title="Rust (CPI)">
    ```rust expandable theme={null}
    use anchor_lang::prelude::*;
    use anchor_lang::solana_program::{
        instruction::{AccountMeta, Instruction},
        program::invoke_signed,
    };
    use anchor_spl::token::{Token, TokenAccount};

    declare_id!("YourProgram1111111111111111111111111111111111");

    const AUTHORITY_SEED: &[u8] = b"vault_authority";

    pub const KVAULT_PROGRAM_ID: Pubkey = pubkey!("KvauGMspG5k6rtzrqqn7WNn3oZdyKqLKwK2XWQ8FLjd");
    pub const KLEND_PROGRAM_ID: Pubkey = pubkey!("KLend2g3cP87fffoy8q1mQqGKjrxjC8boSyAYavgmjD");

    // sha256("global:withdraw")[..8]
    pub const KVAULT_WITHDRAW_DISCRIMINATOR: [u8; 8] = [183, 18, 70, 156, 148, 109, 161, 34];

    #[program]
    pub mod kvault_ops {
        use super::*;

        pub fn withdraw<'info>(ctx: Context<'info, Withdraw<'info>>, shares_amount: u64) -> Result<()> {
            let vault_state_key = ctx.accounts.vault_state.key();
            let authority_seeds: &[&[u8]] = &[
                AUTHORITY_SEED,
                vault_state_key.as_ref(),
                &[ctx.bumps.authority],
            ];

            // data = [discriminator (8 bytes) | shares_amount as u64 LE (8 bytes)]
            let mut data = Vec::with_capacity(16);
            data.extend_from_slice(&KVAULT_WITHDRAW_DISCRIMINATOR);
            data.extend_from_slice(&shares_amount.to_le_bytes());

            // 25 AccountMetas in IDL flattened order. Some accounts repeat across
            // groups (vault_state at 1 & 14; event_authority at 12 & 23;
            // kvault_program at 13 & 24).
            let metas = vec![
                // withdrawFromAvailable (14)
                AccountMeta::new(ctx.accounts.authority.key(), true),
                AccountMeta::new(ctx.accounts.vault_state.key(), false),
                AccountMeta::new_readonly(ctx.accounts.global_config.key(), false),
                AccountMeta::new(ctx.accounts.token_vault.key(), false),
                AccountMeta::new_readonly(ctx.accounts.base_vault_authority.key(), false),
                AccountMeta::new(ctx.accounts.user_token_ata.key(), false),
                AccountMeta::new(ctx.accounts.token_mint.key(), false),
                AccountMeta::new(ctx.accounts.user_shares_ata.key(), false),
                AccountMeta::new(ctx.accounts.shares_mint.key(), false),
                AccountMeta::new_readonly(ctx.accounts.token_program.key(), false),
                AccountMeta::new_readonly(ctx.accounts.shares_token_program.key(), false),
                AccountMeta::new_readonly(ctx.accounts.klend_program.key(), false),
                AccountMeta::new_readonly(ctx.accounts.event_authority.key(), false),
                AccountMeta::new_readonly(ctx.accounts.kvault_program.key(), false),
                // withdrawFromReserveAccounts (9) — vault_state repeats
                AccountMeta::new(ctx.accounts.vault_state.key(), false),
                AccountMeta::new(ctx.accounts.reserve.key(), false),
                AccountMeta::new(ctx.accounts.ctoken_vault.key(), false),
                AccountMeta::new_readonly(ctx.accounts.lending_market.key(), false),
                AccountMeta::new_readonly(ctx.accounts.lending_market_authority.key(), false),
                AccountMeta::new(ctx.accounts.reserve_liquidity_supply.key(), false),
                AccountMeta::new(ctx.accounts.reserve_collateral_mint.key(), false),
                AccountMeta::new_readonly(ctx.accounts.reserve_collateral_token_program.key(), false),
                AccountMeta::new_readonly(ctx.accounts.instruction_sysvar_account.key(), false),
                // trailing — event_authority and kvault_program repeat
                AccountMeta::new_readonly(ctx.accounts.event_authority.key(), false),
                AccountMeta::new_readonly(ctx.accounts.kvault_program.key(), false),
            ];

            let ix = Instruction {
                program_id: KVAULT_PROGRAM_ID,
                accounts: metas,
                data,
            };

            let account_infos = vec![
                ctx.accounts.authority.to_account_info(),
                ctx.accounts.vault_state.to_account_info(),
                ctx.accounts.global_config.to_account_info(),
                ctx.accounts.token_vault.to_account_info(),
                ctx.accounts.base_vault_authority.to_account_info(),
                ctx.accounts.user_token_ata.to_account_info(),
                ctx.accounts.token_mint.to_account_info(),
                ctx.accounts.user_shares_ata.to_account_info(),
                ctx.accounts.shares_mint.to_account_info(),
                ctx.accounts.token_program.to_account_info(),
                ctx.accounts.shares_token_program.to_account_info(),
                ctx.accounts.klend_program.to_account_info(),
                ctx.accounts.event_authority.to_account_info(),
                ctx.accounts.kvault_program.to_account_info(),
                ctx.accounts.vault_state.to_account_info(),
                ctx.accounts.reserve.to_account_info(),
                ctx.accounts.ctoken_vault.to_account_info(),
                ctx.accounts.lending_market.to_account_info(),
                ctx.accounts.lending_market_authority.to_account_info(),
                ctx.accounts.reserve_liquidity_supply.to_account_info(),
                ctx.accounts.reserve_collateral_mint.to_account_info(),
                ctx.accounts
                    .reserve_collateral_token_program
                    .to_account_info(),
                ctx.accounts.instruction_sysvar_account.to_account_info(),
                ctx.accounts.event_authority.to_account_info(),
                ctx.accounts.kvault_program.to_account_info(),
            ];

            invoke_signed(&ix, &account_infos, &[authority_seeds])?;
            Ok(())
        }
    }

    #[derive(Accounts)]
    pub struct Withdraw<'info> {
        #[account(
            mut,
            seeds = [AUTHORITY_SEED, vault_state.key().as_ref()],
            bump,
        )]
        pub authority: SystemAccount<'info>,

        #[account(mut, token::authority = authority)]
        pub user_token_ata: Account<'info, TokenAccount>,

        #[account(mut, token::authority = authority)]
        pub user_shares_ata: Account<'info, TokenAccount>,

        /// CHECK: Kvault state account.
        #[account(mut)]
        pub vault_state: UncheckedAccount<'info>,
        /// CHECK: Kvault global config account.
        pub global_config: UncheckedAccount<'info>,
        /// CHECK: Token vault owned by the Kvault program.
        #[account(mut)]
        pub token_vault: UncheckedAccount<'info>,
        /// CHECK: Kvault base vault authority PDA.
        pub base_vault_authority: UncheckedAccount<'info>,
        /// CHECK: Underlying token mint.
        #[account(mut)]
        pub token_mint: UncheckedAccount<'info>,
        /// CHECK: Shares mint.
        #[account(mut)]
        pub shares_mint: UncheckedAccount<'info>,
        /// CHECK: Event-emit PDA owned by the Kvault program.
        pub event_authority: UncheckedAccount<'info>,

        /// CHECK: Klend reserve account.
        #[account(mut)]
        pub reserve: UncheckedAccount<'info>,
        /// CHECK: cToken vault owned by the Kvault program.
        #[account(mut)]
        pub ctoken_vault: UncheckedAccount<'info>,
        /// CHECK: Klend lending market.
        pub lending_market: UncheckedAccount<'info>,
        /// CHECK: Klend lending market authority PDA.
        pub lending_market_authority: UncheckedAccount<'info>,
        /// CHECK: Reserve's underlying liquidity supply vault.
        #[account(mut)]
        pub reserve_liquidity_supply: UncheckedAccount<'info>,
        /// CHECK: Reserve's cToken mint.
        #[account(mut)]
        pub reserve_collateral_mint: UncheckedAccount<'info>,
        /// CHECK: Reserve's cToken program.
        pub reserve_collateral_token_program: UncheckedAccount<'info>,
        /// CHECK: Solana instructions sysvar.
        #[account(address = pubkey!("Sysvar1nstructions1111111111111111111111111"))]
        pub instruction_sysvar_account: UncheckedAccount<'info>,

        /// CHECK: The Kvault program.
        #[account(address = KVAULT_PROGRAM_ID)]
        pub kvault_program: UncheckedAccount<'info>,
        /// CHECK: The Klend program.
        #[account(address = KLEND_PROGRAM_ID)]
        pub klend_program: UncheckedAccount<'info>,
        pub token_program: Program<'info, Token>,
        pub shares_token_program: Program<'info, Token>,
    }
    ```
  </Tab>
</Tabs>
