当前位置:首页 > 批量操作 > 正文内容

批量查询ERC20代币余额代码示例:从逐一问到一次问完

使用技巧3个月前 (06-07)批量操作78

手上有500个地址,想查它们持有某代币的余额。写个for循环逐个调balanceOf,跑完发现花了20多秒,还被节点429限流。

问题出在每查一个地址就发一次独立RPC请求,500个地址就是500次网络往返。

下面直接给两种能跑通的方案,从逐个查到一次查完,看完就能用。

各大交易所注册链接:

欧易官网

币安官网

芝麻Gate


先看常规写法:逐个查

const { ethers } = require('ethers');
const provider = new ethers.JsonRpcProvider('https://eth.llamarpc.com');

const erc20Abi = ['function balanceOf(address owner) view returns (uint256)'];
const tokenAddress = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'; // USDC

async function getBalance(address) {
  const contract = new ethers.Contract(tokenAddress, erc20Abi, provider);
  return (await contract.balanceOf(address)).toString();
}

// 500个地址 = 500次RPC请求,慢且容易被限流
const addresses = ['0x...', '0x...', '...'];
for (const addr of addresses) {
  console.log(`${addr}: ${await getBalance(addr)}`);
}

耗时参考

地址数逐个查询Multicall批量
10个3-5秒0.3秒
50个15-25秒0.5秒
500个3-5分钟2-4秒

逐个与批量性能对比



核心思路:Multicall3合约

Multicall是部署在各条EVM链上的公共合约,把多个只读调用打包成一个请求发给节点,节点一次处理、一次返回所有结果。

以太坊主网地址:0xcA11bde05977b3631167028862bE2a173976CA11

Polygon、BSC、Arbitrum、Optimism都是同一个地址。


方案一:ethers-multicall(最推荐)


npm install ethers@5 @gainsnetwork/ethers-multicall
const { ethers } = require('ethers');
const { Contract, Provider } = require('@gainsnetwork/ethers-multicall');

const provider = new ethers.providers.JsonRpcProvider('https://eth.llamarpc.com');
const multicallProvider = new Provider(provider);
await multicallProvider.init();

const erc20Abi = ['function balanceOf(address) view returns (uint256)'];
const tokenAddress = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48';
const addresses = [
  '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045',
  '0x47ac0Fb4F2D84898e4D9E7b4DaB3C24507a6D503',
  // ...更多地址
];

const tokenContract = new Contract(tokenAddress, erc20Abi);
const calls = addresses.map(addr => tokenContract.balanceOf(addr));
const results = await multicallProvider.all(calls);

addresses.forEach((addr, i) => {
  console.log(`${addr}: ${ethers.utils.formatUnits(results[i], 6)} USDC`);
});

核心就三步:

  1. Provider替代原生provider

  2. 把每个查询封装成call对象(此时不发请求)

  3. all()一次性提交,只走一次网络


方案二:直接调Multicall3合约(零依赖)

不想加第三方库,可以自己拼合约调用:

const { ethers } = require('ethers');

const provider = new ethers.JsonRpcProvider('https://eth.llamarpc.com');
const multicallAbi = [
  'function aggregate3(tuple(address target,bool allowFailure,bytes callData)[] calls) view returns (tuple(bool success,bytes returnData)[])'
];

const multicallAddress = '0xcA11bde05977b3631167028862bE2a173976CA11';
const tokenAddress = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48';
const BALANCE_OF_ID = '0x70a08231'; // keccak256("balanceOf(address)")[:4]

const addresses = ['0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045'];

async function batchQuery() {
  const mc = new ethers.Contract(multicallAddress, multicallAbi, provider);

  const calls = addresses.map(addr => ({
    target: tokenAddress,
    allowFailure: true,
    callData: BALANCE_OF_ID + addr.slice(2).padStart(64, '0')
  }));

  const results = await mc.aggregate3(calls);

  results.forEach((r, i) => {
    const bal = r.success
      ? ethers.formatUnits('0x' + r.returnData.slice(-64), 6)
      : '0';
    console.log(`${addresses[i]}: ${bal} USDC`);
  });
}

batchQuery();

代码稍长,但不依赖任何第三方封装,适合对底层调用有精确控制需求的场景。


方案三:@evm-balance/sdk(多链多代币场景)

如果要同时查多条链、多种代币,这个SDK更省事:

npm install @evm-balance/sdk
import { createBalanceFetcher, mainnet } from '@evm-balance/sdk';

