Solidity中delegate函数异常:委托关系校验不生效问题排查
问题原因分析
执行步骤
- 部署合约A
- 传入合约A地址部署合约B
- 从地址X调用合约B的
delegate函数,委托给地址Y - 调用
checkDelegates校验X到Y的委托关系,但无法得到true结果
调用返回日志
console.log: delegater == msg.sender delegater== 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4 delegatee == 0xd8b934580fcE35a11B58C6D73aDeE468a2833fa8 Before: delegates[delegater] == delegatee == false After: delegates[delegater] == delegatee == true call to B.checkDelegates CALL [call]from: 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4to: B.checkDelegates(address,address)data: 0x564...33fa8 console.log: msg.sender== 0xd8b934580fcE35a11B58C6D73aDeE468a2833fa8 delegater== 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4 delegatee== 0xd8b934580fcE35a11B58C6D73aDeE468a2833fa8 delegates[delegater] == delegatee == false
合约代码
// SPDX-License-Identifier: MIT pragma solidity ^0.8.9; import "hardhat/console.sol"; contract A { mapping (address => address) public delegates; function delegate(address delegatee) public returns (bool){ require (delegatee != address(0), "can't delegate to 0 address"); address delegater = msg.sender; console.log("delegater == msg.sender"); console.log("delegater==", msg.sender); console.log("delegatee ==",delegatee); console.log("Before: delegates[delegater] == delegatee == ", delegates[delegater] == delegatee); delegates[msg.sender] = delegatee; console.log("After: delegates[delegater] == delegatee == ", delegates[delegater] == delegatee); return true; } function checkDelegates(address delegater, address delegatee) public view returns(bool){ console.log("msg.sender==",msg.sender); console.log("delegater==", delegater); console.log("delegatee==", delegatee); console.log("delegates[delegater] == delegatee == ", delegates[delegater] == delegatee); return delegates[delegater] == delegatee; } } contract B { A a; constructor(A _a) public { a = _a; } function delegate(address addr) public { (bool success,) = address(a).delegatecall(abi.encodeWithSignature("delegate(address)", addr)); require(success, "Failed"); } function checkDelegates(address _delegater,address _delegatee) public view returns(bool success){ success = a.checkDelegates(_delegater,_delegatee); } }
核心原因
问题出在合约B的delegate函数使用了delegatecall调用合约A的方法,这是错误的用法:
delegatecall的特性是调用目标合约的代码逻辑,但使用当前合约(B)的存储、msg.sender、msg.value。也就是说,执行B.delegate()时,虽然跑的是A合约delegate函数的代码,但实际上是修改了合约B的存储,而非合约A的存储。
从日志能看到,执行delegate时delegates[delegater]被设为true,但这个修改只存在合约B的存储里。之后调用checkDelegates是直接读取合约A的存储,自然查不到之前的委托记录,所以返回false。
修复方案
把合约B的delegate函数里的delegatecall改成普通调用方式即可:
方案1:直接调用合约实例方法
function delegate(address addr) public { bool success = a.delegate(addr); require(success, "Failed"); }
方案2:使用普通call调用
function delegate(address addr) public { (bool success,) = address(a).call(abi.encodeWithSignature("delegate(address)", addr)); require(success, "Failed"); }
这两种方式都会让合约A的delegate函数操作自身存储,之后调用checkDelegates就能正确读取到委托关系。
内容的提问来源于stack exchange,提问作者Tomosuke
相关产品推荐
相关产品推荐

