Web3
exchange.IO("abi", ...)
在发明者量化交易平台中,主要通过 exchange.IO() 函数实现区块链相关的各种功能调用。以下文档将根据 exchange.IO() 函数的不同功能分别进行描述。exchange.IO("abi", ...) 函数调用方式用于注册 ABI。
-
支持以太坊(eth)
-
支持波场(tron)
exchange.IO(k, address, abiContent)示例
javascript
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"}]`
// abi 仅使用了局部的 exactOutput 方法内容,完整的 abi 可以在网上搜索
exchange.IO("abi", routerAddress, abi)
}参数
| 名称 | 类型 | 必填 | 描述 |
k | string | 是 |
|
address | string | 是 |
|
abiContent | string | 是 |
|
备注
如果调用的智能合约方法是标准 ERC20 方法,则无需注册。
获取合约的 ABI 内容可以通过以下 URL 获取,只需取 result 字段,例如:
url
https://api.etherscan.io/api?module=contract&action=getabi&address=0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45
exchange.IO("api", blockChain, ...)
exchange.IO("api", "eth", ...)函数的调用方式,用于调用以太坊RPC方法(需要在Web3交易所对象配置时选择eth)。
exchange.IO("api", "tron", ...)函数的调用方式,用于调用波场RPC方法(需要在Web3交易所对象配置时选择tron)。
exchange.IO(k, blockChain, rpcMethod)
exchange.IO(k, blockChain, rpcMethod, ...args)示例
-
查询钱包中ETH的余额:
javascriptfunction main() { // "owner" 需要替换为具体钱包地址 // "latest"字符串位置的参数标签:'latest'、'earliest'或'pending',参考https://eth.wiki/json-rpc/API#the-default-block-parameter // 返回值 ethBalance 为十六进制字符串:0x9b19ce56113070 var ethBalance = exchange.IO("api", "eth", "eth_getBalance", "owner", "latest") // ETH的精度单位为1e18 var ethDecimal = 18 // 由于JavaScript语言精度原因,需要使用系统底层封装的函数BigInt、BigDecimal处理 // 将ethBalance转换为可读数量,0x9b19ce56113070转换为0.043656995388076145 Log(Number((BigDecimal(BigInt(ethBalance))/BigDecimal(Math.pow(10, ethDecimal))).toString())) } -
ETH转账,可以根据具体需求设置
{gasPrice: 11, gasLimit: 111, nonce: 111}参数,该参数设置在exchange.IO()函数的最后一个参数上。可以省略其中的nonce使用系统默认值,或者不设置gasLimit/gasPrice/nonce,全部使用系统默认值。javascriptfunction mian() { // ETH的精度单位为1e18 var ethDecimal = 18 // 转账数量,可读的数量例如:0.01个ETH var sendAmount = 0.01 // 由于JavaScript语言精度原因,需要使用系统底层封装的函数BigInt、BigDecimal处理,并且将可读数量转换为链上处理的数据 var toAmount = (BigDecimal(sendAmount)*BigDecimal(Math.pow(10, ethDecimal))).toFixed(0) // "toAddress"为转账时接收方的ETH钱包地址,需要具体填写,toAmount为转账数量 exchange.IO("api", "eth", "send", "toAddress", toAmount) } -
查询
gasPrice:javascriptfunction 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) } -
查询
eth_estimateGas:javascriptfunction toAmount(s, decimals) { // toAmount函数可以将十六进制编码的数值转换为十进制数值 return Number((BigDecimal(BigInt(s))/BigDecimal(Math.pow(10, decimals))).toString()) } function main() { // 编码approve(授权)方法的调用 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) }
返回值
| 类型 | 描述 |
string / number / bool / object / array / any (平台支持的任意类型) |
|
参数
| 名称 | 类型 | 必填 | 描述 |
k | string | 是 |
|
blockChain | string | 是 |
|
rpcMethod | string | 是 |
|
arg | string / number / bool / object / array / function / any (平台支持的任意类型) | 否 |
|
参考
备注
exchange.IO()函数的第二个参数为"eth"时,可以直接调用以太坊节点服务器可用的RPC方法。
exchange.IO("encode", ...)
exchange.IO("encode", ...)函数用于数据编码操作。
exchange.IO(k, dataFormat, ...args)
exchange.IO(k, address, dataFormat)
exchange.IO(k, address, dataFormat, ...args)示例
-
以编码
unwrapWETH9方法调用为例:javascriptfunction main() { // ContractV3SwapRouterV2 主网地址 : 0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45 // 调用unwrapWETH9方法需要先注册ABI,此处省略注册 // "owner"代表钱包地址,需要具体填写,1代表解包装数量,把一个WETH解包装为ETH var data = exchange.IO("encode", "0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45", "unwrapWETH9(uint256,address)", 1, "owner") Log(data) } -
等同于
Solidity中abi.encode的编码示例:javascriptfunction 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 即 uint256 , FMZ上需要指定类型长度 Log("ret:", ret) /* 000000000000000000000000000000000000000000000000000000000000000a // x 00000000000000000000000002a5fbb259d20a3ad2fdf9ccadef86f6c1c1ccc9 // address 0000000000000000000000000000000000000000000000000000000000000080 // str 的偏移 00000000000000000000000000000000000000000000000000000000000000c0 // array 的偏移 000000000000000000000000000000000000000000000000000000000000000b // str 的长度 48656c6c6f20576f726c64000000000000000000000000000000000000000000 // str 数据 0000000000000000000000000000000000000000000000000000000000000003 // array 的长度 0000000000000000000000000000000000000000000000000000000000000001 // array 第一个数据 0000000000000000000000000000000000000000000000000000000000000002 // array 第二个数据 0000000000000000000000000000000000000000000000000000000000000003 // array 第三个数据 */ } -
支持对元组(tuple)或包含元组的类型顺序进行编码。此类型顺序由
tuple、bytes组成,因此在调用exchange.IO()进行encode时需要传入两个对应参数:- 1、对应tuple类型的变量:
传入的参数必须与{ a: 30, b: 20, c: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2" }tuple的结构、类型保持一致,如同types参数中定义的形式:tuple(a uint256,b uint8,c address)。 - 2、对应
bytes类型的变量:"0011"
javascriptfunction 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) } - 1、对应tuple类型的变量:
-
支持对数组或包含数组的类型顺序进行编码:
javascriptfunction main() { var path = ["0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", "0xdac17f958d2ee523a2206206994597c13d831ec7"] // ETH address, USDT address var ret = exchange.IO("encode", "address[]", path) Log("encode: ", ret) }
返回值
| 类型 | 描述 |
string |
|
参数
| 名称 | 类型 | 必填 | 描述 |
k | string | 是 |
|
address | string | 否 |
|
dataFormat | string | 是 |
|
arg | string / number / tuple / array / any (平台支持的任意类型) | 否 |
|
备注
exchange.IO()函数封装了encode方法,可以将函数调用编码并返回为hex字符串格式。具体使用方法可参考平台公开的「Uniswap V3 交易类库」模板。
对智能合约方法调用进行编码时,需要先注册相应的ABI。
exchange.IO("encodePacked", ...)
exchange.IO("encodePacked", ...)函数用于执行encodePacked编码操作。
exchange.IO(k, dataFormat, ...args)示例
在使用Uniswap V3时需要传入交易路径等参数,此时需要使用encodePacked编码操作:
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)
}返回值
| 类型 | 描述 |
string |
|
参数
| 名称 | 类型 | 必填 | 描述 |
k | string | 是 |
|
dataFormat | string | 是 |
|
arg | string / number / tuple / array / any (平台支持的任意类型) | 是 |
|
exchange.IO("decode", ...)
exchange.IO("decode", ...)函数用于数据解码操作。
exchange.IO(k, dataFormat, data)示例
-
exchange.IO("encode", ...)函数的逆向操作示例:javascriptfunction 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) } -
以下示例首先对
path参数执行encodePacked操作,因为后续需要编码的exactOutput方法调用需要path作为参数。然后对路由合约的exactOutput方法进行编码,该方法只有一个参数,参数类型为tuple类型。exactOutput方法名编码后的结果为:0x09b81346,使用exchange.IO("decode", ...)方法解码得到decodeRaw,其结果与变量dataTuple保持一致。javascriptfunction 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只使用了局部的exactOutput方法的内容,完整的abi可以在网上搜索 // 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) }
返回值
| 类型 | 描述 |
array / string |
|
参数
| 名称 | 类型 | 必填 | 描述 |
k | string | 是 |
|
dataFormat | string | 是 |
|
data | string | 是 |
|
备注
exchange.IO()函数支持数据的双向处理,既可以进行编码(encode)操作,也可以进行解码(decode)操作。
exchange.IO("key", ...)
exchange.IO("key", ...)函数用于切换私钥的调用方式。
exchange.IO(k, key)示例
javascript
function main() {
exchange.IO("key", "Private Key") // "Private Key"代表私钥字符串,需要具体填写
}参数
| 名称 | 类型 | 必填 | 描述 |
k | string | 是 |
|
key | string | 是 |
|
备注
exchange.IO()函数支持切换私钥功能,可以操作多个钱包地址。也可以添加多个交易所对象(参考:exchanges)来操作多个钱包地址。
对于切换私钥的操作:exchange.IO("key", "xxx"),不能使用并发方式进行切换。
exchange.IO("api", ...)
exchange.IO("api", ...)函数用于调用智能合约的方法。
exchange.IO(k, address, method)
exchange.IO(k, address, method, ...args)
exchange.IO(k, address, method, value, ...args)示例
-
decimals方法是ERC20的一个constant方法,不会产生gas消耗,可以查询某个token的精度数据。decimals方法无需参数。返回值:token的精度数据。javascriptfunction main(){ var tokenAddress = "0x111111111117dC0aa78b770fA6A738034120C302" // 代币的合约地址,例子中的代币为1INCH Log(exchange.IO("api", tokenAddress, "decimals")) // 查询,打印1INCH代币的精度指数为18 } -
allowance方法是ERC20的一个constant方法,不会产生gas消耗,可以查询某个token对于某个合约地址的授权额度。allowance方法需要传入2个参数,第一个参数为钱包地址,第二个参数为被授权的地址。返回值:token的授权额度。owner:钱包地址,例子中以字符串"owner"代替,实际使用需要具体填写地址。spender:被授权的合约地址,例子中以字符串"spender"代替,实际使用需要具体填写地址,例如可以是Uniswap V3 router v1地址。javascriptfunction main(){ // 代币的合约地址,例子中的代币为1INCH var tokenAddress = "0x111111111117dC0aa78b770fA6A738034120C302" // 例如查询得出1000000000000000000,除以该token的精度单位1e18,得出当前交易所对象绑定的钱包给spender地址授权了1个1INCH数量 Log(exchange.IO("api", tokenAddress, "allowance", "owner", "spender")) } -
approve方法是ERC20的一个非constant方法,会产生gas消耗,用于给某个合约地址授权token的操作额度。approve方法需要传入2个参数,第一个参数为被授权的地址,第二个参数为授权的额度。返回值:txid。spender:被授权的合约地址,例子中以字符串"spender"代替,实际使用需要具体填写地址,例如可以是Uniswap V3 router v1地址。0xde0b6b3a7640000:授权数量,这里使用十六进制字符串表示,对应的十进制数值为1e18,除以例子中的token精度单位(即1e18),得出授权了1个token。exchange.IO()函数的第三个参数传入方法名approve,也可以写methodId的形式,例如:"0x571ac8b0"。也可以写完整标准方法名,例如:"approve(address,uint256)"。javascriptfunction main(){ // 代币的合约地址,例子中的代币为1INCH var tokenAddress = "0x111111111117dC0aa78b770fA6A738034120C302" // 授权量的十六进制字符串: 0xde0b6b3a7640000 , 对应的十进制字符串: 1e18 , 1e18除以该token的精度单位,即1个代币数量 , 所以这里指授权一个代币 Log(exchange.IO("api", tokenAddress, "approve", "spender", "0xde0b6b3a7640000")) } -
multicall方法是Uniswap V3的一个非constant方法,会产生gas消耗,用于多路兑换代币。multicall方法可能有多种传参方式,具体可以查询包含该方法的ABI,调用该方法之前需要先注册ABI。返回值:txid。具体的
multicall方法调用示例,可以参考平台公开的「Uniswap V3 交易类库」模板这里使用伪代码来描述一些细节:
exchange.IO("api", ContractV3SwapRouterV2, "multicall(uint256,bytes[])", value, deadline, data)ContractV3SwapRouterV2:Uniswap V3的router v2地址。value:转账的ETH数量,如果兑换操作的tokenIn代币不是ETH,则设置为0。deadline:deadline是multicall方法的参数,可以设置为(new Date().getTime() / 1000) + 3600,表示一小时内有效。data:data是multicall方法的参数,需要执行的打包操作数据。与exchange.IO("api", "eth", "send", "toAddress", toAmount)类似,在调用multicall方法时也可以指定方法调用的gasLimit/gasPrice/nonce设置,同样使用伪代码来描述:exchange.IO("api", ContractV3SwapRouterV2, "multicall(uint256,bytes[])", value, deadline, data, {gasPrice: 123456, gasLimit: 21000})可以根据具体需求设置
{gasPrice: 11, gasLimit: 111, nonce: 111}参数,该参数设置在exchange.IO()函数的最后一个参数上。可以省略其中的
nonce使用系统默认值,或者不设置gasLimit/gasPrice/nonce,全部使用系统默认值。javascriptfunction main() { var ContractV3SwapRouterV2 = "0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45" var tokenInName = "ETH" var amountIn = 0.01 var options = {gasPrice: 5000000000, gasLimit: 21000, nonce: 100} // 此处为举例,具体要根据实际场景设置 var data = "" // 编码后的数据,此处为空字符串,具体要根据实际场景设置 var tx = exchange.IO("api", ContractV3SwapRouterV2, "multicall(uint256,bytes[])", (tokenInName == 'ETH' ? amountIn : 0), (new Date().getTime() / 1000) + 3600, data, options || {}) }
返回值
| 类型 | 描述 |
string / number / bool / object / array / any (平台支持的任意类型) |
|
参数
| 名称 | 类型 | 必填 | 描述 |
k | string | 是 |
|
address | string | 是 |
|
method | string | 是 |
|
value | number / string | 否 |
|
arg | string / number / bool / any (平台支持的任意类型) | 否 |
|
exchange.IO("address")
exchange.IO("address")函数用于获取exchange交易所对象所配置钱包的地址。
exchange.IO(k)示例
javascript
function main() {
Log(exchange.IO("address")) // 打印exchange交易所对象上配置的私钥对应的钱包地址
}返回值
| 类型 | 描述 |
string |
|
参数
| 名称 | 类型 | 必填 | 描述 |
k | string | 是 |
|
exchange.IO("base", ...)
exchange.IO("base", ...) 函数用于设置 RPC 节点地址。
exchange.IO(k, address)示例
javascript
function main() {
var chainRpc = "https://bsc-dataseed.binance.org"
exchange.IO("base", chainRpc) // 切换到 BSC 链
}参数
| 名称 | 类型 | 必填 | 描述 |
k | string | 是 |
|
address | string | 是 |
|