Web3
在去中心化交易所Uniswap、PancakeSwap上兑换代币,请使用Uniswap交易所对象,见Uniswap与PancakeSwap;查询链上数据、调用智能合约、发送自定义交易,请使用Web3交易所对象,它支持以太坊等EVM兼容链和波场。
Uniswap与PancakeSwap
Uniswap交易所对象在一条链上连接Uniswap或PancakeSwap的V2、V3资金池,把链上兑换映射为现货交易函数:用exchange.GetTicker()看价格、用exchange.CreateOrder()下单,不需要自己注册ABI、编码合约调用。选路、询价、代币授权、价格保护、发送交易都由交易所对象完成。
什么时候用Uniswap交易所对象,什么时候用Web3
- 在Uniswap、PancakeSwap上兑换代币:用Uniswap交易所对象。
- 调用其它合约、DEX的其它功能(如提供流动性、管理V3头寸)、其它链、自定义交易:用Web3交易所对象,见
以太坊(EVM)。
一个策略可以同时添加两种交易所对象,使用同一个钱包。
配置交易所对象
| 字段 | 说明 |
|---|---|
| DEX | Uniswap或PancakeSwap |
| Chain | Ethereum、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)
}
}参考
以太坊(EVM)
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:
| 字段 | 说明 |
|---|---|
| ChainType | ETH:以太坊及所有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时,方法参数之前要多传一个参数:附带的原生币数量(链上整数)。最后一个参数可以传选项对象:
| 选项 | 说明 |
|---|---|
| gasLimit | gas上限。不传时由节点估算(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())
}
}参考
波场(TRON)
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 Key | TronGrid的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 | 付款地址, 收款或被授权地址, 合约地址, 数量, feeLimit | TRC20转账、授权 |
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余额,读取代币信息
javascriptfunction 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合约一次读取多个合约方法
javascriptfunction 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])) }
参考