5

I have two interacting smart contracts that I am developing/testing in Hardhat and deploying to RSK:

  1. DelegateCallee smart contract with a getData() function, emitting a Received event:
contract DelegateCallee {
  event Received(address sender, uint value);
  function getData() external payable {
    emit Received(msg.sender, msg.value);
  }
}
  1. DelegateCaller smart contract which uses Solidity's delegateCall to invoke getData() from the DelegateCallee:
contract DelegateCaller {
  address callee;

  constructor(address _callee) {
    callee = _callee;
  }

  function delegateCallGetData() external payable {
    (bool success, ) = callee.delegatecall(
      abi.encodeWithSelector(DelegateCallee.getData.selector)
    );
    require(success, "delegate call failed");
  }
}

I am running Hardhat tests to intercept the events emitted by DelegateCallee. When I am invoking getData() directly on the DelegateCallee, the event is being emitted as expected:

  it('getData function on callee should emit the Received event', async () => {
    const value = ethers.utils.parseEther('1');
    const tx = await delagateCallee.getData({ value });
    await expect(tx)
      .to.emit(delagateCallee, 'Received')
      .withArgs(deployer.address, value);
  });
  Low level calls
    ✔ getData function on callee should emit the Received event

However when I use the DelegateCaller contract, invoking its delegateCallGetData(), which in turn invokes the DelegateCallee contract's getData() function, the Received event is not emitted.

  it('delegateCallGetData function on caller should emit the Received event on the callee', async () => {
    const value = ethers.utils.parseEther('1');
    const tx = await delegateCaller.delegateCallGetData({ value });
    await expect(tx)
      .to.emit(delagateCallee, 'Received')
      .withArgs(deployer.address, value);
  });
  1) Low level calls
       delegateCallGetData function on caller should emit the Received event on the callee:
     AssertionError: Expected event "Received" to be emitted, but it wasn't

Where is my event lost?

bguiz
  • 27,371
  • 47
  • 154
  • 243
Aleks Shenshin
  • 2,117
  • 5
  • 18

1 Answers1

5

Most likely, the Received event is there, though Hardhat doesn't recognise it as a DelegateCallee event. This happens because you are invoking the function via delegateCall. Waffle/ Hardhat expects the event emitter to be the DelegateCallee smart contract, but due to the fact that the function was called in the context of another smart contract, the DelegateCaller was probably registered as an emitter. To make sure, you should try the following:

  1. Get the transaction receipt:
const tx = await delegateCaller.delegateCallGetData({ value });
const receipt = await tx.wait();
  1. Find an event emitted by the caller (not the callee)
const event = receipt.events.find(
    (e) => e.address === delegateCaller.address,
);
  1. Use interface.decodeEventLog() (from ethers.js) to decode this event's parameters
const decodedEvent = delagateCallee.interface.decodeEventLog(
    'Received',
    event.data,
    event.topics,
);
  1. Use assertions to verify the received values The whole test should look something like this:
it('should emit on the callee', async () => {
    const value = ethers.utils.parseEther('1');
    const tx = await delegateCaller.delegateCallGetData({ value });
    const receipt = await tx.wait();
    const event = receipt.events.find(
      (e) => e.address === delegateCaller.address,
    );
    const decodedEvent = delagateCallee.interface.decodeEventLog(
      'Received',
      event.data,
      event.topics,
    );
    expect(decodedEvent.sender).to.equal(deployer.address);
    expect(decodedEvent.value).to.equal(value);
});
Gino Osahon
  • 399
  • 1
  • 6