Skip to main content
Your front end moves user funds through a Lend Referral wrapper with six user actions. Each action takes a WrapperContext from resolveWrapper and returns instructions plus a quote. The SDK quick start shows how to read the context and how to sign and send the instructions. This page covers which action to call and how to bound it.

Choose an operation

Users can deposit and withdraw in the market’s underlying token, such as USDC, or in its jlToken. Pick the action by what the user fixes: the amount they give, or the amount they get. Rounding always favours the pool. For a full exit, redeem the whole xToken balance with redeemToUnderlying. Use the exact-amount actions, mintUnderlying and withdrawToUnderlying, only when the user names an exact output.

Set a bound

Every user action takes a required bound. The SDK estimates the result from the context and sends the bound on chain, so the transaction fails instead of executing at a worse rate. DEFAULT_SLIPPAGE_BPS (50) is a suggested tolerance. The SDK never applies it for you. The estimate uses the exchange price Jupiter Lend last stored. The program refreshes that price before it executes, and the price only rises, so the real result can differ from the estimate by the yield since Lend’s last update. Set slippageBps above that. Each quote holds the estimate and the bound it sent. All amounts are raw atoms (bigint). The bound is null for "unbounded". Show the user the estimate and the bound before they sign. If the send fails with WrapperSlippageExceeded (7003), read the wrapper again and rebuild.

Deposit

Each tab builds one deposit. Send the returned instructions in order, in one transaction.
The underlying-token deposits call Jupiter Lend, which mints jlTokens into the wrapper’s vault. The wrapper then mints xTokens to the user. A deposit of fewer than 10 atoms of the underlying token fails, so the SDK throws ReferralSdkError with kind AmountTooSmall before it builds anything.

Withdraw

xBalance is the user’s xToken balance in atoms. Show a user’s position reads it.
On a redeem, the user’s share of the vault is rounded down to whole jlToken atoms. The remainder, less than one atom, goes to the partner’s balance. A user who holds jlTokens can withdraw them to the underlying token on Jupiter Earn.

Exit in edge cases

Most exits are a redeemToUnderlying of the whole balance. Four cases need a different path.

Dust below 10 atoms

The underlying path rejects amounts below 10 atoms, because Jupiter Liquidity rejects smaller operations. The SDK throws AmountTooSmall before it builds, and the program fails with WrapperUnderlyingAmountTooSmall (7017). Move dust with redeemToJLToken, which has no 10-atom minimum.

Last withdrawer

withdrawToUnderlying rounds the xTokens it burns up. It refuses a withdrawal that would burn every circulating xToken while leaving one jlToken or more in the vault, and fails with WrapperExactWithdrawStrandsShares (7015). For a full exit, use redeemToUnderlying, which pays the full proportional amount.

Blocked underlying path

The underlying path calls Jupiter Lend and Liquidity. If they block the withdrawal, for example during a liquidity shortage or under a withdrawal limit, redeemToJLToken still works, because it only moves jlTokens out of the wrapper’s vault. The same applies to a Token-2022 underlying token. If its mint authority later enables a transfer fee or a transfer hook, or freezes the wrapper’s transit account, the underlying path stops working and the jlToken path keeps working.

Protocol pause

Jupiter can pause the whole protocol. A pause blocks every deposit, redeem and withdrawal, the jlToken exit included, and has no time limit. Transactions fail with ProtocolPaused (7016). Check ctx.protocol.data.paused before you build, and show the user that withdrawals are paused instead of letting the transaction fail.

Token accounts

The user’s token accounts can be any token accounts the user owns. By default the SDK uses the user’s associated token accounts (ATAs) under ctx.tokenProgram. The SDK creates only the destination account: the xToken account on a deposit, the underlying or jlToken account on a withdrawal. The xToken account must exist before a deposit, which the default creation covers. The source account must already exist and hold the input. This example withdraws into an existing token account and lets a sponsor pay the rent of any ATA the SDK creates:

Show a user’s position

An xToken’s value in jlTokens rises as yield accrues, net of the partner fee, while the user’s xToken balance stays the same. To show the value, read the balance, then project the wrapper’s books to now with simulateAccrue and convert.
These values are what a redeem would pay at the price Jupiter Lend last stored. The program refreshes the price first, and the price only rises. So a real redeem to the underlying token pays slightly more. A real redeem to jlTokens can pay slightly fewer, because the refresh also charges the partner fee on the new yield. The SDK math uses the wrapper’s totalXShares, not the xToken mint supply, because a holder can burn xTokens directly through the token program. To show users the fee they pay on yield, quote it on the continuous-accrual basis described in Effective fee.