输入/搜索内容
入门
欢迎使用发明者量化交易平台
快速开始
密钥安全性
平台基础
账号与计费
实盘计费与充值
子账号
交易所
通用协议
本地凭据文件
交易所特殊说明
证券与期货
加密货币
托管者
策略库
实盘
编写策略
开发工具
回测系统
进阶专题
数据与研究
对外接口

Web3交易所对象选择ChainType为ETH时,可以连接以太坊以及所有EVM兼容链(BSC、Base、Arbitrum、Optimism、Polygon等)的节点,用exchange.IO()的各个指令查询余额、调用合约、发送交易。本页按一笔链上操作的流程介绍常用指令,每个指令的完整参数见语法手册Web3分类中对应的exchange.IO("指令", ...)。

只是想在Uniswap、PancakeSwap上兑换代币时,请使用Uniswap交易所对象:它直接支持exchange.GetTicker()、exchange.CreateOrder()等标准函数,不需要自己编码合约调用。

1. 配置交易所对象

在「交易所」页面(/m/add-platform)添加交易所,协议选择「加密货币」,交易所选择Web3:

字段说明
ChainTypeETH:以太坊及所有EVM兼容链;TRON:波场,见波场(TRON)
Private Key钱包私钥(十六进制字符串,可以带0x前缀)。支持把私钥本地化部署在托管者上,参看密钥安全性
Rpc Address节点地址,默认https://ethereum-rpc.publicnode.com(以太坊主网公共节点)。连接其它链时填写该链的节点,例如BSC:https://bsc-dataseed.binance.org。支持http(s)://和ws(s)://。可以写多个节点,用逗号分隔,互为备用
Rpc Api Key节点鉴权,可以留空。写成名称: 值(如x-api-key: xxx)时作为该名称的请求头发送;否则作为Authorization: Basic <值>发送

多个节点时,从上次成功的节点开始依次尝试:只有节点不可用(连接失败、超时、限流)时才换下一个,合约执行失败之类的错误直接返回;链ID与第一个节点不同的节点会被跳过,避免把交易发到另一条链。

运行中可以用exchange.IO("base", 节点地址)切换节点(多个节点可以传数组或逗号分隔的字符串),用exchange.IO("key", 私钥)切换钱包私钥,用exchange.IO("address")获取当前钱包地址。

标准函数中只有exchange.GetAccount()、exchange.GetAssets()可用,返回钱包的原生币余额(币种按链ID识别,如BSC为BNB)。

2. 查询余额与读取合约

调用合约的只读方法(view/pure)不消耗gas,直接返回解码后的结果:

javascript
exchange.IO("api", "eth", "eth_getBalance", wallet, "latest") // 原生币余额,链上整数(十六进制字符串) exchange.IO("api", tokenAddress, "balanceOf", wallet) // ERC20余额,链上整数 exchange.IO("api", tokenAddress, "decimals") // 代币精度
  • exchange.IO("api", "eth", 方法, ...参数)直接调用节点的JSON-RPC方法,如eth_gasPrice、eth_blockNumber、eth_getTransactionReceipt。
  • exchange.IO("api", 合约地址, 方法, ...参数)调用合约方法。方法可以写方法名、完整签名(如"approve(address,uint256)",用于区分重载)或方法选择器(如"0x095ea7b3")。
  • 链上数量都是整数。用exchange.IO("fromUnits", 链上整数, 精度)换算成可读数量,用exchange.IO("toUnits", "1.5", 精度)换算回链上整数;精度也可以直接传代币合约地址。两者都按字符串精确计算。
  • 批量读取多个合约时用exchange.IO("multicall", ...)一次请求完成;查询事件日志用exchange.IO("logs", ...)。

3. 注册ABI

标准ERC20方法(balanceOf、decimals、allowance、approve、transfer等)已内置,不需要注册。调用其它合约的方法前,需要用exchange.IO("abi", 合约地址, ABI)注册该合约的ABI。

常用合约可以直接使用内置模板,第三个参数传模板名:"weth"、"uniswapV3Pool"、"uniswapV3Factory"、"uniswapV3QuoterV2"、"uniswapV3SwapRouter02"、"uniswapV3PositionManager"、"permit2"(PancakeSwap V3使用相同的模板,也可以写"pancakeV3Pool"等别名)。常用合约的地址可以用exchange.IO("contracts")查询。

javascript
exchange.IO("abi", poolAddress, "uniswapV3Pool") var slot0 = exchange.IO("api", poolAddress, "slot0")

其它合约的ABI可以从区块浏览器获取,例如Etherscan的V2接口(需要Etherscan的API Key,chainid为链ID,取返回结果中的result字段):

url
https://api.etherscan.io/v2/api?chainid=1&module=contract&action=getabi&address=0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45&apikey=YourApiKey

4. 发送交易

调用合约的写方法时,交易所对象用配置的私钥签名并广播交易,返回交易哈希。发送前可以把"api"换成"call",用exchange.IO("call", ...)预演同一笔调用:在节点上模拟执行,不签名、不消耗gas,执行失败时返回空值,GetLastError()中有合约给出的失败原因。

以授权(approve)为例:

javascript
var amount = exchange.IO("toUnits", "100", tokenAddress) // 100个代币换算成链上整数 var txHash = exchange.IO("api", tokenAddress, "approve", spender, amount)

