使用 ethers js 运行安全帽测试时,合同事件侦听器未触发
Posted
技术标签:
【中文标题】使用 ethers js 运行安全帽测试时,合同事件侦听器未触发【英文标题】:Contract event listener is not firing when running hardhat tests with ethers js 【发布时间】:2021-09-26 15:38:34 【问题描述】:这里有一个非常小的 repo 来显示这个问题:https://github.com/adamdry/ethers-event-issue
但我也会在这里解释一下。这是我的合同:
//SPDX-License-Identifier: UNLICENSED;
pragma solidity 0.8.4;
contract ContractA
event TokensMinted(uint amount);
function mint(uint amount) public
emit TokensMinted(amount);
这是我的测试代码:
import * as chai from 'chai'
import BigNumber, ContractTransaction from 'ethers'
import ethers from 'hardhat'
import ContractA, ContractAFactory from '../typechain'
const expect = chai.expect
describe("Example test", function ()
it("should fire the event", async function ()
const [owner] = await ethers.getSigners();
const contractAFactory = (await ethers.getContractFactory(
'ContractA',
owner,
)) as ContractAFactory
const contractA: ContractA = await contractAFactory.deploy()
contractA.on('TokensMinted', (amount: BigNumber) =>
// THIS LINE NEVER GETS HIT
console.log('###########')
)
const contractTx: ContractTransaction = await contractA.mint(123)
const contractReceipt: ContractReceipt = await contractTx.wait()
for (const event of contractReceipt.events!)
console.log(JSON.stringify(event))
);
);
我原以为 ###########
会打印到控制台,但事实并非如此,因此由于某种原因没有执行侦听器函数。
如果我深入了解 ContractReceipt,就会发现正确的事件数据:
"transactionIndex": 0,
"blockNumber": 2,
"transactionHash": "0x55d118548c8200e5e6c19759d9aab56cb2e6a274186a92643de776d617d51e1a",
"address": "0x5FbDB2315678afecb367f032d93F642f64180aa3",
"topics": [
"0x772f66a00a405709c30e7f18feadcc8f123b20c09c7260165d3eec36c9f21372"
],
"data": "0x000000000000000000000000000000000000000000000000000000000000007b",
"logIndex": 0,
"blockHash": "0x808e6949118509b5a9e482e84cf47921a2fcffbcd943ebbd8ce4f6671469ee01",
"args": [
"type": "BigNumber",
"hex": "0x7b"
],
"event": "TokensMinted",
"eventSignature": "TokensMinted(uint256)"
【问题讨论】:
【参考方案1】:完整答案在这里:https://github.com/nomiclabs/hardhat/issues/1692#issuecomment-905674692
但总而言之,这不起作用的原因是 ethers.js 默认使用轮询来获取事件,轮询间隔为 4 秒。如果您在测试结束时添加:
await new Promise(res => setTimeout(() => res(null), 5000));
事件应该触发。
但是!您还可以像这样调整给定合约的轮询间隔:
// at the time of this writing, ethers' default polling interval is
// 4000 ms. here we turn it down in order to speed up this test.
// see also
// https://github.com/ethers-io/ethers.js/issues/615#issuecomment-848991047
const provider = greeter.provider as EthersProviderWrapper;
provider.pollingInterval = 100;
如此处所示:https://github.com/nomiclabs/hardhat/blob/master/packages/hardhat-ethers/test/index.ts#L642
但是! (再次)如果您想从事件中获取结果,以下方法不需要更改轮询或任何其他基于“时间”的解决方案,根据我的经验,这可能会导致不稳定的测试:
it('testcase', async() =>
const tx = await contract.transfer(...args); // 100ms
const rc = await tx.wait(); // 0ms, as tx is already confirmed
const event = rc.events.find(event => event.event === 'Transfer');
const [from, to, value] = event.args;
console.log(from, to, value);
)
这是我的 TypeScriptyfied 版本(根据我自己的合同,事件和参数略有不同):
const contractTx: ContractTransaction = await tokenA.mint(owner.address, 500)
const contractReceipt: ContractReceipt = await contractTx.wait()
const event = contractReceipt.events?.find(event => event.event === 'TokensMinted')
const amountMintedFromEvent: BigNumber = event?.args!['amount']
这是与上述内容一致的事件声明:
event TokensMinted(uint amount);
【讨论】:
以上是关于使用 ethers js 运行安全帽测试时,合同事件侦听器未触发的主要内容,如果未能解决你的问题,请参考以下文章
区块链-前端交互第四篇:认识 ethers.js并运行测试代码
如何使用 waffle 和 ethers.js 测试应付/外部方法
Web3 系列开发教程——创建你的第一个 NFT使用 Ethers.js 铸造 NFT | 测试用例
ethers.js swapExactETHForTokens 和 swapExactTokensForTokens on pancake swap