Type/to search
Welcome to FMZ Quant Trading Platform
Programming Languages
JavaScript
TypeScript
Python
C++
MyLanguage
PINE Language
Blockly Visual Programming
Workflow
Key Security
Live Trading
Strategy Library
Docker
Deploy Docker
One-Click Docker Rental
Manual Deployment of Bot
Docker Operation Precautions
Global IP Address Specification
Command Line Parameters for Bot Program
Live Trading Data Migration
Docker Monitor
Exchange
Strategy Editor
Backtesting System
Strategy Entry Functions
Strategy Framework and API Functions
Template Library
Strategy Parameters
Interactive Controls
Options Trading
C++ Strategy Writing Guide
JavaScript Strategy Writing Guide
Web3
Built-in Libraries
Extended API Interface
MCP Service
Trading Terminal
Data Explorer
Alpha Factor Analysis Tool
General Protocol
Debugging Tool
Remote Editing
Import and Export of Complete Strategies
Multi-language Support
Live Trading and Strategy Grouping
Live Trading Display
Strategy Sharing and Renting
Live Trading Message Push
Common Causes of Live Trading Errors and Abnormal Exits
Exchange-Specific Notes

FMZ Quant Trading Platform supports Web3 related features, enabling easy access to DeFi exchanges in the cryptocurrency market.

On the FMZ Quant Trading Platform, use the exchange.IO() function to write strategy code to implement Ethereum blockchain RPC method calls and smart contract interactions.

You need to configure access nodes on the FMZ Quant Trading Platform. Access nodes can be self-hosted nodes or third-party services, such as: infura. On the FMZ Quant Trading Platform's "Exchange" page, select protocol: Cryptocurrency, then select exchange as Web3.

Configure Rpc Address (service address of the access node) and Private Key (private key). Supports local deployment of private keys. For details, please refer to "Key Security".

When calling contracts, if using standard ERC20 methods, you can call directly without registration. Calling methods outside of standard contracts requires registering the ABI content first: exchange.IO("abi", tokenAddress, abiContent).

To obtain the contract's ABI content, you can use the following URL and extract only the result field.

url
https://api.etherscan.io/api?module=contract&action=getabi&address=0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45

Use the exchange.IO() function to call Ethereum RPC methods.

  • Query ETH balance in wallet
    exchange.IO("api", "eth", "eth_getBalance", owner, "latest") // owner is the specific wallet address
  • ETH transfer
    exchange.IO("api", "eth", "send", toAddress, toAmount) // toAddress is the wallet address receiving ETH, toAmount is the transfer amount
  • Query Gas price
    exchange.IO("api", "eth", "eth_gasPrice")
  • Query estimated Gas fee
    exchange.IO("api", "eth", "eth_estimateGas", data)

The exchange.IO() function encapsulates the encode method, which can encode function calls and return them as hex string format. For specific usage, please refer to the platform's public "Uniswap V3 Trading Library" template.

The following example shows how to encode the unwrapWETH9 method call:

