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

To swap tokens on the decentralized exchanges Uniswap and PancakeSwap, use the Uniswap exchange object (see Uniswap and PancakeSwap below). To read on-chain data, call smart contracts and send custom transactions, use the Web3 exchange object, which supports Ethereum and other EVM-compatible chains as well as TRON.

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

With ChainType set to ETH, the Web3 exchange object connects to nodes of Ethereum and every EVM-compatible chain (BSC, Base, Arbitrum, Optimism, Polygon and so on), and uses the exchange.IO() commands to query balances, call contracts and send transactions. This page walks through the commands in the order of a typical on-chain operation; for the full parameters of each command see the corresponding exchange.IO("command", ...) in the Web3 category of the syntax manual.

If you only want to swap tokens on Uniswap or PancakeSwap, use the Uniswap exchange object (see Advanced Topics → Web3 → Uniswap and PancakeSwap): it supports standard functions such as exchange.GetTicker() and exchange.CreateOrder() directly, with no contract calls to encode yourself.

1. Configure the exchange object

Add an exchange on the "Exchange" page (/m/add-platform), choose the protocol "Cryptocurrency" and the exchange Web3:

FieldDescription
ChainTypeETH: Ethereum and all EVM-compatible chains; TRON: TRON, see Advanced Topics → Web3 → TRON
Private KeyWallet private key (hex string, the 0x prefix is optional). The key can be deployed locally on the docker, see Getting Started → Key Security
Rpc AddressNode address, by default https://ethereum-rpc.publicnode.com (a public Ethereum mainnet node). For other chains enter a node of that chain, for example BSC: https://bsc-dataseed.binance.org. http(s):// and ws(s):// are supported. Several nodes separated by commas back each other up
Rpc Api KeyNode authentication, may be left empty. Written as Name: value (such as x-api-key: xxx) it is sent as a request header with that name; otherwise it is sent as Authorization: Basic <value>

With several nodes, requests start from the node that last succeeded and move on only when a node is unavailable (connection failure, timeout, rate limiting); errors such as a failed contract execution are returned directly. Nodes whose chain ID differs from the first node's are skipped, so transactions are never sent to another chain.

At runtime, exchange.IO("base", nodeAddress) switches nodes (several nodes can be passed as an array or a comma-separated string), exchange.IO("key", privateKey) switches the wallet private key, and exchange.IO("address") returns the current wallet address.

Among the standard functions only exchange.GetAccount() and exchange.GetAssets() are available; they return the wallet's native coin balance (the currency is recognized from the chain ID, BNB on BSC for example).

2. Query balances and read contracts

Read-only contract methods (view/pure) cost no gas and return decoded results directly:

javascript
exchange.IO("api", "eth", "eth_getBalance", wallet, "latest") // native coin balance, on-chain integer (hex string) exchange.IO("api", tokenAddress, "balanceOf", wallet) // ERC20 balance, on-chain integer exchange.IO("api", tokenAddress, "decimals") // token decimals
  • exchange.IO("api", "eth", method, ...args) calls the node's JSON-RPC methods directly, such as eth_gasPrice, eth_blockNumber and eth_getTransactionReceipt.
  • exchange.IO("api", contractAddress, method, ...args) calls a contract method. The method can be a name, a full signature (such as "approve(address,uint256)", to tell overloads apart) or a selector (such as "0x095ea7b3").
  • On-chain amounts are integers. exchange.IO("fromUnits", onChainInteger, decimals) converts to a readable amount and exchange.IO("toUnits", "1.5", decimals) converts back; a token contract address can be passed in place of the decimals. Both compute exactly on strings.
  • Use exchange.IO("multicall", ...) to read many contracts in one request, and exchange.IO("logs", ...) to query event logs.

3. Register ABIs

Standard ERC20 methods (balanceOf, decimals, allowance, approve, transfer and others) are built in and need no registration. Before calling methods of other contracts, register the contract's ABI with exchange.IO("abi", contractAddress, abi).

Common contracts can use built-in templates by passing the template name as the third argument: "weth", "uniswapV3Pool", "uniswapV3Factory", "uniswapV3QuoterV2", "uniswapV3SwapRouter02", "uniswapV3PositionManager", "permit2" (PancakeSwap V3 uses the same templates, aliases such as "pancakeV3Pool" also work). Addresses of common contracts are available from exchange.IO("contracts").

javascript
exchange.IO("abi", poolAddress, "uniswapV3Pool") var slot0 = exchange.IO("api", poolAddress, "slot0")

The ABI of other contracts can be obtained from a block explorer, for example Etherscan's V2 API (an Etherscan API key is required, chainid is the chain ID, take the result field of the response):

