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

# Combined Operations

> Run create, deposit, borrow, repay, and withdraw in a single transaction using getOperateIx.

Combine multiple borrow operations into one transaction. Pass both `colAmount` and `debtAmount` in a single `getOperateIx` call to batch operations (e.g. [deposit](https://developers.jup.ag/docs/lend/borrow/deposit.md) + [borrow](https://developers.jup.ag/docs/lend/borrow/borrow.md), or [repay](https://developers.jup.ag/docs/lend/borrow/repay.md) + [withdraw](https://developers.jup.ag/docs/lend/borrow/withdraw.md)). When `positionId` is `0`, the first call creates a new position and returns `positionId` for use in the second call. Transactions use versioned (v0) format with address lookup tables.

***

## Combined operations

## 1. Import Dependencies

Import the required packages for Solana RPC, Jupiter Lend borrow SDK, and versioned transaction building.

```typescript theme={null}
import {
  Connection,
  Keypair,
  TransactionMessage,
  VersionedTransaction,
} from "@solana/web3.js";
import BN from "bn.js";
import { getOperateIx } from "@jup-ag/lend/borrow";
import fs from "fs";
import path from "path";
```

> [!NOTE]
>
> Combined operations use multiple `getOperateIx` calls. Pass both `colAmount` and `debtAmount` in one call to batch deposit + borrow or repay + withdraw.

## 2. Load Keypair and Initialise Connection

Load the signer and create the RPC connection. Set vault ID and amounts for each operation.

```typescript theme={null}
const KEYPAIR_PATH = "/path/to/your/keypair.json";
const RPC_URL = "https://api.mainnet-beta.solana.com";
const VAULT_ID = 1;

// Amounts in base units (e.g. USDC 6 decimals: 1_000_000 = 1 USDC)
const DEPOSIT_AMOUNT = new BN(1_000_000);
const BORROW_AMOUNT = new BN(500_000);
const REPAY_AMOUNT = new BN(100_000);
const WITHDRAW_AMOUNT = new BN(200_000);

function loadKeypair(keypairPath: string): Keypair {
  const fullPath = path.resolve(keypairPath);
  const secret = JSON.parse(fs.readFileSync(fullPath, "utf8"));
  return Keypair.fromSecretKey(new Uint8Array(secret));
}

const userKeypair = loadKeypair(KEYPAIR_PATH);
const connection = new Connection(RPC_URL, { commitment: "confirmed" });
const signer = userKeypair.publicKey;
```

## 3. Build Operate Instructions

Call `getOperateIx` twice: once with `positionId: 0` to create a position and deposit + borrow; once with the returned `positionId` to repay + withdraw. Then merge instructions and address lookup tables.

```typescript theme={null}
// 1. Create position + Deposit + Borrow (positionId 0 creates new position)
const { ixs: depositBorrowIxs, addressLookupTableAccounts: depositBorrowAlts, positionId } = await getOperateIx({
  vaultId: VAULT_ID,
  positionId: 0,
  colAmount: DEPOSIT_AMOUNT,
  debtAmount: BORROW_AMOUNT,
  connection,
  signer,
});

// 2. Repay + Withdraw (use positionId from first call)
const repayWithdrawResult = await getOperateIx({
  vaultId: VAULT_ID,
  positionId: positionId!,
  colAmount: WITHDRAW_AMOUNT.neg(),
  debtAmount: REPAY_AMOUNT.neg(),
  connection,
  signer,
});

// Merge instructions and address lookup tables
const allIxs = [
  ...(depositBorrowIxs ?? []),
  ...(repayWithdrawResult.ixs ?? []),
];

const allAlts = [
  ...(depositBorrowAlts ?? []),
  ...(repayWithdrawResult.addressLookupTableAccounts ?? []),
];
const seenKeys = new Set<string>();
const mergedAlts = allAlts.filter((alt) => {
  const k = alt.key.toString();
  if (seenKeys.has(k)) return false;
  seenKeys.add(k);
  return true;
});

if (!allIxs.length) {
  throw new Error("No instructions returned.");
}
```

> [!NOTE]
>
> When `positionId` is `0`, the SDK creates a new position and returns `positionId`. Use that `positionId` in the second call. Deduplicate address lookup tables by key when merging.

## 4. Build and Sign Transaction

Build a v0 message with the merged instructions and address lookup tables, then sign.

```typescript theme={null}
const latestBlockhash = await connection.getLatestBlockhash();
const message = new TransactionMessage({
  payerKey: signer,
  recentBlockhash: latestBlockhash.blockhash,
  instructions: allIxs,
}).compileToV0Message(mergedAlts);

const transaction = new VersionedTransaction(message);
transaction.sign([userKeypair]);
```

## 5. Send and Confirm Transaction

Send the transaction and confirm.

```typescript theme={null}
const signature = await connection.sendTransaction(transaction, {
  skipPreflight: false,
  maxRetries: 3,
  preflightCommitment: "confirmed",
});
await connection.confirmTransaction({ signature, ...latestBlockhash }, "confirmed");

console.log("Combined operate successful! Signature:", signature);
```

## Operate parameters

`getOperateIx` accepts the following parameters:

| Parameter | Type | Description |
| - | - | - |
| `vaultId` | `number` | Target vault (market) ID. |
| `positionId` | `number` | Position NFT ID. Use `0` to create a new position and operate in one transaction. |
| `colAmount` | `BN` | Signed collateral amount in base units. **Positive** = deposit. **Negative** = withdraw. Use `new BN(0)` for borrow/repay-only. |
| `debtAmount` | `BN` | Signed debt amount in base units. **Positive** = borrow. **Negative** = repay. Use `new BN(0)` for deposit/withdraw-only. |
| `connection` | `Connection` | Solana RPC connection. |
| `signer` | `PublicKey` | Wallet that signs the transaction (position owner). |

Combine operations by passing both `colAmount` and `debtAmount` in one call (e.g. deposit + borrow: both positive; repay + withdraw: both negative).

> [accordion] Combining operations
>
> Pass both `colAmount` and `debtAmount` in a single `getOperateIx` call to batch two operations. Deposit + borrow: `colAmount` > 0, `debtAmount` > 0. Repay + withdraw: `colAmount` \< 0, `debtAmount` \< 0.

> [accordion] Using an existing position
>
> If you already have a [position](https://developers.jup.ag/docs/lend/borrow/create-position.md), use its `positionId` instead of `0` in the first call and skip the create step. Or use `positionId: positionId` for both calls when doing repay + withdraw only.

***

## Full code example

```typescript expandable theme={null}
import {
  Connection,
  Keypair,
  TransactionMessage,
  VersionedTransaction,
} from "@solana/web3.js";
import BN from "bn.js";
import { getOperateIx } from "@jup-ag/lend/borrow";
import fs from "fs";
import path from "path";

const KEYPAIR_PATH = "/path/to/your/keypair.json";
const RPC_URL = "https://api.mainnet-beta.solana.com";
const VAULT_ID = 1;

const DEPOSIT_AMOUNT = new BN(1_000_000);
const BORROW_AMOUNT = new BN(500_000);
const REPAY_AMOUNT = new BN(100_000);
const WITHDRAW_AMOUNT = new BN(200_000);

function loadKeypair(keypairPath: string): Keypair {
  const fullPath = path.resolve(keypairPath);
  const secret = JSON.parse(fs.readFileSync(fullPath, "utf8"));
  return Keypair.fromSecretKey(new Uint8Array(secret));
}

const userKeypair = loadKeypair(KEYPAIR_PATH);
const connection = new Connection(RPC_URL, { commitment: "confirmed" });
const signer = userKeypair.publicKey;

// 1. Create position + Deposit + Borrow
const { ixs: depositBorrowIxs, addressLookupTableAccounts: depositBorrowAlts, positionId } = await getOperateIx({
  vaultId: VAULT_ID,
  positionId: 0,
  colAmount: DEPOSIT_AMOUNT,
  debtAmount: BORROW_AMOUNT,
  connection,
  signer,
});

// 2. Repay + Withdraw
const repayWithdrawResult = await getOperateIx({
  vaultId: VAULT_ID,
  positionId: positionId!,
  colAmount: WITHDRAW_AMOUNT.neg(),
  debtAmount: REPAY_AMOUNT.neg(),
  connection,
  signer,
});

const allIxs = [...(depositBorrowIxs ?? []), ...(repayWithdrawResult.ixs ?? [])];
const allAlts = [
  ...(depositBorrowAlts ?? []),
  ...(repayWithdrawResult.addressLookupTableAccounts ?? []),
];
const seenKeys = new Set<string>();
const mergedAlts = allAlts.filter((alt) => {
  const k = alt.key.toString();
  if (seenKeys.has(k)) return false;
  seenKeys.add(k);
  return true;
});

if (!allIxs.length) throw new Error("No instructions returned.");

const latestBlockhash = await connection.getLatestBlockhash();
const message = new TransactionMessage({
  payerKey: signer,
  recentBlockhash: latestBlockhash.blockhash,
  instructions: allIxs,
}).compileToV0Message(mergedAlts);

const transaction = new VersionedTransaction(message);
transaction.sign([userKeypair]);

const signature = await connection.sendTransaction(transaction, {
  skipPreflight: false,
  maxRetries: 3,
  preflightCommitment: "confirmed",
});
await connection.confirmTransaction({ signature, ...latestBlockhash }, "confirmed");

console.log("Combined operate successful! Signature:", signature);
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.