javascript
function main() { // ContractV3SwapRouterV2 mainnet address: 0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45 // Calling the unwrapWETH9 method requires registering the ABI first, registration steps are omitted here // "owner" represents the wallet address, you need to fill in the actual address, 1 represents the unwrap amount, unwrapping one WETH to ETH var data = exchange.IO("encode", "0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45", "unwrapWETH9(uint256,address)", 1, "owner") Log(data) }

When calling the exchange.IO("encode", ...) function, if the second parameter (string type) starts with 0x, it indicates encoding a method call on a smart contract.
If it doesn't start with 0x, it is used to encode the specified type sequence, functionally equivalent to abi.encode in solidity. Please refer to the following example.

javascript
function main() { var x = 10 var address = "0x02a5fBb259d20A3Ad2Fdf9CCADeF86F6C1c1Ccc9" var str = "Hello World" var array = [1, 2, 3] var ret = exchange.IO("encode", "uint256,address,string,uint256[]", x, address, str, array) // uint means uint256, type length must be specified on FMZ Log("ret:", ret) /* 000000000000000000000000000000000000000000000000000000000000000a // x 00000000000000000000000002a5fbb259d20a3ad2fdf9ccadef86f6c1c1ccc9 // address 0000000000000000000000000000000000000000000000000000000000000080 // offset of str 00000000000000000000000000000000000000000000000000000000000000c0 // offset of array 000000000000000000000000000000000000000000000000000000000000000b // length of str 48656c6c6f20576f726c64000000000000000000000000000000000000000000 // str data 0000000000000000000000000000000000000000000000000000000000000003 // length of array 0000000000000000000000000000000000000000000000000000000000000001 // first element of array 0000000000000000000000000000000000000000000000000000000000000002 // second element of array 0000000000000000000000000000000000000000000000000000000000000003 // third element of array */ }

Supports encoding of tuples or type sequences containing tuples:

javascript
function main() { var types = "tuple(a uint256,b uint8,c address),bytes" var ret = exchange.IO("encode", types, { a: 30, b: 20, c: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2" }, "0011") Log("encode: ", ret) }

This type sequence consists of tuple and bytes, so when calling the exchange.IO() function for encode, two parameters need to be passed:

  • Variable corresponding to tuple type:
    json
    { a: 30, b: 20, c: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2" }
    The passed parameters must match the structure and types of the tuple, as defined in the types parameter: tuple(a uint256,b uint8,c address).
  • Variable corresponding to bytes type:
    string
    "0011"

Supports encoding of arrays or type sequences containing arrays:

javascript
function main() { var path = ["0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", "0xdac17f958d2ee523a2206206994597c13d831ec7"] // ETH address, USDT address var ret = exchange.IO("encode", "address[]", path) Log("encode: ", ret) }

For example, when calling methods on Uniswap V3 decentralized exchange, you need to pass parameters such as swap path, which requires using the encodePacked operation:

javascript
function main() { var fee = exchange.IO("encodePacked", "uint24", 3000) var tokenInAddress = "0x111111111117dC0aa78b770fA6A738034120C302" var tokenOutAddress = "0x6b175474e89094c44da98b954eedeac495271d0f" var path = tokenInAddress.slice(2).toLowerCase() path += fee + tokenOutAddress.slice(2).toLowerCase() Log("path:", path) }

Data processing supports not only encoding (encode) but also decoding (decode). Use the exchange.IO("decode", types, rawData) function for decoding operations.

javascript
function main() { // register SwapRouter02 abi var walletAddress = "0x398a93ca23CBdd2642a07445bCD2b8435e0a373f" var routerAddress = "0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45" var abi = `[{"inputs":[{"components":[{"internalType":"bytes","name":"path","type":"bytes"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"amountInMaximum","type":"uint256"}],"internalType":"struct IV3SwapRouter.ExactOutputParams","name":"params","type":"tuple"}],"name":"exactOutput","outputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"}],"stateMutability":"payable","type":"function"}]` exchange.IO("abi", routerAddress, abi) // The ABI here only contains the content of the exactOutput method, the complete ABI can be found online // encode path var fee = exchange.IO("encodePacked", "uint24", 3000) var tokenInAddress = "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2" var tokenOutAddress = "0xdac17f958d2ee523a2206206994597c13d831ec7" var path = tokenInAddress.slice(2).toLowerCase() path += fee + tokenOutAddress.slice(2).toLowerCase() Log("path:", path) var dataTuple = { "path" : path, "recipient" : walletAddress, "amountOut" : 1000, "amountInMaximum" : 1, } // encode SwapRouter02 exactOutput var rawData = exchange.IO("encode", routerAddress, "exactOutput", dataTuple) Log("method hash:", rawData.slice(0, 8)) // 09b81346 Log("params hash:", rawData.slice(8)) // decode exactOutput params var decodeRaw = exchange.IO("decode", "tuple(path bytes,recipient address,amountOut uint256,amountInMaximum uint256)", rawData.slice(8)) Log("decodeRaw:", decodeRaw) }

This example first performs an encodePacked operation when processing the path parameter, as the subsequent exactOutput method call that needs to be encoded requires path as a parameter. Then it encodes the exactOutput method of the router contract, which has only one parameter of tuple type.
The encoded exactOutput method name is: 0x09b81346. The decodeRaw obtained by decoding with the exchange.IO("decode", ...) method is consistent with the variable dataTuple.

Support private key switching to operate multiple wallet addresses, for example:

javascript
function main() { exchange.IO("key", "Private Key") // "Private Key" represents the private key string, you need to fill in the actual private key value }

The following are examples of smart contract method calls.

  • decimals
    decimals method is a constant method of ERC20 (no need to register ABI when calling standard ERC20 methods in FMZ quantitative strategy code), which does not consume gas and can query the precision data of a token.
    The decimals method has no parameters and returns the precision data of the token.

    javascript
    function main(){ var tokenAddress = "0x111111111117dC0aa78b770fA6A738034120C302" // Token contract address, the token in this example is 1INCH Log(exchange.IO("api", tokenAddress, "decimals")) // Query and print that the precision exponent of 1INCH token is 18 }
  • allowance
    allowance method is a constant method of ERC20, which does not consume gas and can query the authorization amount of a token for a certain contract address.
    The allowance method requires 2 parameters, the first parameter is the wallet address, and the second parameter is the authorized address. The return value is the authorization amount of the token.

    javascript
    function main(){ // Token contract address, the token in this example is 1INCH var tokenAddress = "0x111111111117dC0aa78b770fA6A738034120C302" var owner = "" var spender = "" // For example, if the query returns 1000000000000000000, divide by the token's precision unit 1e18 to get that the wallet bound to the current exchange object has authorized 1 1INCH to the spender address Log(exchange.IO("api", tokenAddress, "allowance", owner, spender)) }

    owner: Wallet address, needs to be filled with the specific address in actual use.
    spender: Authorized contract address, needs to be filled with the specific address in actual use, for example, it can be the Uniswap V3 router v1 address.

  • approve
    approve method is a non-constant method of ERC20, which consumes gas and is used to authorize a certain contract address with the operation amount of token.
    The approve method requires 2 parameters, the first parameter is the authorized address, and the second parameter is the authorization amount. The return value is txid.

    javascript
    function main(){ // Token contract address, the token in this example is 1INCH var tokenAddress = "0x111111111117dC0aa78b770fA6A738034120C302" var spender = "" var amount = "0xde0b6b3a7640000" // Hexadecimal string of authorization amount: 0xde0b6b3a7640000, corresponding decimal string: 1e18, 1e18 divided by the token's precision unit equals 1 token amount, so this authorizes one token Log(exchange.IO("api", tokenAddress, "approve", spender, amount)) }

    spender: Authorized contract address, needs to be filled with the specific address in actual use, for example, it can be the Uniswap V3 router v1 address.
    amount: Authorization amount, represented here as a hexadecimal string. The corresponding decimal value is 1e18, divided by the token precision unit in the example (i.e., 1e18), resulting in authorization of 1 token.

    The third parameter of the exchange.IO() function passes the method name approve, which can also be written in the form of methodId, for example: "0x571ac8b0". It can also be written as the complete standard method name, for example: "approve(address,uint256)".

  • multicall
    multicall method is a non-constant method of Uniswap V3, which consumes gas and is used for batch token swaps.
    The multicall method may have multiple parameter passing methods, you can check the ABI containing this method for details. The ABI needs to be registered before calling this method. The return value is txid.

    For specific multicall method call examples, you can refer to the platform's public "Uniswap V3 Trading Library" template

    javascript
    function main() { var ABI_Route = "" var contractV3SwapRouterV2 = "" var value = 0 var deadline = (new Date().getTime() / 1000) + 3600 var data = "" exchange.IO("abi", contractV3SwapRouterV2, ABI_Route) exchange.IO("api", contractV3SwapRouterV2, "multicall(uint256,bytes[])", value, deadline, data) }

    ABI_Route: ABI of Uniswap V3's router v2 contract, needs to be filled according to actual situation.
    contractV3SwapRouterV2: Uniswap V3's router v2 address, needs to be filled with the specific address in actual use.
    value: Amount of ETH to transfer, set to 0 if the tokenIn token for the swap operation is not ETH, needs to be filled according to actual situation.
    deadline: Can be set to (new Date().getTime() / 1000) + 3600, indicating validity within one hour.
    data: Packed operation data to be executed, needs to be filled according to actual situation.

    You can also specify gasLimit/gasPrice/nonce settings for method calls:

    javascript
    exchange.IO("api", contractV3SwapRouterV2, "multicall(uint256,bytes[])", value, deadline, data, {gasPrice: 5000000000, gasLimit: 21000})

    You can set {gasPrice: 5000000000, gasLimit: 21000, nonce: 100} parameters according to specific needs, this parameter is set as the last parameter of the exchange.IO() function.
    You can omit nonce to use the system default value, or not set gasLimit/gasPrice/nonce to use all system default values.

    Note that the stateMutability attribute of the multicall(uint256,bytes[]) method in the example is payable, which requires passing the value parameter.
    The stateMutability":"payable" attribute can be viewed from the ABI, the exchange.IO() function will determine the required parameters based on the stateMutability attribute in the registered ABI.
    If the stateMutability attribute is nonpayable, there is no need to pass the value parameter.

  • Get wallet address configured for exchange object
    javascript
    function main() { Log(exchange.IO("address")) // Print the wallet address corresponding to the private key configured for the exchange object }
  • Switch blockchain RPC node
    javascript
    function main() { var chainRpc = "https://bsc-dataseed.binance.org" // Switch to BSC chain, can also use SetBase function to switch e.IO("base", chainRpc) }

On the FMZ Quant Trading Platform, you can use the exchange.IO() function to call gRPC methods and smart contracts on the TRON blockchain, enabling you to write related strategy code.

Configuration method is similar to Ethereum exchange object configuration, ChainType needs to be selected as TRON. RPC Address defaults to: grpc.trongrid.io:50051, which is the official TRON node address.

TRC20 contract ABI is registered by default, with an underlying mechanism that automatically retrieves contract ABI based on contract address. Manual ABI registration is typically not required, only needed when certain contract ABIs cannot be automatically retrieved.

The method for registering ABI is the same as Ethereum, for example:

javascript
// USDT contract address: TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t let abi = `[{"constant":true,"inputs":[{"name":"who","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}]` // Register balanceOf method exchange.IO("abi", "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t", abi)

Use the exchange.IO() function to call TRON RPC methods. For methods requiring signatures, the underlying layer has automatically encapsulated the signing operations. The following lists commonly used methods. For other methods, please refer to the official TRON project documentation.

  • GetAccount
    javascript
    exchange.IO("api", "tron", "GetAccount", "TKCG...") // "TKCG..." is the TRON wallet address, this function returns the account information for the "TKCG..." address.
  • GetAccountResource
    javascript
    exchange.IO("api", "tron", "GetAccountResource", "TKCG...") // Get resources for the specified wallet address, including energy and bandwidth.
  • GetContractABI
    javascript
    exchange.IO("api", "tron", "GetContractABI", "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t") // Get USDT TRC20 contract ABI
  • GetAssetIssueByName
    javascript
    exchange.IO("api", "tron", "GetAssetIssueByName", "TRX") // Get token asset information by token name
  • GetNowBlock
    javascript
    exchange.IO("api", "tron", "GetNowBlock") // Get current block information
  • GetBlockByNum
    javascript
    exchange.IO("api", "tron", "GetBlockByNum", 70624300)
  • GetTransactionByID
    javascript
    exchange.IO("api", "tron", "GetTransactionByID", "05a8fae2cd1cbf36b61d12e219588d25b4826436f055f93388a96e620ec3f3f2") // Get Transaction by transaction hash
  • TRC20ContractBalance
    javascript
    exchange.IO("api", "tron", "TRC20ContractBalance", "TKCG...", "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t") // Get wallet USDT balance, note that the returned data is not precision-processed, for example, returned data: ```6890251``` means ```6.890251 USDT```.
  • TriggerConstantContract
    javascript
    function main() { let ret = exchange.IO("api", "tron", "TriggerConstantContract", "", "TSUUVjysXV8YqHytSNjfkNXnnB49QDvZpx", "token0()", "") // Call the token0() method of the smart contract, TriggerConstantContract is used to call read-only methods let data = exchange.IO("decode", "address", Encode("raw", "raw", "hex", ret["constant_result"][0])) // Decode data return data // data: 0x891cdb91d149f23b1a45d9c5ca78a88d0cb44c18 }
  • TRC20Call
    javascript
    function main() { let ret = exchange.IO("api", "tron", "TRC20Call", "", "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t", "0x06fdde03", true, 0) // Use TRC20Call to call read-only method 0x06fdde03 let data = Encode("raw", "raw", "hex", ret.constant_result[0]) return exchange.IO("api", "tron", "ParseTRC20StringProperty", data) // Tether USD }
  • Transfer
    javascript
    exchange.IO("api", "tron", "Transfer", "TWTbn...", "TKCG...", 1000000) // Use Transfer method to transfer TRX, from "TWTbn..." to "TKCG...", 1000000 equals 1 TRX.

When the exchange object is set to Web3 and TRON is selected, encoding/decoding operations remain consistent with Ethereum's Web3 exchange object.

  • encode:
    javascript
    let ret = exchange.IO("encode", "address", "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t") Log(ret) // ret: 000000000000000000000000a614f803b6fd780986a42c78ec9c7f77e6ded13c , encoding the TRON address of USDT token.
  • decode:
    javascript
    let data = "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a5465746865722055534400000000000000000000000000000000000000000000" let ret = exchange.IO("decode", "string", data) Log(ret) // ret: Tether USD , similar to the functionality of ParseTRC20StringProperty
  • encodePacked:
    javascript
    let ret = exchange.IO("encodePacked", "address", "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t") Log(ret) // ret: a614f803b6fd780986a42c78ec9c7f77e6ded13c

The switching method is consistent with Web3 Ethereum exchange objects.

Calling smart contract methods on TRON is basically the same as on Ethereum. Here is a specific example demonstrating how to:

  • Call smart contract methods, calling multiple contract methods in a single request:

    javascript
    function main() { let usdtAddress = "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t" // token usdt contract address let data1 = exchange.IO("encode", usdtAddress, "name") // call function: name let data2 = exchange.IO("encode", usdtAddress, "decimals") // call function: decimals let data3 = exchange.IO("encode", usdtAddress, "balanceOf", "TKCG...") // call function: balanceOf var data = [] data.push([usdtAddress, data1]) data.push([usdtAddress, data2]) data.push([usdtAddress, data3]) exchange.IO("abi", "TGXuuKAb4bnrn137u39EKbYzKNXvdCes98", `[{"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"}]`) let ret = exchange.IO("api", "TGXuuKAb4bnrn137u39EKbYzKNXvdCes98", "aggregate", data) 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])) }

    Output:

    log
    Info balanceOf: 6890251 Info decimals: 6 Info name: Tether USD

  • Get wallet address configured for exchange object
    Same usage as Ethereum.

  • Switch blockchain RPC node
    Same usage as Ethereum.

  • Calculate hash value

    javascript
    let algo = "sign" // algo: algorithm/method to use let inputFormat = "string" // inputFormat: input format let outputFormat = "hex" // outputFormat: output format let data = "txHash" // txHash: specific hash value let signature = exchange.IO("hash", algo, inputFormat, outputFormat, data) // returns signature data

    When the algo parameter is set to "sign", it is used for calculating signatures; when set to other algorithm parameters (e.g., "sha256"), the function is equivalent to the Encode() function.