web3区块链-小镇店铺的 “借力办事”:call 与 delegatecall 的区别与联系

加密小镇上有两家店:

  • A 店(水果店) :老板是 Alice,有自己的账本(合约存储),记录着 "苹果库存"(存储变量uint256 public appleStock = 100;),但没学会 "盘点库存""修改库存" 的方法;
  • B 店(管理咨询店) :老板是 Bob,专门帮人做库存管理,有两套核心 "操作手册"(合约函数):
    1. checkStock():读取自己账本上的库存,返回数值;
    2. addStock(uint256 num):把自己账本上的库存增加num个。

A 店想复用 B 店的方法,不用自己写代码 ------ 这就对应 Solidity 里合约间的两种调用方式:calldelegatecall

一、先看 "联系":都是 "借力办事",复用他人逻辑

不管是call还是delegatecall,核心目的都是让 A 店(调用方合约)借用 B 店(被调用方合约)的代码逻辑,不用自己重复开发。就像 A 店不用自己学盘点方法,直接请 B 店的人来 "帮忙操作",本质都是 "复用他人功能"。

另外,两者的基础规则一致:

  • 都会传递msg.sender(调用者身份,比如 Alice 发起调用,msg.sender就是 Alice)和msg.value(附带的 ETH);
  • 都需要知道 B 店的地址和函数签名(比如checkStock()的签名是0x123...);
  • 都是合约间交互的核心方法,属于低级别调用(区别于合约实例.函数名()的高级调用)。

二、核心区别:"借方法时,用谁的账本?"

这是calldelegatecall的本质差异 ------调用方是否使用自己的存储(账本)执行被调用方的代码

场景 1:call 调用 ------"借你的方法,算你的账"

Alice 想让 B 店帮忙 "盘点库存",用了call方式:

  1. Alice 对 B 店说:"麻烦用你的checkStock()方法,帮我看看库存有多少?"
  2. B 店收到请求后,拿出自己的账本(B 店的存储),发现自己的 "苹果库存" 是 0(B 店本来不存水果),于是回复 Alice:"库存是 0";
  3. 后续 Alice 又用call调用 B 店的addStock(50)
    • B 店还是用自己的账本,把自己的库存从 0 改成 50;
    • A 店的账本丝毫没变化,苹果库存依然是 100。

对应 Solidity 逻辑

复制代码
// A店合约(调用方)
contract ShopA {
    uint256 public appleStock = 100; // A店自己的库存(账本)

    function callBCheckStock(address shopB) external returns (uint256) {
        // call调用B店的checkStock(),用B店的存储
        (bool success, bytes memory data) = shopB.call(abi.encodeWithSignature("checkStock()"));
        require(success, "call failed");
        return abi.decode(data, (uint256)); // 返回B店的库存(0)
    }

    function callBAddStock(address shopB) external {
        // call调用B店的addStock(50),修改B店的存储
        shopB.call(abi.encodeWithSignature("addStock(uint256)", 50));
    }
}

// B店合约(被调用方)
contract ShopB {
    uint256 public appleStock = 0; // B店自己的库存(账本)

    function checkStock() external view returns (uint256) {
        return appleStock; // 读取B店的存储
    }

    function addStock(uint256 num) external {
        appleStock += num; // 修改B店的存储
    }
}

call 的核心特点 :被调用方(B 店)的代码,操作的是被调用方自己的存储,调用方(A 店)的存储完全不受影响 ------ 相当于 "借别人的工具,修别人的东西"。

场景 2:delegatecall 调用 ------"借你的方法,修我的账"

Alice 觉得call没用(改的是 B 店的库存),于是换了delegatecall方式:

  1. Alice 对 B 店说:"麻烦用你的checkStock()方法,但帮我查我自己的账本!"
  2. B 店收到请求后,没有拿自己的账本,而是拿起A 店的账本(A 店的存储),读取 A 店的苹果库存 100,回复 Alice:"库存是 100";
  3. 后续 Alice 用delegatecall调用 B 店的addStock(50)
    • B 店依然用 A 店的账本,把 A 店的库存从 100 改成 150;
    • B 店自己的账本还是 0,丝毫没变化。

对应 Solidity 逻辑(B 店合约不变,A 店调用方式改):

