Skip to content

Kamino API (1.0.0)

The Kamino API provides a comprehensive way to interact with Kamino without reading directly from the blockchain.

The API also provides the ability to fetch data that might not be available from just reading the chain.

Kamino products covered by this documentation:

  • Kamino Earn Vaults (kvaults)
  • Kamino Borrow (klend) (checkout out the SDK examples too)
  • Kamino Liquidity (yvaults) (coming soon, in the meantime, checkout out the SDK examples)

The API is rate-limited for unauthenticated users. If you feel you need to make more requests or run into rate-limit issues, please reach out.

Download OpenAPI description
Languages
Servers
Mock server
https://api-docs.kamino.com/_mock/kamino-api/
https://api.kamino.finance/

📊 Vault Data

Endpoints to retrieve information about the state of Kamino Earn Vaults

Operations

🕵️‍♂️ User Data

Endpoints to retrieve information about the state of Kamino Earn Vault users

Operations

Get historical user metrics for all KVault positions

Request

Get historical timeseries of total usd/sol/interest/apy for all user Kamino Earn Vault positions

Path
pubkeystringrequired
Example: AxqtG9SHDkZTLSWg81Sp7VqAzQpRqXtR9ziJ3VQAS8As
Query
startstring or number
Default "1970-01-01T00:00:00.000Z"
Any of:

Start date of the range (defaults to Unix epoch). Accepts ISO string or epoch in ms.

string
Default "1970-01-01T00:00:00.000Z"
endstring or number
Default "2025-11-06T12:14:45.537Z"
Any of:

End date of the range (defaults to current time). Accepts ISO string or epoch in ms.

string
Default "2025-11-06T12:14:45.537Z"
curl -i -X GET \
  'https://api-docs.kamino.com/_mock/kamino-api/kvaults/users/AxqtG9SHDkZTLSWg81Sp7VqAzQpRqXtR9ziJ3VQAS8As/metrics/history?start=1970-01-01T00%3A00%3A00.000Z&end=2025-01-01T00%3A00%3A00.000Z'

Responses

OK

Bodyapplication/jsonArray [
createdOnstring(date-time)required
Example: "2025-03-01T12:00:00.000Z"
usdAmountstringrequired
Example: "1234.56789"
solAmountstringrequired
Example: "1234.56789"
weightedApystringrequired
Example: "0.2"
cumulativeInterestEarnedUsdstringrequired
Example: "1234.56789"
cumulativeInterestEarnedSolstringrequired
Example: "1234.56789"
interestEarnedPerSecondUsdstringrequired
Example: "1234.56789"
interestEarnedPerSecondSolstringrequired
Example: "1234.56789"
]
Response
application/json
[ { "createdOn": "2025-03-01T12:00:00.000Z", "usdAmount": "1234.56789", "solAmount": "1234.56789", "weightedApy": "0.2", "cumulativeInterestEarnedUsd": "1234.56789", "cumulativeInterestEarnedSol": "1234.56789", "interestEarnedPerSecondUsd": "1234.56789", "interestEarnedPerSecondSol": "1234.56789" } ]

Get user KVault transactions

Request

Get all Kamino Earn Vault transactions for a given user

Path
pubkeystringrequired
Example: AxqtG9SHDkZTLSWg81Sp7VqAzQpRqXtR9ziJ3VQAS8As
curl -i -X GET \
  https://api-docs.kamino.com/_mock/kamino-api/kvaults/users/AxqtG9SHDkZTLSWg81Sp7VqAzQpRqXtR9ziJ3VQAS8As/transactions

Responses

OK

