The resource loading... loading...

Web3

The FMZ Quant Trading Platform supports Web3 related functions and can easily access the cryptocurrency market defi exchanges.

Ethereum

On the FMZ Quant Trading Platform, write strategy code to implement the method call of smart contract on the Ethereum chain through the exchange.IO function. First, configure the access node on the FMZ Quant Trading Platform. The access nodes can be self-built nodes or use third-party services, such as infura.

Web3 Exchange Object Configuration

configure the access node on the FMZ Quant Trading Platform. The access nodes can be self-built nodes or use third-party services, such as infura. On the page of “Exchange” FMZ Quant Trading Platform, select the protocol: Cryptocurrency, and then selects the exchange Web3. Configure Rpc Address (service address of access node) and Private Key (private key). It supports localized deployment of private keys, see Key Security.

Register ABI

Calling a contract that is a standard ERC20 method does not require registration and it can be called directly. Calling methods other than the standard contract requires registering the ABI content: exchange.IO("abi", tokenAddress, abiContent). To get the ABI content of a contract, you can use the following URL to get it, taking the result field only.

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

Method of Calling Ethereum RPC

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

  • Query the balance of ETH in the 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 address of the wallet receiving ETH when transferring, toAmount is the quantity
    
  • Query gasPrice
    exchange.IO("api", "eth", "eth_gasPrice")
    
  • Query eth_estimateGas
    exchange.IO("api", "eth", "eth_estimateGas", data)
    

Support encode

The function exchange.IO encapsulates the encode method, which can return the function call encoding to hex string format. You can refer to the platform’s publicly available “Uniswap V3 Trading Class Library” template for specific use. The call of the encoding unwrapWETH9 method is used here as an example:

function main() {
    // Main network address of ContractV3SwapRouterV2: 0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45
    // To call the unwrapWETH9 method, you need to register the ABI first, omit the registration here.
    // "owner" represents the wallet address, it needs to fill in the specific, 1 represents the number of unwrapping, unwrap a WETH into 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 means the method call on the encoded (encode) smart contract. If it does not start with 0x, it is used to code the specified type order. The function is the same as the abi.encode in the solidity. Refer to the following example.

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 i.e. uint256 , the type length needs to be specified on FMZ
    Log("ret:", ret)
    /*
    000000000000000000000000000000000000000000000000000000000000000a    // x
    00000000000000000000000002a5fbb259d20a3ad2fdf9ccadef86f6c1c1ccc9    // address
    0000000000000000000000000000000000000000000000000000000000000080    // offset of str
    00000000000000000000000000000000000000000000000000000000000000c0    // offset of array
    000000000000000000000000000000000000000000000000000000000000000b    // the length of str
    48656c6c6f20576f726c64000000000000000000000000000000000000000000    // str data
    0000000000000000000000000000000000000000000000000000000000000003    // the length of the array
    0000000000000000000000000000000000000000000000000000000000000001    // array the first data
    0000000000000000000000000000000000000000000000000000000000000002    // array the second data
    0000000000000000000000000000000000000000000000000000000000000003    // array the third data
    */
}

Support the sequential encoding of tuples or types containing tuples:

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 order is composed of tuple and bytes, so two parameters need to be passed in when calling exchange.IO to encode:

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

Support for sequential encoding of arrays or types containing arrays:

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

Support for encodePacked

For example, when calling the DEX method of Uniswap V3, you need to pass in parameters, such as the exchange path, so you need to use the encodePackaged operation:

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)
}

Support for decode

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

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)   // abi only uses the contents of the local exactOutput method, the full abi can be searched on the Internet

    // 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)
}

The example performs the encodePacked operation first during the path parameter processing, because the exactOutput method call that needs to be encoded later needs the path as the parameter. Then, the encode method of the exactOutput routing contract has only one parameter, and the parameter type is tuple. The method exactOutput name is encoded as 0x09b81346, which is decoded the result decodeRaw by exchange.IO ("decode",...) method, and it is consistent with the variable dataTuple.

Support for Switching Private Keys

It supports switching private keys to operate multiple wallet addresses, for example:

function main() {
    exchange.IO("key", "Private Key")   // "Private Key" represents the private key string, which needs to be filled in specifically
}

Method of Calling the Smart Contract