url
https://api.etherscan.io/v2/api?chainid=1&module=contract&action=getabi&address=0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45&apikey=YourApiKey

4. Send transactions

When a write method of a contract is called, the exchange object signs the transaction with the configured private key, broadcasts it and returns the transaction hash. Before sending you can replace "api" with "call" and rehearse the same call with exchange.IO("call", ...): it is simulated on the node without signing or spending gas; on failure it returns an empty value and GetLastError() holds the reason given by the contract.

Taking approve as an example:

javascript
var amount = exchange.IO("toUnits", "100", tokenAddress) // 100 tokens as an on-chain integer var txHash = exchange.IO("api", tokenAddress, "approve", spender, amount)

When the method's stateMutability is payable, pass one extra argument before the method arguments: the amount of native coin to attach (on-chain integer). The last argument can be an options object:

OptionDescription
gasLimitGas limit. Estimated by the node (eth_estimateGas) when omitted. Do not use 21000 for contract calls; that is only enough for a plain transfer
gasPriceFixed gas price; when given, a legacy transaction is sent. When omitted, chains that support EIP-1559 get an EIP-1559 transaction: the tip is the larger of the node's suggestion and the tips actually paid in recent blocks, and the max fee is 2 × baseFee + tip
nonceA specific nonce. Allocated automatically when omitted and kept in sync with the on-chain pending count, so consecutive sends never reuse a nonce
dryRunWhen true, sign without broadcasting and return fields such as hash, raw (the signed transaction), nonce and gasLimit, for checking the transaction or sending it through another channel

Native coin is sent with exchange.IO("api", "eth", "send", toAddress, amount), where the amount is an on-chain integer (wei). Its options also accept data (hex call data) to send a transaction {to, data, value} returned by an aggregator API as is; gas is then estimated as for a contract call. Note: a plain transfer without gasPrice bids a fixed 100 Gwei with a gas limit of 21000, usually too high on Ethereum mainnet; query eth_gasPrice first and pass it in.

To send transactions through a private channel (such as Flashbots Protect or MEV Blocker) and avoid front-running, set a node used only for broadcasting with exchange.IO("sendBase", nodeAddress).

5. Wait for the transaction to be mined

exchange.IO("waitReceipt", txHash, {timeout, confirmations}) waits until the transaction is mined with the required confirmations and returns the receipt: status is 1 for success and 0 for failure (with the reason in revertReason), and events holds the events decoded with the registered ABIs. It returns an empty value on timeout.

6. Nonce management, speeding up and cancelling

  • exchange.IO("nonce") shows the on-chain and local nonce counts; exchange.IO("nonce", "sync") resyncs from the chain (use it after the same wallet has sent transactions elsewhere).
  • When a transaction stays unmined for a long time, exchange.IO("speedUp", txHash) resends it with the same nonce and a higher fee, and exchange.IO("cancelTx", txHash) replaces it with a zero-value transaction to yourself using the same nonce. Both only work before the original transaction is mined.
  • The local nonce record only exists inside the current live trading instance: instances that share one wallet cannot see each other's records and may still collide, so give each instance its own wallet.

