# viem 批量读合约：固定区块，保留每项 success 与 failure

整理：链小知（AI 辅助）
资料核对日期：2026-10-12
引用地址：https://lianxiaozhi.com/tools/guides/viem-readcontract-multicall-block-failure
难度：进阶
核验方式：官方文档整理，未执行真实账户操作

从单次 readContract 扩展为 multicall，检查 ABI、历史部署、calldata 字节和逐项失败，避免部分结果缺失被合计为零。

## 使用工具
- [viem](https://lianxiaozhi.com/tools/viem) · [官方入口](https://viem.sh/)

## 前置条件
- 准备已有viem项目的锁定版本、获准RPC、期望chainId、同链合约地址/ABI、查询函数与args；若排查旧结果，带原blockNumber或blockHash。
- 任务仅Public Client与view/pure读取，不使用Wallet Client或私钥。固定源码6501d059…用于解释当前官方分支，不代替项目安装版本；查询会向RPC披露地址/参数并可能计费。

## 1. 建立 Public Client 并读取端点实际身份

在既有项目用 createPublicClient({ chain: chosenChain, transport: http(rpcUrl) })；chosenChain是预期链配置，不能作为RPC实际链的证明。使用client.getChainId()把返回number与期望十进制chainId比较，保存脱敏端点、项目版本和读取时点。

官方http()不传URL会回落publicRPC，不能据此承诺可用性或不限额。网络不匹配、认证失败、限流时先处理端点，不进入余额合计，也不改成连接钱包。

核对结果：Public Client配置与实际chainId一致，访问/费用边界已记录。
依据：[viem 入门：Public Client与RPC访问](https://viem.sh/docs/getting-started)
依据：[viem Public Client：缓存与聚合](https://viem.sh/docs/clients/public)
依据：[viem getChainId：返回number](https://viem.sh/docs/actions/public/getChainId)

## 2. 单次 readContract 验证 ABI 与原返回

以既有ABI as const保留类型，传address、abi、functionName:'balanceOf'、args:[publicAddress]读取目标方法；decimals/totalSupply单独选正确view/pure函数，不按图标或symbol合并合约。

返回类型按ABI推导，uint256通常bigint；保存raw.toString()。需要展示时用formatUnits(raw, knownDecimals)，例如420000000000n与精度9显示'420'；未知decimals保留整数不补18。编码错误、空返回或revert均保留原错。

核对结果：先有一项可解释的ABI结果或明确失败，再决定是否聚合。
依据：[viem readContract：ABI、块与Overrides](https://viem.sh/docs/contract/readContract)
依据：[viem formatUnits：整数与指数](https://viem.sh/docs/utilities/formatUnits)

## 3. 把所有读请求固定在相同块范围

将单次和后续multicall的blockNumber设为同一个bigint，如已有块15121123对应15121123n。网页Type:number与n示例冲突，固定CallParameters源码为bigint；实际项目按对应版本类型校验，不做浮点转换。

blockNumber、blockTag、blockHash选择一类；requireCanonical仅和blockHash配合。默认call标签受client.experimental_blockTag影响，未配置时latest；因此不要把多次latest当同一快照。历史状态、safe/finalized或EIP1898未受节点支持时留错误，不回落最新后仍标旧块。

核对结果：每一项输入的区块选择相同，兼容/历史状态缺口明确。
依据：[viem readContract：ABI、块与Overrides](https://viem.sh/docs/contract/readContract)
依据：[viem 固定源码：block选择与call边界](https://raw.githubusercontent.com/wevm/viem/6501d059faac4f92cadc0099f67cf3fcc6aa6f4f/src/actions/public/call.ts)
依据：[viem Public Client：缓存与聚合](https://viem.sh/docs/clients/public)

## 4. 建立有顺序的 multicall 列表并核部署

将contracts数组按确定顺序列出，每项保留address/abi/functionName/args，在client.multicall({ contracts, blockNumber, allowFailure:true })中使用同一块。另存输入索引，不能按返回显示name猜原合约。

默认使用chain.contracts.multicall3；该地址须在所读历史块已部署。缺地址/部署条件、RPC calldata上限或历史状态问题都可能影响整个批次。deployless或自定义multicallAddress是另一路径，不在错误时自动开启来掩盖配置缺口，也不能当链上部署证明。

核对结果：输入顺序、块范围和Multicall3来源/限制清楚；不是盲目拆分重试。
依据：[viem multicall：逐项失败与字节批次](https://viem.sh/docs/contract/multicall)
依据：[viem 固定源码：multicall结果分支](https://raw.githubusercontent.com/wevm/viem/6501d059faac4f92cadc0099f67cf3fcc6aa6f4f/src/actions/public/multicall.ts)

## 5. 逐项检查状态，保留文档字段差异

当前页面示例与固定源码是{status:'success',result}或{status:'failure',error,result:undefined}；网页Return Value却写data/reverted，不能混用。核项目实际版本/原对象，按对应分支处理并保留原响应。

allowFailure默认true，外层完成不表示全部成功；按原索引逐项归档。合约revert、解码错或整批RPC失败都可成为failure，undefined不是0。allowFailure:false遇错抛出，成功是直接值数组，不能再按包装对象读取；只有成功项可进入数量汇总。

核对结果：输入与返回项一一对应，失败不被吞掉或计为零。
依据：[viem multicall：逐项失败与字节批次](https://viem.sh/docs/contract/multicall)
依据：[viem 固定源码：multicall结果分支](https://raw.githubusercontent.com/wevm/viem/6501d059faac4f92cadc0099f67cf3fcc6aa6f4f/src/actions/public/multicall.ts)

## 6. 按字节控制批次并交付有限快照

batchSize默认1024是编码calldata字节，不是1024次调用；0取消该尺寸分割不等于RPC无上限。Public Client的batch.multicall默认false，Promise.all不会自动证明开启聚合；启用后wait/cache也需记录，不能据优化文档推实际账单。

输出项目版本/chainId、块选择、输入索引、原整数/精度、逐项status与原error。聚合成功只说明该读取范围，不能证明代币安全、实时余额或交易完成；cacheTime毫秒与CCIP offchain查找也影响请求来源/新鲜度，诊断记录应带配置。

核对结果：形成同范围读取表与失败清单，额度/缓存和未覆盖方法单列。
依据：[viem Public Client：缓存与聚合](https://viem.sh/docs/clients/public)
依据：[viem multicall：逐项失败与字节批次](https://viem.sh/docs/contract/multicall)
依据：[viem 固定源码：multicall结果分支](https://raw.githubusercontent.com/wevm/viem/6501d059faac4f92cadc0099f67cf3fcc6aa6f4f/src/actions/public/multicall.ts)
依据：[viem formatUnits：整数与指数](https://viem.sh/docs/utilities/formatUnits)

## 常见错误
- Multicall3的调用环境与普通单次call并非可无条件互换，sender相关方法要核合约实际逻辑。
- 官方返回表与示例存在冲突；固定main代码不是已安装npm版本验收。
- RPC收费/认证/历史部署未实测；没有通过批量调用证明整个链或全部资产覆盖。

## 完成检查
- 得到可复核的同块输入索引—返回状态—原整数/错误对照。
- 缺失项、节点服务范围和版本差异保留；未签名或发送交易。

## 相关知识
- [私有 RPC 能防止所有抢跑和夹子交易吗？](https://lianxiaozhi.com/learn/private-rpc-trust-boundaries)
- [原始、解码和整理后的链上数据有什么区别？](https://lianxiaozhi.com/learn/raw-decoded-curated-data)
- [合约代码已验证，为什么仍不能直接判断安全？](https://lianxiaozhi.com/learn/verified-contract-boundaries)

## 官方依据
- [viem 入门：Public Client与RPC访问](https://viem.sh/docs/getting-started)
- [viem Public Client：缓存与聚合](https://viem.sh/docs/clients/public)
- [viem getChainId：返回number](https://viem.sh/docs/actions/public/getChainId)
- [viem readContract：ABI、块与Overrides](https://viem.sh/docs/contract/readContract)
- [viem formatUnits：整数与指数](https://viem.sh/docs/utilities/formatUnits)
- [viem 固定源码：block选择与call边界](https://raw.githubusercontent.com/wevm/viem/6501d059faac4f92cadc0099f67cf3fcc6aa6f4f/src/actions/public/call.ts)
- [viem multicall：逐项失败与字节批次](https://viem.sh/docs/contract/multicall)
- [viem 固定源码：multicall结果分支](https://raw.githubusercontent.com/wevm/viem/6501d059faac4f92cadc0099f67cf3fcc6aa6f4f/src/actions/public/multicall.ts)
