kvault-interface 0.1.0

Instruction builders for Kamino Vault (kvault) — no anchor-lang dependency
Documentation

kvault-interface

Instruction builders and zero-copy account deserialization for Kamino Vault (Kvault). No anchor-lang dependency; targets solana-sdk v2.x.

Quick start

Add to your Cargo.toml:

[dependencies]
kvault-interface = { git = "https://github.com/Kamino-Finance/kvault" }
klend-interface = { git = "https://github.com/Kamino-Finance/klend" }
solana-pubkey = "2.1"
solana-instruction = "2.1"
solana-sdk = "~2.3"
solana-client = "2.1"
spl-associated-token-account = "6"

Two-level API

The crate provides two levels of instruction building:

  • Low-level ([instructions]): one function per Kvault instruction, returning a single [solana_instruction::Instruction]. You supply every account address manually.

  • High-level ([helpers]): workflow builders that accept [VaultInfo] / [ReserveInfo] and auto-derive PDAs and remaining accounts.

Core types

Type Purpose
[VaultInfo] On-chain vault metadata; built via [VaultInfo::from_account_data].
[ReserveInfo] Klend reserve metadata needed by withdraw / invest / redeem helpers.
[state::VaultState] Zero-copy deserialization of the on-chain VaultState account (62 544 bytes).
[state::GlobalConfig] Zero-copy deserialization of the on-chain GlobalConfig account (1 024 bytes).
[state::VaultAllocation] Per-reserve allocation slot embedded in VaultState (2 160 bytes).

Typical flow

use kvault_interface::{helpers, VaultInfo, KVAULT_PROGRAM_ID};
use solana_pubkey::Pubkey;
# fn example(rpc: &solana_client::rpc_client::RpcClient, owner: Pubkey) -> Result<(), Box<dyn std::error::Error>> {

let vault_pubkey: Pubkey = "HDsayqAsDWy3QvANGqh2yNraqcD8Fnjgh73Mhb3WRS5E".parse()?;

// 1. Fetch the vault account, then the active reserves it allocates to
//    (their lending markets are needed to refresh the reserves on-chain).
let vault_data = rpc.get_account(&vault_pubkey)?;
let vault_state = kvault_interface::from_account_data::<kvault_interface::state::VaultState>(&vault_data.data)?;
let mut reserve_infos = Vec::new();
for reserve_pubkey in VaultInfo::active_reserve_addresses(vault_state) {
    let reserve_data = rpc.get_account(&reserve_pubkey)?;
    reserve_infos.push(kvault_interface::ReserveInfo::from_account_data(reserve_pubkey, &reserve_data.data)?);
}
let vault = VaultInfo::from_account_data(vault_pubkey, &vault_data.data, &reserve_infos)?;

// 2. Derive user token accounts
let user_token_ata = spl_associated_token_account::get_associated_token_address(&owner, &vault.token_mint);
let shares_mint = kvault_interface::pda::shares_mint(&KVAULT_PROGRAM_ID, &vault_pubkey).0;
let user_shares_ata = spl_associated_token_account::get_associated_token_address(&owner, &shares_mint);

// 3. Build a deposit instruction — PDAs and remaining accounts are derived automatically
let ix = helpers::deposit::deposit(&vault, owner, user_token_ata, user_shares_ata, 1_000_000);

// 4. Send the transaction
let message = solana_sdk::message::Message::new(&[ix], Some(&owner));
let recent_blockhash = rpc.get_latest_blockhash()?;
# Ok(())
# }

Scaled fraction fields (_sf)

All on-chain fields ending in _sf (e.g. prev_aum_sf, pending_fees_sf) are fixed-point integers using the [Fraction] type (68 integer bits, 60 fractional bits). Convert with Fraction::from_bits(u128_value).

Examples

The examples/ directory contains runnable examples matching the Kamino developer documentation:

Example Description
deposit Deposit tokens into a vault and receive shares.
withdraw Withdraw shares from a vault to receive tokens.
vault_data Fetch and inspect vault state, allocations, and AUM.
user_position Read a user's share balance and compute token value.