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
相关产品推荐
相关产品推荐

