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

# Repay with Collateral

> Repay debt using collateral from your position with KSwap routing

<p>Repay with collateral reduces debt in a single atomic transaction by converting collateral into the debt token without requiring additional funds. The protocol flash borrows the debt token, repays the debt, withdraws the freed collateral, swaps it to the debt token via KSwap, and repays the flash loan—all in one transaction.</p>

## Repay with Collateral using KSwap

Reduce debt in a single transaction by converting collateral using a flash loan and KSwap.

<Steps>
  <Step>
    ### Import Dependencies

    Import the required packages for Solana RPC communication, Kamino SDK operations, KSwap routing, and transaction building.

    ```typescript theme={null}
    import {
      createSolanaRpc,
      createSolanaRpcSubscriptions,
      address,
      pipe,
      createTransactionMessage,
      setTransactionMessageFeePayerSigner,
      setTransactionMessageLifetimeUsingBlockhash,
      appendTransactionMessageInstructions,
      signTransactionMessageWithSigners,
      sendAndConfirmTransactionFactory,
      getSignatureFromTransaction,
      none,
      compressTransactionMessageUsingAddressLookupTables,
    } from '@solana/kit';
    import type { Address } from '@solana/kit';
    import {
      KaminoMarket,
      MultiplyObligation,
      PROGRAM_ID,
      parseKeypairFile,
      getRepayWithCollIxs,
      getUserLutAddressAndSetupIxs,
      getScopeRefreshIxForObligationAndReserves,
      getComputeBudgetAndPriorityFeeIxs,
      lamportsToNumberDecimal,
      simulateTx,
      DEFAULT_RECENT_SLOT_DURATION_MS,
    } from '@kamino-finance/klend-sdk';
    import { KswapSdk } from '@kamino-finance/kswap-sdk';
    import { Scope } from '@kamino-finance/scope-sdk';
    import Decimal from 'decimal.js';
    import { getKswapQuoter, getKswapSwapper } from './kswap_utils.js';
    import { fetchAllAddressLookupTable } from '@solana-program/address-lookup-table';
    ```
  </Step>

  <Step>
    ### Load Configuration and Initialize SDKs

    Load the keypair, initialize RPC connections, and set up the market and SDKs.

    ```typescript theme={null}
    const KEYPAIR_FILE = '/path/to/your/keypair.json';
    const CDN_ENDPOINT = 'https://cdn.kamino.finance';

    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('5wJeMrUYECGq41fxRESKALVcHnNX26TAWy4W98yULsua'); // xStocks Market
    const debtTokenMint = address('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'); // USDC
    const XSTOCKS_MARKET_LUT = address('8ofreL6hKfEet1DnhHVGvCTnSdz4pg85PpbuCUHnEcKm');

    const market = await KaminoMarket.load(rpc, marketPubkey, DEFAULT_RECENT_SLOT_DURATION_MS);
    const scope = new Scope('mainnet-beta', rpc);
    const kswapSdk = new KswapSdk('https://api.kamino.finance/kswap', rpc, rpcSubscriptions);
    ```
  </Step>

  <Step>
    ### Find Collateral Reserve

    Dynamically discover the collateral token mint by searching the market reserves by symbol.

    ```typescript theme={null}
    const tslaReserve = Array.from(market!.reserves.values()).find((reserve) => reserve.symbol === 'TSLAx');
    if (!tslaReserve) {
      console.log('TSLAx reserve not found in xStocks market');
    }
    const collTokenMint = tslaReserve.getLiquidityMint();
    ```
  </Step>

  <Step>
    ### Fetch Multiply Lookup Tables

    Retrieve the multiply-specific lookup tables from Kamino's CDN.

    ```typescript theme={null}
    const kaminoResourcesResponse = await fetch(`${CDN_ENDPOINT}/resources.json`);
    const kaminoResourcesData = await kaminoResourcesResponse.json();
    const kaminoResources = kaminoResourcesData['mainnet-beta'];
    const multiplyColPairs = kaminoResources.multiplyLUTsPairs[collTokenMint] || {};
    const multiplyLut = multiplyColPairs[debtTokenMint] || [];
    const multiplyLutKeys = multiplyLut.map((lut: string) => address(lut));
    ```
  </Step>

  <Step>
    ### Configure Repay Parameters

    Set the amount of debt to repay using collateral and the slippage tolerance.

    ```typescript theme={null}
    const repayAmount = new Decimal(5); // $5 worth of debt to repay using collateral
    const slippageBps = 100; // 1% slippage
    ```

    <Note>
      Some assets may require higher slippage than others.
    </Note>
  </Step>

  <Step>
    ### Setup Lookup Tables

    Initialize the user's lookup table and execute any required setup transactions.

    ```typescript theme={null}
    const multiplyMints: { coll: Address; debt: Address }[] = [{ coll: collTokenMint, debt: debtTokenMint }];
    const leverageMints: { coll: Address; debt: Address }[] = [];

    const [userLookupTable, setupTxsIxs] = await getUserLutAddressAndSetupIxs(
      market!,
      signer,
      none(),
      true,
      multiplyMints,
      leverageMints
    );

    for (const setupIxs of setupTxsIxs) {
      const { value: setupBlockhash } = await rpc.getLatestBlockhash({ commitment: 'finalized' }).send();

      const setupTx = pipe(
        createTransactionMessage({ version: 0 }),
        (tx) => setTransactionMessageFeePayerSigner(signer, tx),
        (tx) => setTransactionMessageLifetimeUsingBlockhash(setupBlockhash, tx),
        (tx) => appendTransactionMessageInstructions(setupIxs, tx)
      );

      const signedSetupTx = await signTransactionMessageWithSigners(setupTx);

      await sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions })(signedSetupTx, {
        commitment: 'confirmed',
        skipPreflight: true,
      });

      await new Promise((resolve) => setTimeout(resolve, 2000));
    }
    ```

    <Note>
      Setup transactions extend the lookup table with addresses specific to the position. This is a one-time operation per collateral/debt pair.
    </Note>
  </Step>

  <Step>
    ### Fetch Existing Obligation

    Retrieve the current obligation and display position details.

    ```typescript theme={null}
    const currentSlot = await rpc.getSlot().send();
    const collTokenReserve = market!.getReserveByMint(collTokenMint)!;
    const debtTokenReserve = market!.getReserveByMint(debtTokenMint)!;

    const obligationType = new MultiplyObligation(collTokenMint, debtTokenMint, PROGRAM_ID);
    const obligationAddress = await obligationType.toPda(market!.getAddress(), signer.address);
    const obligation = await market!.getObligationByAddress(obligationAddress);

    if (!obligation) {
      console.log('No obligation found. You must have an active position to repay with collateral.');
    }

    const deposited = lamportsToNumberDecimal(
      Array.from(obligation.deposits.values())[0]?.amount.toString() || '0',
      collTokenReserve.state.liquidity.mintDecimals.toNumber()
    );
    const borrowed = lamportsToNumberDecimal(
      Array.from(obligation.borrows.values())[0]?.amount.toString() || '0',
      debtTokenReserve.state.liquidity.mintDecimals.toNumber()
    );

    console.log(`Current position: ${deposited.toString()} TSLAx deposited, ${borrowed.toString()} USDC borrowed`);
    ```

    <Note>
      This example uses `MultiplyObligation`. For standard borrow positions, use `VanillaObligation` instead.
    </Note>
  </Step>

  <Step>
    ### Build Repay Instructions

    Generate repay with collateral instructions with scope price refresh and compute budget.

    ```typescript theme={null}
    const scopeConfiguration = { scope, scopeConfigurations: await scope.getAllConfigurations() };
    const scopeRefreshIx = await getScopeRefreshIxForObligationAndReserves(
      market!,
      collTokenReserve,
      debtTokenReserve,
      obligation,
      scopeConfiguration
    );

    const computeIxs = getComputeBudgetAndPriorityFeeIxs(1_400_000, new Decimal(500000));

    const repayWithCollRoutes = await getRepayWithCollIxs({
      owner: signer,
      kaminoMarket: market!,
      debtTokenMint: debtTokenMint,
      collTokenMint: collTokenMint,
      obligation: obligation,
      referrer: none(),
      currentSlot,
      repayAmount,
      isClosingPosition: false,
      budgetAndPriorityFeeIxs: computeIxs,
      scopeRefreshIx,
      quoter: getKswapQuoter(kswapSdk, signer.address, slippageBps, collTokenReserve, debtTokenReserve),
      swapper: getKswapSwapper(kswapSdk, signer.address, slippageBps),
      useV2Ixs: true,
    });
    ```
  </Step>

  <Step>
    ### Simulate Routes

    Prepare lookup tables, simulate all routes to filter out failures.

    ```typescript theme={null}
    const klendLookupTableKeys: Address[] = [];
    klendLookupTableKeys.push(userLookupTable);
    klendLookupTableKeys.push(...multiplyLutKeys);
    klendLookupTableKeys.push(XSTOCKS_MARKET_LUT);

    const klendLutAccounts = await fetchAllAddressLookupTable(rpc, klendLookupTableKeys);

    const simulationResults = await Promise.all(
      repayWithCollRoutes.map(async (route) => {
        const lookupTables = route.lookupTables;
        lookupTables.push(...klendLutAccounts);

        const simulation = await simulateTx(rpc, signer.address, route.ixs, lookupTables).catch(() => undefined);

        if (!simulation || simulation.value.err) {
          return undefined;
        }

        return {
          ixs: route.ixs,
          luts: lookupTables.map((l) => l.address),
          routeOutput: route.quote!,
          swapInputs: route.swapInputs,
        };
      })
    );

    const passingSimulations = simulationResults.filter((tx) => tx !== undefined);
    ```
  </Step>

  <Step>
    ### Select Best Route

    Compare passing routes by price and select the best one.

    ```typescript theme={null}
    const bestRoute =
      passingSimulations.length > 0
        ? passingSimulations.reduce((best, current) => {
            const inputMintReserve = market!.getReserveByMint(best.swapInputs.inputMint)!;
            const outputMintReserve = market!.getReserveByMint(best.swapInputs.outputMint)!;

            const bestPrice = new Decimal(best.routeOutput.amountsExactIn.amountOutGuaranteed.toString())
              .div(outputMintReserve.getMintFactor())
              .div(new Decimal(best.routeOutput.amountsExactIn.amountIn.toString()).div(inputMintReserve.getMintFactor()));

            const currentPrice = new Decimal(current.routeOutput.amountsExactIn.amountOutGuaranteed.toString())
              .div(outputMintReserve.getMintFactor())
              .div(
                new Decimal(current.routeOutput.amountsExactIn.amountIn.toString()).div(inputMintReserve.getMintFactor())
              );

            return bestPrice.greaterThan(currentPrice) ? best : current;
          })
        : (() => {
            const lookupTables = repayWithCollRoutes[0].lookupTables;
            lookupTables.push(...klendLutAccounts);
            return { ixs: repayWithCollRoutes[0].ixs, luts: lookupTables.map((l) => l.address) };
          })();

    if (!bestRoute) {
      console.log('No route found');
    }
    ```
  </Step>

  <Step>
    ### Build, Sign, and Send Transaction

    Wait briefly to avoid rate limiting, get a fresh blockhash, compress the transaction with LUTs, and send it.

    ```typescript theme={null}
    await new Promise((resolve) => setTimeout(resolve, 2000));

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

    const lutsByAddress: Record<Address, Address[]> = {};
    const bestRouteLutAccounts = await fetchAllAddressLookupTable(rpc, bestRoute.luts || []);
    for (const acc of bestRouteLutAccounts) {
      lutsByAddress[acc.address] = acc.data.addresses;
    }

    const transactionMessage = pipe(
      createTransactionMessage({ version: 0 }),
      (tx) => appendTransactionMessageInstructions(bestRoute.ixs, tx),
      (tx) => setTransactionMessageFeePayerSigner(signer, tx),
      (tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),
      (tx) => compressTransactionMessageUsingAddressLookupTables(tx, lutsByAddress)
    );

    const signedTransaction = await signTransactionMessageWithSigners(transactionMessage);
    const signature = getSignatureFromTransaction(signedTransaction);

    await sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions })(signedTransaction, {
      commitment: 'processed',
      preflightCommitment: 'processed',
      skipPreflight: true,
    });

    console.log(`Repay with collateral successful! Sold TSLAx collateral to repay ${repayAmount.toString()} USDC debt.`);
    console.log(`Transaction signature: ${signature}`);
    ```

    <Check>
      The repay with collateral transaction is complete. Collateral was sold to repay debt without requiring external funds.
    </Check>
  </Step>
