资源加载中... loading...

Web3

exchange.IO("abi", …)

In the FMZ Quant Trading Platform, it mainly implements various functions, calls related to blockchain through exchange.IO() function. The following document describes the exchange.IO() function separately according to its function. The call method of the exchange.IO("abi", ...) function is used to register an ABI.

exchange.IO(k, address, abiContent)

The k parameter is used to set the function of the exchange.IO() function, set to "abi" means that the function is used to register ABI. k true string The address parameter is used to specify the address of the smart contract. address true string The abiContent parameter is used to specify the ABI of the smart contract. abiContent true string

function main() {
    // register Uniswap SwapRouter02 abi
    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"}]`
    
    // Get the ```ABI``` content of the contract can be obtained with the following URL, taking the ```result``` field only, e.g:
    exchange.IO("abi", routerAddress, abi)
}

The methods of calling smart contract do not need to be registered if they are standard ERC20 methods. Get the ABI content of the contract can be obtained with the following URL, taking the result field only, e.g:

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

exchange.IO("api", “eth”, …)

The calling method of exchange.IO("api", "eth", ...) function is used to call the Ethereum RPC method.

The exchange.IO("api", "eth", ...) function returns the return value of the called RPC method. string, number, bool, object, array, null and all other types supported by the system

exchange.IO(k, blockChain, rpcMethod) exchange.IO(k, blockChain, rpcMethod, …args)

The k parameter is used to set the function of the exchange.IO() function, set to "api" indicates that the function is used to extend the call request. k true string The blockChain parameter is used to set the function of the exchange.IO() function, set to "eth" indicates that the function is used for RPC method calls on the Ethereum Network. blockChain true string The rpcMethod parameter is used to set the RPC method to be called by the exchange.IO() function. rpcMethod true string The arg parameter is used to specify the parameters of the RPC method to be called. There may be more than one arg parameter. The type and number of arg parameters depend on the RPC method specified by the rpcMethod parameter. arg false string, number, bool, object, array, function, null, and all other types supported by the system

function main() {
    // "owner" needs to be replaced with the specific wallet address
    // Parameter labels for the "latest" string position: 'latest', 'earliest' or 'pending', please refrer to https://eth.wiki/json-rpc/API#the-default-block-parameter
    // The return value ethBalance is a hexadecimal string: 0x9b19ce56113070
    var ethBalance = exchange.IO("api", "eth", "eth_getBalance", "owner", "latest")              

    // ETH has a precision unit of 1e18
    var ethDecimal = 18              

    // Because of the JavaScript language precision, it is necessary to use the system underlying package function BigInt, BigDecimal to process
    // Convert ethBalance to readable amount, 0x9b19ce56113070 to 0.043656995388076145
    Log(Number((BigDecimal(BigInt(ethBalance))/BigDecimal(Math.pow(10, ethDecimal))).toString()))
}

Check the balance of ETH in your wallet:

function mian() {
    // ETH has a precision unit of 1e18
    var ethDecimal = 18  

    // Number of transfers, readable amount e.g. 0.01 ETH
    var sendAmount = 0.01  

    // Due to the JavaScript language precision, it is necessary to use the system underlying encapsulated functions BigInt, BigDecimal to process, and to convert the readable amount to the data processed on the chain
    var toAmount = (BigDecimal(sendAmount)*BigDecimal(Math.pow(10, ethDecimal))).toFixed(0)
    
    // "toAddress" is the address of the recipient's ETH wallet at the time of the transfer, which needs to be filled in specifically, and toAmount is the number of transfers
    exchange.IO("api", "eth", "send", "toAddress", toAmount)
}

For ETH transfers, you can set the {gasPrice: 11, gasLimit: 111, nonce: 111} parameter, which is set on the last parameter of the exchange.IO() function, according to your specific needs. You can omit the nonce and use the system default, or leave gasLimit/gasPrice/nonce unset and use the system default value for all.

function toAmount(s, decimals) {
    return Number((BigDecimal(BigInt(s))/BigDecimal(Math.pow(10, decimals))).toString())
}

function main() {
    var gasPrice = exchange.IO("api", "eth", "eth_gasPrice")
    Log("gasPrice:", toAmount(gasPrice, 0))   // 5000000000 , in wei (5 gwei)
}

