Skip to main content
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.
1

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.
2

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, 100);
KaminoMarket.load() fetches the current market state including all reserve data and configuration.
3

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.
4

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.
5

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 ? `\nSetup transaction: https://solscan.io/tx/${setupSignature}` : ''
  }`
);
The deposit is complete. The assets are now supplied to the reserve and earning interest.

Deposit for Borrowing

Deposit USDC as collateral to borrow other assets.
1

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';
2

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, 100);
3

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 || []),
];
4

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}`);
5

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}\nSetup 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.