</Steps>

## Helper Functions

The tutorial requires KSwap helper functions for quote retrieval and swap execution.

<AccordionGroup>
  <Accordion title="kswap_utils.ts">
    ```typescript theme={null}
    import BN from 'bn.js';
    import { KaminoReserve } from '@kamino-finance/klend-sdk';
    import type { SwapInputs, SwapQuote, SwapIxs, SwapIxsProvider, SwapQuoteProvider } from '@kamino-finance/klend-sdk';
    import { KswapSdk } from '@kamino-finance/kswap-sdk/dist';
    import type { RouteOutput, RouteParams, RouterType } from '@kamino-finance/kswap-sdk/dist';
    import Decimal from 'decimal.js';
    import type { Address } from '@solana/kit';
    import { PublicKey } from '@solana/web3.js';

    function toLegacyPublicKey(address: Address) {
      return new PublicKey(address.toString());
    }

    export const KSWAP_API = 'https://api.kamino.finance/kswap';
    const ALLOWED_ROUTERS: RouterType[] = ['dflow', 'jupiter', 'jupiterU', 'okx', 'jupiterLite'];

    export async function getTokenPriceFromJupWithFallback(
      kswapSdk: KswapSdk,
      inputMint: Address,
      outputMint: Address
    ): Promise<number> {
      const params = {
        ids: inputMint.toString(),
        vsToken: outputMint.toString(),
      };
      const res = await kswapSdk.getJupiterPriceWithFallback(params);

      return Number(res[inputMint.toString()]?.usdPrice || 0);
    }

    export async function getTokenPriceFromBirdeye(
      kswapSdk: KswapSdk,
      inputMint: Address,
      outputMint: Address
    ): Promise<number> {
      const prices = await kswapSdk.getBatchTokenPrices([toLegacyPublicKey(inputMint), toLegacyPublicKey(outputMint)]);

      return prices[inputMint.toString()] / prices[outputMint.toString()];
    }

    export function getKswapQuoter(
      kswapSdk: KswapSdk,
      executor: Address,
      slippageBps: number,
      inputMintReserve: KaminoReserve,
      outputMintReserve: KaminoReserve
    ): SwapQuoteProvider<RouteOutput> {
      const quoter: SwapQuoteProvider<RouteOutput> = async (
        inputs: SwapInputs,
        klendAccounts: Array<Address>
      ): Promise<SwapQuote<RouteOutput>> => {
        const routeParams: RouteParams = {
          executor: toLegacyPublicKey(executor),
          tokenIn: toLegacyPublicKey(inputs.inputMint),
          tokenOut: toLegacyPublicKey(inputs.outputMint),
          amount: new BN(inputs.inputAmountLamports.toDP(0).toString()),
          maxSlippageBps: slippageBps,
          wrapAndUnwrapSol: false,
          swapType: 'exactIn',
          routerTypes: ALLOWED_ROUTERS,
          includeRfq: false,
          includeLimoLogs: false,
        };

        const routeOutputs = await kswapSdk.getAllRoutes(routeParams);

        const bestRoute = routeOutputs.routes.reduce((best, current) => {
          const inAmountBest = new Decimal(best.amountsExactIn.amountIn.toString()).div(inputMintReserve.getMintFactor());
          const minAmountOutBest = new Decimal(best.amountsExactIn.amountOutGuaranteed.toString()).div(
            outputMintReserve.getMintFactor()
          );
          const priceAInBBest = minAmountOutBest.div(inAmountBest);
          const inAmountCurrent = new Decimal(current.amountsExactIn.amountIn.toString()).div(
            inputMintReserve.getMintFactor()
          );
          const minAmountOutCurrent = new Decimal(current.amountsExactIn.amountOutGuaranteed.toString()).div(
            outputMintReserve.getMintFactor()
          );
          const priceAInBCurrent = minAmountOutCurrent.div(inAmountCurrent);
          return priceAInBBest.greaterThan(priceAInBCurrent) ? best : current;
        });

        const inAmountBest = new Decimal(bestRoute.amountsExactIn.amountIn.toString()).div(
          inputMintReserve.getMintFactor()
        );
        const minAmountOutBest = new Decimal(bestRoute.amountsExactIn.amountOutGuaranteed.toString()).div(
          outputMintReserve.getMintFactor()
        );
        const priceAInBBest = minAmountOutBest.div(inAmountBest);

        return {
          priceAInB: priceAInBBest,
          quoteResponse: bestRoute,
        };
      };

      return quoter;
    }

    export function getKswapSwapper(
      kswapSdk: KswapSdk,
      executor: Address,
      slippageBps: number
    ): SwapIxsProvider<RouteOutput> {
      const swapper: SwapIxsProvider<RouteOutput> = async (
        inputs: SwapInputs,
        klendAccounts: Array<Address>,
        quote: SwapQuote<RouteOutput>
      ): Promise<Array<SwapIxs<RouteOutput>>> => {
        const routeParams: RouteParams = {
          executor: toLegacyPublicKey(executor),
          tokenIn: toLegacyPublicKey(inputs.inputMint),
          tokenOut: toLegacyPublicKey(inputs.outputMint),
          amount: new BN(inputs.inputAmountLamports.toString()),
          maxSlippageBps: slippageBps,
          wrapAndUnwrapSol: false,
          swapType: 'exactIn',
          routerTypes: ALLOWED_ROUTERS,
          includeRfq: false,
          includeLimoLogs: false,
        };

        const routeOutputs = await kswapSdk.getAllRoutes(routeParams);

        return routeOutputs.routes.map((routeOutput) => {
          const inAmount = new Decimal(routeOutput.amountsExactIn.amountIn.toString()).div(
            new Decimal(10).pow(routeOutput.inputTokenDecimals!)
          );
          const minAmountOut = new Decimal(routeOutput.amountsExactIn.amountOutGuaranteed.toString()).div(
            new Decimal(10).pow(routeOutput.outputTokenDecimals!)
          );
          const priceAInB = minAmountOut.div(inAmount);

          const allSwapIxs = [
            ...(routeOutput.instructions?.createInAtaIxs || []),
            ...(routeOutput.instructions?.wrapSolIxs || []),
            ...(routeOutput.instructions?.swapIxs || []),
            ...(routeOutput.instructions?.unwrapSolIxs || []),
            ...(routeOutput.instructions?.createOutAtaIxs || []),
          ];

          return {
            preActionIxs: [],
            swapIxs: allSwapIxs,
            lookupTables: routeOutput.lookupTableAccounts || [],
            quote: {
              priceAInB: new Decimal(priceAInB),
              quoteResponse: routeOutput,
            },
          };
        });
      };

      return swapper;
    }
    ```
  </Accordion>
</AccordionGroup>
