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

Builds a position plan (read-only, costs nothing): from a budget, a price range and a shape, it computes each position's range, liquidity and token amounts, whether the wallet covers them and what must be swapped first.

exchange.IO(k, params)

Examples

  • Plan a ±5% range position in the chosen pool and see the funds needed and whether the wallet covers them.

    javascript
    function main() { var sel = exchange.IO("lp.select", "ETH", 100, {quote: "USDC"}) var params = {symbol: "ETH_USDC", budget: 100, pool: sel.id, range: {pct: [-5, 5]}} var plan = exchange.IO("lp.plan", params) if (!plan) { Log("Plan failed:", GetLastError()) return } var p = plan.positions[0] Log("Range:", p.priceLower, "~", p.priceUpper, "price:", plan.price) Log("Needs:", plan.needBase, "ETH +", plan.needQuote, "USDC") Log("Estimated daily fees:", plan.estDailyFeesUsd, "USD") if (plan.swap) { Log("Swap first:", plan.swap.side, plan.swap.amount, plan.swap.currency) } if (plan.shortfall) { Log("Wallet short:", plan.shortfall) } }
  • A three-layer curve: the positions near the price are the thickest.

    javascript
    function main() { var plan = exchange.IO("lp.plan", {symbol: "ETH_USDC", budget: 100, range: {pct: [-5, 5]}, shape: "curve", layers: 3}) plan.positions.forEach(function (p) { Log(p.priceLower, "~", p.priceUpper, "share of budget:", p.share) }) if (plan.warning) { Log(plan.warning) } }
  • Aerodrome and Velodrome: a plan without range is a basic (volatile) pool plan.

    javascript
    function main() { var plan = exchange.IO("lp.plan", {symbol: "ETH_USDC", budget: 100, stable: false}) Log(plan.protocol, plan.pool, "deposit:", plan.amountBase, "ETH +", plan.amountQuote, "USDC", "LP tokens:", plan.lpTokens) }

Returns

TypeDescription

object / null value

The plan; see the remarks for its fields. Pass it unchanged to lp.simulate or lp.add. Null with an error for bad parameters, an unknown pool or no liquidity at the current price. When the wallet falls short the plan is still returned, with shortfall explaining it.

Arguments

NameTypeRequiredDescription

k

string

Yes

Set to lp.plan to build a plan.

params

object

Yes

Plan parameters such as {symbol: "ETH_USDC", budget: 100, range: {pct: [-5, 5]}}; symbol and budget are required, and concentrated-liquidity pools also need range; see the remarks.

See Also

Remarks

Keys of params:

KeyDescription
symbolPair such as ETH_USDC; prices are quote per base
budgetBudget in the quote token
rangePrice range, in one of three forms: {lower, upper} prices; {pct: [-5, 5]} percent around the current price; {ticks: [lo, hi]} ticks. Aligned to the pool's tick spacing
shapespot (default) one range; curve nested layers, thickest at the price; bidask segments on both sides, thicker further out
layersNumber of layers for curve and bidask, default 3, at most 20
poolPool ID; when omitted the pool is picked by the lp.select rule (using fee and tickSpacing below)
feePreferred fee tier when picking the pool (millionths)
slippagePrice protection, default 0.01 (1%), at most 0.5
tickSpacing, protocolAerodrome and Velodrome: filters when picking a Slipstream pool; protocol defaults to ["slipstream", "slipstream2"]
stableAerodrome and Velodrome basic-pool plan: true a stable pool, false a volatile pool; both when omitted
stakeAerodrome and Velodrome: true makes lp.add stake into the gauge right after adding

Fields of the plan:

FieldDescription
paramsThe parameters, echoed
symbol, pool, protocol, fee, tickSpacing, tick, priceThe chosen pool and the current price
shape, layers, layersRequestedShape, actual and requested layer count; warning explains a count below the request
budget, budgetUsdBudget in the quote token and in USD
positionsEach position {tickLower, tickUpper, priceLower, priceUpper, liquidity, amountBase, amountQuote, share}, share being its part of the budget
needBase, needQuoteToken amounts the positions need
haveBase, haveQuoteAmounts available in the wallet
swapThe swap needed first, {side, amount, currency} (sell sells base, buy buys base), or null
payWithWith a native pair, the coin that pays the wrapped-token side (such as ETH or WETH); otherwise null
shortfallWhy the wallet falls short, or null; lp.add fails while it is set. Its content is described under Funding below
shareOfPoolShare of the pool's liquidity at the current price after adding
volume24hUsd, estDailyFeesUsdThe pool's 24h volume and the daily fees estimated from the pool share (USD), an extrapolation of past volume
slippageBpsPrice protection in basis points

Funding: a token the wallet lacks is bought with the other one (1% extra), swap gives the amount and lp.add places that market order first; when the two tokens together do not cover the budget, shortfall explains it: the two amounts the plan deposits (the same as needBase and needQuote), what the wallet holds (haveBase, haveQuote) and its value in the quote token at the pool price, and, when the missing token has to be bought with the other one, how much of that other token is needed in all (deposit plus swap cost, with 1% headroom) and how much is missing. For example, with a budget of 100 USDC, a plan depositing 0.02 ETH + 50 USDC and a wallet holding only 60 USDC, it says that buying the missing 0.02 ETH takes about 50.5 USDC, so the USDC side needs 100.5 USDC in all (50 to deposit + 50.5 for the swap) and is 40.5 USDC short. About 3 million gas at the current gas price is kept back from the native coin for fees. In V3 and Slipstream pools the native coin takes part as its wrapped token, so with a native pair (such as ETH_USDC) the wrapped-token side is paid either entirely in the native coin or entirely in the wrapped token, never a mix: the native coin when it covers the whole side, otherwise the wrapped token when that does, and when neither does the larger balance is used for the calculation; payWith is the coin actually used. In V4/Infinity CL native and wrapped pools are different pools, each using its own coin.

Pool choice: without pool the pool is picked by the lp.select rule every time, and lp.simulate and lp.add recompute a plan from its params (at the latest pool price), so they may pick another pool; to add to the pool you looked at, put pool in params. V4/Infinity CL pools on Uniswap and PancakeSwap always need pool (from lp.select with {protocol: "v4"} or {protocol: "infinity"}).

Range and shape: a range above the current price holds only the base token and one below it only the quote token; estDailyFeesUsd is then 0, and fees are earned once the price enters the range. curve layers all cross the current price, are distinct and aligned to the tick spacing, nesting inward first and filling with the range's other price-crossing combinations when nesting runs out; a two-sided range holds at most (legal lower bounds below the price) × (legal upper bounds above it) layers, and beyond that the plan gives as many as fit.

Aerodrome and Velodrome: a range means a Slipstream pool; without range (or with a basic pool) the plan is for a basic pool, depositing both tokens in the reserve ratio, and is {params, symbol, pool, protocol, stable, fee, price, budget, budgetUsd, needBase, needQuote, haveBase, haveQuote, amountBase, amountQuote, lpTokens, swap, shortfall, shareOfPool, volume24hUsd, estDailyFeesUsd, slippageBps}, lpTokens being the expected LP tokens. stake true stakes right after adding (see exchange.IO("lp.stake")).