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.
Deposit assets as collateral or supply liquidity into Kamino lending reserves. The SDK handles transaction building, instruction creation, and obligation management for deposit operations.
Deposit for Lending
Supply liquidity to the reserve and earn interest.
Import Dependencies Import the required packages for Solana RPC communication, Kamino SDK operations, and Kit transaction building. import {
createSolanaRpc ,
createSolanaRpcSubscriptions ,
address ,
pipe ,
createTransactionMessage ,
setTransactionMessageFeePayerSigner ,
setTransactionMessageLifetimeUsingBlockhash ,
appendTransactionMessageInstructions ,
signTransactionMessageWithSigners ,
sendAndConfirmTransactionFactory ,
getSignatureFromTransaction ,
type Signature ,
} from '@solana/kit' ;
import {
KaminoMarket ,
KaminoAction ,
LendingObligation
} from '@kamino-finance/klend-sdk' ;
import { parseKeypairFile } from '@kamino-finance/klend-sdk/dist/utils/signer.js' ;
import BN from 'bn.js' ;
@solana/kit provides modern utilities for RPC, transaction building, and signing. @kamino-finance/klend-sdk contains market operation methods.
Load Market and Initialize RPC Load the keypair, initialize RPC connections, and load the Kamino market. 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 marketPubkey = address ( 'DxXdAyU3kCjnyggvHmY5nAwg5cRbbmdyX3npfDMjjMek' );
const market = await KaminoMarket . load ( rpc , marketPubkey , 400 );
KaminoMarket.load() fetches the current market state including all reserve data and configuration.
Build Deposit Instructions Generate deposit reserve liquidity instructions for the specified token and amount. const usdcMint = address ( 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v' );
const depositAmount = new BN ( 1_000_000 ); // 1 USDC (6 decimals)
const obligation = new LendingObligation ( usdcMint , market ! . programId , 0 );
const action = await KaminoAction . buildDepositReserveLiquidityTxns (
market ! ,
depositAmount ,
usdcMint ,
signer ,
obligation ,
undefined
);
buildDepositReserveLiquidityTxns creates a lending obligation for earning interest on deposited assets. The obligation parameter specifies the deposit configuration.
Send Setup Transaction (If Needed) If setup instructions are present, send the setup transaction first and wait for confirmation. let setupSignature : Signature | undefined ;
if ( action . setupIxs && action . setupIxs . length > 0 ) {
const { value : setupBlockhash } = await rpc
. getLatestBlockhash ({ commitment: 'finalized' })
. send ();
const setupTxMessage = pipe (
createTransactionMessage ({ version: 0 }),
( tx ) => setTransactionMessageFeePayerSigner ( signer , tx ),
( tx ) => setTransactionMessageLifetimeUsingBlockhash ( setupBlockhash , tx ),
( tx ) => appendTransactionMessageInstructions ( action . setupIxs , tx )
);
const setupSignedTx = await signTransactionMessageWithSigners ( setupTxMessage );
setupSignature = getSignatureFromTransaction ( setupSignedTx );
await sendAndConfirmTransactionFactory ({ rpc , rpcSubscriptions })( setupSignedTx , {
commitment: 'confirmed' ,
skipPreflight: true ,
});
await new Promise (( resolve ) => setTimeout ( resolve , 2000 ));
}
Setup transactions must be confirmed and Address Lookup Tables (ALTs) must be activated before sending the main deposit transaction. Waiting 2 seconds ensures account settlement.
Build and Send Deposit Transaction Gather all instructions from the action. const lendingInstructions = [
... action . computeBudgetIxs ,
... action . lendingIxs ,
... action . cleanupIxs ,
];
if ( ! lendingInstructions . length ) {
throw new Error ( 'No instructions returned by Kamino SDK' );
}
Fetch the latest blockhash and construct the transaction message. const { value : latestBlockhash } = await rpc
. getLatestBlockhash ({ commitment: 'finalized' })
. send ();
const transactionMessage = pipe (
createTransactionMessage ({ version: 0 }),
( tx ) => setTransactionMessageFeePayerSigner ( signer , tx ),
( tx ) => setTransactionMessageLifetimeUsingBlockhash ( latestBlockhash , tx ),
( tx ) => appendTransactionMessageInstructions ( lendingInstructions , tx )
);
A fresh blockhash is fetched for the main transaction to ensure it remains valid after the setup transaction completes.
Sign and send the transaction. const signedTransaction = await signTransactionMessageWithSigners ( transactionMessage );
const signature = getSignatureFromTransaction ( signedTransaction );
await sendAndConfirmTransactionFactory ({ rpc , rpcSubscriptions })( signedTransaction , {
commitment: 'confirmed' ,
skipPreflight: true ,
});
console . log (
`Deposit successful! Signature: ${ signature }${
setupSignature ? ` \n Setup transaction: https://solscan.io/tx/ ${ setupSignature } ` : ''
} `
);
The deposit is complete. The assets are now supplied to the reserve and earning interest.
klend-interface is a lightweight Rust instruction builder — it creates Vec<Instruction> with required refresh instructions prepended automatically. Use ReserveInfo::from_account_data(pubkey, &data) to construct reserve info from raw RPC account bytes. For obligation-based operations, ObligationContext is the recommended approach — it fetches the obligation and its associated reserves, then provides convenient .deposit(), .borrow(), .repay(), and .withdraw() methods.
Add Dependencies [ dependencies ]
klend-interface = "0.1.0"
solana-pubkey = "2.1"
solana-instruction = "2.1"
solana-sdk = "~2.3"
solana-client = "~2.3"
spl-token = "7"
spl-associated-token-account = "6"
Set Up RPC Client use solana_client :: rpc_client :: RpcClient ;
use solana_sdk :: signer :: keypair :: read_keypair_file;
use solana_sdk :: signer :: Signer ;
let rpc_client = RpcClient :: new ( "https://api.mainnet-beta.solana.com" );
let signer = read_keypair_file ( "/path/to/your/keypair.json" )
. expect ( "Failed to read keypair file" );
let owner = signer . pubkey ();
Fetch Reserve Data Fetch the reserve account from the chain and construct ReserveInfo from its raw bytes. use klend_interface :: helpers;
use klend_interface :: ReserveInfo ;
use klend_interface :: pda;
use klend_interface :: KLEND_PROGRAM_ID ;
use solana_pubkey :: Pubkey ;
use std :: str :: FromStr ;
let reserve_pubkey = Pubkey :: from_str ( "D6q6wuQSrifJKZYpR1M8R4YawnLDtDsMmWM1NbBmgJ59" ) . unwrap ();
let reserve_data = rpc_client . get_account ( & reserve_pubkey ) ? ;
let reserve = ReserveInfo :: from_account_data ( reserve_pubkey , & reserve_data . data) ? ;
Derive Token Accounts Derive the user’s associated token accounts for the liquidity mint and collateral mint. use spl_associated_token_account :: get_associated_token_address;
// User's ATA for the deposit token (e.g. USDC)
let user_source_liquidity = get_associated_token_address ( & owner , & reserve . liquidity_mint);
// Derive the collateral mint PDA, then the user's ATA for it
let ( collateral_mint , _ ) = pda :: reserve_collateral_mint ( & KLEND_PROGRAM_ID , & reserve_pubkey );
let user_destination_collateral = get_associated_token_address ( & owner , & collateral_mint );
Build Instructions let instructions = helpers :: deposit :: deposit (
owner ,
& reserve ,
user_source_liquidity , // user's USDC token account
user_destination_collateral , // user's cToken account
1_000_000 , // 1 USDC (6 decimals)
);
// Returns: [refresh_reserve, deposit_reserve_liquidity]
Build and Send Transaction use solana_sdk :: transaction :: Transaction ;
use solana_sdk :: message :: Message ;
let message = Message :: new ( & instructions , Some ( & owner ));
let recent_blockhash = rpc_client . get_latest_blockhash () ? ;
let tx = Transaction :: new ( & [ & signer ], message , recent_blockhash );
let signature = rpc_client . send_and_confirm_transaction ( & tx ) ? ;
println! ( "Deposit successful! Signature: {signature}" );
View Code
Deposit for Borrowing
Deposit USDC as collateral to borrow other assets.
Import Dependencies Import the required packages for Solana RPC communication, Kamino SDK operations, and Kit transaction building. import {
createSolanaRpc ,
createSolanaRpcSubscriptions ,
address ,
pipe ,
createTransactionMessage ,
setTransactionMessageFeePayerSigner ,
setTransactionMessageLifetimeUsingBlockhash ,
appendTransactionMessageInstructions ,
signTransactionMessageWithSigners ,
sendAndConfirmTransactionFactory ,
getSignatureFromTransaction ,
} from '@solana/kit' ;
import {
KaminoMarket ,
KaminoAction ,
VanillaObligation ,
PROGRAM_ID
} from '@kamino-finance/klend-sdk' ;
import { parseKeypairFile } from '@kamino-finance/klend-sdk/dist/utils/signer.js' ;
import BN from 'bn.js' ;
Load Market and Initialize RPC Load the keypair, initialize RPC connections, and load the Kamino market. 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 marketPubkey = address ( '7u3HeHxYDLhnCoErrtycNokbQYbWGzLs6JSDqGAv5PfF' );
const market = await KaminoMarket . load ( rpc , marketPubkey , 400 );
Build Deposit Instructions Build deposit instructions for depositing collateral to enable borrowing. const usdcMint = address ( 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v' );
const depositAmount = new BN ( 3_000_000 ); // 3 USDC (6 decimals)
const signatures : string [] = [];
let depositAction = await KaminoAction . buildDepositTxns (
market ! ,
depositAmount ,
usdcMint ,
signer ,
new VanillaObligation ( PROGRAM_ID ),
true ,
undefined ,
1_000_000 ,
true ,
false ,
{ skipInitialization: false , skipLutCreation: false }
);
const hasSetup = ( depositAction . setupIxs || []). length > 0 ;
let instructions = [
... ( depositAction . setupIxs || []),
... ( depositAction . lendingIxs || []),
... ( depositAction . cleanupIxs || []),
];
Send Initial Transaction Fetch the latest blockhash and build the transaction message. try {
const { value : depositBlockhash } = await rpc
. getLatestBlockhash ({ commitment: 'finalized' })
. send ();
const depositTx = pipe (
createTransactionMessage ({ version: 0 }),
( tx ) => setTransactionMessageFeePayerSigner ( signer , tx ),
( tx ) => setTransactionMessageLifetimeUsingBlockhash ( depositBlockhash , tx ),
( tx ) => appendTransactionMessageInstructions ( instructions , tx )
);
Sign and send the transaction. const depositSigned = await signTransactionMessageWithSigners ( depositTx );
const depositSignature = getSignatureFromTransaction ( depositSigned );
signatures . push ( depositSignature );
await sendAndConfirmTransactionFactory ({ rpc , rpcSubscriptions })( depositSigned , {
commitment: 'confirmed' ,
skipPreflight: true ,
});
console . log ( `Deposit successful! Signature: ${ depositSignature } ` );
Handle Retry Logic Detect and handle setup-related errors that may occur. } catch ( error : any ) {
const errorMsg = error ?. message || error ?. toString () || '' ;
const is0x17a3 =
errorMsg . includes ( '0x17a3' ) ||
errorMsg . includes ( '6051' ) ||
errorMsg . includes ( 'IncorrectInstructionInPosition' );
if ( hasSetup && is0x17a3 ) {
When setup instructions are included, the first transaction may fail with error 0x17a3. The retry logic handles this by waiting for blockchain state to settle, reloading the market, and rebuilding instructions.
Wait for blockchain state to settle and reload the market. await new Promise (( resolve ) => setTimeout ( resolve , 2000 ));
const reloadedMarket = await KaminoMarket . load ( rpc , marketPubkey , 400 );
Rebuild the deposit instructions with the reloaded market state. depositAction = await KaminoAction . buildDepositTxns (
reloadedMarket ! ,
depositAmount ,
usdcMint ,
signer ,
new VanillaObligation ( PROGRAM_ID ),
true ,
undefined ,
1_000_000 ,
true ,
false ,
{ skipInitialization: false , skipLutCreation: false }
);
instructions = [
... ( depositAction . setupIxs || []),
... ( depositAction . lendingIxs || []),
... ( depositAction . cleanupIxs || []),
];
Fetch a fresh blockhash and build the retry transaction. const { value : retryBlockhash } = await rpc
. getLatestBlockhash ({ commitment: 'finalized' })
. send ();
const retryTx = pipe (
createTransactionMessage ({ version: 0 }),
( tx ) => setTransactionMessageFeePayerSigner ( signer , tx ),
( tx ) => setTransactionMessageLifetimeUsingBlockhash ( retryBlockhash , tx ),
( tx ) => appendTransactionMessageInstructions ( instructions , tx )
);
Sign and send the retry transaction. const retrySigned = await signTransactionMessageWithSigners ( retryTx );
const retrySignature = getSignatureFromTransaction ( retrySigned );
signatures . push ( retrySignature );
await sendAndConfirmTransactionFactory ({ rpc , rpcSubscriptions })( retrySigned , {
commitment: 'confirmed' ,
skipPreflight: true ,
});
console . log (
`Deposit successful! Signature: ${ retrySignature } \n Setup transaction: https://solscan.io/tx/ ${ signatures [ 0 ] } `
);
} else {
console . error ( 'Deposit failed:' , error );
}
}
The deposit is complete. The collateral is now deposited and can be used for borrowing other assets.
klend-interface is a lightweight Rust instruction builder — it creates Vec<Instruction> with required refresh instructions prepended automatically. Use ReserveInfo::from_account_data(pubkey, &data) to construct reserve info from raw RPC account bytes. For obligation-based operations, ObligationContext is the recommended approach — it fetches the obligation and its associated reserves, then provides convenient .deposit(), .borrow(), .repay(), and .withdraw() methods.
Add Dependencies [ dependencies ]
klend-interface = "0.1.0"
solana-pubkey = "2.1"
solana-instruction = "2.1"
solana-sdk = "~2.3"
solana-client = "~2.3"
spl-token = "7"
spl-associated-token-account = "6"
Set Up RPC Client use solana_client :: rpc_client :: RpcClient ;
use solana_sdk :: signer :: keypair :: read_keypair_file;
use solana_sdk :: signer :: Signer ;
let rpc_client = RpcClient :: new ( "https://api.mainnet-beta.solana.com" );
let signer = read_keypair_file ( "/path/to/your/keypair.json" )
. expect ( "Failed to read keypair file" );
let owner = signer . pubkey ();
Build Obligation Context Derive the obligation PDA, fetch its account data along with all associated reserves, and build an ObligationContext. use klend_interface :: ObligationContext ;
use klend_interface :: pda;
use klend_interface :: KLEND_PROGRAM_ID ;
use solana_pubkey :: Pubkey ;
use std :: str :: FromStr ;
let lending_market = Pubkey :: from_str ( "7u3HeHxYDLhnCoErrtycNokbQYbWGzLs6JSDqGAv5PfF" ) . unwrap ();
let reserve_pubkey = Pubkey :: from_str ( "D6q6wuQSrifJKZYpR1M8R4YawnLDtDsMmWM1NbBmgJ59" ) . unwrap ();
// Derive the obligation PDA
let ( obligation_pubkey , _ ) = pda :: obligation (
& KLEND_PROGRAM_ID , 0 , 0 , & owner , & lending_market ,
& Pubkey :: default (), & Pubkey :: default (),
);
// Fetch the obligation account
let obligation_data = rpc_client . get_account ( & obligation_pubkey ) ? ;
// Discover which reserves the obligation references
let reserve_addrs = ObligationContext :: reserve_addresses_for_obligation ( & obligation_data . data) ? ;
// Fetch all reserve accounts in one RPC call
let reserve_accounts = rpc_client . get_multiple_accounts ( & reserve_addrs ) ? ;
// Build the context
let reserves : Vec <( Pubkey , & [ u8 ])> = reserve_addrs . iter ()
. zip ( reserve_accounts . iter ())
. filter_map ( | ( addr , acc ) | acc . as_ref () . map ( | a | ( * addr , a . data . as_slice ())))
. collect ();
let ctx = ObligationContext :: from_account_data (
obligation_pubkey , & obligation_data . data, & reserves
) ? ;
Derive Token Accounts use spl_associated_token_account :: get_associated_token_address;
// User's ATA for the deposit token (e.g. USDC)
let liquidity_mint = Pubkey :: from_str ( "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" ) . unwrap ();
let user_source_liquidity = get_associated_token_address ( & owner , & liquidity_mint );
Build Instructions let instructions = ctx . deposit (
owner ,
& reserve_pubkey ,
user_source_liquidity ,
3_000_000 , // 3 USDC (6 decimals)
) ? ;
// Returns: [refresh_reserves..., refresh_obligation, deposit_and_collateral_v2]
Build and Send Transaction use solana_sdk :: transaction :: Transaction ;
use solana_sdk :: message :: Message ;
let message = Message :: new ( & instructions , Some ( & owner ));
let recent_blockhash = rpc_client . get_latest_blockhash () ? ;
let tx = Transaction :: new ( & [ & signer ], message , recent_blockhash );
let signature = rpc_client . send_and_confirm_transaction ( & tx ) ? ;
println! ( "Deposit successful! Signature: {signature}" );
View Code