方法的stateMutability为payable时,方法参数之前要多传一个参数:附带的原生币数量(链上整数)。最后一个参数可以传选项对象:

选项说明
gasLimitgas上限。不传时由节点估算(eth_estimateGas)。合约调用不要写21000,那只够普通转账
gasPrice固定gas价格,传入时发送传统(legacy)交易。不传时,支持EIP-1559的链发送EIP-1559交易:小费取节点建议值与最近区块实际小费的较大者,最高费用为2 × baseFee + 小费
nonce指定nonce。不传时自动分配,并与链上待处理计数同步,连续发送不会重复使用nonce
dryRun设为true时只签名不广播,返回hash、raw(签名后的交易)、nonce、gasLimit等字段,可用于检查交易或交给其它渠道发送

转出原生币使用exchange.IO("api", "eth", "send", 收款地址, 数量),数量是链上整数(wei)。它的选项还支持data(十六进制调用数据),用于原样发送聚合器等API返回的交易{to, data, value},此时gas按合约调用估算。注意:普通转账不传gasPrice时按固定的100 Gwei出价、gas上限为21000,在以太坊主网上通常偏高,建议先用eth_gasPrice查询后传入。

需要把交易发到私有交易通道(如Flashbots Protect、MEV Blocker)避免被抢跑时,用exchange.IO("sendBase", 节点地址)设置只用于广播交易的节点。

5. 等待交易上链

exchange.IO("waitReceipt", 交易哈希, {timeout, confirmations})等待交易上链并达到确认数,返回交易回执:status为1表示成功、0表示失败(revertReason为失败原因),events为按已注册ABI解码的事件。超时未上链时返回空值。

6. nonce管理、加速与取消

  • exchange.IO("nonce")查看链上与本地的nonce计数;exchange.IO("nonce", "sync")按链上重新同步(在别处用同一个钱包发过交易后使用)。
  • 交易长时间未上链时,用exchange.IO("speedUp", 交易哈希)以同一个nonce、更高的手续费重发;用exchange.IO("cancelTx", 交易哈希)发送一笔同nonce、转给自己的0金额交易顶替原交易。两者都在原交易上链前才有效。
  • 本地nonce记录只在当前实盘内有效:多个实盘共用一个钱包时彼此看不到对方的记录,仍可能冲突,建议每个实盘使用独立的钱包。

其它指令

  • 编码与解码:exchange.IO("encode", ...)编码合约调用数据或按类型编码(同Solidity的abi.encode),exchange.IO("encodePacked", ...)紧凑编码(如Uniswap V3的兑换路径),exchange.IO("decode", ...)按类型解码。
  • 签名:exchange.IO("sign", ...)对32字节哈希签名,exchange.IO("signTypedData", ...)对EIP-712结构化数据签名(如ERC-20 Permit),exchange.IO("signMessage", ...)对消息做EIP-191签名。
  • Uniswap V3数学:exchange.IO("uniswapV3", ...)在tick、价格、sqrtPrice之间换算,在流动性与代币数量之间换算。
  • 哈希:exchange.IO("hash", "keccak256", "raw", "hex", 文本)计算keccak256等摘要,可用于计算方法选择器、EIP-712摘要,参数与Encode()函数相同。
  • 完整范例:通过聚合器兑换(询价、生成交易、用exchange.IO("call", ...)预演、带data发送)见手册中exchange.IO("call", ...)的范例;ERC-20 Permit签名并由合约验签见exchange.IO("sign", ...)、exchange.IO("signTypedData", ...)的范例。

示例

示例:查余额、授权并等待上链

以以太坊主网的USDC为例。注意这段代码会发出真实交易、消耗gas。

javascript
function main() { var usdc = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" // 以太坊主网 USDC var spender = "0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45" // 被授权的合约,这里以 Uniswap SwapRouter02 为例 var wallet = exchange.IO("address") // 查余额:标准 ERC20 方法不需要注册 ABI var eth = exchange.IO("fromUnits", exchange.IO("api", "eth", "eth_getBalance", wallet, "latest"), 18) var usdcBalance = exchange.IO("fromUnits", exchange.IO("api", usdc, "balanceOf", wallet), usdc) Log("ETH:", eth, "USDC:", usdcBalance) // 读合约:当前授权额度 var allowance = exchange.IO("api", usdc, "allowance", wallet, spender) Log("当前授权:", exchange.IO("fromUnits", allowance, usdc)) // 授权 100 USDC:先预演,再发送 var amount = exchange.IO("toUnits", "100", usdc) if (!exchange.IO("call", usdc, "approve", spender, amount)) { Log("预演失败:", GetLastError()) return } var txHash = exchange.IO("api", usdc, "approve", spender, amount) Log("交易哈希:", txHash) // 等待上链,最多 3 分钟 var receipt = exchange.IO("waitReceipt", txHash, {timeout: 180000}) if (receipt && receipt.status == 1) { Log("授权成功,区块:", receipt.blockNumber) } else if (receipt) { Log("交易失败:", receipt.revertReason) } else { // 未上链:可以用 speedUp 加价重发,或 cancelTx 取消 Log("超时未上链:", GetLastError()) } }

参考