> ## Documentation Index
> Fetch the complete documentation index at: https://dev.jup.ag/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Vault Swap

> Move a position from one vault to another atomically.

<Info>
  These flows use many instructions. If you hit compute limits, add `ComputeBudgetProgram.setComputeUnitLimit({ units: 1_000_000 })` (or higher) as the first instruction in your transaction.
</Info>

<Warning>
  Solana transactions have a 1232-byte size limit. If the serialised transaction exceeds 1232 bytes, the vault swap will not be possible in a single transaction. Consider splitting large positions or using simpler swap routes.
</Warning>

A vault swap moves your position from one vault to another atomically. Use Jupiter **Lite API** for swap quotes and instructions.

**Flow:** swap from SOL/USDC to SOL/USDT:

1. Flashloan USDC
2. Repay all USDC debt and withdraw all SOL from vault 1
3. Deposit SOL into vault 2 and borrow USDT with a 1-2 USD buffer for slippage
4. Swap the new debt token → USDC
5. Pay back the flashloan

***

## Vault Swap

<Steps>
  <Step titleSize="h2" title="Import Dependencies">
    ```typescript theme={null}
    import {
      AddressLookupTableAccount,
      Connection,
      Keypair,
      PublicKey,
      TransactionInstruction,
      TransactionMessage,
      VersionedTransaction,
    } from "@solana/web3.js";
    import BN from "bn.js";
    import { getFlashBorrowIx, getFlashPaybackIx } from "@jup-ag/lend/flashloan";
    import { getOperateIx, MAX_REPAY_AMOUNT, MAX_WITHDRAW_AMOUNT } from "@jup-ag/lend/borrow";
    import fs from "fs";
    import path from "path";
    ```
  </Step>

  <Step titleSize="h2" title="Set Parameters">
    Hardcode vault IDs, `positionId` (NFT ID in vault 1), `debtAmount`, `collateralSupply`, and a borrow buffer (e.g. 2 USDC) for slippage.

    ```typescript theme={null}
    const KEYPAIR_PATH = "/path/to/your/keypair.json";
    const RPC_URL = "https://api.mainnet-beta.solana.com";
    function loadKeypair(p: string): Keypair {
      return Keypair.fromSecretKey(new Uint8Array(JSON.parse(fs.readFileSync(path.resolve(p), "utf8"))));
    }
    const userKeypair = loadKeypair(KEYPAIR_PATH);
    const connection = new Connection(RPC_URL, { commitment: "confirmed" });
    const signer = userKeypair.publicKey;

    const usdcMint = new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v");
    const usdtMint = new PublicKey("Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB");

    const VAULT_1_ID = 1; // SOL/USDC
    const VAULT_2_ID = 2; // SOL/USDT
    const positionId = 7360; // NFT ID of position in vault 1
    const debtAmount = new BN(20_000_000); // USDC debt to repay (6 decimals)
    const collateralSupply = new BN(1_000_000_000); // SOL to move (9 decimals)
    const BORROW_BUFFER = new BN(2_000_000); // 2 USDC extra for slippage
    const borrowAmount = debtAmount.add(BORROW_BUFFER);
    ```
  </Step>

  <Step titleSize="h2" title="Get Flashloan Instructions">
    Flashloan the **debt asset** of vault 1 (e.g. USDC).

    ```typescript theme={null}
    const flashParams = { connection, signer, asset: usdcMint, amount: debtAmount };
    const flashBorrowIx = await getFlashBorrowIx(flashParams);
    const flashPaybackIx = await getFlashPaybackIx(flashParams);
    ```
  </Step>

  <Step titleSize="h2" title="Close Position in Vault 1 (Repay + Withdraw)">
    ```typescript theme={null}
    const { ixs: operate1Ixs, addressLookupTableAccounts: alts1 } = await getOperateIx({
      vaultId: VAULT_1_ID,
      positionId,
      colAmount: MAX_WITHDRAW_AMOUNT,
      debtAmount: MAX_REPAY_AMOUNT,
      connection,
      signer,
    });
    ```
  </Step>

  <Step titleSize="h2" title="Open Position in Vault 2 (Deposit + Borrow)">
    Use `positionId: 0` for a new position. Deposit the withdrawn collateral and borrow the new debt token plus buffer.

    ```typescript theme={null}
    const { ixs: operate2Ixs, addressLookupTableAccounts: alts2 } = await getOperateIx({
      vaultId: VAULT_2_ID,
      positionId: 0,
      colAmount: collateralSupply,
      debtAmount: borrowAmount,
      connection,
      signer,
    });
    ```
  </Step>

  <Step titleSize="h2" title="Get Quote and Swap Instructions">
    Fetch a quote to swap the new debt token (e.g. USDT) → USDC. Ensure output is at least `debtAmount` to cover the flashloan.

    ```typescript theme={null}
    const LITE_API = "https://lite-api.jup.ag/swap/v1";
    const quoteResponse = await fetch(
      `${LITE_API}/quote?inputMint=${usdtMint.toBase58()}&outputMint=${usdcMint.toBase58()}&amount=${borrowAmount.toString()}&slippageBps=100`
    ).then((r) => r.json());

    const swapRes = await fetch(`${LITE_API}/swap-instructions`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ quoteResponse, userPublicKey: signer.toBase58() }),
    }).then((r) => r.json());
    const swapIx = jupIxToTransactionInstruction(swapRes.swapInstruction);
    ```

    The `jupIxToTransactionInstruction` helper converts the JSON instruction from the API into a Solana `TransactionInstruction`:

    ```typescript theme={null}
    function jupIxToTransactionInstruction(ix: {
      programId: string;
      accounts: Array<{ pubkey: string; isSigner: boolean; isWritable: boolean }>;
      data: string;
    }): TransactionInstruction {
      return new TransactionInstruction({
        programId: new PublicKey(ix.programId),
        keys: ix.accounts.map((a) => ({
          pubkey: new PublicKey(a.pubkey),
          isSigner: a.isSigner,
          isWritable: a.isWritable,
        })),
        data: Buffer.from(ix.data, "base64"),
      });
    }
    ```
  </Step>

  <Step titleSize="h2" title="Assemble and Execute the Transaction">
    **Order:** Flashloan Borrow → Vault 1 Operate (repay + withdraw) → Vault 2 Operate (deposit + borrow) → Swap (new debt → USDC) → Flashloan Payback

    The `getAddressLookupTableAccounts` helper resolves lookup table addresses into account objects:

    ```typescript theme={null}
    async function getAddressLookupTableAccounts(
      connection: Connection,
      keys: string[]
    ): Promise<AddressLookupTableAccount[]> {
      if (!keys?.length) return [];
      const infos = await connection.getMultipleAccountsInfo(keys.map((k) => new PublicKey(k)));
      return infos.reduce((acc, info, i) => {
        if (info) {
          acc.push(new AddressLookupTableAccount({
            key: new PublicKey(keys[i]),
            state: AddressLookupTableAccount.deserialize(info.data),
          }));
        }
        return acc;
      }, [] as AddressLookupTableAccount[]);
    }
    ```

    ```typescript theme={null}
    const swapAlts = await getAddressLookupTableAccounts(connection, swapRes.addressLookupTableAddresses ?? []);
    const allAlts = [...(alts1 ?? []), ...(alts2 ?? []), ...swapAlts];

    const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash();
    const message = new TransactionMessage({
      payerKey: signer,
      recentBlockhash: blockhash,
      instructions: [flashBorrowIx, ...operate1Ixs, ...operate2Ixs, swapIx, flashPaybackIx],
    }).compileToV0Message(allAlts);

    const tx = new VersionedTransaction(message);
    tx.sign([userKeypair]);
    const signature = await connection.sendTransaction(tx, { skipPreflight: false, maxRetries: 3 });
    console.log("Vault swap successful:", signature);
    ```
  </Step>
</Steps>