Query gasPrice:

function toAmount(s, decimals) {
    // The toAmount function can convert hex-encoded values to decimal values
    return Number((BigDecimal(BigInt(s))/BigDecimal(Math.pow(10, decimals))).toString())
}                

function main() {
    // Coding approve (authorization) method calls
    var data = exchange.IO("encode", "0x111111111117dC0aa78b770fA6A738034120C302", "approve", "0xe592427a0aece92de3edee1f18e0157c05861564", "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")
    Log("data:", data)
    var gasPrice = exchange.IO("api", "eth", "eth_gasPrice")
    Log("gasPrice:", toAmount(gasPrice, 0))
    var obj = {
        "from" : "0x0xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",   // walletAddress
        "to"  : "0x111111111117dC0aa78b770fA6A738034120C302",
        "gasPrice" : gasPrice,
        "value" : "0x0",
        "data" : "0x" + data,
    }
    
    var gasLimit = exchange.IO("api", "eth", "eth_estimateGas", obj)
    Log("gasLimit:", toAmount(gasLimit, 0))
    Log("gas fee", toAmount(gasLimit, 0) * toAmount(gasPrice, 0) / 1e18)
}

Query eth_estimateGas:

The second parameter of the exchange.IO() function with "eth" can directly call the RPC methods available to the Ethereum node server.

{@fun BigDecimal}, {@fun BigInt}

exchange.IO("encode", …)

The exchange.IO("encode", ...) function is called for data encoding.

The exchange.IO("encode", ...) function returns the encoded data. string

exchange.IO(k, dataFormat, …args) exchange.IO(k, address, dataFormat) exchange.IO(k, address, dataFormat, …args)

The k parameter is used to set the function of the exchange.IO() function, set to "encode" means the function is used for data encoding. k true string The address parameter is used to set the address of the smart contract. When calling the exchange.IO("encode", ...) function, passing in the address parameter indicates encoding the method call on the smart contract. When calling the exchange.IO("encode", ...) function, if the address parameter is not passed, the function is used to encode the specified type order and is functionally equivalent to abi.encode in Solidity. address false string The dataFormat parameter is used to specify the method, type, and order of the encoded data. dataFormat true string The arg parameter is used to specify the specific data value that matches the dataFormat parameter. There may be more than one arg parameter, and the type and number of arg parameters depends on the dataFormat parameter setting. arg false string, number, tuple, array, and all other types supported by the system

function main() {
    // Main network address of ContractV3SwapRouterV2: 0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45
    // Calling the unwrapWETH9 method requires registering the ABI first, which is omitted here
    // "owner" represents the wallet address, which need to fill in the specific, 1 represents the number of unpacking, unpacking a WETH into ETH
    var data = exchange.IO("encode", "0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45", "unwrapWETH9(uint256,address)", 1, "owner")
    Log(data)
}

For example, calling the encoding method unwrapWETH9:

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

It’s equivalent to the encoding example of abi.encode in Solidity:

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

