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 metrics for market reserves

Request

Get current metrics for all reserves in a Kamino Lending market, including APY, TVL, borrow/supply amounts, and LTV ratios.

Path
pubkeystringrequired
Example: 7u3HeHxYDLhnCoErrtycNokbQYbWGzLs6JSDqGAv5PfF
Query
envstring

Solana cluster environment

Default "mainnet-beta"
Enum"mainnet-beta""devnet""localnet"
Example: env=mainnet-beta
curl -i -X GET \
  'https://api-docs.kamino.com/_mock/kamino-api/kamino-market/7u3HeHxYDLhnCoErrtycNokbQYbWGzLs6JSDqGAv5PfF/reserves/metrics?env=mainnet-beta'

Responses

OK

Bodyapplication/jsonArray [
reservestringrequired
Example: "VEG1EMtttdHunMbSza8uoms1R18VXmYSph2bBpHcSJd"
liquidityTokenstringrequired

Liquidity token symbol

Example: "SOL"
liquidityTokenMintstringrequired
Example: "VEG1EMtttdHunMbSza8uoms1R18VXmYSph2bBpHcSJd"
maxLtvstringrequired
Example: "0.65"
borrowApystringrequired
Example: "0.05450988511483601"
supplyApystringrequired
Example: "0.038266801210808055"
totalSupplystringrequired
Example: "1234.56789"
totalBorrowstringrequired
Example: "1234.56789"
totalBorrowUsdstringrequired
Example: "1234.56789"
totalSupplyUsdstringrequired
Example: "1234.56789"
]
Response
application/json
[ { "reserve": "VEG1EMtttdHunMbSza8uoms1R18VXmYSph2bBpHcSJd", "liquidityToken": "SOL", "liquidityTokenMint": "VEG1EMtttdHunMbSza8uoms1R18VXmYSph2bBpHcSJd", "maxLtv": "0.65", "borrowApy": "0.05450988511483601", "supplyApy": "0.038266801210808055", "totalSupply": "1234.56789", "totalBorrow": "1234.56789", "totalBorrowUsd": "1234.56789", "totalSupplyUsd": "1234.56789" } ]

Get KLend market reserves account data

Request

Fetch reserve account data for given Klend markets. Returns base64-encoded account data for all reserves in the specified markets.

Query
programIdstring
Default "KLend2g3cP87fffoy8q1mQqGKjrxjC8boSyAYavgmjD"
marketsstring or Array of stringsrequired
Any of:
string
curl -i -X GET \
  'https://api-docs.kamino.com/_mock/kamino-api/kamino-market/reserves/account-data?programId=KLend2g3cP87fffoy8q1mQqGKjrxjC8boSyAYavgmjD&markets=string'

Responses

OK

Bodyapplication/jsonArray [
marketstringrequired
Example: "VEG1EMtttdHunMbSza8uoms1R18VXmYSph2bBpHcSJd"
reservesArray of objectsrequired
reserves[].​pubkeystringrequired
Example: "VEG1EMtttdHunMbSza8uoms1R18VXmYSph2bBpHcSJd"
reserves[].​datastringrequired

Base64 encoded reserve account data

]
Response
application/json
[ { "market": "VEG1EMtttdHunMbSza8uoms1R18VXmYSph2bBpHcSJd", "reserves": [] } ]

🍬 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