Bodyapplication/jsonArray [
createdOnstring(date)required

Date when the instruction was created

instructionstringrequired

Type of instruction

tokenMintstringrequired
Example: "VEG1EMtttdHunMbSza8uoms1R18VXmYSph2bBpHcSJd"
tokenAmountstringrequired
Example: "1234.56789"
tokenPricestringrequired
Example: "1234.56789"
solPricestringrequired
Example: "1234.56789"
sharePricestringrequired
Example: "1234.56789"
numberOfSharesstringrequired
Example: "1234.56789"
usdValuestringrequired
Example: "1234.56789"
transactionstringrequired

Transaction signature

kvaultstringrequired
Example: "VEG1EMtttdHunMbSza8uoms1R18VXmYSph2bBpHcSJd"
latestPositionbooleanrequired

Indicates if this is the latest position

]
Response
application/json
[ { "createdOn": "2019-08-24", "instruction": "string", "tokenMint": "VEG1EMtttdHunMbSza8uoms1R18VXmYSph2bBpHcSJd", "tokenAmount": "1234.56789", "tokenPrice": "1234.56789", "solPrice": "1234.56789", "sharePrice": "1234.56789", "numberOfShares": "1234.56789", "usdValue": "1234.56789", "transaction": "string", "kvault": "VEG1EMtttdHunMbSza8uoms1R18VXmYSph2bBpHcSJd", "latestPosition": true } ]

Get all user KVault positions

Request

Get a list of user positions for each Kamino Earn Vault the user has currently deposited into. Returns staked and unstaked shares.

Path
pubkeystringrequired
Example: AxqtG9SHDkZTLSWg81Sp7VqAzQpRqXtR9ziJ3VQAS8As
curl -i -X GET \
  https://api-docs.kamino.com/_mock/kamino-api/kvaults/users/AxqtG9SHDkZTLSWg81Sp7VqAzQpRqXtR9ziJ3VQAS8As/positions

Responses

OK

Bodyapplication/jsonArray [
addressstringrequired
Example: "VEG1EMtttdHunMbSza8uoms1R18VXmYSph2bBpHcSJd"
stateobject(The vault state object)required
state.​vaultAdminAuthoritystringrequired
Example: "VEG1EMtttdHunMbSza8uoms1R18VXmYSph2bBpHcSJd"
state.​baseVaultAuthoritystringrequired
Example: "VEG1EMtttdHunMbSza8uoms1R18VXmYSph2bBpHcSJd"
state.​baseVaultAuthorityBumpnumberrequired

Bump seed for the base vault authority

state.​tokenMintstringrequired
Example: "VEG1EMtttdHunMbSza8uoms1R18VXmYSph2bBpHcSJd"
state.​tokenMintDecimalsnumberrequired

Decimals of the token mint

state.​tokenVaultstringrequired
Example: "VEG1EMtttdHunMbSza8uoms1R18VXmYSph2bBpHcSJd"
state.​tokenProgramstringrequired
Example: "VEG1EMtttdHunMbSza8uoms1R18VXmYSph2bBpHcSJd"
state.​sharesMintstringrequired
Example: "VEG1EMtttdHunMbSza8uoms1R18VXmYSph2bBpHcSJd"
state.​sharesMintDecimalsnumberrequired

Decimals of the shares mint

state.​tokenAvailablestringrequired

Amount of tokens available in the vault

state.​sharesIssuedstringrequired

Total shares issued by the vault

state.​availableCrankFundsstringrequired

Available crank funds in the vault

state.​performanceFeeBpsnumberrequired

Performance fee in basis points

state.​managementFeeBpsnumberrequired

Management fee in basis points

state.​lastFeeChargeTimestampnumberrequired

Timestamp of the last fee charge

state.​prevAumstringrequired
Example: "1234.56789"
state.​pendingFeesstringrequired
Example: "1234.56789"
state.​vaultAllocationStrategyArray of objectsrequired

List of vault allocation strategies

state.​vaultAllocationStrategy[].​reservestringrequired
Example: "VEG1EMtttdHunMbSza8uoms1R18VXmYSph2bBpHcSJd"
state.​vaultAllocationStrategy[].​ctokenVaultstringrequired
Example: "VEG1EMtttdHunMbSza8uoms1R18VXmYSph2bBpHcSJd"
state.​vaultAllocationStrategy[].​targetAllocationWeightnumberrequired

Target allocation weight as a whole number

state.​vaultAllocationStrategy[].​tokenAllocationCapstringrequired

Token allocation cap in lamports

state.​vaultAllocationStrategy[].​ctokenVaultBumpnumberrequired

cToken vault bump seed

state.​vaultAllocationStrategy[].​ctokenAllocationstringrequired

cToken allocation in lamports

state.​vaultAllocationStrategy[].​lastInvestSlotstringrequired

Last invest slot

state.​vaultAllocationStrategy[].​tokenTargetAllocationstringrequired
Example: "1234.56789"
state.​minDepositAmountstringrequired

Minimum deposit amount in lamports

state.​minWithdrawAmountstringrequired

Minimum withdraw amount in lamports

state.​minInvestAmountstringrequired

Minimum invest amount in lamports

state.​minInvestDelaySlotsnumberrequired

Minimum invest delay in slots

state.​crankFundFeePerReservestringrequired

Crank fund fee per reserve

state.​pendingAdminstringrequired
Example: "VEG1EMtttdHunMbSza8uoms1R18VXmYSph2bBpHcSJd"
state.​cumulativeEarnedIntereststringrequired
Example: "1234.56789"
state.​cumulativeMgmtFeesstringrequired
Example: "1234.56789"
state.​cumulativePerfFeesstringrequired
Example: "1234.56789"
state.​namestringrequired

Name of the vault

state.​vaultLookupTablestringrequired
Example: "VEG1EMtttdHunMbSza8uoms1R18VXmYSph2bBpHcSJd"
state.​vaultFarmstringrequired
Example: "VEG1EMtttdHunMbSza8uoms1R18VXmYSph2bBpHcSJd"
state.​creationTimestampnumberrequired

Timestamp of vault creation

state.​allocationAdminstringrequired
Example: "VEG1EMtttdHunMbSza8uoms1R18VXmYSph2bBpHcSJd"
programIdstringrequired
Example: "VEG1EMtttdHunMbSza8uoms1R18VXmYSph2bBpHcSJd"
vaultAddressstringrequired
Example: "VEG1EMtttdHunMbSza8uoms1R18VXmYSph2bBpHcSJd"
stakedSharesstringrequired
Example: "1234.56789"
unstakedSharesstringrequired
Example: "1234.56789"
totalSharesstringrequired
Example: "1234.56789"
]
Response
application/json
[ { "vaultAddress": "VEG1EMtttdHunMbSza8uoms1R18VXmYSph2bBpHcSJd", "stakedShares": "1234.56789", "unstakedShares": "1234.56789", "totalShares": "1234.56789" } ]

