exchange.IO("call", ...)
Simulates the execution of any smart contract method (including write methods that modify on-chain state) via eth_call. The process does not sign or broadcast a transaction and consumes no gas, making it suitable for on-chain quoting, transaction dry runs, and checking whether a transaction can execute successfully.
exchange.IO(k, address, method, ...args)
exchange.IO(k, address, calldata)
exchange.IO(k, address, calldata, options)Examples
-
Use Uniswap QuoterV2 to get a quote and simulate the execution of write operations.
javascriptfunction main() { var WETH = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2" var USDC = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" // Uniswap V3 QuoterV2 (Ethereum mainnet) var QUOTER = "0x61fFE014bA17989E743c5F6cB21bF9697530B21e" // The quote methods of QuoterV2 have a state mutability of nonpayable; when called with // exchange.IO("api", ...), they are treated as write operations and a transaction is sent; // when called with exchange.IO("call", ...), the execution is only simulated: no signing, no // broadcasting, and no gas consumed exchange.IO("abi", QUOTER, JSON.stringify([{ type: "function", name: "quoteExactInputSingle", stateMutability: "nonpayable", inputs: [{ name: "params", type: "tuple", components: [ { name: "tokenIn", type: "address" }, { name: "tokenOut", type: "address" }, { name: "amountIn", type: "uint256" }, { name: "fee", type: "uint24" }, { name: "sqrtPriceLimitX96", type: "uint160" }] }], outputs: [{ name: "amountOut", type: "uint256" }, { name: "sqrtPriceX96After", type: "uint160" }, { name: "initializedTicksCrossed", type: "uint32" }, { name: "gasEstimate", type: "uint256" }] }])) // Quote: how much USDC 1 WETH can be swapped for in the 0.05% fee tier pool var q = exchange.IO("call", QUOTER, "quoteExactInputSingle", [WETH, USDC, "1000000000000000000", 500, 0]) Log("amountOut:", q.amountOut, "gasEstimate:", q.gasEstimate) // Simulated write: execute transfer as the current wallet address; returns null if the balance // is insufficient, and the revert reason returned by the contract can be obtained via // GetLastError() var ok = exchange.IO("call", USDC, "transfer", "0x1111111254EEB25477B68fb85Ed929f73A960582", 1) Log("transfer simulation:", ok, GetLastError()) // Use the from parameter to simulate the call as another address Log("simulation with specified from:", exchange.IO("call", USDC, "transfer", "0x1111111254EEB25477B68fb85Ed929f73A960582", 1, { from: "0x55FE002aefF02F77364de339a1292923A15844B8" })) } -
Use an aggregator (the KyberSwap public API) to complete the full workflow of getting a quote, building the transaction, simulating it, and sending it. When
DRY_RUNistrue, the transaction is only signed and not broadcast.javascript// Aggregator quote and swap example (using the KyberSwap public API, no key required): quote → // build transaction → eth_call simulation → send // When DRY_RUN = true, only sign without broadcasting var DRY_RUN = true // Chain name used by KyberSwap: ethereum / bsc / base / arbitrum … var CHAIN = "ethereum" // Native token address as defined by aggregator convention var NATIVE = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" var TOKEN_IN = NATIVE // Sell: ETH var TOKEN_OUT = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" // Buy: USDC var AMOUNT_IN = "10000000000000000" // 0.01 ETH (in the smallest unit) var SLIPPAGE_BPS = 50 // 0.5% // Sender address used for simulation: defaults to the current wallet; if the wallet balance is // insufficient, the simulation will inevitably fail, so you can temporarily change it to an address // holding the token, solely to verify whether the route can be executed var SIMULATE_FROM = "" var API = "https://aggregator-api.kyberswap.com/" + CHAIN + "/api/v1" var HEADERS = { "x-client-id": "fmz-example", "Content-Type": "application/json" } function getJSON(url, opts) { var r = JSON.parse(HttpQuery(url, opts || { headers: HEADERS })) if (r.code !== 0) throw "aggregator error: " + JSON.stringify(r) return r.data } // Before selling an ERC20 token, it must first be approved for the router contract; the native // token requires no approval function ensureAllowance(token, spender, amount) { if (token.toLowerCase() === NATIVE.toLowerCase()) return var owner = exchange.IO("address") var allowance = BigInt(exchange.IO("api", token, "allowance", owner, spender)) if (allowance >= BigInt(amount)) return Log("Approve", token, "→", spender) var tx = exchange.IO("api", token, "approve", spender, amount, DRY_RUN ? { dryRun: true } : {}) Log("approve:", DRY_RUN ? tx.hash + " (dryRun, not broadcast)" : tx) } function main() { var wallet = exchange.IO("address") // 1. Get a quote var route = getJSON(API + "/routes?tokenIn=" + TOKEN_IN + "&tokenOut=" + TOKEN_OUT + "&amountIn=" + AMOUNT_IN, { headers: HEADERS }) var s = route.routeSummary Log("Quote:", s.amountIn, "→", s.amountOut, "(approx. $" + Number(s.amountOutUsd).toFixed(2) + "), gas≈" + s.gas) // 2. Build the transaction: the aggregator returns the router contract address, the calldata, // and the amount of native token to attach var from = SIMULATE_FROM || wallet var built = getJSON(API + "/route/build", { method: "POST", headers: HEADERS, body: JSON.stringify({ routeSummary: s, sender: from, recipient: from, slippageTolerance: SLIPPAGE_BPS, deadline: Math.floor(Date.now() / 1000) + 600, }), }) var router = built.routerAddress var value = built.transactionValue || "0" Log("Router contract:", router, "| calldata", (built.data.length - 2) / 2, "bytes | value", value) // 3. Simulate: pass the complete calldata to IO("call") for execution, without signing or // broadcasting; on failure, the revert reason given by the contract is returned var ret = exchange.IO("call", router, built.data, { from: from, value: value }) if (ret === null) { Log("Simulation failed, not sending:", GetLastError()) return } var decoded = exchange.IO("decode", "uint256,uint256", String(ret).replace(/^0x/, "")) Log("Simulation succeeded, actual amount out:", decoded[0], "(quoted", s.amountOut + ")") // 4. Send: approve (when selling an ERC20 token) → send the transaction with the calldata // attached; gasLimit is set to 1.3x the estimated value ensureAllowance(TOKEN_IN, router, AMOUNT_IN) var opts = { data: built.data, gasLimit: Math.ceil(Number(built.gas || s.gas) * 1.3) } if (DRY_RUN) opts.dryRun = true var tx = exchange.IO("api", "eth", "send", router, value, opts) if (DRY_RUN) { Log("Signed (dryRun, not broadcast): hash", tx && tx.hash, "| nonce", tx && tx.nonce, "| type", tx && tx.type) } else { Log("Sent:", tx) } }
Returns
| Type | Description |
string / number / bool / object / array | When called with a method name, returns the ABI-decoded return value (an object when there are multiple return values); when the method has no return value, returns |
Arguments
| Name | Type | Required | Description |
k | string | Yes | Function of the |
address | string | Yes | Address of the smart contract. |
method | string | No | Method to simulate. It can be a method name, a full method signature (e.g. |
calldata | string | No | Complete call data (a hexadecimal string starting with |
args | string / number / bool / object / array / any | No | Method arguments, laid out exactly as when calling a contract method with |
See Also
Web3 exchange.IO exchange.IO("api") exchange.IO("multicall") exchange.IO("encode") exchange.IO("decode")
Remarks
The quoting methods of contracts such as Uniswap QuoterV2 are declared as nonpayable. When called with exchange.IO("api", ...), they are treated as write operations, and a transaction is signed and sent. Therefore, exchange.IO("call", ...) should be used for quoting.
The contract revert reason is parsed into readable text: Error(string) returns the reason string, Panic(uint256) returns the error code and its meaning, and custom errors return the error selector and raw data.
When a transaction actually needs to be sent, exchange.IO("api", "eth", "send", toAddress, value, {data: calldata}) can be used to send arbitrary call data; when dryRun: true is set, the transaction is only signed and not broadcast.
Currently only Ethereum (EVM) chains are supported; TRON is not supported.