You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ethers.js getBlockNumber在BSC测试网返回区块号不更新如何解决

问题原因

这个异常是BSC测试网的RPC缓存策略、链参数和ethers.js默认适配逻辑不匹配导致的,和循环逻辑本身没有直接关系:

  • ethers.js的Web3Provider默认会缓存第一次获取到的区块号,正常在以太坊系网络上会通过监听新区块事件自动更新缓存,但BSC测试网的区块头广播格式和标准以太坊存在细微差异,导致自动刷新缓存的逻辑失效,后续调用getBlockNumber()只会直接返回第一次缓存的固定值。
  • BSC测试网公开RPC节点对eth_blockNumber接口的缓存策略比Rinkeby严格,部分节点默认缓存时间可达2-5秒,即使绕过本地缓存,短时间内重复请求也可能拿到滞后的高度值。
  • 如果把new ethers.providers.Web3Provider(window.ethereum)写在了循环内部,每次循环都会生成全新的provider实例,每个实例初始化时只会拉取一次区块号存入缓存,自然不会随区块高度更新。
修复方法

按改动成本从低到高排序,选一种即可:

1. 绕过本地缓存直接调用底层RPC

把原来调用getBlockNumber()的逻辑替换为直接发RPC请求,跳过ethers的内置缓存层,这是改动最小的方案:

// 原代码:const blockNumber = await provider.getBlockNumber();
// 替换为:
const blockNumber = await provider.send("eth_blockNumber", [])
  .then(hexBlockNum => parseInt(hexBlockNum, 16));

2. 初始化Provider时关闭缓存、适配BSC参数

不要在循环内重复创建provider实例,在循环外全局初始化一次,传入配置关闭缓存、调整轮询间隔匹配BSC3秒出块的速度:

// 放在循环外初始化,只执行一次
const provider = new ethers.providers.Web3Provider(window.ethereum, {
  chainId: 97,
  name: "bsc-testnet",
  cache: "no-store", // 禁用内置缓存
  pollingInterval: 3000 // 轮询间隔和BSC出块时间对齐
});

// 循环内直接调用即可
const blockNumber = await provider.getBlockNumber();

3. 调用getLogs时不依赖本地维护的区块号

如果获取区块号的核心用途是传给getLogs当toBlock参数,直接传字符串"latest"即可,让RPC节点自动匹配当前最新高度,完全避免本地区块号不准的问题:

const logs = await provider.getLogs({
  address: "目标合约地址",
  topics: [/* 你需要的事件topic */],
  fromBlock: /* 你设定的起始区块 */,
  toBlock: "latest" // 不需要自己传当前区块号
});
避坑提示
  • 不要在循环内重复实例化provider,不仅会加重缓存异常问题,还容易触发钱包、RPC节点的请求频率限制
  • BSC测试网公开节点的限流阈值比Rinkeby低很多,轮询间隔不要低于2秒,否则很容易被节点拦截返回固定数据
  • 如果对区块号实时性要求极高,可以在请求前加100-200ms的延时,避开RPC节点的接口缓存窗口

内容的提问来源于stack exchange,提问作者sdfsdf

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 22:42:15