The following content is an example of some smart contract method calls.

  • decimals The decimals method is a constant method of ERC20 that does not generate gas consumption, and it can query the precision data of a token. The decimals method has no parameters. Return value: the precision data of token.

    function main(){
        var tokenAddress = "0x111111111117dC0aa78b770fA6A738034120C302"    // The contract address of the token, in the example the token is 1INCH
        Log(exchange.IO("api", tokenAddress, "decimals"))                  // Query, print 1INCH tokens with precision index of 18
    }
    
  • allowance The allowance method is a constant method of ERC20 that does not generate gas consumption, and it can query the authorized amount of a certain token for a certain contract address. The allowance method need to pass in 2 parameters, the first one is the wallet address, and the second one is the authorized address. Return value: the authorization amount of token.

    function main(){
        // The contract address of the token, in the example the token is 1INCH
        var tokenAddress = "0x111111111117dC0aa78b770fA6A738034120C302"            
        var owner = ""
        var spender = ""
        
        // For example, the query yields 1000000000000000000, divided by the precision unit of the token 1e18, the current exchange object bound to the wallet to the spender address authorized 1 1INCH.
        Log(exchange.IO("api", tokenAddress, "allowance", owner, spender))
    }
    

    owner: The wallet address is replaced by the string “owner” in the example. In actual use, you need to fill in the address. spender: The authorized contract address is replaced by the string “spender” in the example. In actual use, you need to fill in the address specifically, for example, the address can be Uniswap V3 router v1.

  • approve The approve method is a non-constant method of ERC20 that generates gas consumption, which is used to authorize a token operation amount to a certain contract address. The approve method need to pass in 2 parameters, the first one is the address to be authorized and the second one is the authorized amount. Return value: txid.

    function main(){
        // The contract address of the token, in the example the token is 1INCH
        var tokenAddress = "0x111111111117dC0aa78b770fA6A738034120C302"
        var spender = ""
        var amount = "0xde0b6b3a7640000"
        
        // The hexadecimal string of the authorization amount: 0xde0b6b3a7640000 , the corresponding decimal string: 1e18 , 1e18 divided by the precision unit of the token, i.e. 1 token amount, so this refers to the authorization of one token.
        Log(exchange.IO("api", tokenAddress, "approve", spender, amount))
    }
    

    spender: The address of the authorized contract, the example is replaced by the string ‘‘spender’’, the actual use needs to fill in the specific address, for example, it can be Uniswap V3 router v1 address. amount: The number of authorizations, represented here using a hexadecimal string, corresponds to a decimal value of 1e18, divided by the token precision unit in the example (i.e., 1e18), yielding 1 token authorized.

    The third parameter of the exchange.IO function is passed the method name approve, which can also be written in the form of methodId, for example: “0x571ac8b0”. It is also possible to write the full standard method name, such as ‘‘approve(address,uint256)’’.

  • multicall The multicall method is a non-constant method of Uniswap V3, which will generate gas consumption and be used to exchange tokens in multiple ways. The multicall method may have multiple methods of passing in parameters. You can query the ABI containing the method for details. You need to register the ABI before calling the method. Return value: txid.

    For specific examples of multicall method calls, please refer to the public “Uniswap V3 Trading Class Library” template of our platform.

    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: The ABI of Uniswap V3’s router v2 contract, it needs to be filled in according to the actual situation. contractV3SwapRouterV2: router v2 address of Uniswap V3, the actual use requires a specific address to be filled in… value: The amount of ETH transferred, set it to 0 if the tokenIn token for the exchange operation is not ETH, it needs to be filled in according to the actual situation. deadline: It can be set to (new Date().getTime() / 1000) + 3600, which means it is valid for one hour. data: The data of the packing operation to be performed, it needs to be filled in according to the actual situation.

    It is also possible to specify the gasLimit/gasPrice/nonce setting for method calls:

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

    You can set parameter {gasPrice: 5000000000, gasLimit: 21000, nonce: 100} according to your specific needs, the parameter is set to the last parameter of the exchange.IO function. You can omit the nonce and use the system defaults, or do not set gasLimit/gasPrice/nonce and use all the system default values.

    It should be noted that in the example, the attribute of stateMutability in multicall(uint256,bytes[]) method is payable, and the value parameter needs to be passed in. The attribute of stateMutability":"payable" can be viewed from the ABI. The exchange.IO function will determine the required parameters according to the stateMutability attribute in the ABI that has been registered. If the stateMutability attribute is nonpayable, the parameter value does not need to be passed in.

Other Function Calls

  • Gets the address of the wallet configured by the exchange object
    function main() {
        Log(exchange.IO("address"))         // Print the wallet address of the private key configured on the exchange object
    }
    
  • Switch blockchain RPC nodes
    function main() {
        var chainRpc = "https://bsc-dataseed.binance.org"
        
        // Switch to BSC chain
        e.IO("base", chainRpc)
    }
    
JavaScript Strategy Writing Instructions Built-in Library