You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 12:18:09