为什么通过“delegateCall”调用的智能合约函数发出的事件会消失?

Ale*_*hin 5 solidity rsk ethers.js hardhat

我有两个交互的智能合约,正在 Hardhat 中开发/测试并部署到RSK

\n
    \n
  1. DelegateCallee带有函数的智能合约getData(),发出一个Received事件:
  2. \n
\n
contract DelegateCallee {\n  event Received(address sender, uint value);\n  function getData() external payable {\n    emit Received(msg.sender, msg.value);\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n
    \n
  1. DelegateCaller智能合约使用 Solidity来delegateCall调用:getData()DelegateCallee
  2. \n
\n
contract DelegateCaller {\n  address callee;\n\n  constructor(address _callee) {\n    callee = _callee;\n  }\n\n  function delegateCallGetData() external payable {\n    (bool success, ) = callee.delegatecall(\n      abi.encodeWithSelector(DelegateCallee.getData.selector)\n    );\n    require(success, "delegate call failed");\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

我正在运行 Hardhat 测试来拦截DelegateCallee. 当我getData()直接调用 时DelegateCallee,事件将按预期发出:

\n
contract DelegateCallee {\n  event Received(address sender, uint value);\n  function getData() external payable {\n    emit Received(msg.sender, msg.value);\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n
contract DelegateCaller {\n  address callee;\n\n  constructor(address _callee) {\n    callee = _callee;\n  }\n\n  function delegateCallGetData() external payable {\n    (bool success, ) = callee.delegatecall(\n      abi.encodeWithSelector(DelegateCallee.getData.selector)\n    );\n    require(success, "delegate call failed");\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

但是,当我使用DelegateCaller合约时,\n调用它的\n delegateCallGetData(),\n进而调用DelegateCallee合约的\ngetData()函数,该Received事件不会被发出。

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

我的事件在哪里丢失了?

\n

Gin*_*hon 5

最有可能的是,该Received事件就在那里,尽管 Hardhat 并不认为它是一个DelegateCallee事件。发生这种情况是因为您通过调用该函数delegateCall。Waffle/ Hardhat 期望事件发射器是DelegateCallee智能合约,但由于该函数是在另一个智能合约的上下文中调用的,因此DelegateCaller可能已注册为发射器。为了确保这一点,您应该尝试以下操作:

  1. 获取交易收据:
const tx = await delegateCaller.delegateCallGetData({ value });
const receipt = await tx.wait();
Run Code Online (Sandbox Code Playgroud)
  1. 查找调用者(而不是被调用者)发出的事件
const event = receipt.events.find(
    (e) => e.address === delegateCaller.address,
);
Run Code Online (Sandbox Code Playgroud)
  1. 使用interface.decodeEventLog()(来自 ethers.js)解码此事件的参数
const decodedEvent = delagateCallee.interface.decodeEventLog(
    'Received',
    event.data,
    event.topics,
);
Run Code Online (Sandbox Code Playgroud)
  1. 使用断言来验证接收到的值整个测试应该如下所示:
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);
});
Run Code Online (Sandbox Code Playgroud)