Logo
新用户订阅 RPC,首月享 6.5 折优惠查看优惠
OnFinality Learn
集成与开发阅读约 11 分钟

查询所有链上的钱包余额:一种可复现的多链方法

一套实用且可验证的工作流程,通过直接 RPC 调用读取多条链上的原生代币和代币余额,涵盖单位归一化、缓存和故障处理。

TL;DR

查询所有链上的钱包余额意味着要查询每条链自身的状态,因为没有任何单一网络知道某个地址在其他链上持有什么。可靠的方法是调用每条链 RPC 端点上的原生余额方法,然后针对 ERC-20 类资产单独查询代币合约。本文提供了一套可运行的流程,使用 curl、JavaScript 和 Python,并给出结果表方法,让你可以自行记录和验证每一个值。文章还涵盖了导致多链余额查询结果看似错误的故障模式:错误的链 ID、过期的缓存、小数位数和速率限制。

跨所有链查询余额究竟意味着什么

区块链地址不是全局账户。同一个字符串可以存在于以太坊、BNB Smart Chain、Arbitrum、Optimism、Polygon 以及数十个其他网络上,但每个网络独立存储自己的状态。当你查询所有链上的钱包余额时,你是在每条链上运行一次查询,然后在链下聚合这些答案。

这一区别很重要,因为围绕多链余额工具的大多数困惑都源于将结果视为单一数字。投资组合视图是许多独立 RPC 响应的客户端或服务端聚合,每个响应都以该链的原生单位计价。决定如何组合和显示数值的是聚合层,而不是区块链。

本文重点介绍一种可复现的方法,你可以针对任何一组 EVM 兼容链自行运行。同样的思维模型也适用于非 EVM 网络,但 RPC 方法和代币标准不同,因此这些将作为单独的集成来处理。

  • 原生余额:每条链一次 RPC 调用,以该链的最小单位返回。
  • 代币余额:每个代币合约每条链一次调用,使用代币的小数位数。
  • 聚合:你的代码转换单位,按需应用价格,并记录数据来源。

前提条件和假设

你需要一份你关心的链列表,每条链都有链 ID 和你获授权使用的 RPC 端点。你还需要你要检查的公开地址;绝不要使用私钥,因为余额读取是公开操作。对于代币余额,你需要代币合约地址及其小数位数,这些可以从合约本身读取。

示例使用 RPC_URL 和 WALLET_ADDRESS 等占位符。请将它们替换为你自己的端点和地址。如果你还没有每个网络的端点,多链 RPC 端点指南解释了端点选择的工作原理,OnFinality 网络目录列出了你可以映射到链列表的支持网络。

  • 包含 chainId、name、RPC_URL 和原生符号的链列表。
  • 校验和或小写十六进制形式的公开钱包地址。
  • 可选:用于 ERC-20 余额的代币合约地址。
  • 用于记录结果的地方,例如电子表格或 JSON 文件。

思维模型:每条链一次查询,每个答案一个单位

每个 EVM 链都暴露 eth_getBalance,它以十六进制字符串返回以 wei 计的原生余额。Wei 是最小单位;在类以太坊链上,一个 ether 是 10^18 wei,但其他链的原生单位可能使用不同的小数位数。RPC 响应不会告诉你符号或小数位数,因此你的链列表必须携带这些元数据。

代币余额则不同。ERC-20 合约存储从地址到金额的映射,你通过 balanceOf 函数读取它。返回的整数必须除以 10^decimals,其中 decimals 从合约的 decimals() 函数读取。因此,代币余额查询每个代币需要两次调用:一次获取 decimals(可缓存),一次获取 balanceOf。

这种分离正是钱包余额查询器能快速显示原生总额但代币查询耗时更长的原因。原生余额每条链一次调用;代币余额随你检查的合约数量而扩展。

可运行流程:使用 curl 查询原生余额

从单条链开始,确认你的端点和地址格式。下面的 JSON-RPC 请求查询最新区块的原生余额。params 数组包含地址和区块标签;使用 "latest" 获取当前余额,或使用区块号进行历史查询。

  • 成功的响应包含 result 字段,其中是十六进制字符串,例如 0x0 或 0xde0b6b3a7640000。
  • error 字段通常表示地址格式错误、方法不受支持或端点问题。
  • 将十六进制字符串转换为整数,然后除以 10^nativeDecimals 得到人类可读的金额。
