为何通过delegateCall调用智能合约函数时触发的事件会消失?
delegatecall场景下Hardhat测试捕获不到事件的原因
核心原因来自Solidity中delegatecall的运行机制:delegatecall会在调用方合约的上下文中执行被调用方的代码,全程不会切换执行所属的合约地址。
具体到你的场景:
- 直接调用
DelegateCallee合约的getData()函数时,代码运行在DelegateCallee自身的上下文,EVM会将Received事件标记为从DelegateCallee地址发出,因此测试中在delagateCallee实例上监听事件可以正常匹配。 - 通过
DelegateCaller的delegateCallGetData()发起delegatecall时,DelegateCallee的getData()逻辑实际运行在DelegateCaller的上下文内,使用DelegateCaller的存储、DelegateCaller的地址作为执行主体:- 此时触发的
Received事件,EVM会标记为从DelegateCaller地址发出,而非DelegateCallee地址 - 你的测试代码是在
delagateCallee实例上监听事件,Hardhat的chai匹配器会自动过滤掉地址不匹配的日志,自然无法捕获到从DelegateCaller发出的同名事件,最终抛出“事件未被触发”的断言错误。
- 此时触发的
修正方案
将测试中监听事件的合约实例替换为delegateCaller即可正常捕获事件,修正后的测试代码片段如下:
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(delegateCaller, 'Received') .withArgs(deployer.address, value); });
补充说明:delegatecall模式下事件的发送方永远是发起delegatecall的合约地址,与逻辑来源的被调用合约无关,是delegatecall的常见易错点。另外你的代码中变量名
delagateCallee存在拼写错误(正确拼写为delegateCallee),虽然不影响当前逻辑运行,建议修正避免后续排查问题时产生误导。
内容的提问来源于stack exchange,提问作者Aleks Shenshin
相关产品推荐
相关产品推荐