🌍 Global Data

Endpoints to retrieve information about the global state of Kamino Earn Vaults

Operations

🪙 Share Mint Data

Endpoints to retrieve information about the global state of Kamino Earn Vault share mints

Operations

🌱 Deposit

Get unsigned Solana transactions to deposit into Kamino Earn Vaults

Deposit Transaction
import {
  address,
  Address,
  addSignersToTransactionMessage,
  CompiledTransactionMessage,
  createSolanaRpc,
  createSolanaRpcSubscriptions,
  decompileTransactionMessageFetchingLookupTables, generateKeyPairSigner,
  getCompiledTransactionMessageDecoder,
  getSignatureFromTransaction,
  getTransactionDecoder,
  pipe,
  sendAndConfirmTransactionFactory,
  setTransactionMessageFeePayerSigner,
  setTransactionMessageLifetimeUsingBlockhash,
  Signature,
  signTransactionMessageWithSigners,
  TransactionMessageBytes,
  TransactionSigner
} from '@solana/kit';

async function fetchTx(wallet: Address, kvault: Address, amount: number): Promise<TransactionMessageBytes> {
  const resp = await fetch(
    `https://api.kamino.finance/ktx/kvault/deposit`,
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        wallet: `${wallet}`,
        kvault: `${kvault}`,
        amount: amount.toString()
      })
    }
  );

  const data = (await resp.json()) as { transaction: string };

  const txBuffer = Buffer.from(data.transaction, 'base64');
  return getTransactionDecoder().decode(txBuffer).messageBytes;
}