复制代码
contract ShopA {
    uint256 public appleStock = 100; // A店自己的库存(账本)

    function delegateCallBCheckStock(address shopB) external returns (uint256) {
        // delegatecall调用B店的checkStock(),用A店的存储
        (bool success, bytes memory data) = shopB.delegatecall(abi.encodeWithSignature("checkStock()"));
        require(success, "delegatecall failed");
        return abi.decode(data, (uint256)); // 返回A店的库存(100)
    }

    function delegateCallBAddStock(address shopB) external {
        // delegatecall调用B店的addStock(50),修改A店的存储
        shopB.delegatecall(abi.encodeWithSignature("addStock(uint256)", 50));
    }
}

delegatecall 的核心特点 :被调用方(B 店)的代码,操作的是调用方(A 店)的存储------ 相当于 "借别人的工具,修自己的东西"。

⚠️ 关键注意点:delegatecall要求 "调用方和被调用方的存储结构对齐"!比如 A 店和 B 店都必须有uint256 public appleStock(且变量顺序一致),否则会读取 / 修改错误的存储位置(比如把 A 店的其他变量当成库存改了)。

三、区别与联系总结表

维度 call delegatecall
核心联系 均为合约间低级别调用,复用被调用方代码逻辑,传递 msg.sender/msg.value
操作的存储 被调用方(B 店)的存储 调用方(A 店)的存储
核心用途 调用其他合约的功能,且需要修改对方的状态(比如调用 Uniswap 的 swap 函数,修改 Uniswap 的流动性) 复用通用逻辑(如权限校验、数学计算),且需要修改自身状态(比如多个合约共用 "修改所有者" 的逻辑,用 delegatecall 调用代理合约的代码,修改自身的 owner)
存储要求 无强制对齐要求(调用方和被调用方存储可不同) 必须对齐存储结构(变量类型、顺序一致),否则会出现存储错乱
场景类比 请维修师傅用他的工具,修他的设备 请维修师傅用他的工具,修你的设备

四、真实开发中的典型场景

  1. call 的常见场景

    • 调用其他 DApp 的合约功能(比如调用 USDT 的transfer转账,修改 USDT 合约中的余额记录);
    • 批量执行多个合约操作(比如一次调用多个合约的查询方法)。
  2. delegatecall 的常见场景

    • 代理模式(Proxy Pattern):比如升级合约时,代理合约(用户交互的合约)用delegatecall调用逻辑合约(存储业务代码),确保用户数据始终存在代理合约中,逻辑合约可替换;
    • 通用逻辑复用(比如多个合约都需要 "只有管理员能操作" 的权限校验,把校验逻辑写在一个 "权限合约" 里,其他合约用delegatecall调用,修改自身的管理员状态)。

简单说:call是 "帮别人办事",delegatecall是 "请别人帮自己办事"------ 核心区别就在 "办事时用谁的账本(存储)"。

Web3-智能合约-整数溢出攻击:"凭空造币"的秘密

相关推荐
Max_uuc6 小时前
【C++ 硬核】利用链接器魔法 (Linker Sections) 实现“去中心化”的自动初始化与插件系统
去中心化·区块链
devmoon6 小时前
在 Polkadot 上部署独立区块链Paseo 测试网实战部署指南
开发语言·安全·区块链·polkadot·erc-20·测试网·独立链
傻小胖6 小时前
22.ETH-智能合约-北大肖臻老师客堂笔记
笔记·区块链·智能合约
傻小胖21 小时前
21.ETH-权益证明-北大肖臻老师客堂笔记
笔记·区块链
硅基流动1 天前
硅基流动 × ValueCell:8K+Star,去中心化金融智能体加速投资决策
金融·去中心化·区块链
devmoon1 天前
使用 Hardhat 在 Polkadot Hub 测试网部署基础 Solidity 合约(完整实战指南)
web3·区块链·智能合约·波卡·hardhat
威胁猎人1 天前
【黑产大数据】2025年全球KYC攻击风险研究报告
大数据·区块链
焦点链创研究所1 天前
去中心化实体基础设施网络的崛起:比较分析
网络·去中心化·区块链
MicroTech20251 天前
微算法科技(NASDAQ :MLGO)量子测量区块链共识机制:保障数字资产安全高效存储与交易
科技·安全·区块链
区块链蓝海1 天前
Ardor v2.6.0 正式发布:Nxt迁移完成,Ardor迈入多链协同新阶段
人工智能·区块链