curl -s -X POST "$RPC_URL" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "eth_getBalance",
    "params": ["WALLET_ADDRESS", "latest"]
  }'

可运行流程:使用 JavaScript 查询所有链

下面的脚本遍历链列表,在每个端点上调用 eth_getBalance,转换结果并打印表格。它使用 fetch,现代 Node.js 和浏览器中可用。将链列表放在一个地方,以便相同的元数据同时驱动查询和显示。

这种模式是任何多链余额检查的核心。重要的工程决策包括如何处理失败的链、如何避免速率限制,以及如何记录每个值来自哪个端点。

const chains = [
  { name: "Ethereum", chainId: 1, rpc: "RPC_URL_ETH", symbol: "ETH", decimals: 18 },
  { name: "BNB Smart Chain", chainId: 56, rpc: "RPC_URL_BSC", symbol: "BNB", decimals: 18 },
  { name: "Arbitrum One", chainId: 42161, rpc: "RPC_URL_ARB", symbol: "ETH", decimals: 18 }
];

const address = "WALLET_ADDRESS";

async function getNativeBalance(chain) {
  const res = await fetch(chain.rpc, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      jsonrpc: "2.0",
      id: chain.chainId,
      method: "eth_getBalance",
      params: [address, "latest"]
    })
  });
  const json = await res.json();
  if (json.error) throw new Error(`${chain.name}: ${json.error.message}`);
  const wei = BigInt(json.result);
  const divisor = 10n ** BigInt(chain.decimals);
  return Number(wei) / Number(divisor);
}

for (const chain of chains) {
  try {
    const balance = await getNativeBalance(chain);
    console.log(`${chain.name}\t${balance}\t${chain.symbol}`);
  } catch (err) {
    console.log(`${chain.name}\tERROR\t${err.message}`);
  }
}

可运行流程:使用 Python 查询代币余额

原生余额只是全貌的一部分。要查询代币余额,使用 eth_call 调用合约的 balanceOf 函数。balanceOf(address) 的函数选择器是 0x70a08231,后面跟填充到 32 字节的地址。decimals() 的选择器是 0x313ce567。

下面的 Python 示例构建 calldata、调用合约并转换结果。它按合约缓存 decimals,因为小数位数很少变化。先针对一条链运行,然后扩展到你的链列表。

import json, urllib.request

RPC_URL = "RPC_URL"
TOKEN = "TOKEN_CONTRACT_ADDRESS"
WALLET = "WALLET_ADDRESS"

def rpc(method, params):
    payload = json.dumps({"jsonrpc": "2.0", "id": 1, "method": method, "params": params}).encode()
    req = urllib.request.Request(RPC_URL, data=payload, headers={"Content-Type": "application/json"})
    with urllib.request.urlopen(req) as resp:
        body = json.loads(resp.read())
    if "error" in body:
        raise RuntimeError(body["error"]["message"])
    return body["result"]

def pad_address(addr):
    return addr.lower().replace("0x", "").rjust(64, "0")

decimals_hex = rpc("eth_call", [{"to": TOKEN, "data": "0x313ce567"}, "latest"])
decimals = int(decimals_hex, 16)

balance_hex = rpc("eth_call", [{"to": TOKEN, "data": "0x70a08231" + pad_address(WALLET)}, "latest"])
balance = int(balance_hex, 16) / (10 ** decimals)
print(f"Token balance: {balance}")

在验证表中记录结果

只有当你能够复现时,多链余额检查才可信。每条链记录一行,包含链名称、链 ID、端点标签、区块号、原始十六进制结果、转换后的金额和时间戳。区块号很重要,因为余额会变化;没有它,不同时间的两次运行无法比较。

