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 liquidity or collateral from the reserve. The SDK handles transaction building, instruction creation, and collateral release calculations.
Withdraw for Lending
Withdraw liquidity from the reserve.
Import Dependencies Import the required packages for Solana RPC communication, Kamino SDK operations, and Kit transaction building. import "dotenv/config" ;
import {
createSolanaRpc ,
createSolanaRpcSubscriptions ,
address ,
pipe ,
createTransactionMessage ,
setTransactionMessageFeePayerSigner ,
setTransactionMessageLifetimeUsingBlockhash ,
appendTransactionMessageInstructions ,
signTransactionMessageWithSigners ,
sendAndConfirmTransactionFactory ,
getSignatureFromTransaction ,
} from "@solana/kit" ;
import {
KaminoMarket ,
KaminoAction ,
VanillaObligation ,
PROGRAM_ID ,
DEFAULT_RECENT_SLOT_DURATION_MS ,
} from "@kamino-finance/klend-sdk" ;
import BN from "bn.js" ;
import { getKeypair } from "../../utils/keypair" ;
@solana/kit provides modern utilities for RPC, transaction building, and
signing. @kamino-finance/klend-sdk contains market operation methods.
Initialize RPC and Load Market Initialize the RPC connection and load the Kamino market instance. const signer = await getKeypair ();
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 , DEFAULT_RECENT_SLOT_DURATION_MS );
KaminoMarket.load() fetches the current market state including all reserve
data and configuration.
Build Withdraw Reserve Liquidity Instructions Fetch the existing obligation from the blockchain and build withdraw instructions. const usdcMint = address ( "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" );
const withdrawAmount = new BN ( 1_000_000 ); // 1 USDC (6 decimals)
// Fetch the existing obligation from the blockchain
const obligation = await market ! . getObligationByWallet (
address ( signer . address ),
new VanillaObligation ( PROGRAM_ID )
);
if ( ! obligation ) {
throw new Error (
"No obligation found for this wallet. You must deposit first."
);
}
const action = await KaminoAction . buildWithdrawTxns (
market ! ,
withdrawAmount ,
usdcMint ,
signer ,
obligation ,
true , // useV2Ixs
undefined // scopeRefreshConfig
);
const instructions = [
... action . computeBudgetIxs ,
... action . setupIxs ,
... action . lendingIxs ,
... action . cleanupIxs ,
];
if ( ! instructions . length ) {
throw new Error ( "No instructions returned by Kamino SDK" );
}
Build and Sign Transaction Use Kit’s functional pipe pattern to build and sign the transaction. 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
);
Kit’s pipe function enables functional composition of transaction building
steps for cleaner, more maintainable code.
Send and Confirm Transaction Send the transaction and confirm it using WebSocket. const signature = getSignatureFromTransaction ( signedTransaction );
await sendAndConfirmTransactionFactory ({ rpc , rpcSubscriptions })( signedTransaction , {
commitment: "confirmed" ,
skipPreflight: true ,
});
console . log ( "Withdrawal successful! Signature:" , signature );
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 simple lending withdrawals (no obligation), use helpers::withdraw::redeem() to redeem cTokens for the underlying liquidity plus accrued interest.
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 use klend_interface :: ReserveInfo ;
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 use klend_interface :: pda;
use klend_interface :: KLEND_PROGRAM_ID ;
use spl_associated_token_account :: get_associated_token_address;
// User's ATA for the underlying token (e.g. USDC)
let user_destination_liquidity = get_associated_token_address ( & owner , & reserve . liquidity_mint);
// User's ATA for the cToken (collateral token received when depositing)
let ( collateral_mint , _ ) = pda :: reserve_collateral_mint ( & KLEND_PROGRAM_ID , & reserve_pubkey );
let user_source_collateral = get_associated_token_address ( & owner , & collateral_mint );
Build Instructions use klend_interface :: helpers;
let collateral_amount = 1_000_000 ; // amount of cTokens to redeem
let instructions = helpers :: withdraw :: redeem (
owner ,
& reserve ,
user_source_collateral , // cToken account (burned)
user_destination_liquidity , // receives underlying tokens + interest
collateral_amount ,
);
// Returns: [refresh_reserve, redeem_reserve_collateral]
Use ObligationContext::withdraw() if you need to withdraw collateral from an obligation (borrowing flow).
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! ( "Redeem successful! Signature: {signature}" );
View Code
Withdraw for Borrowing
Withdraw collateral from an obligation.
Import Dependencies Import the required packages for Solana RPC communication, Kamino SDK operations, and Kit transaction building. import "dotenv/config" ;
import {
createSolanaRpc ,
createSolanaRpcSubscriptions ,
address ,
pipe ,
createTransactionMessage ,
setTransactionMessageFeePayerSigner ,
setTransactionMessageLifetimeUsingBlockhash ,
appendTransactionMessageInstructions ,
signTransactionMessageWithSigners ,
sendAndConfirmTransactionFactory ,
getSignatureFromTransaction ,
} from "@solana/kit" ;
import {
KaminoMarket ,
KaminoAction ,
VanillaObligation ,
PROGRAM_ID ,
DEFAULT_RECENT_SLOT_DURATION_MS ,
} from "@kamino-finance/klend-sdk" ;
import BN from "bn.js" ;
import { getKeypair } from "../../utils/keypair" ;
@solana/kit provides modern utilities for RPC, transaction building, and
signing. @kamino-finance/klend-sdk contains market operation methods.
Initialize RPC and Load Market Initialize the RPC connection and load the Kamino market instance. const signer = await getKeypair ();
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 , DEFAULT_RECENT_SLOT_DURATION_MS );
KaminoMarket.load() fetches the current market state including all reserve
data and configuration.
Build Withdraw Collateral Instructions Build withdraw instructions for collateral withdrawal. const usdcMint = address ( "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" );
const withdrawAmount = new BN ( 1_000_000 ); // 1 USDC (6 decimals)
const withdrawAction = await KaminoAction . buildWithdrawTxns (
market ! ,
withdrawAmount ,
usdcMint ,
signer ,
new VanillaObligation ( PROGRAM_ID ),
true , // useV2Ixs
undefined , // scopeRefreshConfig
1_000_000 , // extraComputeBudget
true , // includeAtaIxs
false // requestElevationGroup
);
// Combine all instructions
const allInstructions = [
... ( withdrawAction . setupIxs || []),
... ( withdrawAction . lendingIxs || []),
... ( withdrawAction . cleanupIxs || []),
];
Build and Sign Transaction Use Kit’s functional pipe pattern to build and sign the transaction. // Get fresh blockhash
const { value : latestBlockhash } = await rpc
. getLatestBlockhash ({
commitment: "finalized" ,
})
. send ();
// Build transaction using functional pipe pattern
const transactionMessage = pipe (
createTransactionMessage ({ version: 0 }),
( tx ) => setTransactionMessageFeePayerSigner ( signer , tx ),
( tx ) => setTransactionMessageLifetimeUsingBlockhash ( latestBlockhash , tx ),
( tx ) => appendTransactionMessageInstructions ( allInstructions , tx )
);
// Sign transaction
const signedTransaction = await signTransactionMessageWithSigners (
transactionMessage
);
Send and Confirm Transaction Send the transaction and confirm it using WebSocket. const signature = getSignatureFromTransaction ( signedTransaction );
await sendAndConfirmTransactionFactory ({ rpc , rpcSubscriptions })( signedTransaction , {
commitment: "confirmed" ,
skipPreflight: true ,
});
console . log ( "Withdrawal successful! Signature:" , signature );
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 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 withdrawal token (e.g. USDC)
let liquidity_mint = Pubkey :: from_str ( "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" ) . unwrap ();
let user_dest_liquidity = get_associated_token_address ( & owner , & liquidity_mint );
Build Instructions let instructions = ctx . withdraw (
owner ,
& reserve_pubkey ,
user_dest_liquidity ,
1_000_000 , // 1 USDC (6 decimals)
) ? ;
// Returns: [refresh_reserves..., refresh_obligation, withdraw_and_redeem]
Use helpers::withdraw::redeem() if you hold cTokens without an obligation — see the Withdraw for Lending tab.
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! ( "Withdrawal successful! Signature: {signature}" );
View Code
What Happens When You Withdraw Collateral?
The collateral amount is removed from your obligation
Your borrowing capacity decreases
Your loan-to-value (LTV) ratio increases