Type/to search
Built-in Functions
Structures
Built-in Variables and Constants
Conventions
Exchanges
Capability Matrix
Centralized Exchanges
Decentralized Exchanges (DEX)
Prediction Markets
Securities and Futures
On-chain & Custom

Connects to the Jupiter aggregator on the Solana chain and maps swaps onto the spot trading functions. One exchange object corresponds to one Solana wallet: quotes and swap transactions are produced by the Jupiter Swap API (which splits and routes orders across the many DEXs and market makers on Solana), and the docker signs them with the configured private key and broadcasts them through a Solana node. Without a Jupiter API key the free lite-api.jup.ag is used; with one, api.jup.ag, and by default native limit orders and DCA with a USDC or USDT quote then switch to Jupiter Trigger V2, whose funds are deposited into a vault held in Jupiter's custody (no longer in the wallet's own on-chain order account); to keep the non-custodial v1, call exchange.IO("trigger.api", "v1") at the start of the strategy. See "Limit orders" and "DCA" below.

The same exchange object also provides concentrated liquidity on Meteora DLMM, Raydium CLMM and Orca Whirlpools pools (exchange.IO("lp.*", ...), see "Concentrated liquidity" below): the connector builds and signs the transactions itself and broadcasts them through a Solana node, with commands and result fields aligned with the LP commands of the Uniswap exchange object.

pump.fun launches (exchange.IO("pump.*", ...), see "Pump.fun launches" below): newly created coins are discovered on chain and quoted, bought and sold directly on the pump.fun bonding curve (the connector builds, simulates and signs the transactions itself); once a coin graduates to PumpSwap, trades go through Jupiter swaps automatically. New coins are extremely risky; see the risks in that section.

Names and Configuration

exchange.GetName(): Jupiter (Spot).

FieldRequiredDefaultDescription
Private KeyYes—The Solana wallet private key: the base58 string exported by Phantom or Solflare, or the JSON byte array generated by solana-keygen.
Rpc AddressNohttps://api.mainnet-beta.solana.com,https://solana-rpc.publicnode.comSolana RPC nodes; separate several with commas, and the next one is used automatically when a node fails. The default is the official public node plus publicnode.
Jupiter Api KeyNo—Optional. (more below the table)
Liquidity SourcesNo—Optional. (more below the table)

