Skip to main content
Swapping against the Lend AMM from TypeScript is always three steps:
  1. Quote the swap off-chain with @jup-ag/lend-read.
  2. Derive a slippage guard from the quote (amountOutMin / amountInMax).
  3. Build and send the swap instruction with @jup-ag/lend/dex.
Two packages, by design:

Install

The /dex entry point ships from @jup-ag/lend v0.2.0. If the latest tag is still older, install the beta tag: npm install @jup-ag/lend@beta.

1. Request a quote

estimateSwapIn / estimateSwapOut return a SwapResult: You normally only need amountIn / amountOut. The col* / debt* legs are the per-side routing split (they sum to the total) and are exposed for analytics. The quote is pure math on a fresh snapshot and mirrors the on-chain formulas, so it can throw before you build a transaction if the swap would be rejected on-chain, for example when the trade exceeds 50% of the input reserves, falls below the dust floor, or the pool’s swaps are paused. Treat a thrown quote as “not executable right now”.

Exact-out quoting

2. Derive the slippage guard

The quote is a point-in-time estimate; the pool can move before your transaction lands. Turn the quote into a hard bound the program enforces.
If the market moves past your bound, the swap reverts with DexNotEnoughAmountOut (6024) for swap_in or DexExceedsAmountInMax (6051) for swap_out. No funds move.

3. Build and send the swap

getSwapInIx / getSwapOutIx resolve every account for the pool (mints, token programs, reserves, vaults, the four liquidity positions), auto-attach the external center-price accounts when the pool needs them, and load the pool’s address lookup table. They return the instruction(s) plus the loaded ALT. You sign and send.
Exact-out is symmetric: getSwapOutIx takes amountOut + amountInMax.

Return shape

Both builders return: Because the builders return raw instructions, you can compose a swap with your own instructions (wrap/unwrap SOL, create an ATA, etc.) in the same transaction. Include all of them and pass addressLookupTableAccounts when compiling.

Sending output to a different recipient

Pass recipient (a PublicKey) to deliver the output elsewhere; the builder derives that owner’s ATAs for both mints. The recipient’s associated token account for the output mint must already exist (or be created in the same transaction).

Full example: quote to swap (exact-in)

Why a versioned transaction and lookup table

A swap touches ~25 fixed accounts, plus the oracle and its price sources for external-center-price pools. A legacy transaction can exceed the 1232-byte limit, so always send a versioned (v0) transaction and pass the pool’s ALT (returned by the builders as addressLookupTableAccounts). This is not optional for external-center-price pools and is good practice for all of them.

Exact on-chain quoting via simulation (optional)

estimateSwapIn / estimateSwapOut are kept in lockstep with the program and are enough for production. If you want the pool’s exact on-chain result (for example, to cross-check), build a swap whose recipient is the all-zero pubkey (PublicKey.default, the ADDRESS_DEAD sentinel) and simulate it. The instruction runs the full pricing path and deliberately reverts, logging:
Parse the log from simulateTransaction. This never moves funds.
Never use the ADDRESS_DEAD recipient in a real (non-simulated) swap. The instruction always reverts with it.

Error handling

Anchor throws an AnchorError; read err.error.errorCode.number and map it. The full table is in Errors; the ones you’ll hit most: