题目
一个代币列表页要同时读取多个代币的余额与元数据。上线后出现两种问题:某个代币的读取会 revert,结果整个列表变空白;另一个页面则相反,部分失败被静默吞掉,用户看到余额显示为 0。请说明 useReadContracts 的行为与正确的处理方式。
考察目标
- 批量读的聚合方式与默认行为。
- allowFailure 的默认值与逐项判断。
- calldata 体积与聚合方法的差异。
说明批量读的聚合方式、allowFailure 的默认值与逐项判断,以及 Multicall3 聚合方法的差异。
一个代币列表页要同时读取多个代币的余额与元数据。上线后出现两种问题:某个代币的读取会 revert,结果整个列表变空白;另一个页面则相反,部分失败被静默吞掉,用户看到余额显示为 0。请说明 useReadContracts 的行为与正确的处理方式。
useReadContracts 会把 contracts 数组中的多项聚合成尽量少的调用,默认使用 Multicall3 地址;allowFailure 默认为 true,单个子调用 revert 时整体不抛错,而是在结果数组里标记失败项,因此 UI 需要逐项判断;把 allowFailure 设为 false 时任一失败会让整体失败。batchSize 默认 1024 字节,用于限制单次聚合的 calldata 体积。实现差异与工程取舍见深入回答。
// 省略上下文:abi 与代币地址来自项目内的配置,展示层组件未展开。
const { data } = useReadContracts({
contracts: [
{
abi: erc20Abi,
address: tokenA,
functionName: "balanceOf",
args: [account],
},
{
abi: erc20Abi,
address: tokenB,
functionName: "balanceOf",
args: [account],
},
],
allowFailure: true,
query: { enabled: Boolean(account) },
});
data?.forEach((item) => {
if (item.status === "failure") {
// 该项读取失败:展示兜底内容,避免把 undefined 渲染成 0。
return;
}
// item.result 仅在 status 为 success 时可用。
});
发现这道题有问题? 反馈此题