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

在去中心化交易所Uniswap、PancakeSwap上兑换代币,请使用Uniswap交易所对象,见Uniswap与PancakeSwap;查询链上数据、调用智能合约、发送自定义交易,请使用Web3交易所对象,它支持以太坊等EVM兼容链和波场。

Uniswap交易所对象在一条链上连接Uniswap或PancakeSwap的V2、V3资金池,把链上兑换映射为现货交易函数:用exchange.GetTicker()看价格、用exchange.CreateOrder()下单,不需要自己注册ABI、编码合约调用。选路、询价、代币授权、价格保护、发送交易都由交易所对象完成。

什么时候用Uniswap交易所对象,什么时候用Web3

  • 在Uniswap、PancakeSwap上兑换代币:用Uniswap交易所对象。
  • 调用其它合约、DEX的其它功能(如提供流动性、管理V3头寸)、其它链、自定义交易:用Web3交易所对象,见以太坊(EVM)。

一个策略可以同时添加两种交易所对象,使用同一个钱包。

配置交易所对象

字段说明
DEXUniswap或PancakeSwap
ChainEthereum、Arbitrum、Base、BNB Chain。一个交易所对象只对应一条链上的一个DEX
Private Key钱包私钥(十六进制字符串)。支持把私钥本地化部署在托管者上,参看密钥安全性
Rpc Address该链的节点地址,选择Chain时自动填入公共节点(如以太坊为https://ethereum-rpc.publicnode.com)。可以写多个节点,用逗号分隔,互为备用
Rpc Api Key节点鉴权,可以留空。写成名称: 值时作为该名称的请求头发送,否则作为Authorization: Basic <值>发送

第一次调用时会核对节点所在的链与Chain是否一致,不一致时报错,不会把交易发到别的链上。钱包里需要有该链的原生币(ETH或BNB)支付gas。

交易对

  • 交易对写作ETH_USDC、UNI_USDT这样的基础币_计价币。
  • 代币名按以下顺序解析:内置的常用代币(原生币、包装币、USDC、USDT等)→ 用exchange.IO("token", 名字, 合约地址)登记的代币 → 官方代币列表。官方列表中同一条链上有同名代币时,请改用合约地址。
  • 不在代币表中的代币可以直接用合约地址作为交易对的一部分,例如0x1f9840a85d5af5bf1d1762f925bdaddc4201f984_USDC。
  • 原生币与包装币是两种资产:ETH与WETH、BNB与WBNB分别是不同的币。交易原生币时路由合约会自动包装、解包。两者之间的转换不能下单,用exchange.IO("wrap", 数量)、exchange.IO("unwrap", 数量)直接调用包装币合约,1:1兑换,只花gas。
  • exchange.GetMarkets()只列出常用的交易对,没有列出的交易对同样可以交易。

标准函数的含义

函数行为
exchange.GetTicker()买一、卖一是按一定规模实际询价得到的可成交价格(已包含池子手续费);链上没有24小时统计
exchange.GetDepth()按逐档递增的规模在链上询价,推算出的价位,不是真实的挂单簿
exchange.GetTrades()该交易对资金池最近的链上兑换记录
exchange.GetAccount()、exchange.GetAssets()钱包中原生币和代币表中各代币的余额
exchange.CreateOrder()立即在链上兑换,见下文
exchange.GetOrder()订单ID就是交易哈希,状态来自交易回执:上链前为未完成,上链后为成交或失败
exchange.GetOrders()本次运行发出、还没有上链的订单
exchange.CancelOrder()用同一个nonce发送一笔替换交易,尽力撤销,见下文

不支持exchange.GetRecords()、exchange.GetTickers()、exchange.GetHistoryOrders()。

下单

DEX没有挂单簿,每笔订单都是一次立即执行的链上兑换:要么整笔成交,要么整笔回滚(只损失gas),不会部分成交、也不会挂在那里等价格。

  • 限价单:限价是最差成交价。下单时先询价,按当前价格达不到限价时直接报错,不发交易;达得到时把「最少得到/最多支付」写进链上交易,交易上链前价格变动导致达不到时整笔回滚。
  • 市价单:按询价结果扣除滑点得到最少得到/最多支付的数量,滑点默认0.5%,用exchange.IO("slippage", 比例)修改。
  • 数量:卖出时是卖出的基础币数量;限价买入时是要买到的基础币数量;市价买入时是要花费的计价币数量。
  • 单笔订单可以在方向参数后附加设置,例如exchange.CreateOrder("ETH_USDC", 'sell;{"slippage":0.01,"route":"v3"}', -1, 0.1):slippage为本单滑点,route限定路径类型(v2、v3、hop两跳、direct直连)。
  • 卖出代币(ERC20)前会检查路由合约的授权额度,不够时先发送授权交易并等待上链。默认只授权本次需要的数量,exchange.IO("approve", "max")改为无限授权,省去之后的授权交易。
  • 交易超过截止时间(默认120秒,exchange.IO("deadline", 秒数)修改)仍未上链时会回滚,避免在价格大幅变化后才成交。

撤单

exchange.CancelOrder()用原订单的nonce发送一笔转给自己的0金额交易,手续费更高,先上链则原订单失效。这只是尽力撤销:原订单可能在替换交易之前上链并成交;原订单已经上链时撤单直接报错。撤单后用exchange.GetOrder()确认最终状态。

常用的exchange.IO()指令

指令作用
exchange.IO("slippage", 比例)市价单滑点,默认0.005
exchange.IO("deadline", 秒数)交易截止时间,默认120秒
exchange.IO("gasMultiplier", 倍数)gas上限 = 节点估算值 × 倍数,默认1.2
exchange.IO("approve", "exact" 或 "max")授权模式
exchange.IO("token", 名字, 合约地址)登记代币;不传参数时列出代币表
exchange.IO("route", 交易对, 方向, 数量)只询价:各候选路径的报价和最优路径,不下单
exchange.IO("simulate", 交易对, 方向, 数量[, 价格])按下单逻辑构造交易,只在链上模拟执行,不花gas
exchange.IO("transfer", 收款地址, 数量[, 代币])转出原生币或代币,数量可以写"all"
exchange.IO("receipt", 交易哈希[, 等待毫秒])查询转账等交易的回执,可以等待上链
exchange.IO("wrap", 数量)、exchange.IO("unwrap", 数量)原生币与包装币1:1互换
exchange.IO("contracts")当前DEX在当前链上的合约地址
exchange.IO("base", 节点地址)、exchange.IO("sendBase", 节点地址)切换节点;设置只用于广播交易的节点(私有交易通道)
exchange.IO("address")钱包地址

各指令的参数与返回值见语法手册Uniswap分类。

示例

示例:询价、模拟,然后市价卖出

以以太坊上的ETH_USDC为例。注意CreateOrder会发出真实交易。

javascript
function main() { var symbol = "ETH_USDC" exchange.IO("slippage", 0.003) // 市价单滑点 0.3% var t = exchange.GetTicker(symbol) Log("买一:", t.Buy, "卖一:", t.Sell) // 只询价:卖出 0.1 ETH 的最优路径 var r = exchange.IO("route", symbol, "sell", 0.1) Log("最优路径:", r.best, "价格:", r.price) // 链上模拟一遍,不花 gas if (!exchange.IO("simulate", symbol, "sell", 0.1)) { Log("模拟失败:", GetLastError()) return } // 市价卖出 0.1 ETH,订单 ID 是交易哈希 var id = exchange.CreateOrder(symbol, "sell", -1, 0.1) if (!id) { Log("下单失败:", GetLastError()) return } while (true) { var o = exchange.GetOrder(id) if (o && o.Status != ORDER_STATE_PENDING) { Log("状态:", o.Status, "成交数量:", o.DealAmount, "成交均价:", o.AvgPrice) break } Sleep(3000) } }

参考

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

参考

Web3交易所对象选择ChainType为TRON时连接波场节点。用法与以太坊(EVM)基本一致:注册ABI、调用合约、编码解码、签名、切换私钥等exchange.IO()指令相同,地址使用波场格式(T开头),TRX数量的单位是sun(1 TRX = 1000000 sun)。本页说明配置和波场特有的部分。

配置交易所对象

字段说明
ChainType选择TRON
Private Key钱包私钥(十六进制字符串)。支持把私钥本地化部署在托管者上,参看密钥安全性
Rpc Address波场全节点的HTTP地址,例如官方节点https://api.trongrid.io(测试网:https://nile.trongrid.io、https://api.shasta.trongrid.io)
Rpc Api KeyTronGrid的API Key,只填Key本身,会作为TRON-PRO-API-KEY请求头发送。不填也能使用,但TronGrid对没有Key的请求限频更严格

托管者通过全节点的HTTP接口(/wallet/...)访问波场,不再使用gRPC。选择TRON时表单默认填入的旧gRPC地址grpc.trongrid.io:50051会自动换成https://api.trongrid.io(grpc.nile.trongrid.io:50051、grpc.shasta.trongrid.io:50051同样换成对应测试网的HTTP地址);其它gRPC地址会报错,请改填节点的HTTP地址。

运行中可以用exchange.IO("base", 节点地址)切换节点,用exchange.IO("key", 私钥)切换钱包,用exchange.IO("address")获取当前钱包地址(T开头)。标准函数exchange.GetAccount()、exchange.GetAssets()返回钱包的TRX余额。账户还未激活(链上没有记录)时余额为0。

调用智能合约

与以太坊相同,使用exchange.IO("api", 合约地址, 方法, ...参数):只读方法直接返回结果,写方法签名并广播交易,返回交易ID。TRC20标准方法已内置;其它合约没有注册ABI时,会自动从链上读取该合约的ABI,读取不到时再用exchange.IO("abi", 合约地址, ABI)手动注册。写方法的最后一个参数可以传{gasLimit: 数量}设置手续费上限(feeLimit,单位sun)。

javascript
// USDT(TRC20)合约 var usdt = "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t" Log(exchange.IO("api", usdt, "balanceOf", exchange.IO("address"))) // 链上整数,USDT精度为6

编码解码与以太坊一致,地址参数可以直接写T开头的地址:

javascript
exchange.IO("encode", "address", "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t") // 000000000000000000000000a614f803b6fd780986a42c78ec9c7f77e6ded13c exchange.IO("encodePacked", "address", "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t") // a614f803b6fd780986a42c78ec9c7f77e6ded13c exchange.IO("decode", "string", "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a5465746865722055534400000000000000000000000000000000000000000000") // Tether USD

调用波场节点的方法

exchange.IO("api", "tron", 方法, ...参数)调用波场节点的方法,方法名不区分大小写。需要签名的方法(转账、触发合约等)会自动签名并广播。常用方法:

方法参数说明
send收款地址, 数量(sun)从当前钱包转出TRX
Transfer付款地址, 收款地址, 数量(sun)转出TRX,付款地址必须是当前钱包
GetAccount地址账户信息
GetAccountResource地址账户的能量、带宽资源
GetContractABI合约地址合约在链上的ABI
GetAssetIssueByName名称TRC10资产信息
GetNowBlock无当前区块
GetBlockByNum区块高度指定区块
GetTransactionByID交易ID交易内容
GetTransactionInfoByID交易ID交易执行结果(手续费、能量消耗、日志等)
GetChainParameters无链参数
TriggerConstantContract调用者地址(可为空), 合约地址, 方法, 参数编码只读调用合约,结果在constant_result中(十六进制字符串,可以用exchange.IO("decode", ...)解码)
TRC20ContractBalance地址, 合约地址TRC20余额(链上整数)
TRC20GetName、TRC20GetSymbol、TRC20GetDecimals合约地址TRC20的名称、符号、精度
TRC20Send、TRC20Approve付款地址, 收款或被授权地址, 合约地址, 数量, feeLimitTRC20转账、授权
TRC20Call调用者地址(可为空), 合约地址, 调用数据, 是否只读, feeLimit用原始调用数据调用合约
ParseTRC20NumericProperty、ParseTRC20StringProperty十六进制数据解析TRC20返回的数值、字符串

表中没有的节点接口,可以直接传路径和请求体:exchange.IO("api", "tron", "/wallet/接口名", {请求体})。

与以太坊的差异

以下指令只支持以太坊(EVM),在波场上调用会报错:call、multicall、logs、waitReceipt、nonce、speedUp、cancelTx、contracts;sendBase和多个节点互为备用也只对以太坊有效。在波场上模拟执行合约调用可以用节点方法TriggerConstantContract,查询交易的执行结果用GetTransactionInfoByID。

toUnits、fromUnits、uniswapV3、编码解码和签名指令在波场上同样可用,精度参数可以直接传TRC20合约地址:

javascript
var usdt = "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t" var raw = exchange.IO("api", usdt, "balanceOf", exchange.IO("address")) Log(exchange.IO("fromUnits", raw, usdt)) // 按合约的 decimals() 换算为可读数量

合约调用在节点校验阶段被拒绝时(例如合约不存在),错误信息中是节点给出的原因,例如tron contract call rejected (CONTRACT_VALIDATE_ERROR): Smart contract is not exist.;合约执行失败(revert)时报tron contract execution failed及失败原因。

签名

exchange.IO("hash", "sign", "hex", "hex", 交易哈希)用当前私钥对32字节哈希签名,返回65字节签名r‖s‖v(v为0或1);hash的其它算法(如"sha256")用于计算摘要,功能与Encode()函数相同。需要分别取得r、s、v(v为27或28)用于合约校验时,使用exchange.IO("sign", ...),见语法手册Web3分类。

示例

  • 示例

    查询TRX与USDT余额,读取代币信息

    javascript
    function main() { var usdt = "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t" var wallet = exchange.IO("address") // TRX 余额(标准函数,单位 TRX) Log("账户:", exchange.GetAccount()) // USDT 余额:链上整数,按精度换算 var raw = exchange.IO("api", "tron", "TRC20ContractBalance", wallet, usdt) var decimals = exchange.IO("api", "tron", "TRC20GetDecimals", usdt) Log("USDT:", raw / Math.pow(10, decimals)) // 用 TRC20Call 只读调用 name()(选择器 0x06fdde03),再解析返回的字符串 var ret = exchange.IO("api", "tron", "TRC20Call", "", usdt, "0x06fdde03", true, 0) // constant_result 中是十六进制字符串,直接解析 Log("名称:", exchange.IO("api", "tron", "ParseTRC20StringProperty", ret.constant_result[0])) }
  • 用Multicall合约一次读取多个合约方法

    javascript
    function main() { var usdt = "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t" var multicall = "TGXuuKAb4bnrn137u39EKbYzKNXvdCes98" var wallet = exchange.IO("address") var calls = [ [usdt, exchange.IO("encode", usdt, "name")], [usdt, exchange.IO("encode", usdt, "decimals")], [usdt, exchange.IO("encode", usdt, "balanceOf", wallet)] ] // 注册 Multicall 合约的 aggregate 方法 exchange.IO("abi", multicall, `[{"inputs":[{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"bytes","name":"callData","type":"bytes"}],"internalType":"struct TronMulticall.Call[]","name":"calls","type":"tuple[]"}],"name":"aggregate","outputs":[{"internalType":"uint256","name":"blockNumber","type":"uint256"},{"internalType":"bytes[]","name":"returnData","type":"bytes[]"}],"stateMutability":"view","type":"function"}]`) var ret = exchange.IO("api", multicall, "aggregate", calls) Log("name:", exchange.IO("decode", "string", ret.returnData[0])) Log("decimals:", exchange.IO("decode", "uint8", ret.returnData[1])) Log("balanceOf:", exchange.IO("decode", "uint256", ret.returnData[2])) }

参考