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

Runs a plan's add-liquidity transaction with eth_call (nothing is signed, broadcast or paid) to confirm it executes and see the amounts it uses.

exchange.IO(k, plan)
exchange.IO(k, plan, stateOverride)

Examples

  • Give the wallet a temporary 100 ETH and simulate a range above the price that uses only ETH.

    javascript
    function main() { var plan = exchange.IO("lp.plan", {symbol: "ETH_USDC", budget: 100, range: {pct: [2, 10]}}) var ov = {} ov[exchange.IO("address")] = {balance: "0x56bc75e2d63100000"} var r = exchange.IO("lp.simulate", plan, ov) if (!r) { Log("Simulation failed:", GetLastError()) return } Log("Pool:", r.pool, "ETH sent (wei):", r.value) r.positions.forEach(function (p) { Log("Liquidity:", p.liquidity, "uses:", p.amountBase, "ETH +", p.amountQuote, "USDC") }) }
  • Aerodrome and Velodrome: a basic-pool plan simulates without any balance.

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

Returns

TypeDescription

object / null value

{pool, positions: [{liquidity, amountBase, amountQuote}], value, swapNeeded, note}; see the remarks. Null with an error when the transaction would revert (short balance or allowance, price beyond the protection, and so on).

Arguments

NameTypeRequiredDescription

k

string

Yes

Set to lp.simulate to simulate adding liquidity.

plan

object

Yes

A plan returned by exchange.IO("lp.plan", ...), or its params; it is recomputed from params at the latest pool price and then simulated.

stateOverride

object

No

State override for the node's eth_call (geth's state override set), to give the wallet a temporary balance or allowance, such as {"0xWalletAddress": {"balance": "0x56bc75e2d63100000"}}; it applies to this simulation only.

See Also

Remarks

Fields: positions gives each position's minted liquidity and the token amounts used; value is the native coin sent with the transaction (decimal string in the smallest unit, non-zero when paying in the native coin); swapNeeded true means the plan needs a swap first, so the post-swap amounts are simulated and the token the wallet does not hold yet must be supplied with stateOverride, or the call reverts on the transfer (an error such as execution reverted: STF).

Allowances: the transaction pulls tokens from the wallet, so a short allowance for the PositionManager (Permit2 for V4/Infinity CL) also reverts; a real lp.add approves automatically, while a simulation either pays in the native coin (no approval needed) or supplies the allowance in stateOverride. A plan that needs only the native coin (such as a range above the price on a native pair) simulates with just a balance for the wallet.

V4/Infinity CL pools on Uniswap and PancakeSwap pay tokens through Permit2, so supply the token's approval of Permit2 and the PositionManager allowance inside Permit2 (the Permit2 address is v4.permit2 in exchange.IO("contracts")); their PositionManager returns no amounts, so the result shows the planned amounts plus tickLower and tickUpper, and note says so (no revert means the amounts fit).

Basic pools on Aerodrome and Velodrome are simulated with the Router's quoteAddLiquidity, which needs no balances or approvals, returning {pool, protocol, lpTokens, amountBase, amountQuote, swapNeeded, note}.