Keeper合约调用delegatecall修改ContractB状态变量失效问题
问题根源
你混淆了delegatecall和普通call的使用场景,这是出现问题的核心原因:
delegatecall的运行机制是借用目标合约的代码,在当前发起调用的合约(也就是Counter合约)的存储上下文中执行,所有状态读写、事件触发都作用在Counter合约上,完全不会修改目标ContractB的任何状态。你现在看到的事件触发地址实际上是Counter合约地址,而非ContractB地址,ContractB本身的tokenName变量没有任何改动。- 你如果想修改ContractB自己的状态变量,应该使用普通的外部调用
call,而非delegatecall。
你可以调用Counter合约的getTokenName方法验证上面的逻辑:你之前的调用已经把Counter合约自己的tokenName变量改成了1,这正是delegatecall的预期行为。
解决方案
把Counter.sol中performUpkeep方法里的delegatecall替换为普通call即可,修改后的代码片段如下:
function performUpkeep(bytes calldata) external override { lastTimeStamp = block.timestamp; counter=0; counter = counter + 1; // 把delegatecall改为call (bool success, bytes memory returndata) = contractBAddress.call( abi.encodeWithSignature("setTokenName(uint256)", counter) ); // 后面的校验逻辑保持不变即可 if (success == false) { if (returndata.length > 0) { assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert("Function call reverted"); } } }
修改后重新部署即可正常修改ContractB的tokenName状态变量,直接调用ContractB的getTokenName就能看到更新后的值。
内容的提问来源于stack exchange,提问作者Irwing Tello
相关产品推荐
相关产品推荐

