如何在Rootstock上通过ethers.js聚合ERC20余额与授权查询请求?
在ethers.js中聚合多笔ERC20查询为单个JSON-RPC请求
当然可以通过两种主流方式实现请求聚合,大幅降低JSON-RPC调用频率:
方法一:ethers.js原生批量JSON-RPC请求
利用JSON-RPC的批量请求特性,通过ethers.js的provider.send方法一次性发送多个eth_call请求,每个请求对应一个ERC20的balanceOf或allowance调用。
代码示例
import { ethers } from "ethers"; // 初始化Rootstock Provider const provider = new ethers.providers.JsonRpcProvider("https://rpc.rootstock.io/mainnet"); // ERC20核心ABI片段 const erc20Abi = [ "function balanceOf(address owner) view returns (uint256)", "function allowance(address owner, address spender) view returns (uint256)" ]; const erc20Interface = new ethers.utils.Interface(erc20Abi); // 定义查询参数 const userAddress = "0x..."; // 你的用户地址 const spenderAddress = "0x..."; // 授权的合约地址(比如DEX) const tokenAddresses = [ "0xToken1Address", "0xToken2Address" ]; // 构造批量请求数组 const batchRequests = []; tokenAddresses.forEach(tokenAddr => { // 添加balanceOf查询 batchRequests.push({ jsonrpc: "2.0", id: batchRequests.length + 1, method: "eth_call", params: [ { to: tokenAddr, data: erc20Interface.encodeFunctionData("balanceOf", [userAddress]) }, "latest" ] }); // 添加allowance查询 batchRequests.push({ jsonrpc: "2.0", id: batchRequests.length + 1, method: "eth_call", params: [ { to: tokenAddr, data: erc20Interface.encodeFunctionData("allowance", [userAddress, spenderAddress]) }, "latest" ] }); }); // 发送批量请求 const batchResults = await provider.send("eth_sendBatch", batchRequests); // 解析结果 batchResults.forEach(result => { if (result.error) { console.error(`请求失败: ${result.error.message}`); return; } // 根据id或顺序解析对应的函数返回值 const value = ethers.BigNumber.from(result.result); console.log(`查询结果: ${value.toString()}`); });
方法二:使用Multicall合约(更推荐)
Multicall是一个标准合约,允许在单个eth_call中执行多个视图函数调用,并返回所有结果(包括每个调用的成功状态)。Rootstock网络支持部署或使用已有的Multicall合约,这种方式比原生批量请求更结构化,还能处理部分调用失败的场景。
代码示例
import { ethers } from "ethers"; const provider = new ethers.providers.JsonRpcProvider("https://rpc.rootstock.io/mainnet"); // Multicall合约ABI(核心方法) const multicallAbi = [ "function aggregate(tuple(address target, bytes callData)[] calls) view returns (uint256 blockNumber, bytes[] returnData)" ]; // 替换为Rootstock上的Multicall合约地址(若未部署可自行部署标准Multicall) const multicallAddress = "0xMulticallAddressOnRootstock"; const multicallContract = new ethers.Contract(multicallAddress, multicallAbi, provider); // ERC20接口和查询参数(同方法一) const erc20Abi = [ "function balanceOf(address owner) view returns (uint256)", "function allowance(address owner, address spender) view returns (uint256)" ]; const erc20Interface = new ethers.utils.Interface(erc20Abi); const userAddress = "0x..."; const spenderAddress = "0x..."; const tokenAddresses = ["0xToken1", "0xToken2"]; // 构造Multicall调用数组 const calls = []; tokenAddresses.forEach(tokenAddr => { calls.push([ tokenAddr, erc20Interface.encodeFunctionData("balanceOf", [userAddress]) ]); calls.push([ tokenAddr, erc20Interface.encodeFunctionData("allowance", [userAddress, spenderAddress]) ]); }); // 执行Multicall查询 const [blockNumber, returnData] = await multicallContract.aggregate(calls); // 解析每个结果 returnData.forEach((data, index) => { // 根据索引判断是balanceOf还是allowance const isBalance = index % 2 === 0; const value = ethers.BigNumber.from(data); const tokenAddr = tokenAddresses[Math.floor(index / 2)]; console.log(`${tokenAddr} ${isBalance ? '余额' : '授权额度'}: ${value.toString()}`); });
两种方式对比
- 原生批量请求:无需依赖额外合约,直接利用JSON-RPC协议特性,适合快速实现。
- Multicall合约:返回结果结构化,支持部分失败处理,且链上执行更高效,适合长期维护的DApp。
内容的提问来源于stack exchange,提问作者bguiz
相关产品推荐
相关产品推荐