下表是模板,不是实测数据。请根据你自己的运行结果填写。如果某个值看起来不对,原始十六进制和区块号让你可以重新查询同一状态,并确认问题出在链数据还是你的转换逻辑中。

  • | 链 | 链 ID | 端点标签 | 区块 | 原始十六进制 | 转换后 | 时间戳 |
  • | Ethereum | 1 | primary | (你的区块) | (你的十六进制) | (你的值) | (你的时间) |
  • | BNB Smart Chain | 56 | primary | (你的区块) | (你的十六进制) | (你的值) | (你的时间) |
  • | Arbitrum One | 42161 | primary | (你的区块) | (你的十六进制) | (你的值) | (你的时间) |

预期验证结果及其解读方式

一次正确的运行会为每条链生成一行,每行都有区块号和转换后的金额。地址从未收到资金的链应返回 0x0,转换为零。返回错误的链与零余额不同;将其记录为错误并单独调查。

如果你将输出与第三方钱包追踪器比较,预期会有小差异。追踪器可能使用不同的区块标签、缓存值,或包含你的仅原生查询未覆盖的代币和 DeFi 头寸。协调的方法是比较相同的区块号和相同的资产类别,而不是头条投资组合总额。

  • 零余额:有效响应,结果为 0x0。
  • 非零余额:从链列表确认小数位数和符号。
  • 错误:检查地址格式、端点健康状况和方法支持。
  • 与追踪器不匹配:在得出结论之前,对齐区块号和资产范围。

排查常见故障模式

大多数不正确的多链余额结果来自少数几个原因。第一个是使用错误的链 ID 或端点,这会返回另一个网络的有效余额。第二个是小数位数混淆,尤其是在原生单位不是 18 位小数的链上。第三个是速率限制:并行查询许多链和代币可能触发 429 响应,一些脚本会静默地将其视为零。

第四个原因是过期的缓存。如果你的服务缓存余额,确保缓存键包含链 ID、地址和区块标签。仅以地址为键的缓存会为每条链返回同一条链的余额。最后,确认你的端点确实支持你调用的方法;一些受限端点允许 eth_getBalance 但不允许 eth_call。

  • 在信任余额之前,使用 eth_chainId 验证返回的链 ID。
  • 绝不要将 RPC 错误视为零余额。
  • 为 429 和 5xx 响应添加带退避的重试。
  • 在每个缓存键中包含链 ID 和区块标签。
  • 为每个结果记录端点标签以便审计。

直接 RPC 余额查询的局限性和权衡

直接 RPC 查询给你最大的控制权和最清晰的数据来源,但并非没有权衡。查询许多链意味着许多端点,每个都有自己的可靠性特征、速率限制和延迟。代币余额使调用次数成倍增加,历史余额需要支持归档的节点,这是不同类别的服务。

公共端点对于偶尔检查很方便,但通常有速率限制,可能不支持归档查询。对于重复或生产使用,专用节点或托管 RPC 服务提供更可预测的吞吐量。区块链节点托管指南解释了运营差异,RPC 定价页面描述了请求量如何映射到套餐。

  • 延迟:除非并行化,否则最慢的链决定总响应时间。
  • 成本:代币查询随合约数量而扩展,而不仅仅是链。
  • 归档数据:历史余额需要归档节点。
  • 一致性:不同链以不同速度推进,因此快照是近似的。

后续步骤:从脚本到可靠的多链余额服务

一旦基本脚本工作,下一步是使其具有弹性。使用并发限制并行化链查询,重试瞬时错误,并持久化结果及区块号。如果你需要代币和 DeFi 头寸,为每条链添加代币列表并缓存小数位数。如果你需要历史余额,将这些查询路由到支持归档的端点。

对于跨许多网络的端点选择,RPC Assistant 的多链 RPC 端点指南涵盖了决策标准,API 服务页面描述了托管访问。如果你为特定链选择提供商,请从 RPC Assistant 开始,而不是将公共端点硬编码到生产环境。

  • 添加并发限制和重试策略。
  • 持久化原始十六进制、区块号和端点标签。
  • 分离原生、代币和历史查询路径。
  • 在扩展之前查看多链 RPC 端点指南。

永远不用担心基础设施

OnFinality 消除了 DevOps 的繁重工作,让您能够更聪明、更快地构建。

开始