Type/to search
Getting Started
Welcome to FMZ Quant Trading Platform
Quick Start
Key Security
Platform Basics
Account and Billing
Live Robot Billing and Top-up
Sub-accounts
Exchange
General Protocol
Local Credential Files
Exchange-Specific Notes
Securities and Futures
Crypto
Docker
Strategy Library
Live Trading
Writing Strategies
Development Tools
Backtesting System
Advanced Topics
Data and Research
Integrations

The Uniswap exchange object connects to the V2 and V3 pools of Uniswap or PancakeSwap on one chain and maps on-chain swaps to spot trading functions: check prices with exchange.GetTicker() and place orders with exchange.CreateOrder(), with no ABIs to register and no contract calls to encode. Routing, quoting, token approval, price protection and sending transactions are all handled by the exchange object.

When to use the Uniswap exchange object and when to use Web3

  • To swap tokens on Uniswap or PancakeSwap: use the Uniswap exchange object.
  • To call other contracts or other DEX features (such as providing liquidity or managing V3 positions), to use other chains, or to build custom transactions: use the Web3 exchange object, see Advanced Topics → Web3 → Ethereum (EVM).

A strategy can add both kinds of exchange objects and use the same wallet with them.

Configure the exchange object

FieldDescription
DEXUniswap or PancakeSwap
ChainEthereum, Arbitrum, Base, BNB Chain. One exchange object is one DEX on one chain
Private KeyWallet private key (hex string). The key can be deployed locally on the docker, see Getting Started → Key Security
Rpc AddressNode address of the chain; a public node is filled in when the chain is chosen (https://ethereum-rpc.publicnode.com for Ethereum, for example). Several nodes separated by commas back each other up
Rpc Api KeyNode authentication, may be left empty. Written as Name: value it is sent as a request header with that name; otherwise it is sent as Authorization: Basic <value>

On the first call the exchange object checks that the node is on the configured chain and reports an error otherwise, so transactions never go to another chain. The wallet needs the chain's native coin (ETH or BNB) to pay gas.

Trading pairs

  • Pairs are written as base_quote, such as ETH_USDC or UNI_USDT.
  • Token names are resolved in this order: built-in common tokens (native coin, wrapped native coin, USDC, USDT, etc.) → tokens registered with exchange.IO("token", name, contractAddress) → the official token lists. When the official list has several tokens with the same name on a chain, use the contract address instead.
  • Tokens not in the token table can be used directly by contract address as part of the pair, for example 0x1f9840a85d5af5bf1d1762f925bdaddc4201f984_USDC.
  • The native coin and its wrapped token are two different assets: ETH and WETH, BNB and WBNB are different currencies. When trading the native coin, the router wraps and unwraps automatically. Converting between the two cannot be done with orders; use exchange.IO("wrap", amount) and exchange.IO("unwrap", amount), which call the wrapped token contract directly, 1:1, costing only gas.
  • exchange.GetMarkets() lists only common pairs; pairs not listed can be traded as well.

What the standard functions do

FunctionBehavior
exchange.GetTicker()Best bid and ask are executable prices from actual quotes of a certain size (pool fees included); there are no 24-hour statistics on chain
exchange.GetDepth()Price levels derived from on-chain quotes of increasing size, not a real order book
exchange.GetTrades()Recent on-chain swaps in the pair's pools
exchange.GetAccount(), exchange.GetAssets()Balances of the native coin and of the tokens in the token table
exchange.CreateOrder()Swaps on chain immediately, see below
exchange.GetOrder()The order ID is the transaction hash and the status comes from the receipt: unfinished before it is mined, filled or failed after
exchange.GetOrders()Orders sent in the current run that are not mined yet
exchange.CancelOrder()Sends a replacement transaction with the same nonce, best effort, see below

exchange.GetRecords(), exchange.GetTickers() and exchange.GetHistoryOrders() are not supported.

Placing orders

A DEX has no order book; every order is a swap executed on chain immediately. It either fills completely or reverts completely (losing only gas); it never fills partially and never rests waiting for a price.

  • Limit orders: the limit price is the worst execution price. A quote is taken first; if the current price cannot reach the limit, an error is returned and no transaction is sent. Otherwise the minimum to receive / maximum to pay is written into the on-chain transaction, and if the price moves before the transaction is mined so that the limit can no longer be met, the whole swap reverts.
  • Market orders: the minimum to receive / maximum to pay is the quote minus slippage. Slippage defaults to 0.5% and is changed with exchange.IO("slippage", ratio).
  • Amount: for sells, the amount of base currency to sell; for limit buys, the amount of base currency to buy; for market buys, the amount of quote currency to spend.
  • Settings for a single order can be appended after the side argument, for example exchange.CreateOrder("ETH_USDC", 'sell;{"slippage":0.01,"route":"v3"}', -1, 0.1): slippage is the slippage for this order, route restricts the route type (v2, v3, hop for two hops, direct).
  • Before selling a token (ERC20), the router's allowance is checked; if it is not enough, an approval transaction is sent first and waited for. By default only the amount needed is approved; exchange.IO("approve", "max") switches to unlimited approval and saves later approval transactions.
  • A transaction not mined before its deadline (120 seconds by default, change it with exchange.IO("deadline", seconds)) reverts, so it cannot fill after the price has moved a lot.

Cancelling orders

exchange.CancelOrder() sends a zero-value transaction to yourself with the original order's nonce and a higher fee; if it is mined first, the original order becomes invalid. This is a best effort: the original order may be mined and filled before the replacement, and cancelling an order that is already mined returns an error. Check the final status with exchange.GetOrder() after cancelling.

Common exchange.IO() commands

CommandPurpose
exchange.IO("slippage", ratio)Slippage for market orders, default 0.005
exchange.IO("deadline", seconds)Transaction deadline, default 120 seconds
exchange.IO("gasMultiplier", x)Gas limit = node estimate × x, default 1.2
exchange.IO("approve", "exact" or "max")Approval mode
exchange.IO("token", name, contractAddress)Register a token; without arguments, list the token table
exchange.IO("route", symbol, side, amount)Quote only: prices of the candidate routes and the best one, no order
exchange.IO("simulate", symbol, side, amount[, price])Build the transaction as an order would and only simulate it on chain, no gas spent
exchange.IO("transfer", toAddress, amount[, token])Send the native coin or a token; the amount can be "all"
exchange.IO("receipt", txHash[, waitMs])Receipt of a transfer or other transaction, optionally waiting for it to be mined
exchange.IO("wrap", amount), exchange.IO("unwrap", amount)Convert between the native coin and the wrapped token 1:1
exchange.IO("contracts")Contract addresses of this DEX on this chain
exchange.IO("base", nodeAddress), exchange.IO("sendBase", nodeAddress)Switch nodes; set a node used only for broadcasting transactions (private transaction channel)
exchange.IO("address")Wallet address

For the parameters and return values of each command see the Uniswap category of the syntax manual.

Examples

Example: quote, simulate, then sell at market

Uses ETH_USDC on Ethereum. Note that CreateOrder sends a real transaction.

javascript
function main() { var symbol = "ETH_USDC" exchange.IO("slippage", 0.003) // 0.3% slippage for market orders var t = exchange.GetTicker(symbol) Log("bid:", t.Buy, "ask:", t.Sell) // quote only: best route for selling 0.1 ETH var r = exchange.IO("route", symbol, "sell", 0.1) Log("best route:", r.best, "price:", r.price) // simulate on chain first, no gas spent if (!exchange.IO("simulate", symbol, "sell", 0.1)) { Log("simulation failed:", GetLastError()) return } // sell 0.1 ETH at market; the order ID is the transaction hash var id = exchange.CreateOrder(symbol, "sell", -1, 0.1) if (!id) { Log("order failed:", GetLastError()) return } while (true) { var o = exchange.GetOrder(id) if (o && o.Status != ORDER_STATE_PENDING) { Log("status:", o.Status, "filled:", o.DealAmount, "average price:", o.AvgPrice) break } Sleep(3000) } }

See Also