async function signAndSendTx(wallet: TransactionSigner, txBytes: TransactionMessageBytes): Promise<Signature> {
  const rpc = createSolanaRpc('https://api.mainnet-beta.solana.com');
  const rpcSubscriptions = createSolanaRpcSubscriptions('wss://api.mainnet-beta.solana.com');

  const dMessage: CompiledTransactionMessage = getCompiledTransactionMessageDecoder().decode(txBytes);

  const bh = await rpc.getLatestBlockhash({ commitment: 'finalized' }).send();

  const tx = await pipe(
    await decompileTransactionMessageFetchingLookupTables(dMessage, rpc),
    (tx) => setTransactionMessageLifetimeUsingBlockhash(bh.value, tx),
    (tx) => setTransactionMessageFeePayerSigner(wallet, tx),
    (tx) => addSignersToTransactionMessage([wallet], tx),
    (tx) => signTransactionMessageWithSigners(tx)
  );

  const encodedTxMessage = Buffer.from(tx.messageBytes).toString('base64');
  console.log(`🔍 Simulation URL: https://explorer.solana.com/tx/inspector?message=${encodeURIComponent(encodedTxMessage)}&cluster=mainnet-beta&signatures=${encodeURIComponent(`[${wallet}]`)}`);

  const sig = getSignatureFromTransaction(tx);

  console.log(`Sending tx with signature: https://solscan.io/tx/${sig}`);
  await sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions })(tx, {
    commitment: 'confirmed',
    preflightCommitment: 'confirmed',
    maxRetries: 0n,
    skipPreflight: true,
    minContextSlot: bh.context.slot,
  });

  console.log(`✅ Tx sent successfully`)

  return sig;
}

async function main() {
  const wallet = await generateKeyPairSigner();
  // Neutral Trade USDC Max Yield Vault
  const vault = address('67dqmR76uAbjX6e81A1ganKv3ou31WUMEdeWJkwVfeXy');

  // 100 USDC
  const depositAmount = 100.0;

  console.log(`Using temporary wallet: ${wallet.address} to deposit into vault: ${vault}`);

  const tx = await fetchTx(wallet.address, vault, depositAmount);
  await signAndSendTx(wallet, tx);
}

main().then(() => `⚡️Deposit transaction complete`).catch((err) => console.error(err));
Operations

🏃‍♂️ Withdraw

Get unsigned Solana transactions to withdraw from Kamino Earn Vaults

Withdraw Transaction
import {
  address,
  Address,
  addSignersToTransactionMessage,
  CompiledTransactionMessage,
  createSolanaRpc,
  createSolanaRpcSubscriptions,
  decompileTransactionMessageFetchingLookupTables, generateKeyPairSigner,
  getCompiledTransactionMessageDecoder,
  getSignatureFromTransaction,
  getTransactionDecoder,
  pipe,
  sendAndConfirmTransactionFactory,
  setTransactionMessageFeePayerSigner,
  setTransactionMessageLifetimeUsingBlockhash,
  Signature,
  signTransactionMessageWithSigners,
  TransactionMessageBytes,
  TransactionSigner
} from '@solana/kit';

async function fetchTx(wallet: Address, kvault: Address, amount: number): Promise<TransactionMessageBytes> {
  const resp = await fetch(
    `https://api.kamino.finance/ktx/kvault/withdraw`,
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        wallet: `${wallet}`,
        kvault: `${kvault}`,
        amount: amount.toString()
      })
    }
  );

  const data = (await resp.json()) as { transaction: string };

  const txBuffer = Buffer.from(data.transaction, 'base64');
  return getTransactionDecoder().decode(txBuffer).messageBytes;
}

async function signAndSendTx(wallet: TransactionSigner, txBytes: TransactionMessageBytes): Promise<Signature> {
  const rpc = createSolanaRpc('https://api.mainnet-beta.solana.com');
  const rpcSubscriptions = createSolanaRpcSubscriptions('wss://api.mainnet-beta.solana.com');

  const dMessage: CompiledTransactionMessage = getCompiledTransactionMessageDecoder().decode(txBytes);

  const bh = await rpc.getLatestBlockhash({ commitment: 'finalized' }).send();

  const tx = await pipe(
    await decompileTransactionMessageFetchingLookupTables(dMessage, rpc),
    (tx) => setTransactionMessageLifetimeUsingBlockhash(bh.value, tx),
    (tx) => setTransactionMessageFeePayerSigner(wallet, tx),
    (tx) => addSignersToTransactionMessage([wallet], tx),
    (tx) => signTransactionMessageWithSigners(tx)
  );

  const encodedTxMessage = Buffer.from(tx.messageBytes).toString('base64');
  console.log(`🔍 Simulation URL: https://explorer.solana.com/tx/inspector?message=${encodeURIComponent(encodedTxMessage)}&cluster=mainnet-beta&signatures=${encodeURIComponent(`[${wallet}]`)}`);

  const sig = getSignatureFromTransaction(tx);

  console.log(`Sending tx with signature: https://solscan.io/tx/${sig}`);
  await sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions })(tx, {
    commitment: 'confirmed',
    preflightCommitment: 'confirmed',
    maxRetries: 0n,
    skipPreflight: true,
    minContextSlot: bh.context.slot,
  });

  console.log(`✅ Tx sent successfully`)

  return sig;
}

