如何通过EthersJS获取以太坊交易所在区块的Finalized状态
使用EthersJS等待区块Finalized以确保事件不被回滚
核心逻辑
当监听到合约事件时,先记录该事件所在的区块号,等待链上的finalized区块号大于等于这个目标区块号——此时该区块已被以太坊共识层最终确认,不会发生回滚,即可安全处理后续业务逻辑。
实现步骤(基于EthersJS v6)
1. 初始化Provider与合约实例
先连接到支持finalized区块查询的以太坊节点(如Infura、Alchemy提供的RPC节点),并初始化合约实例:
const { ethers } = require("ethers"); // 替换为你的RPC节点地址 const provider = new ethers.JsonRpcProvider("https://mainnet.infura.io/v3/你的项目ID"); // 替换为你的合约ABI和地址 const contractABI = [ // 包含你要监听的事件定义,示例: "event YourEvent(address indexed sender, uint256 value)" ]; const contractAddress = "0x1234567890abcdef1234567890abcdef12345678"; const contract = new ethers.Contract(contractAddress, contractABI, provider);
2. 监听目标事件并触发等待逻辑
监听合约的目标事件,收到事件后立即启动等待区块finalized的流程:
// 监听目标事件 contract.on("YourEvent", async (sender, value, event) => { const eventBlockNum = event.blockNumber; console.log(`捕获事件,所在区块:${eventBlockNum}`); // 等待区块被finalized await waitForFinalizedBlock(eventBlockNum); // 区块确认后的业务逻辑 console.log(`区块 ${eventBlockNum} 已Finalized,开始处理业务`); // 这里写入你的业务代码:更新数据库、发送通知等 });
3. 实现等待Finalized区块的函数
推荐使用监听finalized区块事件的方式,避免无效轮询:
async function waitForFinalizedBlock(targetBlockNum) { return new Promise(async (resolve) => { // 先检查当前已finalized的区块是否满足要求 const currentFinalizedBlock = await provider.getBlock("finalized"); if (currentFinalizedBlock.number >= targetBlockNum) { resolve(); return; } // 监听后续finalized区块,满足条件后移除监听并返回 const listener = provider.on("finalized", (block) => { if (block.number >= targetBlockNum) { provider.off("finalized", listener); resolve(); } }); }); }
兼容EthersJS v5的实现
如果使用v5版本,调整Provider初始化和监听逻辑即可:
// v5初始化Provider const provider = new ethers.providers.JsonRpcProvider("你的RPC地址"); // 等待finalized区块的函数(v5) async function waitForFinalizedBlock(targetBlockNum) { return new Promise(async (resolve) => { const currentFinalizedBlock = await provider.getBlock("finalized"); if (currentFinalizedBlock.number >= targetBlockNum) { resolve(); return; } // v5监听区块事件,间接判断finalized状态 const listener = provider.on("block", async (blockNum) => { const finalizedBlock = await provider.getBlock("finalized"); if (finalizedBlock.number >= targetBlockNum) { provider.off("block", listener); resolve(); } }); }); }
注意事项
- 确保你的RPC节点支持
finalized区块查询:只有以太坊合并后的网络(主网、Sepolia、Goerli等)才有finalized状态,旧测试网如Ropsten已废弃,不支持。 - 异常处理:建议在代码中添加try-catch块,处理节点连接失败、查询超时等异常,避免业务流程阻塞。
- 轮询备选方案:如果需要自定义查询间隔,也可以用定时轮询替代事件监听,比如每10秒查询一次finalized区块状态。
内容的提问来源于stack exchange,提问作者Remi_la
相关产品推荐
相关产品推荐

