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

🌍 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

Get all Kamino Markets config

Request

Get all Kamino Lending markets configuration for a specific program ID. This is the V2 endpoint that allows filtering by program ID instead of cluster.

Query
programIdstring

KLend program ID

Default "KLend2g3cP87fffoy8q1mQqGKjrxjC8boSyAYavgmjD"
Example: programId=KLend2g3cP87fffoy8q1mQqGKjrxjC8boSyAYavgmjD
curl -i -X GET \
  'https://api-docs.kamino.com/_mock/kamino-api/v2/kamino-market?programId=KLend2g3cP87fffoy8q1mQqGKjrxjC8boSyAYavgmjD'

Responses

OK

Bodyapplication/jsonArray [
namestringrequired

Market name

Example: "Main Market"
isPrimarybooleanrequired

Whether this is a primary market

Example: true
descriptionstringrequired

Market description

Example: "Primary market on mainnet"
lendingMarketstringrequired
Example: "7u3HeHxYDLhnCoErrtycNokbQYbWGzLs6JSDqGAv5PfF"
lookupTablestringrequired
Example: "FGMSBiyVE8TvZcdQnZETAAKw28tkQJ2ccZy6pyp95URb"
isCuratedbooleanrequired

Whether this market is curated

Example: false
]
Response
application/json
[ { "name": "Main Market", "isPrimary": true, "description": "Primary market on mainnet", "lendingMarket": "7u3HeHxYDLhnCoErrtycNokbQYbWGzLs6JSDqGAv5PfF", "lookupTable": "FGMSBiyVE8TvZcdQnZETAAKw28tkQJ2ccZy6pyp95URb", "isCurated": false } ]

Get KLend reserve history

Request

Get historical metrics for a specific reserve in a Kamino Lending market over a specified time range with configurable frequency (minute, hour, or day).

Path
marketPubkeystringrequired
Example: 7u3HeHxYDLhnCoErrtycNokbQYbWGzLs6JSDqGAv5PfF
reservePubkeystringrequired
Example: FBSyPnxtHKLBZ4UeeUyAnbtFuAmTHLtso9YtsqRDRWpM
Query
envstring

Solana cluster environment

Default "mainnet-beta"
Enum"mainnet-beta""devnet""localnet"
Example: env=mainnet-beta
startstring or number
Default "1970-01-01T00:00:00.000Z"
Any of:

Date input (ISO 8601 string or epoch in ms)

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

Date input (ISO 8601 string or epoch in ms)

string
Default "2025-11-06T12:14:45.482Z"
frequencystring

Frequency of the snapshots

Default "hour"
Enum"hour""day"
Example: frequency=hour
curl -i -X GET \
  'https://api-docs.kamino.com/_mock/kamino-api/kamino-market/7u3HeHxYDLhnCoErrtycNokbQYbWGzLs6JSDqGAv5PfF/reserves/FBSyPnxtHKLBZ4UeeUyAnbtFuAmTHLtso9YtsqRDRWpM/metrics/history?env=mainnet-beta&start=2024-01-01T00%3A00%3A00.000Z&end=2024-01-01T00%3A00%3A00.000Z&frequency=hour'

Responses

OK

Bodyapplication/json
any or null
Response
application/json
null

Get Kamino Market config

Request

Get configuration for a specific Kamino Lending market by market address. Optionally filter by program ID.

Path
pubkeystringrequired
Example: 7u3HeHxYDLhnCoErrtycNokbQYbWGzLs6JSDqGAv5PfF
Query
programIdstring

KLend program ID

Default "KLend2g3cP87fffoy8q1mQqGKjrxjC8boSyAYavgmjD"
Example: programId=KLend2g3cP87fffoy8q1mQqGKjrxjC8boSyAYavgmjD
curl -i -X GET \
  'https://api-docs.kamino.com/_mock/kamino-api/v2/kamino-market/7u3HeHxYDLhnCoErrtycNokbQYbWGzLs6JSDqGAv5PfF?programId=KLend2g3cP87fffoy8q1mQqGKjrxjC8boSyAYavgmjD'

Responses

OK

Bodyapplication/json
namestringrequired

Market name

Example: "Main Market"
isPrimarybooleanrequired

Whether this is a primary market

Example: true
descriptionstringrequired

Market description

Example: "Primary market on mainnet"
lendingMarketstringrequired
Example: "7u3HeHxYDLhnCoErrtycNokbQYbWGzLs6JSDqGAv5PfF"
lookupTablestringrequired
Example: "FGMSBiyVE8TvZcdQnZETAAKw28tkQJ2ccZy6pyp95URb"
isCuratedbooleanrequired

Whether this market is curated

Example: false
Response
application/json
{ "name": "Main Market", "isPrimary": true, "description": "Primary market on mainnet", "lendingMarket": "7u3HeHxYDLhnCoErrtycNokbQYbWGzLs6JSDqGAv5PfF", "lookupTable": "FGMSBiyVE8TvZcdQnZETAAKw28tkQJ2ccZy6pyp95URb", "isCurated": false }

🍬 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