async function main() {
  const wallet = await generateKeyPairSigner();
  // Neutral Trade USDC Max Yield Vault
  const vault = address('67dqmR76uAbjX6e81A1ganKv3ou31WUMEdeWJkwVfeXy');

  // 50 USDC
  const withdrawAmount = 50.0;

  console.log(`Using temporary wallet: ${wallet.address} to withdraw from vault: ${vault}`);

  const tx = await fetchTx(wallet.address, vault, withdrawAmount);
  await signAndSendTx(wallet, tx);
}

main().then(() => `⚡️Withdrawal transaction complete`).catch((err) => console.error(err));
Operations

🏦 Markets

Operations

🍬 Rewards

Get up-to-date and historical rewards data.

Operations

👥 Users and Loans

Operations

📝 Deposit

Get unsigned Solana transactions to deposit liquidity into a Kamino Lend market reserve, which can then be used as collateral to borrow

Deposit Transaction
import {
  address,
  Address,
  addSignersToTransactionMessage,
  CompiledTransactionMessage,
  createSolanaRpc,
  createSolanaRpcSubscriptions,
  decompileTransactionMessageFetchingLookupTables, generateKeyPairSigner,
  getCompiledTransactionMessageDecoder,
  getSignatureFromTransaction,
  getTransactionDecoder,
  pipe,
  sendAndConfirmTransactionFactory,
  setTransactionMessageFeePayerSigner,
  setTransactionMessageLifetimeUsingBlockhash,
  Signature,
  signTransactionMessageWithSigners,
  TransactionMessageBytes,
  TransactionSigner
} from '@solana/kit';

async function fetchTx(wallet: Address, market: Address, reserve: Address, amount: number): Promise<TransactionMessageBytes> {
  const resp = await fetch(
    `https://api.kamino.finance/ktx/klend/deposit`,
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        wallet: `${wallet}`,
        market: `${market}`,
        reserve: `${reserve}`,
        amount: amount.toString()
      })
    }
  );

  const data = (await resp.json()) as { transaction: string };

  const txBuffer = Buffer.from(data.transaction, 'base64');
  return getTransactionDecoder().decode(txBuffer).messageBytes;
}

async function signAndSendTx(wallet: TransactionSigner, txBytes: TransactionMessageBytes): Promise<Signature> {
  const rpc = createSolanaRpc('https://api.mainnet-beta.solana.com');
  const rpcSubscriptions = createSolanaRpcSubscriptions('wss://api.mainnet-beta.solana.com');

  const dMessage: CompiledTransactionMessage = getCompiledTransactionMessageDecoder().decode(txBytes);

  const bh = await rpc.getLatestBlockhash({ commitment: 'finalized' }).send();

  const tx = await pipe(
    await decompileTransactionMessageFetchingLookupTables(dMessage, rpc),
    (tx) => setTransactionMessageLifetimeUsingBlockhash(bh.value, tx),
    (tx) => setTransactionMessageFeePayerSigner(wallet, tx),
    (tx) => addSignersToTransactionMessage([wallet], tx),
    (tx) => signTransactionMessageWithSigners(tx)
  );

  const encodedTxMessage = Buffer.from(tx.messageBytes).toString('base64');
  console.log(`🔍 Simulation URL: https://explorer.solana.com/tx/inspector?message=${encodeURIComponent(encodedTxMessage)}&cluster=mainnet-beta&signatures=${encodeURIComponent(`[${wallet}]`)}`);

  const sig = getSignatureFromTransaction(tx);

  console.log(`Sending tx with signature: https://solscan.io/tx/${sig}`);
  await sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions })(tx, {
    commitment: 'confirmed',
    preflightCommitment: 'confirmed',
    maxRetries: 0n,
    skipPreflight: true,
    minContextSlot: bh.context.slot,
  });

  console.log(`✅ Tx sent successfully`)

  return sig;
}