It supports encoding a tuple or a type order containing a tuple. This type order consists of tuple, bytes, so when calling exchange.IO() for encoding, you need to continue passing two parameters:

    1. Variables corresponding to the tuple type:
    
    {
        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 of the form: tuple(a uint256,b uint8,c address).

    1. Variables corresponding to type bytes:
    "0011"
    
function main() {
    var path = ["0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", "0xdac17f958d2ee523a2206206994597c13d831ec7"]   // ETH address, USDT address
    var ret = exchange.IO("encode", "address[]", path)
    Log("encode: ", ret)
}

It supports for sequential encoding of arrays or types containing arrays:

The exchange.IO() function encapsulates the encode method, which can return the function call code to hex string format. For specific use, you can refer to the platform’s publicly available “Uniswap V3 Trade” Template. When coding method calls on smart contracts, the corresponding ABI needs to be registered first.

exchange.IO("encodePacked", …)

The exchange.IO("encodePacked", ...) function is called in a way that is used for encodePacked encoding.

The exchange.IO("encodePacked", ...) function returns the encodePacked encoded data. string

exchange.IO(k, dataFormat, …args)

The k parameter is used to set the function of the exchange.IO() function, set to "encodePacked" means that the function is used for data encodePacked encoding. k true string The dataFormat parameter is used to specify the type and order of the encodePacked encoded data. dataFormat true string The arg parameter is used to specify the specific data value that matches the dataFormat parameter. There may be more than one arg parameter, and the type and number of arg parameters depends on the dataFormat parameter setting. arg true string, number, tuple, array, and all other types supported by the system

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

When using Uniswap V3, you need to pass in parameters like the exchange path, you need to use the encodePacked encoding operation:

exchange.IO("decode", …)

The exchange.IO("decode", ...) function is called in a way that is used for decoding.

The exchange.IO("decode", ...) function returns the decoded data. Returns a string when there is only one data specified by the dataFormat parameter. Returns an array when there is more than one data specified by the dataFormat parameter. array、string

exchange.IO(k, dataFormat, data)

The k parameter is used to set the function of the exchange.IO() function, and setting it to "decode" means that the function is used for data decoding. k true string The dataFormat parameter is used to specify the type and order of the decoded data. dataFormat true string The data parameter is used to set the data to be decoded. data true string

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)            

    var rawData = exchange.IO("decode", types, ret)
    Log("decode:", rawData)
}

Inverse operation of the exchange.IO("encode", ...) function:

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 following example first performs an encodePacked operation on the path parameter processing, because the exactOutput method call that needs to be encoded later requires path as a parameter. Then encode the exactOutput method of the routing contract, which has only one parameter of type tuple. The method name exactOutput is encoded as: 0x09b81346, and using the exchange.IO("decode", ...) method to decode the resulting decodeRaw, consistent with the variable dataTuple.

For data processing, the exchange.IO() function supports not only encoding, but also decoding.

exchange.IO("key", …)

The exchange.IO("key", ...) function is called in a way to switch private keys.

exchange.IO(k, key)

The parameter k is used to set the function of the exchange.IO() function, set to "key" means the function is used to switch the private key. k true string The key parameter is used to set the private key. key true string

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

The exchange.IO() function supports switching private keys and it can manipulate multiple wallet addresses. It is also possible to add multiple exchange objects (see: {@var/EXCHANGE/exchanges exchanges}) for manipulating multiple wallet addresses.

exchange.IO("api", …)

The exchange.IO("api", ...) function is called in a way that is used to call the methods of the smart contract.

The exchange.IO("api", ...) function returns the return value of the called smart contract method. string, number, bool, object, array, null and all other types supported by the system

exchange.IO(k, address, method) exchange.IO(k, address, method, …args) exchange.IO(k, address, method, value, …args)

The k parameter is used to set the function of the exchange.IO() function, set to "api" indicates that the function is used to extend the call request. k true string The address parameter is used to specify the address of the smart contract. address true string The method parameter is used to specify the method of the smart contract to be called. method true string The value parameter is used to set the amount of ETH to be sent. The stateMutability attribute of the smart contract method to be executed is payable, then the value parameter needs to be passed. The "stateMutability": "payable" attribute can be viewed from the ABI. The exchange.IO() function will determine the required parameter based on the stateMutability attribute in the ABI that has been registered. If the stateMutability attribute is nonpayable, then the value parameter does not need to be passed. value false number、string The arg parameter is used to specify the parameters of the method of the smart contract to be called. There may be more than one arg parameter, and the type and number of arg parameters depends on the method of the smart contract to be called. arg false string, number, bool, and all other types supported by the system

function main(){
    var tokenAddress = "0x111111111117dC0aa78b770fA6A738034120C302"    // The contract address of the token, the token is 1INCH in the example
    Log(exchange.IO("api", tokenAddress, "decimals"))                  // Query, print 1INCH tokens with precision index of 18
}

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

function main(){
    // The contract address of the token, in the example the token is 1INCH
    var tokenAddress = "0x111111111117dC0aa78b770fA6A738034120C302"                          

    // For example, the query yields 1000000000000000000, divided by the precision unit of the token 1e18, the wallet to which the current exchange object is bound has authorized 1 1INCH to the spender address
    Log(exchange.IO("api", tokenAddress, "allowance", "owner", "spender"))   
}

