Skip to main content

kvault_interface/
lib.rs

1//! # kvault-interface
2//!
3//! Instruction builders and zero-copy account deserialization for
4//! Kamino Vault (Kvault). No `anchor-lang` dependency;
5//! targets `solana-sdk` v2.x.
6//!
7//! ## Quick start
8//!
9//! Add to your `Cargo.toml`:
10//!
11//! ```toml
12//! [dependencies]
13//! kvault-interface = { git = "https://github.com/Kamino-Finance/kvault" }
14//! klend-interface = { git = "https://github.com/Kamino-Finance/klend" }
15//! solana-pubkey = "2.1"
16//! solana-instruction = "2.1"
17//! solana-sdk = "~2.3"
18//! solana-client = "2.1"
19//! spl-associated-token-account = "6"
20//! ```
21//!
22//! ## Two-level API
23//!
24//! The crate provides two levels of instruction building:
25//!
26//! - **Low-level** ([`instructions`]): one function per Kvault instruction, returning a single
27//!   [`solana_instruction::Instruction`]. You supply every account address manually.
28//!
29//! - **High-level** ([`helpers`]): workflow builders that accept [`VaultInfo`] /
30//!   [`ReserveInfo`] and auto-derive PDAs and remaining accounts.
31//!
32//! ## Core types
33//!
34//! | Type | Purpose |
35//! |------|---------|
36//! | [`VaultInfo`] | On-chain vault metadata; built via [`VaultInfo::from_account_data`]. |
37//! | [`ReserveInfo`] | Klend reserve metadata needed by withdraw / invest / redeem helpers. |
38//! | [`state::VaultState`] | Zero-copy deserialization of the on-chain VaultState account (62 544 bytes). |
39//! | [`state::GlobalConfig`] | Zero-copy deserialization of the on-chain GlobalConfig account (1 024 bytes). |
40//! | [`state::VaultAllocation`] | Per-reserve allocation slot embedded in VaultState (2 160 bytes). |
41//!
42//! ## Typical flow
43//!
44//! ```rust,no_run
45//! use kvault_interface::{helpers, VaultInfo, KVAULT_PROGRAM_ID};
46//! use solana_pubkey::Pubkey;
47//! # fn example(rpc: &solana_client::rpc_client::RpcClient, owner: Pubkey) -> Result<(), Box<dyn std::error::Error>> {
48//!
49//! let vault_pubkey: Pubkey = "HDsayqAsDWy3QvANGqh2yNraqcD8Fnjgh73Mhb3WRS5E".parse()?;
50//!
51//! // 1. Fetch the vault account, then the active reserves it allocates to
52//! //    (their lending markets are needed to refresh the reserves on-chain).
53//! let vault_data = rpc.get_account(&vault_pubkey)?;
54//! let vault_state = kvault_interface::from_account_data::<kvault_interface::state::VaultState>(&vault_data.data)?;
55//! let mut reserve_infos = Vec::new();
56//! for reserve_pubkey in VaultInfo::active_reserve_addresses(vault_state) {
57//!     let reserve_data = rpc.get_account(&reserve_pubkey)?;
58//!     reserve_infos.push(kvault_interface::ReserveInfo::from_account_data(reserve_pubkey, &reserve_data.data)?);
59//! }
60//! let vault = VaultInfo::from_account_data(vault_pubkey, &vault_data.data, &reserve_infos)?;
61//!
62//! // 2. Derive user token accounts
63//! let user_token_ata = spl_associated_token_account::get_associated_token_address(&owner, &vault.token_mint);
64//! let shares_mint = kvault_interface::pda::shares_mint(&KVAULT_PROGRAM_ID, &vault_pubkey).0;
65//! let user_shares_ata = spl_associated_token_account::get_associated_token_address(&owner, &shares_mint);
66//!
67//! // 3. Build a deposit instruction — PDAs and remaining accounts are derived automatically
68//! let ix = helpers::deposit::deposit(&vault, owner, user_token_ata, user_shares_ata, 1_000_000);
69//!
70//! // 4. Send the transaction
71//! let message = solana_sdk::message::Message::new(&[ix], Some(&owner));
72//! let recent_blockhash = rpc.get_latest_blockhash()?;
73//! # Ok(())
74//! # }
75//! ```
76//!
77//! ## Scaled fraction fields (`_sf`)
78//!
79//! All on-chain fields ending in `_sf` (e.g. `prev_aum_sf`, `pending_fees_sf`) are
80//! fixed-point integers using the [`Fraction`] type (68 integer bits, 60 fractional bits).
81//! Convert with `Fraction::from_bits(u128_value)`.
82//!
83//! ## Examples
84//!
85//! The `examples/` directory contains runnable examples matching the
86//! [Kamino developer documentation](https://docs.kamino.finance):
87//!
88//! | Example | Description |
89//! |---------|-------------|
90//! | [`deposit`](https://github.com/Kamino-Finance/kvault/blob/master/libs/kvault-interface/examples/deposit.rs) | Deposit tokens into a vault and receive shares. |
91//! | [`withdraw`](https://github.com/Kamino-Finance/kvault/blob/master/libs/kvault-interface/examples/withdraw.rs) | Withdraw shares from a vault to receive tokens. |
92//! | [`vault_data`](https://github.com/Kamino-Finance/kvault/blob/master/libs/kvault-interface/examples/vault_data.rs) | Fetch and inspect vault state, allocations, and AUM. |
93//! | [`user_position`](https://github.com/Kamino-Finance/kvault/blob/master/libs/kvault-interface/examples/user_position.rs) | Read a user's share balance and compute token value. |
94
95pub mod discriminators;
96pub mod errors;
97pub mod helpers;
98pub mod instructions;
99pub mod pda;
100pub mod state;
101pub mod util;
102
103// Convenience re-exports for the most commonly used types.
104pub use errors::KvaultError;
105pub use helpers::info::{ReserveInfo, VaultInfo, VaultInfoError, VaultReserve};
106pub use state::{from_account_data, AccountDataError};
107
108use solana_pubkey::{pubkey, Pubkey};
109
110/// Fixed-point type for `_sf` (scaled fraction) fields: 68 integer bits, 60 fractional bits.
111///
112/// All on-chain values ending in `_sf` (e.g. `prev_aum_sf`, `pending_fees_sf`) are
113/// stored as `u128` / `PodU128` bit patterns of this type. Convert with
114/// `Fraction::from_bits(u128_value)` and `fraction.to_bits()`.
115pub type Fraction = fixed::types::U68F60;
116
117/// Maximum number of reserves a vault can allocate to.
118pub const MAX_RESERVES: usize = 25;
119
120/// Kamino Vault (mainnet) program ID.
121pub const KVAULT_PROGRAM_ID: Pubkey = pubkey!("KvauGMspG5k6rtzrqqn7WNn3oZdyKqLKwK2XWQ8FLjd");
122
123/// Kamino Vault (staging) program ID.
124pub const KVAULT_STAGING_PROGRAM_ID: Pubkey =
125    pubkey!("stKvQfwRsQiKnLtMNVLHKS3exFJmZFsgfzBPWHECUYK");
126
127// Re-export well-known IDs from klend-interface to avoid duplication.
128pub use klend_interface::{
129    KLEND_PROGRAM_ID, SYSTEM_PROGRAM_ID, SYSVAR_INSTRUCTIONS_ID, SYSVAR_RENT_ID, TOKEN_PROGRAM_ID,
130};