> 技术文档 > 如何查询一百万个以太坊地址上的所有代币余额_以太坊地址查询

如何查询一百万个以太坊地址上的所有代币余额_以太坊地址查询

本文将带你从以太坊的存储模型开始,深入理解如何通过 RPC 查询某个地址上持有的所有代币(ERC-20),并介绍相关工具和局限。


一、以太坊的账户与存储模型
以太坊有两类账户:

  1. 外部账户(EOA):由私钥控制,用户的钱包地址就是 EOA。
  2. 合约账户:由代码控制,是智能合约部署后的地址。
    每个账户都有一个状态结构(Account State):
  • nonce(用来记录发出的交易,每次交易+1,从0开始)
  • balance(以太币余额)
  • storageRoot(指向该合约的「存储 trie(状态树)」的根节点哈希)
  • codeHash(指向该合约代码(bytecode)的哈希)
    对于合约账户,核心是 storageRoot,它指向一个存储 Trie(状态树),键值对存储了所有的合约内部变量。
    对于外部账户,
    storageRoot= keccak256(RLP(\"\")= 0x56e81f171bcc55a6ff8345e69... codeHash = keccak256(\'\') = 0x5f...36
    引申: 判断是不是合约账户需要用 eth_getCode(\"0xAddress\", \"latest\")

二、ERC-20 代币是如何存储的?
ERC-20 是一种智能合约接口,代币不是“存在地址上”,而是记录在代币合约的存储中。
关键字段:
mapping(address => uint256) public balanceOf;
这意味着,每个代币合约内部都有一个哈希映射,记录了哪个地址持有多少代币。
以太坊虚拟机(EVM)会将这个 mapping 映射编译成一个如下的存储位置:
keccak256(pad(address) ++ pad(slot_number))
slot_number 是 balanceOf 在合约中的存储槽位(通常是 0)。所以,要知道某地址在某个代币合约上的余额,就必须计算这个地址的 balanceOf 的位置,并读取合约的存储。
但这种方式 不能批量查所有代币,因为我们不知道地址持有哪些代币合约。同时我们也不知道链上有哪些代币合约。


三、RPC 是怎么工作的?
以太坊节点对外暴露 JSON-RPC 接口。常用的 RPC 方法包括:

  • eth_getBalance:获取地址的 ETH 余额。
  • eth_call:调用合约的 balanceOf 方法。
  • eth_getStorageAt:获取合约某个槽位的原始存储值。
    例如:
{ \"jsonrpc\": \"2.0\", \"method\": \"eth_call\", \"params\": [{ \"to\": \"0xTokenAddress\", \"data\": \"0x70a08231000000000000000000000000
\" // balanceOf(address) }, \"latest\"], \"id\": 1}

这个调用执行合约的 balanceOf(address) 函数。


四、那么如何获取一个地址上所有的代币余额?
方式一:链上原生方法不可行
以太坊本身不维护一个「地址拥有的代币列表」,因为每个代币的持仓信息都存在于代币合约自己的存储中。
换句话说,以太坊的状态是分散在每个代币合约中的,没有统一索引。
因此:
无法通过一个 RPC 调用获取某个地址持有的所有代币。


方式二:使用离线索引服务(如 Etherscan、Covalent、Zerion、Alchemy)
这类服务会:

  1. 全链扫描所有 ERC-20 转账事件(Transfer)
  2. 为每个地址构建代币资产索引
  3. 提供聚合 API 查询:
    比如:
    GET https://api.covalenthq.com/v1/eth-mainnet/address/
    /balances_v2/
    返回:
{ \"data\": { \"items\": [ { \"contract_name\": \"USDC\", \"contract_address\": \"0x...\", \"balance\": \"1000000\", \"decimals\": 6 }, ...] }}

方式三:自己实现索引器
如果你不想依赖第三方服务,可以自己实现:

  1. 从区块 0 开始,扫描所有交易和合约事件。
  2. 解析所有 ERC-20 合约的 Transfer 事件:
  3. event Transfer(address indexed from, address indexed to, uint256 value);
  4. 为每个地址建立代币转入转出索引。
  5. 可选:将合约地址做缓存,定期抓取 symbol, decimals, name 信息。
    这种方式要求你运行一个节点 + 搭建 ETL 管道和数据库索引。

方案四:通过cpbox.io代理实现索引器

如果你只是查询个别地址上的token余额,你可以使用余额查询,支持 ETH, BSC 等等的evm链,还支持 Tron, Solana, Sui,BTC等等的主流链。
如果你要查询上千,上网甚至几十万的地址余额,cpbox有一个余额监听的功能,此功能可以帮助用户监听上百万的地址上所有的ERC-20 代币,NFT等等所有的资产,也支持主流的EVM链,首先你可以联系cpbox的小助手查询历史的数据,同时还可以满足你未来有资产进来通知你的需求。


五、常用工具和库
Web3 库

  • web3.js / ethers.js
    可用于编写脚本批量查询某地址在指定代币上的余额。
    示例代码(ethers.js):
const { ethers } = require(\"ethers\");const provider = new ethers.JsonRpcProvider(\"https://mainnet.infura.io/v3/YOUR_KEY\");const tokenContract = new ethers.Contract(tokenAddress, [\"function balanceOf(address) view returns (uint256)\",\"function decimals() view returns (uint8)\"], provider);const balance = await tokenContract.balanceOf(userAddress);

六、补充:对 NFT 的支持
NFT(如 ERC-721)也使用 ownerOf(tokenId) 来记录归属,和 ERC-20 类似,也无法直接通过链上查询某地址持有哪些 NFT,通常也需要事件索引或 OpenSea 等聚合器。


如果你有特定使用场景,比如只查询某一小批代币,可以批量调用 balanceOf。如果要构建钱包/浏览器插件级别的功能,建议依赖第三方 API 或自建索引。

本文到此结束,更多相关文章,请,,https://t.me/gtokentool