The allowance method is a constant method of ERC20 that does not generate gas consumption and can query the authorized amount of a token for a certain contract address. The allowance method takes 2 parameters, the first one is the wallet address and the second one is the authorized address. Return value: the authorization amount of the token.
owner: the address of the wallet, the example is replaced by the string “owner”, the actual use needs to fill in the specific address. 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.

function main(){
    // The contract address of the token, the token is 1INCH in the example
    var tokenAddress = "0x111111111117dC0aa78b770fA6A738034120C302"                 

    // 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", "0xde0b6b3a7640000"))  
}```
The ```approve``` method is a non-```constant``` method of ERC20, which generates gas consumption and is used to authorize the operation amount of a token to a contract address. The ```approve``` method takes 2 parameters, the first one is the address to be authorized and the second one is the amount to be authorized. Return value: txid.  
```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. ```0xde0b6b3a7640000```: the number of authorizations, here is the hexadecimal string, the corresponding decimal value is 1e18, divided by the token precision unit in the example (i.e. 1e18). The result is that 1 token is authorized. The third parameter of the ```exchange.IO()``` function is passed to the method name ```approve```, which can also be written in the form of methodId, such as "0x571ac8b0". It is also possible to write the full standard method name, for example: "approve(address,uint256)".
```javascript
function main() {
    var ContractV3SwapRouterV2 = "0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45"
    var tokenInName = "ETH"
    var amountIn = 0.01
    var options = {gasPrice: 5000000000, gasLimit: 21000, nonce: 100}   // This is an example, depending on the actual scene settings
    var data = ""                                                       // The encoded data, here is the empty string, depending on the actual scene settings
    var tx = exchange.IO("api", ContractV3SwapRouterV2, "multicall(uint256,bytes[])", (tokenInName == 'ETH' ? amountIn : 0), (new Date().getTime() / 1000) + 3600, data, options || {})
}

The multicall method is a non-constant method of Uniswap V3 that generates gas consumption and is used to redeem tokens in multiple ways. The multicall method may have various ways of passing parameters, you can check the ABI that contains the method specifically, you need to register the ABI before calling the method. Return value: txid.

For specific examples of multicall method calls, you can refer to the platform’s publicly available “Uniswap V3 Trade” Template

Some details are described here using pseudo-code:


exchange.IO("api", ContractV3SwapRouterV2, "multicall(uint256,bytes[])", value, deadline, data)

ContractV3SwapRouterV2: address of router v2 of Uniswap V3. value: the amount of ETH to transfer, set to 0 if the tokenIn token of the exchange operation is not ETH. deadline: deadline is the parameter of the multicall method, which can be set to (new Date().getTime() / 1000) + 3600, indicating that it is valid for one hour. data: data is the parameter of the multicall method, the data of the packing operation to be performed.

Similar to exchange.IO("api", "eth", "send", "toAddress", toAmount), the gasLimit/gasPrice/nonce setting of the method call can be specified when calling the multicall method. Again, we use pseudo-code to describe:


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

The parameter {gasPrice: 11, gasLimit: 111, nonce: 111} can be set according to specific needs, which is set to the last parameter of the exchange.IO() function. You can omit the nonce and use the system default value, or leave gasLimit/gasPrice/nonce unset and use the system default value for all.

exchange.IO("address")

The exchange.IO("address") function is called in such a way as to get the address of the wallet configured by the {@var/EXCHANGE exchange} exchange object.

The exchange.IO("address") function returns the configured wallet address. string

exchange.IO(k)

The k parameter is used to set the function of the exchange.IO() function, set to "address" means that the function is used to get the configured wallet address. k true string

function main() {
    Log(exchange.IO("address"))         // Print the wallet address of the private key configured on the exchange object
}

exchange.IO("base", …)

The exchange.IO("base", ...) function is called in a way to set the RPC node address.

exchange.IO(k, address)

The k parameter is used to set the function of the exchange.IO() function, set to "base" means the function is used to switch RPC nodes. k true string The address parameter is used to set the RPC node address. address true string

function main() {
    var chainRpc = "https://bsc-dataseed.binance.org"
    e.IO("base", chainRpc)    // Switching to BSC chain
}
Threads TA