Other commands

  • Encoding and decoding: exchange.IO("encode", ...) encodes contract call data or values by type (like Solidity's abi.encode), exchange.IO("encodePacked", ...) does packed encoding (for example a Uniswap V3 swap path), and exchange.IO("decode", ...) decodes by type.
  • Signing: exchange.IO("sign", ...) signs a 32-byte hash, exchange.IO("signTypedData", ...) signs EIP-712 typed data (such as ERC-20 Permit), and exchange.IO("signMessage", ...) signs a message with EIP-191.
  • Uniswap V3 math: exchange.IO("uniswapV3", ...) converts between ticks, prices and sqrtPrice, and between liquidity and token amounts.
  • Hashing: exchange.IO("hash", "keccak256", "raw", "hex", text) computes keccak256 and other digests, for example method selectors and EIP-712 digests; its parameters are the same as those of the Encode() function.
  • Complete examples: a swap through an aggregator (quote, build the transaction, rehearse it with exchange.IO("call", ...), send it with data) is in the examples of exchange.IO("call", ...) in the syntax manual; an ERC-20 Permit signature verified by the contract is in the examples of exchange.IO("sign", ...) and exchange.IO("signTypedData", ...).

Examples

Example: query balances, approve and wait for the transaction

Uses USDC on Ethereum mainnet. Note that this code sends a real transaction and spends gas.

javascript
function main() { var usdc = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" // USDC on Ethereum mainnet var spender = "0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45" // the contract to approve, Uniswap SwapRouter02 here var wallet = exchange.IO("address") // balances: standard ERC20 methods need no ABI registration var eth = exchange.IO("fromUnits", exchange.IO("api", "eth", "eth_getBalance", wallet, "latest"), 18) var usdcBalance = exchange.IO("fromUnits", exchange.IO("api", usdc, "balanceOf", wallet), usdc) Log("ETH:", eth, "USDC:", usdcBalance) // read a contract: current allowance var allowance = exchange.IO("api", usdc, "allowance", wallet, spender) Log("current allowance:", exchange.IO("fromUnits", allowance, usdc)) // approve 100 USDC: rehearse first, then send var amount = exchange.IO("toUnits", "100", usdc) if (!exchange.IO("call", usdc, "approve", spender, amount)) { Log("rehearsal failed:", GetLastError()) return } var txHash = exchange.IO("api", usdc, "approve", spender, amount) Log("tx hash:", txHash) // wait at most 3 minutes for the transaction to be mined var receipt = exchange.IO("waitReceipt", txHash, {timeout: 180000}) if (receipt && receipt.status == 1) { Log("approved in block:", receipt.blockNumber) } else if (receipt) { Log("transaction failed:", receipt.revertReason) } else { // not mined yet: speedUp resends with a higher fee, cancelTx cancels it Log("not mined before timeout:", GetLastError()) } }

See Also

With ChainType set to TRON, the Web3 exchange object connects to a TRON node. Usage is largely the same as on Ethereum (see Advanced Topics → Web3 → Ethereum (EVM)): the exchange.IO() commands for registering ABIs, calling contracts, encoding and decoding, signing and switching private keys are the same, addresses use the TRON format (starting with T), and TRX amounts are in sun (1 TRX = 1000000 sun). This page covers the configuration and the TRON-specific parts.

Configure the exchange object

FieldDescription
ChainTypeChoose TRON
Private KeyWallet private key (hex string). The key can be deployed locally on the docker, see Getting Started → Key Security
Rpc AddressHTTP address of a TRON full node, for example the official node https://api.trongrid.io (testnets: https://nile.trongrid.io, https://api.shasta.trongrid.io)
Rpc Api KeyTronGrid API key. Enter only the key itself; it is sent as the TRON-PRO-API-KEY request header. It works without a key, but TronGrid rate-limits keyless requests more strictly

The docker accesses TRON through the full node's HTTP API (/wallet/...) and no longer uses gRPC. The old gRPC address grpc.trongrid.io:50051 that the form fills in by default for TRON is replaced automatically with https://api.trongrid.io (grpc.nile.trongrid.io:50051 and grpc.shasta.trongrid.io:50051 likewise become the HTTP addresses of the corresponding testnets); any other gRPC address is rejected, so enter the node's HTTP address instead.

At runtime, exchange.IO("base", nodeAddress) switches nodes, exchange.IO("key", privateKey) switches wallets, and exchange.IO("address") returns the current wallet address (starting with T). The standard functions exchange.GetAccount() and exchange.GetAssets() return the wallet's TRX balance. An account that has not been activated yet (no on-chain record) reads as 0 TRX.

Calling smart contracts

As on Ethereum, use exchange.IO("api", contractAddress, method, ...args): read-only methods return results directly, write methods sign and broadcast a transaction and return the transaction ID. Standard TRC20 methods are built in; for other contracts without a registered ABI, the ABI is read from the chain automatically, and only when that fails do you need to register it with exchange.IO("abi", contractAddress, abi). The last argument of a write method can be {gasLimit: amount} to set the fee limit (feeLimit, in sun).

javascript
// USDT (TRC20) contract var usdt = "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t" Log(exchange.IO("api", usdt, "balanceOf", exchange.IO("address"))) // on-chain integer, USDT has 6 decimals

Encoding and decoding work as on Ethereum; address arguments can be written directly as T addresses:

javascript
exchange.IO("encode", "address", "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t") // 000000000000000000000000a614f803b6fd780986a42c78ec9c7f77e6ded13c exchange.IO("encodePacked", "address", "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t") // a614f803b6fd780986a42c78ec9c7f77e6ded13c exchange.IO("decode", "string", "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a5465746865722055534400000000000000000000000000000000000000000000") // Tether USD

Calling TRON node methods

exchange.IO("api", "tron", method, ...args) calls TRON node methods; method names are case-insensitive. Methods that need a signature (transfers, triggering contracts, etc.) are signed and broadcast automatically. Common methods:

MethodArgumentsDescription
sendto address, amount (sun)Send TRX from the current wallet
Transferfrom address, to address, amount (sun)Send TRX; the from address must be the current wallet
GetAccountaddressAccount information
GetAccountResourceaddressThe account's energy and bandwidth resources
GetContractABIcontract addressThe contract's on-chain ABI
GetAssetIssueByNamenameTRC10 asset information
GetNowBlocknoneCurrent block
GetBlockByNumblock heightA given block
GetTransactionByIDtransaction IDTransaction content
GetTransactionInfoByIDtransaction IDExecution result of a transaction (fees, energy used, logs, etc.)
GetChainParametersnoneChain parameters
TriggerConstantContractcaller address (may be empty), contract address, method, encoded argumentsRead-only contract call; the result is in constant_result (hex strings, decode them with exchange.IO("decode", ...))
TRC20ContractBalanceaddress, contract addressTRC20 balance (on-chain integer)
TRC20GetName, TRC20GetSymbol, TRC20GetDecimalscontract addressName, symbol and decimals of a TRC20 token
TRC20Send, TRC20Approvefrom address, to or spender address, contract address, amount, feeLimitTRC20 transfer and approval
TRC20Callcaller address (may be empty), contract address, call data, read-only flag, feeLimitCall a contract with raw call data
ParseTRC20NumericProperty, ParseTRC20StringPropertyhex dataParse numbers and strings returned by TRC20

Node endpoints not in the table can be called with a path and a request body: exchange.IO("api", "tron", "/wallet/endpointName", {body}).

Differences from Ethereum

The following commands only work on Ethereum (EVM) and report an error on TRON: call, multicall, logs, waitReceipt, nonce, speedUp, cancelTx and contracts; sendBase and multiple fallback nodes also only apply to Ethereum. On TRON, simulate a contract call with the node method TriggerConstantContract, and query a transaction's execution result with GetTransactionInfoByID.

toUnits, fromUnits, uniswapV3, the encoding and decoding commands and the signing commands work on TRON as well, and a TRC20 contract address can be passed as the decimals:

javascript
var usdt = "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t" var raw = exchange.IO("api", usdt, "balanceOf", exchange.IO("address")) Log(exchange.IO("fromUnits", raw, usdt)) // converted to a readable amount with the contract's decimals()

When the node rejects a contract call at validation time (for example because the contract does not exist), the error carries the node's reason, such as tron contract call rejected (CONTRACT_VALIDATE_ERROR): Smart contract is not exist.; a contract execution failure (revert) reports tron contract execution failed with the reason.

Signing

exchange.IO("hash", "sign", "hex", "hex", txHash) signs a 32-byte hash with the current private key and returns the 65-byte signature r‖s‖v (v is 0 or 1); other hash algorithms (such as "sha256") compute digests, the same as the Encode() function. When r, s and v (v being 27 or 28) are needed separately for contract verification, use exchange.IO("sign", ...), see the Web3 category of the syntax manual.

Examples

  • Examples

    Query TRX and USDT balances and read token information

    javascript
    function main() { var usdt = "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t" var wallet = exchange.IO("address") // TRX balance (standard function, in TRX) Log("account:", exchange.GetAccount()) // USDT balance: an on-chain integer, scale it by the decimals var raw = exchange.IO("api", "tron", "TRC20ContractBalance", wallet, usdt) var decimals = exchange.IO("api", "tron", "TRC20GetDecimals", usdt) Log("USDT:", raw / Math.pow(10, decimals)) // read-only call of name() (selector 0x06fdde03) with TRC20Call, then parse the returned string var ret = exchange.IO("api", "tron", "TRC20Call", "", usdt, "0x06fdde03", true, 0) // constant_result holds hex strings, parse them directly Log("name:", exchange.IO("api", "tron", "ParseTRC20StringProperty", ret.constant_result[0])) }
  • Read several contract methods at once with a Multicall contract

    javascript
    function main() { var usdt = "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t" var multicall = "TGXuuKAb4bnrn137u39EKbYzKNXvdCes98" var wallet = exchange.IO("address") var calls = [ [usdt, exchange.IO("encode", usdt, "name")], [usdt, exchange.IO("encode", usdt, "decimals")], [usdt, exchange.IO("encode", usdt, "balanceOf", wallet)] ] // register the aggregate method of the Multicall contract exchange.IO("abi", multicall, `[{"inputs":[{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"bytes","name":"callData","type":"bytes"}],"internalType":"struct TronMulticall.Call[]","name":"calls","type":"tuple[]"}],"name":"aggregate","outputs":[{"internalType":"uint256","name":"blockNumber","type":"uint256"},{"internalType":"bytes[]","name":"returnData","type":"bytes[]"}],"stateMutability":"view","type":"function"}]`) var ret = exchange.IO("api", multicall, "aggregate", calls) Log("name:", exchange.IO("decode", "string", ret.returnData[0])) Log("decimals:", exchange.IO("decode", "uint8", ret.returnData[1])) Log("balanceOf:", exchange.IO("decode", "uint256", ret.returnData[2])) }

See Also