How to test for events that are dispatched in a nested operation?

I have a function in a contract that dispatches an event. But it also executes functions on other contracts that dispatch an event each. The first event is part of the transaction, the latter events not.

Example code:

function assignTeamVesting(address teamVesting) public onlyOwner {
    require(teamVesting != address(0), "xxxTokenSale: no Zero address allowed as team vesting address");

    _teamVesting = teamVesting;
    emit TeamVestingAssigned(_teamVesting);

    // update time locks/token vesting contracts as well
    if(_timeLocks.length > 0) {
        for(uint256 i = 0; i < _timeLocks.length; i++) {
            TokenVesting vesting = _timeLocks[i];
            vesting.updateBeneficiary(teamVesting);
        }
    }
}

The TokenVesting contracts function updateBeneficiary is:

function updateBeneficiary(address beneficiary) public onlyOwnerOrUpdater {
    address oldBeneficiary = _beneficiary;
    _beneficiary = beneficiary;
    emit BeneficiaryUpdate(_beneficiary, oldBeneficiary);
}

truffle test --show-events shows that all events was dispatched as expected:

TeamVestingAssigned(teamVesting: 0x90dAE34cb6e655868e1ee6D358591548D605d7D8 (address))
BeneficiaryUpdate(beneficiary: 0x90dAE34cb6e655868e1ee6D358591548D605d7D8 (address), oldBeneficiary: 0xFcCe0b0346cde1e6c609757345Fc34d5D9a60102 (address))
BeneficiaryUpdate(beneficiary: 0x90dAE34cb6e655868e1ee6D358591548D605d7D8 (address), oldBeneficiary: 0xFcCe0b0346cde1e6c609757345Fc34d5D9a60102 (address))

But in a test, when I execute the following line:

      const tx = await this.tokenSale.assignTeamVesting(another, {from: owner});

only the first event can be found in tx.logs:

[ { logIndex: 0,
    transactionIndex: 0,
    transactionHash:
     '0x2323194579b38689dd14cb1c81f4e69683cb3f6005f60b6c17bced36ea06c291',
    blockHash:
     '0x37506a8e1f0500422efa530ef187dd182c46995f160fce3c0f8e25109d87d7d1',
    blockNumber: 4696,
    address: '0x5BEd044dc960763E7C058B11dbc18e37C45CAe05',
    type: 'mined',
    id: 'log_65c362d2',
    event: 'TeamVestingAssigned',
    args:
     Result {
       '0': '0x90dAE34cb6e655868e1ee6D358591548D605d7D8',
       __length__: 1,
       teamVesting: '0x90dAE34cb6e655868e1ee6D358591548D605d7D8' } } ]

Is there any way to test for the other events as well?

(Maybe I should also ask on truffle-repo)

1 Like

Use openzeppelin-test-helpers expectEvent.inTransaction to test for events from other contracts

https://github.com/OpenZeppelin/openzeppelin-test-helpers#async-intransaction-txhash-emitter-eventname-eventargs--

async inTransaction (txHash, emitter, eventName, eventArgs = {})

Same as inLogs , but for events emitted in an arbitrary transaction (of hash txHash ), by an arbitrary contract ( emitter ), even if it was indirectly called (i.e. if it was called by another smart contract and not an externally owned account).


I put together the following sample code to illustrate

B.test.js

const { expectEvent } = require('openzeppelin-test-helpers');

const A = artifacts.require('A');
const B = artifacts.require('B');

contract('B', function () {
  beforeEach(async function () {
    this.a = await A.new();
    this.b = await B.new(this.a.address);
  });

  it('does stuff', async function () {
    const receipt = await this.b.doBStuff();

    // Log-checking will not only look at the event name, but also the values, which can be addresses, strings, numbers, etc.
    await expectEvent.inLogs(receipt.logs, 'DidBStuff', { message: "B" });
    await expectEvent.inTransaction(receipt.tx, A, 'DidAStuff', { message: "A" });
  });
});

A.sol

pragma solidity ^0.5.0;

contract A {

    function doAStuff() public {
        emit DidAStuff("A");
    }

    event DidAStuff(string message);
}

B.sol

pragma solidity ^0.5.0;

import "./A.sol";

contract B {

    A _a;

    constructor(A a) public {
        _a = a;
    }

    function doBStuff() public {
        _a.doAStuff();
        emit DidBStuff("B");
    }

    event DidBStuff(string message);
}
2 Likes

Hi @itinance if my reply answered your question, could you mark it as the solution please, thank you. :pray: Otherwise let me know if you need more information.

Hey guys… sorry to have to reopen this. I’m trying to use expectEvent.inTransaction with
the transaction hash, and contract instance however im getting errors.
code:

contract instance

the function

where I get my receipt from

from my test, the bottom expectEvent works fine, top one gives me error

error:

mintAndRedeem is the function that indirectly emits the events. I can see the Transfer event emitted by the mintNFT function just fine. I cant however see the Transfer event from the transfer function right below it. any help would be amazing! I can supply more info if needed! disclaimer: im a beginner and im working on refactoring my code incase you see anything dumb lol

Never mind, I think I got it. I was passing in the wrong contract instance