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.
Flash Loans
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,
none,
getProgramDerivedAddress,
getAddressEncoder,
} from '@solana/kit';
import {
KaminoMarket,
PROGRAM_ID,
getFlashLoanInstructions
} from '@kamino-finance/klend-sdk';
import { parseKeypairFile } from '@kamino-finance/klend-sdk/dist/utils/signer.js';
import { getAddMemoInstruction } from '@solana-program/memo';
import Decimal from 'decimal.js';
Setup Market and Signer
Load your 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'); // Main Market
const market = await KaminoMarket.load(rpc, marketPubkey, 400);
Set the token mint, flash loan amount, and derive the user’s token account address.const usdcMint = address('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'); // USDC
const usdcReserve = market!.getReserveByMint(usdcMint)!;
// Flash loan amount: 1 USDC
const flashLoanAmount = new Decimal(1_000_000);
// Derive user's USDC token account (ATA)
const ASSOCIATED_TOKEN_PROGRAM = address('ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL');
const TOKEN_PROGRAM = usdcReserve.getLiquidityTokenProgram();
const addressEncoder = getAddressEncoder();
const [usdcAta] = await getProgramDerivedAddress({
programAddress: ASSOCIATED_TOKEN_PROGRAM,
seeds: [
addressEncoder.encode(signer.address),
addressEncoder.encode(TOKEN_PROGRAM),
addressEncoder.encode(usdcMint)
],
});
Build Flash Loan Instructions
Generate the flash borrow and flash repay instructions using the Kamino SDK.// Build flash loan instructions
const lendingMarketAuthority = await market!.getLendingMarketAuthority();
const { flashBorrowIx, flashRepayIx } = getFlashLoanInstructions({
borrowIxIndex: 0,
userTransferAuthority: signer,
lendingMarketAuthority,
lendingMarketAddress: market!.getAddress(),
reserve: usdcReserve,
amountLamports: flashLoanAmount,
destinationAta: usdcAta,
referrerAccount: none(),
referrerTokenState: none(),
programId: PROGRAM_ID,
});
The borrowIxIndex:0 parameter specifies that the flash borrow instruction is at index 0 in the transaction. The SDK uses this to validate that repayment occurs in the same transaction.
Add Your Custom Instructions
Create your custom instructions to execute with the borrowed funds. In this example, we use a simple memo instruction.// Create memo instruction
const memoIx = getAddMemoInstruction({ memo: 'Hello Kamino' });
// Arrange instructions: flash borrow → your instructions → flash repay
const allInstructions = [flashBorrowIx, memoIx, flashRepayIx];
Replace the memo instruction with your actual business logic.
Build and Send Transaction
Build the transaction with a fresh blockhash and send it to the network.// Get fresh blockhash
const { value: latestBlockhash } = await rpc
.getLatestBlockhash({ commitment: 'finalized' })
.send();
// Build transaction
const transactionMessage = pipe(
createTransactionMessage({ version: 0 }),
(tx) => appendTransactionMessageInstructions(allInstructions, tx),
(tx) => setTransactionMessageFeePayerSigner(signer, tx),
(tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx)
);
const signedTransaction = await signTransactionMessageWithSigners(transactionMessage);
const signature = getSignatureFromTransaction(signedTransaction);
await sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions })(
signedTransaction,
{
commitment: 'confirmed',
skipPreflight: false,
}
);
console.log(`Flash loan successful! Signature: ${signature}`);
Your flash loan transaction is complete! The borrowed funds were used for your operations and automatically repaid within the same atomic transaction.
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
use klend_interface::helpers;
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 spl_associated_token_account::get_associated_token_address;
let user_token_account = get_associated_token_address(&owner, &reserve.liquidity_mint);
Build Flash Loan Instructions
let (borrow_ix, repay_ix) = helpers::flash::flash_loan(
owner,
&reserve,
user_token_account, // source for repayment
user_token_account, // destination for borrowed funds
1_000_000, // 1 USDC (6 decimals)
0, // borrow instruction index in the transaction
None, // no referrer
);
The borrow_instruction_index parameter specifies the position of the flash borrow instruction in the final transaction. Set to 0 if it is the first instruction.
Compose and Send Transaction
use solana_sdk::transaction::Transaction;
use solana_sdk::message::Message;
// Insert your custom instructions between borrow and repay
let your_custom_instructions = vec![/* your logic here */];
let mut all_instructions = vec![borrow_ix];
all_instructions.extend(your_custom_instructions);
all_instructions.push(repay_ix);
let message = Message::new(&all_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!("Flash loan successful! Signature: {signature}");
The flash borrow and repay must be in the same transaction. You can insert any number of instructions between them.