使用 Wagmi 读取过去区块的合约状态
Source: Dev.to
核心思路
以太坊(以及一般的 EVM 链)允许你查询任意历史区块时的合约状态。如果合约方法是 view 或 pure,就可以在过去的区块上读取,而无需任何特殊的索引基础设施。Wagmi 通过 blockNumber 参数直接提供了这种能力。
场景:在过去的时间点获取 Chainlink 价格
Chainlink 价格喂价合约提供 latestRoundData() 等方法。如果你已经知道轮次 ID,可以直接查询,但有时你只知道时间戳。一个可靠的做法是把感兴趣的时间戳转换为区块号,然后在该区块上读取合约。
import { readContract } from '@wagmi/core';
import { parseAbi } from 'viem';
const aggregatorAbi = parseAbi([
'function latestRoundData() view returns (uint80,int256,uint256,uint256,uint80)'
]);
const priceFeedAddress = '0x...';
const blockNumber = 18_500_000n; // 对应目标时间戳的区块
const data = await readContract({
address: priceFeedAddress,
abi: aggregatorAbi,
functionName: 'latestRoundData',
blockNumber,
});
Wagmi 向 RPC 节点询问:“假装链在区块 18,500,000 时的状态。这个函数会返回什么?”返回的结果正是 Chainlink 合约在该区块时会返回的值,而不是当前值。
为什么可行
以太坊节点会保存历史状态。当你指定 blockNumber 时,节点会在那个历史快照上执行调用——不需要子图或外部索引器,只依赖确定性的区块链状态。
将时间映射到区块
通常你知道的是时间戳而不是区块号。获取对应区块的常见方法:
- 二分搜索:使用
eth_getBlockByNumber按时间戳搜索区块。 - 辅助 API(例如区块浏览器服务),一次获取后缓存映射关系。
- 归档节点:获取最精确的结果。
得到区块号后,Wagmi 会处理剩下的工作。
使用 React 的前端示例
如果在 React 应用中使用 Wagmi Hook:
import { useReadContract } from 'wagmi';
const { data } = useReadContract({
address: priceFeedAddress,
abi: aggregatorAbi,
functionName: 'latestRoundData',
blockNumber: 18_500_000n,
});
用例
- 历史价格图表
- 审计过去的合约状态
- 任何需要特定时间点链上数据的功能
重要注意事项
- RPC 支持:并非所有 RPC 提供商都暴露历史状态;通常需要归档节点。
- 性能:查询旧区块的速度可能慢于当前状态的调用。
最后思考
Wagmi 并未大量宣传这个特性,但一旦意识到可以在任意时间点读取合约状态,许多问题就会变得更简单。利用 blockNumber 可以从过去检索准确的链上数据,而无需依赖链下快照或索引器。