solidity - 使用 ethers js 运行安全帽测试时,契约(Contract)事件监听器未触发

标签 solidity ethers.js hardhat

这是一个非常小的 repo 来显示问题:https://github.com/adamdry/ethers-event-issue
但我也会在这里解释。这是我的契约(Contract):

//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)"
}

最佳答案

完整答案在这里: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
然而! (再次)如果您想从事件中获取结果,以下方法不需要更改轮询或任何其他基于“时间”的解决方案,根据我的经验,这可能会导致 flakey 测试:
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 版本(根据我自己的契约(Contract),事件和参数略有不同):
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);

关于solidity - 使用 ethers js 运行安全帽测试时,契约(Contract)事件监听器未触发,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68432609/

相关文章:

ethereum - 调用带参数的函数时如何获取返回值

Solidity 类型错误 : This type is only supported in the new experimental ABI encoder

javascript - tx.wait 与 Ethers.js 永远不会解决

bash - 与托管以太坊节点的操作系统交互

syntax-error - 新手 Solidity 错误/问题 — 'ParserError: Expected identifier but got ' 函数'...'

javascript - 在带有以太 JS 的 Next Js 中使用 window.ethereum 时出错

rust - 等效于 abi.encodePacked

chainlink - 我正在尝试在 TRON 区 block 链上实现 openzeppelin 的最小代理克隆合约

reactjs - 使用 React 配置 Solidity 合约的最佳方法是什么?

ethereum - 如何使用不同的地址来调用安全帽测试和脚本中的函数?