async function main() {
  const wallet = await generateKeyPairSigner();
  // Main market
  const market = address('7u3HeHxYDLhnCoErrtycNokbQYbWGzLs6JSDqGAv5PfF');
  // USDC reserve in main market
  const reserve = address('D6q6wuQSrifJKZYpR1M8R4YawnLDtDsMmWM1NbBmgJ59');

  // 50 USDC
  const depositAmount = 50.0;

  console.log(`Using temporary wallet: ${wallet.address} to deposit into market: ${market}, reserve: ${reserve}`);

  const tx = await fetchTx(wallet.address, market, reserve, depositAmount);
  await signAndSendTx(wallet, tx);
}

main().then(() => `⚡️Deposit transaction complete`).catch((err) => console.error(err));
Operations

💳 Borrow

Get unsigned Solana transactions to borrow liquidity from a given Kamino Lend market reserve

Borrow Transaction
import {
  address,
  Address,
  addSignersToTransactionMessage,
  CompiledTransactionMessage,
  createSolanaRpc,
  createSolanaRpcSubscriptions,
  decompileTransactionMessageFetchingLookupTables, generateKeyPairSigner,
  getCompiledTransactionMessageDecoder,
  getSignatureFromTransaction,
  getTransactionDecoder,
  pipe,
  sendAndConfirmTransactionFactory,
  setTransactionMessageFeePayerSigner,
  setTransactionMessageLifetimeUsingBlockhash,
  Signature,
  signTransactionMessageWithSigners,
  TransactionMessageBytes,
  TransactionSigner
} from '@solana/kit';

async function fetchTx(wallet: Address, market: Address, reserve: Address, amount: number): Promise<TransactionMessageBytes> {
  const resp = await fetch(
    `https://api.kamino.finance/ktx/klend/borrow`,
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        wallet: `${wallet}`,
        market: `${market}`,
        reserve: `${reserve}`,
        amount: amount.toString()
      })
    }
  );

  const data = (await resp.json()) as { transaction: string };

  const txBuffer = Buffer.from(data.transaction, 'base64');
  return getTransactionDecoder().decode(txBuffer).messageBytes;
}

async function signAndSendTx(wallet: TransactionSigner, txBytes: TransactionMessageBytes): Promise<Signature> {
  const rpc = createSolanaRpc('https://api.mainnet-beta.solana.com');
  const rpcSubscriptions = createSolanaRpcSubscriptions('wss://api.mainnet-beta.solana.com');

  const dMessage: CompiledTransactionMessage = getCompiledTransactionMessageDecoder().decode(txBytes);

  const bh = await rpc.getLatestBlockhash({ commitment: 'finalized' }).send();

  const tx = await pipe(
    await decompileTransactionMessageFetchingLookupTables(dMessage, rpc),
    (tx) => setTransactionMessageLifetimeUsingBlockhash(bh.value, tx),
    (tx) => setTransactionMessageFeePayerSigner(wallet, tx),
    (tx) => addSignersToTransactionMessage([wallet], tx),
    (tx) => signTransactionMessageWithSigners(tx)
  );

  const encodedTxMessage = Buffer.from(tx.messageBytes).toString('base64');
  console.log(`🔍 Simulation URL: https://explorer.solana.com/tx/inspector?message=${encodeURIComponent(encodedTxMessage)}&cluster=mainnet-beta&signatures=${encodeURIComponent(`[${wallet}]`)}`);

  const sig = getSignatureFromTransaction(tx);

  console.log(`Sending tx with signature: https://solscan.io/tx/${sig}`);
  await sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions })(tx, {
    commitment: 'confirmed',
    preflightCommitment: 'confirmed',
    maxRetries: 0n,
    skipPreflight: true,
    minContextSlot: bh.context.slot,
  });

  console.log(`✅ Tx sent successfully`)

  return sig;
}