The rest of the longer descriptions in the table:

  • Jupiter Api Key: Without it the free lite-api.jup.ag is used; with it api.jup.ag is used, the request rate can be raised according to your plan (see exchange.IO("rps", ...)), and native limit orders and DCA with a USDC or USDT quote switch by default to Jupiter Trigger V2 (funds deposited into a vault in Jupiter's custody, see "Limit orders" and "DCA"; to keep the non-custodial v1, call exchange.IO("trigger.api", "v1")). Get the key on the Jupiter developer platform; a key restricted by endpoint permissions must include Trigger.
  • Liquidity Sources: Swap only through these liquidity sources, for example pump (the pump.fun bonding curve and PumpSwap), meteora, raydium, orca, or Jupiter source names; empty means all sources. It can be changed at run time with exchange.IO("dexes", ...).

Trading Pairs and Contract Codes

Pair examples: SOL_USDC, JUP_USDC, BONK_SOL

SOL is the native coin and is wrapped and unwrapped automatically during swaps. Built-in tokens: SOL, USDC, USDT, JUP, BONK, WIF, JTO, PYTH, RAY, JitoSOL, mSOL, JupSOL, TRUMP, PENGU, POPCAT. Other tokens can be written in three ways: by token name, resolved to the verified token with exactly that name in the Jupiter token list (an error is raised when several verified tokens share the name); by mint address; or after registering them with exchange.IO("token", ...). Pairs are not upper-cased (mint addresses and token names such as mSOL are case-sensitive), so write them as they are, for example DB5QKorV9KHY54y73h5xWQqYyuD8P3jC1p5Ea8DBpump_SOL.

Exchange-specific IO Commands
CommandArgumentsDescriptionReturns
routesymbol, side, qtyQuote only, no order, on the same basis as orders: sell quotes selling qty base tokens as exact input; buy quotes buying qty base tokens as exact output, and when there is no exact-output route (sources such as Meteora and PumpSwap do not support exact output) it estimates with exact input instead, the same fallback as a limit buy (a market buy is exact input itself). See the sub-page exchange.IO("route", ...).{symbol, side, qty, route, quote, price, priceImpactPct, swapMode, baseQty}: swapMode is ExactIn or ExactOut; baseQty is the base amount this quote actually buys or sells (with the exact-input estimate for a buy it may differ slightly from qty), and price is quote divided by baseQty. (more below the table)
token[name, mint]Register a token (for this run only) so its name can be used in pairs; without arguments, list the token table. See the sub-page exchange.IO("token", ...).{symbol, mint, decimals} when registering, an array of {symbol, mint, decimals, native} when listing.
transferto, amountTransfer SOL out of the wallet; amount may be all (the whole balance minus the fee). Only SOL is supported; a third argument naming another token fails. Waits for the broadcast only, not for confirmation. See the sub-page exchange.IO("transfer", ...).{signature, amount, lastValidBlockHeight}
receiptsignature[, waitMs]Query the status of a Solana transaction, optionally waiting until it is confirmed or fails (up to 10 minutes). For an order, pass the transaction signature after the comma in the order ID. See the sub-page exchange.IO("receipt", ...).{signature, state, slot, confirmationStatus, err, fee, blockTime}, where state is success, failed or pending; fee and blockTime appear only once it is confirmed or failed. (more below the table)
dca.createparamsCreates a DCA order. See the sub-page exchange.IO("dca.*", ...).{id, version, signature, symbol, side, total, perOrder, numberOfOrders, interval, minPrice, maxPrice, startAt, priorityFeeLamports}: version is v1 or v2; a v1 id is the DCA order account address and a V2 id is Jupiter's order UUID, signature being the deposit transaction, plus vault.
dca.list[params]Lists the wallet's DCA orders (v1 first, then V2 when an API key is set or trigger.api is v2): params.status is active (default, running) or history (ended), and params.symbol limits the list to that pair. See the sub-page exchange.IO("dca.*", ...).[{id, version, symbol, side, state, total, perOrder, numberOfOrders, ordersFilled, interval, minPrice, maxPrice, spent, received, filledQty, avgPrice, fee, createdAt, updatedAt, openTx, closeTx, info}], where state is active, completed or canceled, and for V2 also pending (depositing), canceling (canceled, funds not yet withdrawn) or failed (deposit failed); V2 rows also carry nextFillAt, and their fee is null (the fee is already taken from the tokens received); spent is the input token spent so far and received the output token received (fee, in the output token, is the fee deducted from it); filledQty and avgPrice are the filled amount and average price in base-token terms; info is Jupiter's raw row. (more below the table)
dca.cancelidCancels a running DCA order and returns the remaining input token to the wallet; v1 and V2 are told apart by the shape of id (a V2 ID is a UUID and needs the API key). See the sub-page exchange.IO("dca.*", ...).v1: {id, version, signature, priorityFeeLamports[, indexed]}; V2: {id, version, signature, refund, refundCurrency, roundsRemaining, info}, where signature is the withdrawal transaction and refund the amount of input token returned.
dca.simulateparams[, wallet]Builds the transaction of dca.create from the same parameters and runs it with the node's simulateTransaction (not signed, nothing broadcast), to check balances, minimums and so on before ordering; with params written {cancel: id} it simulates canceling that DCA order. See the sub-page exchange.IO("dca.*", ...).{ok, err, reason, logs, unitsConsumed, version, wallet, id, plan, priorityFeeLamports} (version is v1 or v2; V2 adds vault and has no id): ok tells whether the simulation succeeded and reason explains a failure (when the paying wallet has no SOL at all the node only returns err AccountNotFound, and reason adds "the paying wallet has no SOL"); id is the address of the DCA account that would be created; plan holds the normalized DCA parameters.
trigger.simulateparams[, wallet]Builds the transaction of a Jupiter limit order and runs it with the node's simulateTransaction (not signed, nothing broadcast). See the sub-page exchange.IO("trigger.*", ...).v1: {ok, err, reason, logs, unitsConsumed, wallet, order, makingAmount, takingAmount, priorityFeeLamports}, where order is the order account address and makingAmount and takingAmount are the amounts given and received in smallest units; V2: {ok, err, reason, logs, unitsConsumed, version, wallet, vault, inputMint, inputAmount, triggerPriceUsd, triggerCondition, expiresAt, priorityFeeLamports}. (more below the table)
trigger.api[mode]Which API new native limit orders (tif gtc) and new DCA orders use: auto (default) uses Trigger V2 (custodial vault) when an API key is set and the quote is USDC or USDT, and v1 otherwise; v1 always uses v1 (non-custodial, funds in an on-chain order account), even with an API key; v2 always uses V2, fails orders whose quote is not USDC or USDT, and works without an API key too (Jupiter currently accepts V2 calls without a key, but its documentation says a key is required and this may be enforced at any time). See the sub-page exchange.IO("trigger.*", ...).The current mode: auto, v1 or v2.
trigger.vault[action]Queries this wallet's custodial vault in Jupiter Trigger V2 and its balances (needs the API key, or exchange.IO("trigger.api", "v2")). See the sub-page exchange.IO("trigger.*", ...).{wallet, registered: false} while there is no vault; otherwise {wallet, registered: true, vault, sol, tokens, info}, where tokens is [{mint, symbol, amount, raw}] (symbol only for tokens in the token table) and info is Jupiter's vault record.
slippage[ratio]Slippage protection of market orders, range [0, 0.5), default 0.005 (0.5%), enforced on chain. Limit orders do not use it. Queries when called without an argument. See the sub-page exchange.IO("slippage", ...).The current slippage ratio.
priorityFee[sol]Cap on the priority fee of each swap transaction, in SOL, 0 to 0.1, default 0.001; Jupiter estimates the actual priority fee from network congestion, up to the cap. Queries when called without an argument. See the sub-page exchange.IO("priorityFee", ...).The current cap (SOL).
dexes[sources]Restrict the liquidity sources swaps go through (comma-separated) to the group shorthands pump, meteora, raydium, orca or Jupiter source names (case-sensitive); "" removes the restriction. Market data, quotes and orders all use only these sources. Queries when called without an argument. See the sub-page exchange.IO("dexes", ...).The array of restricted source names (groups expanded; an empty array means no restriction).
rps[n]Maximum requests per second to the Jupiter API, 0.1 to 500, default 1. Requests are queued with at least 1/n seconds between two of them. Queries when called without an argument. See the sub-page exchange.IO("rps", ...).The current maximum requests per second.
base[url]Without an argument, returns the current Solana node address; with an address, switches the node (separate several with commas).The previous address.
address—The wallet address (base58).—
pump.new[options]Coins recently created on pump.fun, discovered on chain: the recent signatures of the pump.fun mint authority (TSLvdd1pWpHVjahSpsvCXUbgwsL3JAcvokwaKt1eokM, which appears only in creation transactions) are listed and each transaction's CreateEvent is parsed, newest first. See the sub-page exchange.IO("pump.*", ...).[{signature, slot, time, mint, name, symbol, uri, creator, user, bondingCurve, tokenProgram, quote, quoteMint, mayhem, cashback, holderReward, devBuy}]: time is the block time (milliseconds); user is the wallet that sent the creation (usually the creator); tokenProgram is spl-token or token-2022; quote is SOL, USDC or the quote token's mint (only SOL coins can be traded with pump.buy and pump.sell); devBuy is the first buy in the same transaction, {tokens, quoteAmount} (quoteAmount includes fees and is null when the quote is neither SOL nor USDC), or null when there is none. (more below the table)
pump.infomintThe coin's bonding-curve state, read with one getMultipleAccounts (curve, mint, global config and fee table). Fails for a coin that is not a pump.fun coin. See the sub-page exchange.IO("pump.*", ...).{mint, bondingCurve, creator, tokenProgram, decimals, quote, quoteMint, complete, migrated, virtualTokenReserves, virtualQuoteReserves, realTokenReserves, realQuoteReserves, tokenTotalSupply, price, marketCap, progress, fees, mayhem, cashback, holderReward}: reserves and supply are in readable units; price is the quote (SOL) value of one coin and marketCap is price times the total supply; progress is the share of the curve sold (%), and at 100 the curve is complete (complete) and then migrates to PumpSwap (migrated, reserves zeroed, price, marketCap and fees null); fees is the current rate {protocolBps, creatorBps} in basis points.
pump.quotemint, side, amountQuotes locally on the bonding curve (no transaction), with the on-chain program's formula and fees: for buy amount is the SOL to spend (fees included), for sell the number of coins to sell. See the sub-page exchange.IO("pump.*", ...).{venue, mint, side, quote, amountIn, amountOut, price, spotPrice, priceAfter, priceImpactPct, fees, protocolFeeBps, creatorFeeBps, crossesCurve}: venue is pump (bonding curve) or jupiter (graduated, which adds route, the liquidity sources used); price is the average fill price including fees (SOL per coin), priceImpactPct is how far the average price before fees is from the current price spotPrice (%), and fees is the protocol plus creator fee (SOL); crossesCurve true means the buy goes past the end of the curve (see "Pump.fun launches" below). (more below the table)
pump.buymint, solAmount[, options]Buys on the bonding curve for solAmount SOL (protocol and creator fees included): the wallet's token account is created idempotently, then buy_exact_quote_in_v3 is sent with a minimum output of the local quote × (1 − slippage), enforced on chain. See the sub-page exchange.IO("pump.*", ...).{txHash, venue, mint, side, solAmount, tokenAmount, price, fee, slot}: venue is pump or jupiter (which adds route); solAmount is the SOL paid (protocol and creator fees included, network fee and account deposits excluded), tokenAmount the coins bought and fee the network fee (SOL). (more below the table)
pump.sellmint, amount[, options]Sells amount coins on the bonding curve (all for the whole balance) with sell_v3, with a minimum SOL output of the local quote × (1 − slippage). See the sub-page exchange.IO("pump.*", ...).Same as pump.buy: solAmount is the SOL received (protocol and creator fees deducted).
pump.holdings[wallet]The pump.fun coins held by a wallet (by default this exchange object's wallet): the token balances that have a bonding curve, valued on the curve. See the sub-page exchange.IO("pump.*", ...).[{mint, amount, quote, complete, price, value}]: value is the quote (fees deducted) that selling the whole balance on the curve would return now; price and value are null once the curve is complete or when the balance exceeds what the curve can buy back (value graduated coins with exchange.IO("route", ...)).
lp.poolstoken[, quote]Lists a token's Meteora DLMM, Raydium CLMM and Orca Whirlpools pools (against the given quote, or SOL, USDC and USDT) with fee, bin step and active bin (DLMM) or tick spacing and current tick (CLMM), current price, both token balances of the pool and TVL; 24h volume and fees and the USD TVL come from Meteora's data API (dlmm.datapi.meteora.ag), the Raydium API v3 (api-v3.raydium.io) and Orca's public API (api.orca.so) respectively, while prices, balances and the like are read on chain, Raydium's fee from the pool's AmmConfig account. See the sub-page exchange.IO("lp.pools", ...).[{id, protocol, dex, symbol, base, quote, fee, feeRate, feeRateNow, binStep, activeId, price, reserveBase, reserveQuote, tvlQuote, tvlUsd, volume24hUsd, fees24hUsd, enabled, rewards, empty}]: protocol is meteora; price is quote per token; fee is the base fee in millionths (the unit of Uniswap's fee), feeRate the same as a ratio, and feeRateNow the current rate including the volatility surcharge; rewards are the pool's farm reward tokens [{mint, symbol}]. (more below the table)
lp.selecttoken, budget[, options]Picks a pool by rule. See the sub-page exchange.IO("lp.select", ...).One lp.pools row with an extra reason.
lp.poolpoolIdLive state of a pool. See the sub-page exchange.IO("lp.pool", ...).{id, protocol, dex, tokenX, tokenY, symbolX, symbolY, decimalsX, decimalsY, price, fee, feeRate, feeRateNow, binStep, activeId, reserveX, reserveY, tvlUsd, volume24hUsd, fees24hUsd, enabled, rewards}, where price is Y per X (the pool's own order). (more below the table)
lp.tickspoolId[, lowerBinId, upperBinId[, step]]The liquidity distribution over a bin range (DLMM has bins rather than ticks): both token amounts per segment, step being the segment width in bins (default 1). See the sub-page exchange.IO("lp.ticks", ...).{pool, activeId, binStep, price, tokenX, tokenY, symbolX, symbolY, segments: [{binLower, binUpper, priceLower, priceUpper, amountX, amountY, current}]}, prices in Y per X. (more below the table)
lp.planparamsA position plan (read-only, costs nothing). See the sub-page exchange.IO("lp.plan", ...).{params, symbol, pool, protocol, fee, feeRate, price, shape, strategy, budget, budgetUsd, binStep, activeId, lowerBinId, upperBinId, bins, positions: [{lowerBinId, upperBinId, priceLower, priceUpper, amountBase, amountQuote, share}], needBase, needQuote, haveBase, haveQuote, swap, shortfall, shareOfPool, volume24hUsd, estDailyFeesUsd, slippageBps, maxActiveBinSlippage, binArraysToCreate, rentSol, rentRefundableSol, warning?}: swap is the swap needed before adding ({side, amount, currency} or null); shareOfPool is the plan's share of the active bin and estDailyFeesUsd = 24h volume × fee rate × that share (assuming the price stays in range); rentSol is the rent the positions lock, of which rentRefundableSol belongs to the position accounts (returned when they are closed) and the rest to new bin arrays (binArraysToCreate, not returned). (more below the table)
lp.simulateplan[, payer]Builds the complete add-liquidity transactions of the plan (creating bin arrays, the position, ATAs, wrapping SOL, adding liquidity, unwrapping) and runs them with the node's simulateTransaction (no signature check, nothing broadcast), one transaction per position. See the sub-page exchange.IO("lp.simulate", ...).{pool, protocol, payer, ok, positions: [{position, lowerBinId, upperBinId, amountBase, amountQuote, ok, err, error, logs, unitsConsumed, txBytes}], swapNeeded, shortfall, note}: logs is the tail of the program logs and error the failure reason (DLMM, Raydium CLMM and Orca Whirlpool error codes with their names); Raydium and Orca rows carry tickLower and tickUpper instead of the bin fields, and position is the mint of the position NFT.
lp.addplanAdds liquidity by the plan. See the sub-page exchange.IO("lp.add", ...).{txHash, txHashes, swapTxHash, pool, protocol, symbol, positions: [{id, txHash, lowerBinId, upperBinId, priceLower, priceUpper, amountBase, amountQuote, rentSol, feeSol}]}, with position ids written meteora:address, raydium:position NFT mint or orca:position NFT mint; Raydium and Orca rows carry tickLower and tickUpper instead of the bin fields.
lp.increasepositionId, budget[, options]Adds to a position, splitting budget (in the quote token) over the position's bin range at the current price and swapping first for whichever token is short. See the sub-page exchange.IO("lp.increase", ...).{txHash, swapTxHash, id, symbol, amountBase, amountQuote, feeSol}
lp.decreasepositionId, ratio[, options]Withdraws a ratio (0 to 1, converted to basis points) from every bin of the position that has liquidity and claims all fees and farm rewards; the position stays. See the sub-page exchange.IO("lp.decrease", ...).{txHash, txHashes, positions: [{id, symbol, txHashes, principalBase, principalQuote, feesBase, feesQuote, receivedBase, receivedQuote, rewards, rentRefundSol, feeSol}], sells}: received* come from the balances before and after the transactions, and the fee part is split off by the amounts claimable on chain before sending.
lp.collectpositionIds[, sell]Claims fees and farm rewards (claim_fee2 and claim_reward2 on DLMM, a zero-liquidity decrease_liquidity_v2 on Raydium, update_fees_and_rewards + collect_fees_v2 + collect_reward_v2 on Orca), one transaction per position; with sell true the collected base token is sold for the quote. See the sub-page exchange.IO("lp.collect", ...).Same as lp.decrease.
lp.removepositionIds[, ratio[, sell]]Withdraws liquidity and claims fees and rewards, each position in its own transactions; ratio defaults to 1, which withdraws everything and closes the position account (on Raydium, close_position burns the NFT and closes the NFT account, the NFT mint and the position account), returning its rent (rentRefundSol); below 1 only that share is withdrawn. See the sub-page exchange.IO("lp.remove", ...).Same as lp.decrease.
lp.positions[filter[, wallet]]Every Meteora DLMM position of the wallet (found by owner with the node's getProgramAccounts, which the node must serve) and every Raydium CLMM and Orca Whirlpools position (NFTs with amount 1 found with getTokenAccountsByOwner, then their position accounts; on Orca positions in Position Bundles the wallet holds are listed too): bin range, in-range flag, current amounts of both tokens (from the position's share of each bin), value, uncollected fees and farm rewards, and the rent it locks. See the sub-page exchange.IO("lp.positions", ...).[{id, pool, protocol, symbol, fee, lowerBinId, upperBinId, activeId, binStep, width, priceLower, priceUpper, price, inRange, liquidity, amountBase, amountQuote, value, feesBase, feesQuote, feesValue, rewards, rentSol, feeOwner, closed}], with value and feesValue in the quote token, rewards as [{mint, symbol, amount}] and liquidity the sum of the bin shares. (more below the table)
lp.trackpositionIdsRegisters positions (meteora:address, raydium:NFT mint, orca:NFT mint or the bare address, Raydium and Orca also accepting the position account address; verified to be positions of that protocol) so that lp.positions checks them as well (ownership is verified on chain each time; transferred-away and closed positions are not listed). (more below the table)All position addresses remembered in this process.
bridge.chains—Chains supported by the bridge (LI.FI): EVM chains and Solana, cached for an hour. See the sub-page exchange.IO("bridge.*", ...).An array of {id, key, name, chainType, nativeToken}, chainType being EVM or SVM.
bridge.tokens[chain]Tokens LI.FI knows on a chain (Solana by default), cached for an hour. See the sub-page exchange.IO("bridge.*", ...).An array of {symbol, address, decimals, name, priceUSD}.
bridge.quoteparamsA bridge quote (read-only) from this exchange object's chain. See the sub-page exchange.IO("bridge.*", ...).{tool, toolName, fromChain, toChain, toChainName, fromToken, toToken, fromAmount, toAmount, toAmountMin, fromAmountRaw, toAmountRaw, toAmountMinRaw, fromAmountUSD, toAmountUSD, slippage, toAddress, feeCosts, gasCosts, executionDuration, approvalAddress, steps, transactionId, request}: amounts are in token units (*Raw in the smallest unit); toAmountMin is the amount guaranteed after slippage; feeCosts is [{name, amount, token, amountUSD, percentage, included}] (fees with included false are paid on top); gasCosts estimates source-chain gas; executionDuration is the expected time in seconds; approvalAddress is the contract to approve (null for the native coin and on Solana); steps lists the steps (cross bridges, swap swaps); request echoes the parameters.
bridge.simulateparams|quoteRe-quotes, signs LI.FI's serialized transaction with this wallet and runs simulateTransaction (with signature verification) without broadcasting. See the sub-page exchange.IO("bridge.*", ...).The bridge.quote result plus simulated (true), unitsConsumed and signature.
bridge.sendparams|quoteBridges out: re-quotes → checks the balance → signs LI.FI's serialized transaction with this wallet (refusing one this wallet does not sign) → simulateTransaction → broadcasts through the Solana node. See the sub-page exchange.IO("bridge.*", ...).{txHash, tool, fromChain, toChain, toChainName, fromToken, toToken, fromAmount, toAmount, toAmountMin, toAddress, transactionId}; txHash is the source-chain signature, whose state receipt returns.
bridge.statussignature[, options]Bridge progress (LI.FI /status) for the source-chain transaction signature returned by bridge.send; options may give {toChain, bridge} to speed up the lookup. See the sub-page exchange.IO("bridge.*", ...).{status, substatus, substatusMessage, tool, sending, receiving, fromAddress, toAddress, explorer, bridgeExplorer}: status is NOT_FOUND, INVALID, PENDING, DONE or FAILED; when DONE, substatus is COMPLETED (delivered as quoted), PARTIAL (a different token arrived) or REFUNDED; sending and receiving are {txHash, txLink, chainId, amount, amountRaw, token, timestamp}, with receiving.amount and receiving.token being what actually arrived.
bridge.apiKey[key]Sets the LI.FI API key (header x-lifi-api-key, for this exchange object only, never echoed); "" clears it and no argument just queries. Without a key requests are rate-limited per outgoing IP, see below. See the sub-page exchange.IO("bridge.*", ...).Whether a key is set (a boolean).

The rest of the longer descriptions in the table:

  • lp.track: Positions of all three protocols are found by owner, so registering is normally unnecessary; the command is kept for parity with Uniswap, and positions created by lp.add in this process are remembered automatically. Looking up another wallet's Orca position uses the node's getTokenLargestAccounts to find the NFT holder (public nodes rate-limit it tightly).

For the common command currency see exchange.IO.

The common command api is not supported (the call fails with an error and returns null).

Function Support

Not supported (the call fails with an error and returns null): exchange.GetTickers, exchange.GetTrades, exchange.CreateConditionOrder, exchange.ModifyConditionOrder, exchange.CancelConditionOrder, exchange.GetConditionOrder, exchange.GetConditionOrders, exchange.GetHistoryConditionOrders.

Served through a fallback:

  • exchange.GetRecords: Native periods are 1m, 5m, 15m, 1h, 4h, 12h, 1d (GeckoTerminal's timeframes); other periods of a minute or longer are synthesized by the docker, while second-level periods are unavailable (there is no trade feed to build them from).

Notes
FunctionBehavior on Jupiter
exchange.GetTicker()Bid and ask are executable prices for about $1000 (two quotes); for small tokens that cannot absorb $1000 the size steps down to $100 and then $10, and the sell side shrinks further if needed; the actual sizes are in sizeQuote and bidSize of Info. There are no 24h statistics: High, Low and Open are the current price and Volume is 0
exchange.GetDepth()Derived from quotes at sizes increasing from about $1000, two quotes per level. Bid prices strictly descend and ask prices strictly ascend. The two sides are quotes taken at different moments in opposite directions and occasionally cross (best bid not below best ask): the crossing levels are then merged into one level around the mid of the two (best bid and best ask at the mid minus and plus 0.0001%, amounts kept), and the original best bid and ask are in crossed of Info, which is null when the book did not cross
exchange.GetRecords()K-lines come from GeckoTerminal's pool-level data: Jupiter is an aggregator, so the pool (on any DEX) holding exactly the two tokens (SOL as wSOL) with the largest USD liquidity on GeckoTerminal is used (searched among the top 20 pools of each token; the chosen pool is cached for an hour). Prices are that pool's trade prices (including the pool fee, so they may differ from the mid price of exchange.GetTicker() by about one pool fee) in quote per base, and Volume is in the base token; periods without trades repeat the previous close with zero volume, and the last bar may still be open. GeckoTerminal data lags by tens of seconds up to a minute and its public API allows about 10 requests per minute: bars of the same period are cached for a minute, requests queue inside the docker and wait and retry when rate-limited, and fail if that still does not succeed. Fails when no pool of the pair is found
exchange.GetMarkets()Pairs of the tokens in the token table against USDC, USDT and SOL (routes are not verified pair by pair). The amount precision is the base token's decimals, the minimum quantity is one smallest unit, and the price precision is fixed at 8
exchange.GetAssets()Wallet balances of SOL and of the tokens in the token table, with separately held wSOL reported as WSOL; holdings outside the token table are not in the balance list but in unlisted of Info ({mint, amount})
exchange.CreateOrder()Swaps immediately: a limit price is the worst acceptable fill, enforced on chain by the program through the slippage bound, and if the current quote cannot reach it the call fails without sending a transaction; market orders are protected by the slippage setting; a market buy is sized in the quote currency to spend. The order extra parameter slippage overrides the slippage for that order. Jupiter simulates the transaction when building it, so problems such as an insufficient balance fail immediately. Before broadcasting, the Solana node simulates it again on the latest state: if that simulation fails, nothing is sent and the connector quotes and builds the transaction again, up to 2 retries (from the second retry on it avoids the liquidity sources of the failed routes, never going beyond the exchange.IO("dexes", ...) restriction); if it still fails the call fails with the simulation's err and the relevant program logs in the error message. On success preflightRetries in Info is the number of retries. With the order option tif set to gtc it places a native Jupiter limit order instead of swapping (Trigger V2 with an API key and a USDC or USDT quote, otherwise v1; exchange.IO("trigger.api", ...) can force either), see "Limit orders" below
exchange.GetOrder()The order ID looks like SOL_USDC,5VERv8… (the pair, a comma, then the transaction signature). A confirmed transaction is filled, with the filled amounts computed from the wallet balances before and after the transaction; a transaction that fails on chain is canceled (the fee is still charged); one still not included after its validity period (about one minute) is canceled. A transaction not sent by this program can only be queried once it has succeeded (the direction is inferred from the balance changes); one not yet confirmed or failed on chain returns an error. A limit order's ID is the pair plus the order account address (v1) or Jupiter's order UUID (V2), see "Limit orders" below
exchange.GetOrders()Swaps sent by this program that are neither confirmed nor expired, plus all open limit orders of the wallet (not only those placed by this program; V2 limit orders only when an API key is set)
exchange.CancelOrder()Limit orders only. Swaps cannot be canceled: a sent Solana transaction cannot be recalled, and one that is not included expires automatically after about one minute
exchange.ModifyOrder()Changes the price (and the order extra parameter slippage) of V2 limit orders only, see "Limit orders" below. v1 limit orders and swaps cannot be modified
exchange.GetHistoryOrders()Filled and canceled limit orders, plus expired V2 orders (funds still in the vault, reported as pending; see "Limit orders" below), no swaps (query swaps with exchange.GetOrder()). Without since and limit it returns the latest 10 v1 orders in each direction (buy and sell) and the latest 10 V2 orders; with since all created at or after it, and with limit until that many are collected. v1 and V2 are merged, newest created first
  • Request rate: Jupiter limits requests per second and returns 429 above the limit. The connector queues requests at the rate set with exchange.IO("rps", ...) (1 per second by default), and the exchange objects in one docker process that use the same Jupiter API address share this pace. With the default setting exchange.GetTicker() takes about 2 seconds and a 5-level exchange.GetDepth() about 10 seconds; for more speed, get a Jupiter API key and raise rps according to your plan.
  • Fees: the Solana base fee and priority fee of each transaction (capped by exchange.IO("priorityFee", ...)) are deducted from the SOL balance, and the order's Info contains fee. The first time a token is bought, the wallet has to create an account for it, which takes a deposit of about 0.002 SOL; the deposit still belongs to the wallet and is not counted in the fill.
Pump.fun launches

exchange.IO("pump.*", ...) trades coins on the pump.fun bonding curve (program 6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P) directly, without Jupiter. Accounts, instructions and quote formulas follow pump.fun's official public documentation (the IDLs and docs of pump-fun/pump-public-docs on GitHub).

  • New coins: pump.new discovers new coins on chain, and a strategy polls it in a loop (see the example below). Public nodes rate-limit per second and are slow (about 4 seconds for 20 transactions); for sniping, configure your own paid node in "Rpc Address".
  • Only SOL-quoted coins are traded: pump.fun also has coins quoted in USDC or in another pump coin (quote of pump.new), for which pump.buy and pump.sell fail; cashback coins (no longer created) fail too and can be traded through Jupiter with exchange.Buy and exchange.Sell.
  • Price protection: a buy is exact input (spending solAmount, fees included) with a minimum output of the local quote × (1 − slippage); a sell's minimum SOL output is the local quote × (1 − slippage). The default slippage is 5%, enforced on chain; when it cannot be met the whole transaction fails and only the network fee is charged.
  • Fees: the protocol and creator fees follow the Pump Fees program's fee table, tiered by market cap (0.95% + 0.30% in October 2026; pump.info and pump.quote return the actual rates, and coins without a creator charge no creator fee), plus the Solana network and priority fees. The first buy of a coin creates its token account (a deposit of about 0.002 SOL, returned by default when pump.sell sells all and closes the account); a wallet's first pump.fun trade also creates its volume account (about 0.0018 SOL, not returned).
  • Buying past the curve: when a buy's budget exceeds what the curve has left to sell (except on mayhem coins), the rest fills at the price of the PumpSwap pool the migration will create (pump.fun's "synthetic migration"), and crossesCurve of pump.quote is true; after that the curve cannot be traded until the migration happens. On mayhem coins a buy beyond what is left fails.
  • Graduation: for coins whose curve is complete (complete), pump.buy and pump.sell swap through this exchange object's Jupiter at market instead (pair MINT_SOL, price protection by the call's slippage, which Jupiter requires to be below 0.5) and return venue jupiter; the priorityFee and computeUnitLimit options do not apply then (they fail), the priority fee cap is exchange.IO("priorityFee", ...), and dryRun is not available (quote with pump.quote, which goes to Jupiter for graduated coins as well). In the short time after a curve completes and before it migrates, both routes can fail.
  • Every transaction is simulated by the node before sending and is not sent when the simulation fails, with the reason in the error (pump.fun error codes with their names, such as BuySlippageBelowMinTokensOut); broadcasting and deciding whether a transaction went out work as for swap orders, and waiting for confirmation works as for concentrated liquidity (an undecided outcome is reported as outcome is unknown with the signature, never as nothing happened).
  • When sniping, pay enough priority fee (options.priorityFee), or under congestion the transaction may expire without being included.

Sniping loop example (JavaScript):

javascript
var seen = {} var start = Date.now() while (true) { var coins = exchange.IO("pump.new", {limit: 20, since: start}) for (var i = 0; coins && i < coins.length; i++) { var c = coins[i] if (seen[c.mint] || c.quote != "SOL" || c.mayhem) continue seen[c.mint] = true if (c.devBuy && c.devBuy.quoteAmount > 1) continue // creator bought too much, skip var r = exchange.IO("pump.buy", c.mint, 0.01, {slippage: 0.2, priorityFee: 0.0005}) if (r) Log("bought", r.tokenAmount, c.symbol, "for", r.solAmount, "SOL", r.txHash) } Sleep(1000) }

Risks:

  • Most new coins go to zero quickly; the creator or wallets bundled with it often buy in the creation transaction or the next few blocks and can dump at any moment (rug pull).
  • Sniping bots and MEV compete hard: the order within a block depends on priority fees and tips, and a wide slippage invites sandwich attacks that fill at the worst price.
  • The name, symbol and metadata link are whatever the creator chose and can impersonate well-known projects; go by the mint address.
  • Protocol, creator, network and priority fees are paid on every buy and sell, so frequent in-and-out trading is costly.
Limit orders (tif gtc)

exchange.CreateOrder(symbol, side, price, amount, {tif: "gtc"}) (or exchange.Buy and exchange.Sell with the same order option) does not swap immediately but places a native Jupiter limit order, which Jupiter's keepers fill asynchronously once the price is reached. Without tif, or with another tif, orders behave as before (immediate swaps). There are two APIs:

Trigger v1Trigger V2
Used when (trigger.api at its default auto)No API key is set, or the quote is not USDC or USDTAn API key is set and the quote is USDC or USDT
Where funds sitAn on-chain order account (program j1o2qRpjcyUwEvwtcfhEQefh773ZgjxcVRry7LDqg5X); open orders are public on chainA vault in Jupiter's custody (Privy), one per wallet, shared by all V2 orders; orders are kept off chain by Jupiter and stay private until they fill
Trigger and fillOn the exchange rate of the two tokens, filled at the limit price (zero slippage by default); the amount received is guaranteedOn the base token's USD price; once reached, the order swaps at the market rate through Jupiter routing: the amount received is not guaranteed and fills may be partial; slippage is set automatically by Jupiter by default
Order IDSOL_USDC,order account addressSOL_USDC,order UUID
MinimumThe side given or received worth at least 5 USDThe side given worth at least 10 USD
ExpiryOptional, none by default; Jupiter does not close an expired order, which stays open with its funds locked in the order account until canceledRequired, 30 days by default; an expired order no longer fills and its funds stay in the vault until withdrawn by canceling
ModifyNot supportedexchange.ModifyOrder() changes the price

With an API key the default is the custodial V2: the funds of an order leave the wallet for a vault in Jupiter's custody. To keep the non-custodial v1, call exchange.IO("trigger.api", "v1"); conversely exchange.IO("trigger.api", "v2") forces V2 even without an API key (Jupiter currently accepts V2 calls without a key, but its documentation requires one).

Jupiter has marked v1 as no longer developed (critical fixes only, no shutdown announced); new features go to V2. Existing v1 orders are not affected by the configuration and can still be queried and canceled (the shape of the order ID selects the API, regardless of trigger.api; V2 orders need the API key or trigger.api set to v2 to be queried, canceled or modified).

Common rules:

  • Amounts: amount is in the base token; a buy gives amount × price of the quote token (rounded down to the smallest unit, never paying more), a sell gives amount of the base token (v1 also asks for amount × price of the quote token in return, rounded up, never receiving less). Market orders are not possible (a price of -1 or type market fails).
  • Order extra parameters: expiry is the expiry timestamp in milliseconds; slippage is an allowed slippage ratio (such as 0.003). v1 defaults to 0 (exact fills at the limit price), and a slippage raises the fill rate at the cost of filling below the limit; V2 lets Jupiter set it automatically from market conditions by default, and a given slippage is used as a fixed value.
  • States: open is pending, open with fills is partially filled, fully filled is filled, and canceled is canceled. Info is Jupiter's raw order row.
  • Expiry: Jupiter does not close expired orders for you, and the funds stay locked until exchange.CancelOrder() withdraws them (v1 returns the principal and the order account's rent; V2 withdraws from the vault). An expired order that has not been canceled is therefore still pending (or partially filled), with Info.expired true and Info.note saying it must be canceled to get the funds back. Expired v1 orders are still listed by exchange.GetOrders() (Jupiter keeps them open); Jupiter files expired V2 orders under history, so they are not in exchange.GetOrders() but are found by exchange.GetOrder() and exchange.GetHistoryOrders(). After the cancel withdraws the funds the order is canceled.
  • Canceling: for an order already filled or canceled exchange.CancelOrder() returns false with the reason in GetLastError().
  • Every exchange.GetOrders() call requests Jupiter's v1 limit order list once (and the V2 list once more when an API key is set), paced by exchange.IO("rps", ...).
  • Token-2022 tokens with the transfer fee or transfer hook extension cannot be used in limit orders (Jupiter does not support them).
  • Use exchange.IO("trigger.simulate", ...) to simulate an order before placing it.

Trigger v1:

  • Placing and canceling are on-chain transactions signed by the connector and broadcast through a Solana node; when the node's preflight simulation fails nothing is sent and the call fails. Each pays a Solana transaction fee and priority fee, and the first order in a token may create token accounts, whose deposits remain the wallet's. Canceling waits for the broadcast only; confirm the result with exchange.GetOrder().
  • Fees: Jupiter deducts its fee from the token received (0.03% for stable pairs and 0.1% otherwise per its documentation; each fill record is authoritative). The order's AvgPrice is before fees, and the amounts actually received are in trades of Info.
  • exchange.GetOrder() looks in the open orders and then in the history; an order this program has just placed that Jupiter has not indexed yet is reported from its on-chain state (an existing order account means open).

Trigger V2:

  • Authentication: the connector signs Jupiter's login challenge with the wallet's private key (only a message that starts with Jupiter's fixed login text and names this wallet) for a 15-minute access token, refreshed automatically and kept in memory only. A leaked token cannot move funds (withdrawals need a transaction signed by the wallet) but can cancel orders and change their prices.
  • Placing: the connector looks up the wallet's vault and registers one if there is none (this creates a custodial account for the wallet at Jupiter; exchange.IO("trigger.vault", "register") does it ahead of time); it then has Jupiter build the deposit transaction and checks it instruction by instruction (the wallet pays the fee, only the System, Token, Associated Token Account, Compute Budget and Memo programs are called, the tokens leaving the wallet go only to the vault side and add up to exactly the order amount, and the only accounts created with the wallet's rent are the order's input account handed to the vault (Jupiter creates it with CreateAccountWithSeed based on the vault, and closes it after a cancel or fill with the rent returned to the wallet) and associated token accounts of the wallet and the vault), signs it, simulates it with the node first (nothing is sent and the call fails when the simulation fails), and hands it to Jupiter with the order parameters; Jupiter lands the deposit during that call and returns the order UUID. If that call fails the deposit may already have landed: the error names the deposit transaction signature; check it with exchange.IO("receipt", ...) and exchange.IO("trigger.vault") before retrying.
  • Trigger: a buy buys when the base token's USD price falls to price or below, a sell sells when it rises to price or above. The quote token is taken as 1 USD (if USDC or USDT depegs, the trigger drifts accordingly). Jupiter does not check whether the price makes sense: a buy above or a sell below the market fills at once at the market rate.
  • Amounts and fills: the order's Amount is the base token deposited for a sell and the quote token deposited ÷ price for a buy; DealAmount and AvgPrice come from the amounts actually used and received (the fee is already taken from the amount received and not listed separately on V2).
  • Modifying: exchange.ModifyOrder() changes only the price (and slippage), only for an open single order that has not started filling, and not the trigger price of a trailing order; the amount is fixed by the deposit and cannot change (an amount passed in must equal the original). A buy keeps the quote amount it spends when its price changes, so its Amount becomes amount ÷ new price.
  • Canceling takes two steps: Jupiter stops the order (no more fills from then on) and builds a withdrawal transaction, which the connector checks (the wallet only pays the fee) and signs; Jupiter adds the vault's signature and lands it, and the unfilled tokens return to the wallet. If the second step fails the order waits for withdrawal (still listed by exchange.GetOrders()), and canceling it again completes the withdrawal. Expired orders are withdrawn with exchange.CancelOrder() too.
  • Custody risk: while an order is open its funds are in Jupiter's Privy-held vault, not in the wallet; they are not counted by exchange.GetAssets() (see exchange.IO("trigger.vault")). Withdrawals depend on Jupiter building the withdrawal transaction and co-signing it, so they cannot be done alone while Jupiter's service is down. Jupiter still labels the V2 API beta, and its behavior may change.
  • Fees: the wallet pays the network fees of the deposit and withdrawal transactions; fills are executed by Jupiter's keepers, who pay their network fees, and Jupiter's swap fee is already taken from the amount received. There is no integrator fee.
  • Requests: queries and orders authenticate first (the token is cached in the process), using one or two extra requests of the rate limit; an API key restricted by endpoint permissions must include Trigger, otherwise those calls fail with 403.
  • The order functions list all V2 limit orders of the wallet, including those placed elsewhere such as on jup.ag (each take-profit, stop-loss or OCO leg is listed as one order priced at its USD trigger, with the direction in triggerCondition of Info). An order that triggers on a USD price but is not quoted in USDC or USDT, or that does not trigger on the base token's price, cannot be expressed as a price of its pair, and queries that meet it fail (a query for a given pair only looks at that pair's orders).
DCA

exchange.IO("dca.create", ...), dca.list, dca.cancel and dca.simulate: with an API key and a USDC or USDT quote new DCA orders use Jupiter Trigger V2 DCA by default (custodial vault; exchange.IO("trigger.api", ...) applies as well: v1 keeps Recurring v1, v2 forces V2), otherwise the Recurring API v1 (time-based DCA, program DCA265Vj8a9CEuX1eb1LWRnDT7uK6q1xMipnNyatn23M). Jupiter no longer maintains Recurring v1 (it still works); DCA has moved into Trigger V2. dca.list lists both (V2 needs the API key), and dca.cancel tells v1 (an address) from V2 (a UUID) by the shape of the DCA ID. DCA orders do not appear in exchange.GetOrders() and the other order functions.

  • v1: the total is deposited into a DCA order account at once, Jupiter's keepers swap one installment every interval seconds, and the bought tokens are sent back to the wallet. At least 2 installments, each worth at least 50 USD; Token-2022 tokens are not supported; Jupiter takes a 0.1% fee from the tokens received. Creating and canceling are on-chain transactions with transaction and priority fees; canceling returns the remaining tokens to the wallet. Jupiter takes a few minutes to index a new DCA order; meanwhile dca.list lists the orders this program just created from their on-chain accounts (indexed: false), and dca.cancel cancels them all the same.
  • V2: authentication, the vault, the deposit and the custody risk are as for V2 limit orders above. The total is deposited into the vault and the keepers swap one installment per round, sending the tokens received straight to the wallet and paying the network fees; slippage is set automatically by Jupiter from market conditions and cannot be specified; the fee is already taken from the amount received. At least 2 installments, each worth at least 10 USD, an interval of 60 seconds to 1 year, a start at most 30 days ahead, and at most 10 running DCA orders per wallet. With minPrice or maxPrice a round swaps only while the base token's USD price is in the range, and a round outside it moves to the next interval. DCA orders cannot be modified; canceling takes two steps (as for V2 limit orders), and rounds already filled are not reversed.
Concentrated liquidity (LP)

The exchange.IO("lp.*", ...) commands provide concentrated liquidity on Meteora DLMM pools (program LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo), Raydium CLMM pools (program CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK) and Orca Whirlpools pools (program whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc; how these two differ from DLMM is below): pool selection, planning and simulation, adding, increasing and decreasing, collecting fees and farm rewards, withdrawal and position queries. Command names, parameters and result fields follow the lp.* commands of the Uniswap exchange object, with these differences:

  • Prices are discretized into bins rather than ticks: bin i has price (1 + binStep/10000)^i (scaled by the two tokens' decimals), ranges are given as lowerBinId and upperBinId (both inclusive), and the bin holding the current price is activeId; range may be written {bins: [lo, hi]}. The active bin holds both tokens, bins above it only X and bins below only Y, so the shape of a position is how much goes into each bin (spot, curve, bidask).
  • A position is an account, not an NFT: its id is meteora:position address (the bare address is accepted too); a position holds at most 70 bins, and wider spot ranges are split into several positions.
  • Rent: each position account locks its rent-exempt minimum (from the node's getMinimumBalanceForRentExemption, about 0.042 SOL in October 2026), returned when lp.remove withdraws everything and closes it; bin array accounts (70 bins each) missing in the range are created first, and their rent (about 0.052 SOL each in October 2026) is not returned. rentSol, rentRefundableSol and binArraysToCreate of the plan give these amounts. Holding a token for the first time also creates a token account (about 0.0015 SOL, still the wallet's).
  • SOL is wrapped and unwrapped automatically: when one side of the pool is SOL, the transaction wraps the SOL to deposit into wSOL and closes the wallet's wSOL account at the end (wSOL already in the wallet turns back into SOL as well).
  • Swaps go through this exchange object's Jupiter swaps (market orders, slippage per exchange.IO("slippage", ...)).
  • Every write transaction is simulated by the node before sending and is not sent when the simulation fails, with the reason in the error (DLMM, Raydium CLMM and Orca Whirlpool error codes with their names). The compute-unit limit is the simulated usage plus headroom, and the priority fee is the 75th percentile of recent fees paid by transactions writing the same accounts, capped by exchange.IO("priorityFee", ...). Broadcasting and the "may it have been sent" judgment are those of swap orders; commands return after confirmation, with actual amounts from the balances before and after the transaction. While waiting, every node is asked for the transaction status, and a transaction is reported expired (expired without being included … nothing happened, it really did not run) only when some nodes block height is past its last valid block height and a node past that point does not know it; a lagging or failing node never makes it look expired. When it is neither confirmed nor provably expired within 120 seconds the error says outcome is unknown and carries the signature: it may have landed, so check it with exchange.IO("receipt", signature) before retrying. When a nodes preflight reports BlockhashNotFound (it has not seen the blockhash yet; nothing was sent) the transaction is signed again with a fresh blockhash and resent, up to 2 times.
  • Withdrawals have no minimum-output protection: DLMM withdraws the position's share of each bin, so the amounts depend on the bins' composition at that moment.
  • Token-2022 tokens with a transfer hook are not supported, nor are positions whose fees go to another wallet (feeOwner); there is no lp.history, lp.stake, lp.unstake or lp.claim (farm rewards are claimed by lp.collect, lp.decrease and lp.remove).
  • lp.positions finds positions by owner with the node's getProgramAccounts; the official public node serves it, some nodes do not or rate-limit it, and then the call fails. Public nodes rate-limit per second and the connector backs off and retries; for frequent queries configure your own node.

How Raydium CLMM and Orca Whirlpools differ from DLMM above (they use the same tick model as Uniswap V3):

  • Prices are discretized into ticks: tick i has price 1.0001^i (scaled by the two tokens' decimals), a position covers [tickLower, tickUpper), and both ends are multiples of the pool's tick spacing (tickSpacing); a position in range holds both tokens, one out of range holds one. Shapes and layers are those of the Uniswap exchange object: curve and bidask consist of several positions.
  • A position is an NFT: its id is raydium:NFT mint or orca:NFT mint (the bare mint or the position account address is accepted too); whoever holds the NFT owns the position. lp.positions finds positions from the NFTs with amount 1 in the wallet and needs no getProgramAccounts. Token-2022 tokens with a transfer hook are not supported.
  • Raydium: new positions are Token-2022 NFTs, and opening creates no metadata to keep the deposits minimal: rent for the position account, the NFT mint and the NFT account (about 0.0053 SOL together in October 2026), all returned when lp.remove closes the position. The TickArrays (60 tick spacings each) at the two ends of the range are created by the opening transaction when missing, and their rent (about 0.072 SOL each in October 2026) is not returned; tickArraysToCreate of the plan lists them. Adding and increasing fix the amount of one token (the SOL side first, otherwise the more valuable side) and cap the other at the planned amount × (1 + slippage); when a price move pushes the other token beyond the cap the program fails with PriceSlippageCheck and nothing is sent. Withdrawals require at least the amounts at the current price × 0.99. Every initialized reward of the pool (even an ended one) is settled with each withdrawal or collection, and the reward tokens' ATAs are created first. Ticks beyond the pool's built-in range need the pool's TickArray bitmap extension account, and its absence is an error. Looking up a single position that is not in this wallet (lp.track) finds the NFT's holder by mint with the node's getProgramAccounts (the official public node serves it).
  • Orca: new positions use a Token-2022 NFT (with the metadata extension, as the official Orca SDK does); older SPL Token NFT positions and positions in a Position Bundle can be queried, increased, decreased, collected and closed as well. Each position locks the rent-exempt minimums of the position account, the NFT mint and the NFT token account plus the rent of two ticks (about 0.0084 SOL in all in October 2026), all returned when lp.remove closes it; a tick array (covering 88 tick spacings) missing at either end of the range is first created as a dynamic tick array, and its rent (about 0.0019 SOL each) is not returned. Adding liquidity passes the two token amounts on chain as maxima (increase_liquidity_by_token_amounts_v2), and the program adds the most liquidity they allow at the execution price, failing when the price is outside the bounds implied by slippage; withdrawals are protected by minimum amounts (those estimated at the current price less 1%). Decreasing or closing a locked position (lock_position) fails in simulation; only the main program deployment is supported (not Orca's immutable deployment). Pool statistics come from Orca's public API (api.orca.so); looking up a position that is not in this wallet finds the NFT holder with the node's getTokenLargestAccounts (public nodes rate-limit it tightly).

Monitoring and exits are left to the strategy: lp.positions reports whether each position is in range, its current value and uncollected fees, and the strategy decides when to rebalance or stop out.

Risks:

  • Impermanent loss: the further the price moves from the entry, the lower the position is worth compared with simply holding; as the price rises the position sells X bin by bin, and as it falls it buys X.
  • Once the price leaves the range the position holds a single token and earns no fees until the price returns.
  • Rent locked when opening: the position account's rent (and the NFT's on Raydium and Orca) comes back on withdrawal, new bin arrays' and TickArrays' rent does not; a plan split into several positions locks rent for each.
  • Every add, adjustment, collection and withdrawal is an on-chain transaction with fees and priority fees; frequent rebalancing can cost more than it earns.
  • The estimated daily fees in a plan come from the past 24h volume and the plan's current share of the active bin; they do not predict future income.
Cross-chain bridge

exchange.IO("bridge.*", ...) bridges tokens from this exchange object's chain to another chain through the bridges LI.FI aggregates (Across, Stargate, CCTP, deBridge, Mayan, Relay, NEAR Intents and more), between EVM chains and between EVM and Solana (from Solana). LI.FI supplies the quote and transaction, as a serialized transaction that the docker signs, simulates with simulateTransaction and broadcasts. Requests carry the integrator tag integrator=fmz and add no fee; fees in a quote such as LIFI Fixed Fee are LI.FI's and the bridges' own and are already deducted from toAmount.

Typical flow: bridge.quote for a quote → bridge.simulate to simulate → bridge.send to send → bridge.status every 10 to 30 seconds until DONE or FAILED.

  • Bridging is asynchronous: when bridge.send returns, only the source-chain transaction has been broadcast; delivery takes seconds to tens of minutes (see executionDuration in the quote) and must be confirmed by polling bridge.status; PARTIAL means a different token arrived on the destination chain and REFUNDED that the funds went back to the source chain.
  • Cross-VM transfers (EVM↔Solana) require toAddress, checked against the destination chain's address format; funds sent to a wrong address cannot be recovered.
  • Risks: the security of bridge contracts and cross-chain messaging rests with each bridge; quotes move, and bridge.send re-quotes before sending; the destination wallet needs gas to use what arrives. Test with a small amount first.
  • Rate limits: without an API key LI.FI limits by outgoing IP, /quote to 75 requests per 2 hours (bridge.quote, bridge.simulate and bridge.send use one each) and other endpoints to 100 per minute; strategies on the same docker share the quota. With a key set through exchange.IO("bridge.apiKey", key) the limit is 100 per minute. A rate-limited call fails with rate limited (HTTP 429 …).
Strategy examples

Complete runnable strategies: Jupiter Limit-Order Grid (native limit orders), DEX Dollar-Cost Averaging (native DCA), pump.fun Sniping Strategy and DEX-CEX Arbitrage; see DEX Strategy Examples.

In this sectionRead the whole section

NameDescription
exchange.IO("route", ...)On a Jupiter exchange object, request a quote: returns the quote and route of a swap without placing an order.
exchange.IO("token", ...)Register a token on a Jupiter exchange object, or list the token table.
exchange.IO("transfer", ...)Transfer SOL out of the wallet of a Jupiter exchange object.
exchange.IO("receipt", ...)On a Jupiter exchange object, query the status of a Solana transaction, optionally waiting until it is confirmed.
exchange.IO("slippage", ...)Set the slippage protection for market orders on a Jupiter exchange object.
exchange.IO("priorityFee", ...)When called as exchange.IO("priorityFee", ...), this function sets the priority fee cap for swap transactions on a Jupiter exchange object.
exchange.IO("dexes", ...)When called as exchange.IO("dexes", ...), this function restricts the liquidity sources that swaps go through on a Jupiter exchange object, for example to trade…
exchange.IO("rps", ...)When called as exchange.IO("rps", ...), this function sets the rate of requests to the Jupiter API on a Jupiter exchange object.
exchange.IO("lp.pools", ...)List a token's concentrated-liquidity pools on Meteora DLMM, Raydium CLMM and Orca Whirlpools, largest USD TVL first.
exchange.IO("lp.select", ...)Pick a concentrated-liquidity pool for a token automatically, by a TVL floor and the 24h volume.
exchange.IO("lp.pool", ...)Read the live state and statistics of a concentrated-liquidity pool.
exchange.IO("lp.ticks", ...)View a pool's liquidity distribution over a range, with both token amounts per bin or tick segment.
exchange.IO("lp.plan", ...)Work out a position plan from a budget, a price range and a shape: which pool, how many positions, how much of each token, whether to swap first and how much re…
exchange.IO("lp.simulate", ...)Build all add-liquidity transactions of a plan and run them with the node's simulation, without signing or broadcasting.
exchange.IO("lp.add", ...)Open concentrated-liquidity positions by a plan: the missing token is swapped through Jupiter first, and each position is one on-chain transaction.
exchange.IO("lp.increase", ...)Add to an existing position: the budget is split into the two tokens by the position's range and the current price, swapping first for the one that is short.
exchange.IO("lp.decrease", ...)Withdraw a share of a position's liquidity and claim all fees and farm rewards; the position stays.
exchange.IO("lp.collect", ...)Collect a position's fees and farm rewards without touching the principal.
exchange.IO("lp.remove", ...)Withdraw a position's liquidity and claim fees and rewards; withdrawing everything closes the position and returns its rent.
exchange.IO("lp.positions", ...)List a wallet's Meteora DLMM, Raydium CLMM and Orca Whirlpools positions with range, current amounts, value and uncollected fees.
exchange.IO("pump.*", ...)Discover new coins on the pump.fun bonding curve, read the curve state, quote, buy and sell; graduated coins trade through Jupiter swaps automatically.
exchange.IO("dca.*", ...)Buy or sell in installments with Jupiter's DCA: create, list, cancel and simulate.
exchange.IO("trigger.*", ...)Helper commands for Jupiter's native limit orders: choose the v1 or V2 API, view the V2 custodial vault, and simulate placing or canceling.
exchange.IO("bridge.*", ...)Bridge tokens from Solana to EVM chains through bridges aggregated by LI.FI: list chains and tokens, quote, simulate, send and track progress.