如何在Hardhat测试中监听外部库触发的事件?
监听未在被调用合约ABI中声明的库事件(Hardhat+Chai测试)
要监听这种由独立库触发、未被调用合约(A)声明的事件,核心思路是直接解析交易收据(receipt)中的日志,手动用事件所在库的ABI解码。具体步骤如下:
1. 定义Events库的事件ABI
首先抽取出Events库中DataChanged事件的ABI(支持人类可读格式或JSON格式):
const EVENTS_ABI = [ "event DataChanged()" ];
2. 编写测试逻辑
在测试用例中,调用合约方法后获取交易收据,通过事件签名的哈希匹配日志,再解码验证:
const { expect } = require("chai"); const { ethers } = require("hardhat"); // 定义Events库的事件ABI const EVENTS_ABI = [ "event DataChanged()" ]; describe("Contract A Event Test", function () { let contractA; beforeEach(async function () { // 部署合约A const ContractA = await ethers.getContractFactory("A"); contractA = await ContractA.deploy(); await contractA.deployed(); }); it("should capture DataChanged event from Events library", async function () { // 调用合约A的callToB方法,获取交易响应 const txResponse = await contractA.callToB(); // 等待交易上链,获取完整收据 const txReceipt = await txResponse.wait(); // 创建Events库的Interface实例,用于解码事件 const eventsInterface = new ethers.Interface(EVENTS_ABI); // 计算DataChanged事件的topic(事件签名的Keccak256哈希) const dataChangedTopic = ethers.id("DataChanged()"); // 从收据日志中筛选出目标事件 const targetLogs = txReceipt.logs.filter(log => log.topics[0] === dataChangedTopic); // 验证事件是否被触发 expect(targetLogs.length).to.equal(1, "DataChanged event should be emitted once"); // 解码事件(无参数时args为空数组) const decodedEvent = eventsInterface.parseLog(targetLogs[0]); expect(decodedEvent.name).to.equal("DataChanged"); }); });
关键说明
- 无法直接用
contractA.on("DataChanged")监听:因为合约A的ABI中未包含该事件,合约实例无法识别这个事件的签名。 - 事件topic的作用:每一个事件的签名(如
DataChanged())都会被哈希成唯一的topic,作为日志的第一个元素,用来匹配对应事件。 - 库事件的日志会被包含在交易收据中:不管触发事件的是库还是合约,只要交易执行了事件触发逻辑,日志就会被记录在交易收据里。
内容的提问来源于stack exchange,提问作者JuanXavier
相关产品推荐
相关产品推荐