async function main() {
  const wallet = await generateKeyPairSigner();
  // Main market
  const market = address('7u3HeHxYDLhnCoErrtycNokbQYbWGzLs6JSDqGAv5PfF');
  // USDC reserve in main market
  const reserve = address('D6q6wuQSrifJKZYpR1M8R4YawnLDtDsMmWM1NbBmgJ59');

  // 50 USDC
  const borrowAmount = 50.0;

  console.log(`Using temporary wallet: ${wallet.address} to borrow from market: ${market}, reserve: ${reserve}`);

  const tx = await fetchTx(wallet.address, market, reserve, borrowAmount);
  await signAndSendTx(wallet, tx);
}

main().then(() => `⚡️Borrow transaction complete`).catch((err) => console.error(err));
Operations

📝 Repay

Get unsigned Solana transactions to repay debt for a given Kamino Lend user position

Repay Transaction
import {
  address,
  Address,
  addSignersToTransactionMessage,
  CompiledTransactionMessage,
  createSolanaRpc,
  createSolanaRpcSubscriptions,
  decompileTransactionMessageFetchingLookupTables, generateKeyPairSigner,
  getCompiledTransactionMessageDecoder,
  getSignatureFromTransaction,
  getTransactionDecoder,
  pipe,
  sendAndConfirmTransactionFactory,
  setTransactionMessageFeePayerSigner,
  setTransactionMessageLifetimeUsingBlockhash,
  Signature,
  signTransactionMessageWithSigners,
  TransactionMessageBytes,
  TransactionSigner
} from '@solana/kit';

async function fetchTx(wallet: Address, market: Address, reserve: Address, amount: number): Promise<TransactionMessageBytes> {
  const resp = await fetch(
    `https://api.kamino.finance/ktx/klend/repay`,
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        wallet: `${wallet}`,
        market: `${market}`,
        reserve: `${reserve}`,
        amount: amount.toString()
      })
    }
  );

  const data = (await resp.json()) as { transaction: string };

  const txBuffer = Buffer.from(data.transaction, 'base64');
  return getTransactionDecoder().decode(txBuffer).messageBytes;
}

async function signAndSendTx(wallet: TransactionSigner, txBytes: TransactionMessageBytes): Promise<Signature> {
  const rpc = createSolanaRpc('https://api.mainnet-beta.solana.com');
  const rpcSubscriptions = createSolanaRpcSubscriptions('wss://api.mainnet-beta.solana.com');

  const dMessage: CompiledTransactionMessage = getCompiledTransactionMessageDecoder().decode(txBytes);

  const bh = await rpc.getLatestBlockhash({ commitment: 'finalized' }).send();

  const tx = await pipe(
    await decompileTransactionMessageFetchingLookupTables(dMessage, rpc),
    (tx) => setTransactionMessageLifetimeUsingBlockhash(bh.value, tx),
    (tx) => setTransactionMessageFeePayerSigner(wallet, tx),
    (tx) => addSignersToTransactionMessage([wallet], tx),
    (tx) => signTransactionMessageWithSigners(tx)
  );

  const encodedTxMessage = Buffer.from(tx.messageBytes).toString('base64');
  console.log(`🔍 Simulation URL: https://explorer.solana.com/tx/inspector?message=${encodeURIComponent(encodedTxMessage)}&cluster=mainnet-beta&signatures=${encodeURIComponent(`[${wallet}]`)}`);

  const sig = getSignatureFromTransaction(tx);

  console.log(`Sending tx with signature: https://solscan.io/tx/${sig}`);
  await sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions })(tx, {
    commitment: 'confirmed',
    preflightCommitment: 'confirmed',
    maxRetries: 0n,
    skipPreflight: true,
    minContextSlot: bh.context.slot,
  });

  console.log(`✅ Tx sent successfully`)

  return sig;
}

async function main() {
  const wallet = await generateKeyPairSigner();
  // Main market
  const market = address('7u3HeHxYDLhnCoErrtycNokbQYbWGzLs6JSDqGAv5PfF');
  // USDC reserve in main market
  const reserve = address('D6q6wuQSrifJKZYpR1M8R4YawnLDtDsMmWM1NbBmgJ59');

  // 50 USDC
  const repayAmount = 50.0;

  console.log(`Using temporary wallet: ${wallet.address} to repay debt from market: ${market}, reserve: ${reserve}`);

  const tx = await fetchTx(wallet.address, market, reserve, repayAmount);
  await signAndSendTx(wallet, tx);
}