const fetcher = createBalanceFetcher({
  chain: mainnet,
  rpc: 'https://eth.llamarpc.com'
});

const results = await fetcher.FetchBalances({
  config: { chain: mainnet },
  mode: { type: 'addresses', addresses: ['0x...', '0x...'] },
  options: {
    tokens: ['0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'],
    includeNative: true
  }
});

results.forEach(r => {
  console.log(`${r.address}:`, r.balances.map(b => `${b.symbol} ${b.formatted}`).join(', '));
});

内部用的也是Multicall3,自带分页(默认500条/批)、超时控制和进度追踪。


Multicall3架构示意

实用技巧

CSV地址导入

const fs = require('fs');
const csvData = fs.readFileSync('addresses.csv', 'utf-8');
const addresses = csvData
  .split('\n')
  .map(l => l.trim())
  .filter(a => a.startsWith('0x') && a.length === 42);

分批处理(防单次请求过大)

const BATCH = 200;

async function queryAll(addresses) {
  const all = [];
  for (let i = 0; i < addresses.length; i += BATCH) {
    const batch = addresses.slice(i, i + BATCH);
    all.push(...await batchBalanceQuery(batch));
  }
  return all;
}

小数位处理

不同代币小数位不一样,别硬写18:

const decimals = {
  '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48': 6,  // USDC
  '0xdAC17F958D2ee523a2206206994597C13D831ec7': 6,  // USDT
  '0x6B175474E89094C44Da98b954EedeAC495271d0F': 18  // DAI
};

方案怎么选

场景推荐
<20个地址,单链逐个查就行,别折腾
20-1000个地址,单链ethers-multicall,简单够用
要精确控制底层调用直接调Multicall3合约
多链+多代币混合查@evm-balance/sdk

免责声明

代码仅供开发参考,不构成任何投资建议。请根据实际合约地址和ABI自行调整,合理使用RPC节点。


相关文章

 批量生成钱包地址的工具有哪些?

批量生成钱包地址的工具有哪些?

有段时间我在做测试网交互,需要大量地址来模拟不同用户的行为。网上搜了一圈找到一堆"在线批量生成钱包地址"的网站,界面花花绿绿的,有的还标着"秒出私钥""...

Web3钱包批量授权+转账,老玩家都在用的效率神器

Web3钱包批量授权+转账,老玩家都在用的效率神器

一、前言:2026年撸毛内卷,手动操作已经彻底过时如今Web3赛道早已告别单地址佛系交互时代,优质空投、项目白名单、早期激励权益,全部依赖多地址批量布局、高频差异化链上交互。对于普通撸毛玩家和小型工作...

Web3钱包批量归集实战:百个地址一键转账,Gas费省下40%的秘诀

Web3钱包批量归集实战:百个地址一键转账,Gas费省下40%的秘诀

前言:多地址运维的隐形亏损,90%用户都在白白浪费Gas随着2026年Layer2生态爆发、空投交互常态化,几乎所有深耕Web3的用户都会布局多地址分仓,用于分散交互风险、埋伏多生态空投、拆分大额资金...

Web3多地址批量操作全教程:地址簿管理、一键批量转账归集工具实操与风控

Web3多地址批量操作全教程:地址簿管理、一键批量转账归集工具实操与风控

引言:多地址操作刚需爆发,第三方批量工具暗藏安全隐患2026 年 DeFi 空投、代币分红、多钱包分仓囤币、社群奖励分发成为常态,大量用户需要同时管理数十甚至上百个链上地址。传统操作方式分为两类:一是...

Web3钱包批量转账实操:从多地址USDT分发到批量授权撤销,机构级操作效率提升5倍完整工作流

Web3钱包批量转账实操:从多地址USDT分发到批量授权撤销,机构级操作效率提升5倍完整工作流

前言:单条手动链上操作效率低下,批量工具成为 2026 机构标配2026 年 Web3 空投项目、工作室批量资金分发、大户月度合约风控需求爆发,慢雾安全上半年链上行为统计显示,普通用户手动单笔转账、逐...

用欧亿Web3钱包批量打U:一次操作100个地址,还能自动规避粉尘攻击

用欧亿Web3钱包批量打U:一次操作100个地址,还能自动规避粉尘攻击

一、引言:2026年批量打U新需求,欧易Web3钱包实现效率与安全双突破2026年Web3生态进入规模化发展阶段,链上空投分发、项目方资金结算、工作室撸毛收益归集等场景,对批量转账USDT(俗称“打U...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。