在发明者量化交易平台主要通过exchange.IO()
函数实现区块链相关的各种功能、调用。 以下文档把exchange.IO()
函数根据其功能分别描述。 exchange.IO("abi", ...)
函数的调用方式用于注册ABI。
exchange.IO(k, address, abiContent)
k
true
string
```address```参数用于指定智能合约的地址。
address
true
string
```abiContent```参数用于指定智能合约的```ABI```。
abiContent
true
string
```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)
}
调用的智能合约方法如果是标准ERC20方法,则不需要注册。
获取合约的ABI
内容可以通过下面URL获取,只取result
字段,例如:
https://api.etherscan.io/api?module=contract&action=getabi&address=0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45
```exchange.IO("api", "eth", ...)```函数返回调用的RPC方法的返回值。
string、number、bool、object、array、空值等系统支持的所有类型
exchange.IO(k, blockChain, rpcMethod)
exchange.IO(k, blockChain, rpcMethod, ...args)
```k```参数用于设置```exchange.IO()```函数的功能,设置为```"api"```表示该函数用于扩展调用请求。
k
true
string
```blockChain```参数用于设置```exchange.IO()```函数的功能,设置为```"eth"```表示该函数用于以太坊网络的RPC方法调用。
blockChain
true
string
```rpcMethod```参数用于设置```exchange.IO()```函数调用的RPC方法。
rpcMethod
true
string
```arg```参数用于指定所要调用的RPC方法的参数。 ```arg```参数可能有多个,```arg```参数的类型与个数根据```rpcMethod```参数指定的RPC方法而定。
arg
false
string、number、bool、object、array、function、空值等系统支持的所有类型
```javascript
function 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的余额:
function 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)
}
ETH转账,可以根据具体需求设置{gasPrice: 11, gasLimit: 111, nonce: 111}
参数,该参数设置在exchange.IO()
函数的最后一个参数上。 可以省略其中的nonce
使用系统默认的值,或者不设置gasLimit/gasPrice/nonce
,全部使用系统默认值。
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)
}
查询gasPrice
:
function toAmount(s, decimals) {
// toAmount函数可以把hex编码的数值转换为十进制数值
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)
}
查询eth_estimateGas
:
{@fun BigDecimal}, {@fun BigInt}
### exchange.IO("encode", ...)
```exchange.IO("encode", ...)```函数的调用方式用于数据编码。
```exchange.IO("encode", ...)```函数返回编码后的数据。
string
exchange.IO(k, dataFormat, ...args)
exchange.IO(k, address, dataFormat)
exchange.IO(k, address, dataFormat, ...args)
```k```参数用于设置```exchange.IO()```函数的功能,设置为```"encode"```表示该函数用于数据编码。
k
true
string
```address```参数用于设置智能合约的地址。 在调用```exchange.IO("encode", ...)```函数时,如果传入```address```参数表示编码(encode)智能合约上的方法调用。 在调用```exchange.IO("encode", ...)```函数时,如果未传入```address```参数,则该函数用于编码指定的类型顺序,功能等同```Solidity```中的```abi.encode```。
address
false
string
```dataFormat```参数用于指定编码数据的方法、类型、顺序。
dataFormat
true
string
```arg```参数用于指定与```dataFormat```参数匹配的具体数据值。 ```arg```参数可能有多个,```arg```参数的类型与个数根据```dataFormat```参数设置而定。
arg
false
string、number、tuple、array等系统支持的所有类型
```javascript
function main() {
// ContractV3SwapRouterV2 主网地址 : 0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45
// 调用unwrapWETH9方法需要先注册ABI,此处省略注册
// "owner"代表钱包地址,需要具体填写,1代表解包装数量,把一个WETH解包装为ETH
var data = exchange.IO("encode", "0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45", "unwrapWETH9(uint256,address)", 1, "owner")
Log(data)
}
编码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 即 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 第三个数据
*/
}
等同Solidity
中abi.encode
的编码范例:
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)
}
支持对元组(tuple)或者包含元组的类型顺序编码,
这个类型顺序由tuple
、bytes
组成,所以在调用exchange.IO()
进行encode时需要继续传入两个参数:
{
a: 30,
b: 20,
c: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"
}
传入的参数也必须和tuple
的结构、类型保持一致,如同types
参数中定义的形式:tuple(a uint256,b uint8,c address)
。
- 2、对应bytes
类型的变量:
"0011"
function main() {
var path = ["0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", "0xdac17f958d2ee523a2206206994597c13d831ec7"] // ETH address, USDT address
var ret = exchange.IO("encode", "address[]", path)
Log("encode: ", ret)
}
支持对数组或者包含数组的类型顺序编码:
编码智能合约上的方法调用时,需要先注册对应的ABI。
### exchange.IO("encodePacked", ...)
```exchange.IO("encodePacked", ...)```函数的调用方式用于```encodePacked```编码。
```exchange.IO("encodePacked", ...)```函数返回```encodePacked```编码后的数据。
string
exchange.IO(k, dataFormat, ...args)
```k```参数用于设置```exchange.IO()```函数的功能,设置为```"encodePacked"```表示该函数用于数据```encodePacked```编码。
k
true
string
```dataFormat```参数用于指定```encodePacked```编码数据的类型、顺序。
dataFormat
true
string
```arg```参数用于指定与```dataFormat```参数匹配的具体数据值。 ```arg```参数可能有多个,```arg```参数的类型与个数根据```dataFormat```参数设置而定。
arg
true
string、number、tuple、array等系统支持的所有类型
```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)
}
使用Uniswap V3
时需要传入兑换路径之类的参数,就需要使用encodePacked
编码操作:
```exchange.IO("decode", ...)```函数返回解码后的数据。 当```dataFormat```参数指定的数据只有一个时返回一个字符串。 当```dataFormat```参数指定的数据有多个时返回一个数组。
array、string
exchange.IO(k, dataFormat, data)
```k```参数用于设置```exchange.IO()```函数的功能,设置为```"decode"```表示该函数用于数据解码。
k
true
string
```dataFormat```参数用于指定解码数据的类型、顺序。
dataFormat
true
string
```data```参数用于设置所要解码的数据。
data
true
string
```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)
var rawData = exchange.IO("decode", types, ret)
Log("decode:", rawData)
}
```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) // 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)
}
以下例子首先在path
参数处理时进行了encodePacked
操作,因为之后需要编码的exactOutput
方法调用需要path
作为参数。 然后encode
路由合约的exactOutput
方法,该方法只有一个参数,参数类型是tuple
类型。 exactOutput
这个方法名编码后即:0x09b81346
,使用exchange.IO("decode", ...)
方法解码得出decodeRaw
,与变量dataTuple
一致。
对于数据的处理,exchange.IO()
函数不仅支持编码(encode),也支持解码(decode)。
exchange.IO(k, key)
```k```参数用于设置```exchange.IO()```函数的功能,设置为```"key"```表示该函数用于切换私钥。
k
true
string
```key```参数用于设置私钥。
key
true
string
```javascript
function main() {
exchange.IO("key", "Private Key") // "Private Key"代表私钥字符串,需要具体填写
}
### exchange.IO("api", ...)
```exchange.IO("api", ...)```函数的调用方式用于调用智能合约的方法。
```exchange.IO("api", ...)```函数返回所调用的智能合约方法的返回值。
string、number、bool、object、array、空值等系统支持的所有类型
exchange.IO(k, address, method)
exchange.IO(k, address, method, ...args)
exchange.IO(k, address, method, value, ...args)
```k```参数用于设置```exchange.IO()```函数的功能,设置为```"api"```表示该函数用于扩展调用请求。
k
true
string
```address```参数用于指定智能合约的地址。
address
true
string
```method```参数用于指定所要调用的智能合约的方法。
method
true
string
```value```参数用于设置发送的ETH数量。 所要执行的智能合约方法的```stateMutability```属性是```payable```,则需要传```value```参数。 ```"stateMutability":"payable"```属性可以从ABI中查看,```exchange.IO()```函数会根据已经注册的ABI中的```stateMutability```属性判断所需要的参数, 如果```stateMutability```属性是```nonpayable```则不需要传```value```参数。
value
false
number、string
```arg```参数用于指定所要调用智能合约的方法的参数。 ```arg```参数可能有多个,```arg```参数的类型与个数根据所要调用的智能合约的方法而定。
arg
false
string、number、bool等系统支持的所有类型
```javascript
function main(){
var tokenAddress = "0x111111111117dC0aa78b770fA6A738034120C302" // 代币的合约地址,例子中的代币为1INCH
Log(exchange.IO("api", tokenAddress, "decimals")) // 查询,打印1INCH代币的精度指数为18
}
```javascript
function main(){
// 代币的合约地址,例子中的代币为1INCH
var tokenAddress = "0x111111111117dC0aa78b770fA6A738034120C302"
// 例如查询得出1000000000000000000,除以该token的精度单位1e18,得出当前交易所对象绑定的钱包给spender地址授权了1个1INCH数量
Log(exchange.IO("api", tokenAddress, "allowance", "owner", "spender"))
}
```owner```:钱包地址,例子中以字符串"owner"代替,实际使用需要具体填写地址。 ```spender```:被授权的合约地址,例子中以字符串"spender"代替,实际使用需要具体填写地址,例如可以是```Uniswap V3 router v1```地址。
```javascript
function main(){
// 代币的合约地址,例子中的代币为1INCH
var tokenAddress = "0x111111111117dC0aa78b770fA6A738034120C302"
// 授权量的十六进制字符串: 0xde0b6b3a7640000 , 对应的十进制字符串: 1e18 , 1e18除以该token的精度单位,即1个代币数量 , 所以这里指授权一个代币
Log(exchange.IO("api", tokenAddress, "approve", "spender", "0xde0b6b3a7640000"))
}
```spender```:被授权的合约地址,例子中以字符串"spender"代替,实际使用需要具体填写地址,例如可以是```Uniswap V3 router v1```地址。 ```0xde0b6b3a7640000```:授权数量,这里使用的是十六进制字符串表示,对应的十进制数值为1e18,除以例子中的token精度单位(即1e18), 得出授权了1个token。 ```exchange.IO()```函数的第三个参数传入方法名```approve```,也可以写methodId的形式,例如:"0x571ac8b0"。 也可以写完整标准方法名,例如:"approve(address,uint256)"。
```javascript
function 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 || {})
}
```multicall```方法可能有多种传参方式,具体可以查询包含该方法的ABI,调用该方法之前需要先注册ABI。返回值:txid。
具体的```multicall```方法调用例子,可以参考平台公开的[「Uniswap V3 交易类库」模板](https://www.fmz.com/strategy/397260)
这里使用伪代码来描述一些细节:
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```,全部使用系统默认值。
### exchange.IO("address")
```exchange.IO("address")```函数的调用方式用于获取{@var/EXCHANGE exchange}交易所对象配置的钱包的地址。
```exchange.IO("address")```函数返回配置的钱包地址。
string
exchange.IO(k)
```k```参数用于设置```exchange.IO()```函数的功能,设置为```"address"```表示该函数用于获取配置的钱包地址。
k
true
string
```javascript
function main() {
Log(exchange.IO("address")) // 打印exchange交易所对象上配置的私钥的钱包地址
}
exchange.IO(k, address)
```k```参数用于设置```exchange.IO()```函数的功能,设置为```"base"```表示该函数用于切换RPC节点。
k
true
string
```address```参数用于设置RPC节点地址。
address
true
string
```javascript
function main() {
var chainRpc = "https://bsc-dataseed.binance.org"
e.IO("base", chainRpc) // 切换到BSC链
}
Threads
TA