main().then(() => `⚡️Repay transaction complete`).catch((err) => console.error(err));
Operations

🐝 Withdraw

Get unsigned Solana transactions to withdraw liquidity from a given Kamino Lend market reserve

Withdraw Transaction
import {
  address,
  Address,
  addSignersToTransactionMessage,
  CompiledTransactionMessage,
  createSolanaRpc,
  createSolanaRpcSubscriptions,
  decompileTransactionMessageFetchingLookupTables, generateKeyPairSigner,
  getCompiledTransactionMessageDecoder,
  getSignatureFromTransaction,
  getTransactionDecoder,
  pipe,
  sendAndConfirmTransactionFactory,
  setTransactionMessageFeePayerSigner,
  setTransactionMessageLifetimeUsingBlockhash,
  Signature,
  signTransactionMessageWithSigners,
  TransactionMessageBytes,
  TransactionSigner
} from '@solana/kit';

async function fetchTx(wallet: Address, market: Address, reserve: Address, amount: number): Promise<TransactionMessageBytes> {
  const resp = await fetch(
    `https://api.kamino.finance/ktx/klend/withdraw`,
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        wallet: `${wallet}`,
        market: `${market}`,
        reserve: `${reserve}`,
        amount: amount.toString()
      })
    }
  );

  const data = (await resp.json()) as { transaction: string };

  const txBuffer = Buffer.from(data.transaction, 'base64');
  return getTransactionDecoder().decode(txBuffer).messageBytes;
}

async function signAndSendTx(wallet: TransactionSigner, txBytes: TransactionMessageBytes): Promise<Signature> {
  const rpc = createSolanaRpc('https://api.mainnet-beta.solana.com');
  const rpcSubscriptions = createSolanaRpcSubscriptions('wss://api.mainnet-beta.solana.com');

  const dMessage: CompiledTransactionMessage = getCompiledTransactionMessageDecoder().decode(txBytes);

  const bh = await rpc.getLatestBlockhash({ commitment: 'finalized' }).send();

  const tx = await pipe(
    await decompileTransactionMessageFetchingLookupTables(dMessage, rpc),
    (tx) => setTransactionMessageLifetimeUsingBlockhash(bh.value, tx),
    (tx) => setTransactionMessageFeePayerSigner(wallet, tx),
    (tx) => addSignersToTransactionMessage([wallet], tx),
    (tx) => signTransactionMessageWithSigners(tx)
  );

  const encodedTxMessage = Buffer.from(tx.messageBytes).toString('base64');
  console.log(`🔍 Simulation URL: https://explorer.solana.com/tx/inspector?message=${encodeURIComponent(encodedTxMessage)}&cluster=mainnet-beta&signatures=${encodeURIComponent(`[${wallet}]`)}`);

  const sig = getSignatureFromTransaction(tx);

  console.log(`Sending tx with signature: https://solscan.io/tx/${sig}`);
  await sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions })(tx, {
    commitment: 'confirmed',
    preflightCommitment: 'confirmed',
    maxRetries: 0n,
    skipPreflight: true,
    minContextSlot: bh.context.slot,
  });

  console.log(`✅ Tx sent successfully`)

  return sig;
}

async function main() {
  const wallet = await generateKeyPairSigner();
  // Main market
  const market = address('7u3HeHxYDLhnCoErrtycNokbQYbWGzLs6JSDqGAv5PfF');
  // USDC reserve in main market
  const reserve = address('D6q6wuQSrifJKZYpR1M8R4YawnLDtDsMmWM1NbBmgJ59');

  // 50 USDC
  const withdrawAmount = 50.0;

  console.log(`Using temporary wallet: ${wallet.address} to withdraw from market: ${market}, reserve: ${reserve}`);

  const tx = await fetchTx(wallet.address, market, reserve, withdrawAmount);
  await signAndSendTx(wallet, tx);
}

main().then(() => `⚡️Withdraw transaction complete`).catch((err) => console.error(err));
Operations