Kamino Vault instructions
The package exposes the same args, accounts, programId? instruction builder
structure as @exponent-labs/klend-idl. Existing root exports (IDL, the string
PROGRAM_ID, and decodeVault) are preserved.
@exponent-labs/kamino-vault-idl/instructions:deposit,withdraw, andgetVaultReserveAccounts, with their argument/account types.@exponent-labs/kamino-vault-idl/accounts:fetchVaultReserves.
The deposit and withdrawal account layouts match the
upstream v2.2.2 IDL
and the mainnet program's on-chain IDL, checked on 2026-10-06. The bundled IDL's
withdrawal entries include the required global_config account. The rest of the
bundled IDL retains its existing layout.
Refresh and build instructions
import BN from "bn.js"
import { fetchVaultReserves } from "@exponent-labs/kamino-vault-idl/accounts"
import {
type DepositAccounts,
type WithdrawAccounts,
deposit,
getVaultReserveAccounts,
withdraw,
} from "@exponent-labs/kamino-vault-idl/instructions"
import { refreshReservesBatch } from "@exponent-labs/klend-idl/instructions"
// connection, vaultAddress, depositAccounts, and withdrawAccounts are caller inputs.
const reserves = await fetchVaultReserves(connection, vaultAddress)
const refreshIx = refreshReservesBatch({ skipPriceUpdates: true })
refreshIx.keys.push(
...reserves.flatMap(({ reserveAddress, marketAddress }) => [
{ pubkey: reserveAddress, isSigner: false, isWritable: true },
{ pubkey: marketAddress, isSigner: false, isWritable: false },
]),
)
const remainingAccounts = getVaultReserveAccounts(reserves)
const depositIx = deposit(
{ maxAmount: new BN("1000000") }, // 1 USDC, including crank fees
{ ...depositAccounts, remainingAccounts } satisfies DepositAccounts,
)
const withdrawIx = withdraw(
{ sharesAmount: new BN("936761") }, // Raw vault shares, not a USDC amount
{ ...withdrawAccounts, remainingAccounts } satisfies WithdrawAccounts,
)
Send [refreshIx, depositIx] or [refreshIx, withdrawIx] in one transaction,
after any needed token account creation instructions. Refresh and the vault
instruction need to execute in the same slot. skipPriceUpdates: true accrues
reserve interest without fetching oracle updates.
KLend refresh accounts alternate reserve, market. KVault's remaining accounts contain all reserves, then all markets, in allocation order. Repeated markets must be retained.
fetchVaultReserves validates account ownership and discriminators and returns
reserveAddress, marketAddress, ctokenVault, and the decoded KLend account.
It skips unused allocation slots and throws if an active reserve is missing.
Withdrawal takes the IDL's nested withdrawFromAvailable and
withdrawFromReserveAccounts account groups. Select a reserve with sufficient
withdrawable liquidity; the decoded reserve exposes its collateral mint and
liquidity supply. Derive the lending market authority with seeds
[Buffer.from("lma"), marketAddress.toBuffer()] under the KLend program. Derive
globalConfig with [Buffer.from("global_config")] under the KVault program.
These builders return individual instructions; reserve selection, ATA creation,
and any farm unstaking are handled by the caller. If a vault requires a
permissioning authority, append its signer meta after the reserve/market blocks.
Run yarn workspace @exponent-labs/kamino-vault-idl build and
yarn workspace @exponent-labs/kamino-vault-idl test to validate the bindings.