web3区块链-小镇店铺的 “借力办事”:call 与 delegatecall 的区别与联系Web3-智能合约-整数溢出攻击:“凭空造币”的秘密

作者:想ai抽日期:2025/12/4

加密小镇上有两家店:

  • 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 逻辑

1// A店合约(调用方)
2contract ShopA {
3    uint256 public appleStock = 100; // A店自己的库存(账本)
4
5    function callBCheckStock(address shopB) external returns (uint256) {
6        // call调用B店的checkStock(),用B店的存储
7        (bool success, bytes memory data) = shopB.call(abi.encodeWithSignature("checkStock()"));
8        require(success, "call failed");
9        return abi.decode(data, (uint256)); // 返回B店的库存(0)
10    }
11
12    function callBAddStock(address shopB) external {
13        // call调用B店的addStock(50),修改B店的存储
14        shopB.call(abi.encodeWithSignature("addStock(uint256)", 50));
15    }
16}
17
18// B店合约(被调用方)
19contract ShopB {
20    uint256 public appleStock = 0; // B店自己的库存(账本)
21
22    function checkStock() external view returns (uint256) {
23        return appleStock; // 读取B店的存储
24    }
25
26    function addStock(uint256 num) external {
27        appleStock += num; // 修改B店的存储
28    }
29}
30

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 店调用方式改):

1contract ShopA {
2    uint256 public appleStock = 100; // A店自己的库存(账本)
3
4    function delegateCallBCheckStock(address shopB) external returns (uint256) {
5        // delegatecall调用B店的checkStock(),用A店的存储
6        (bool success, bytes memory data) = shopB.delegatecall(abi.encodeWithSignature("checkStock()"));
7        require(success, "delegatecall failed");
8        return abi.decode(data, (uint256)); // 返回A店的库存(100)
9    }
10
11    function delegateCallBAddStock(address shopB) external {
12        // delegatecall调用B店的addStock(50),修改A店的存储
13        shopB.delegatecall(abi.encodeWithSignature("addStock(uint256)", 50));
14    }
15}
16

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

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

三、区别与联系总结表

维度calldelegatecall
核心联系均为合约间低级别调用,复用被调用方代码逻辑,传递 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-智能合约-整数溢出攻击:“凭空造币”的秘密


web3区块链-小镇店铺的 “借力办事”:call 与 delegatecall 的区别与联系Web3-智能合约-整数溢出攻击:“凭空造币”的秘密》 是转载文章,点击查看原文


相关推荐


Webpack打包机制与Babel转译原理深度解析
老前端的功夫2025/12/13

Webpack打包机制与Babel转译原理深度解析 引言:现代前端构建工具的核心原理 Webpack和Babel是现代前端开发不可或缺的两个核心工具。Webpack解决了模块化、资源管理和打包优化的难题,而Babel则确保了JavaScript代码的浏览器兼容性。理解它们的底层原理不仅有助于更好地配置和使用这些工具,还能在遇到复杂问题时快速定位和解决。 一、Webpack打包机制深度解析 1.1 Webpack核心概念与架构设计 Webpack的整体架构: // Webpack 的核心抽象概念


【前端必看】手把手教你把 Strapi 5 自动化部署到宝塔,再也不用手动传代码了!
知航驿站2025/12/21

前言 兄弟们,作为一名普通前端,每次写完接口还要自己登录服务器、手动上传代码、装依赖、再重启 PM2,这一套“广播体操”做下来,天都黑了。 今天咱们就花 10 分钟,把这套活儿交给 GitHub Actions。以后你只管在本地 git push,剩下的脏活累活全让机器人帮你干! 在线文档 在线源码 一、 整体思路(大白话版) 代码放 GitHub:这大家都会。 GitHub Actions 开工:你一推代码,它就跳出来执行一个脚本。 SSH 远程登录:GitHub 像个“代跑腿”的,拿着你的


[服务器][教程]EC2开启自定义端口
踏雪Vernon2025/12/30

网上很多教程并没有说这一点。直接就说新建安全组之后就可以用了。 很坑,我一直以为我的服务器服务搭建的有问题。因为即使端口开了,端口没有对应的服务用端口扫描也是显示无连接的!! 1. 新建安全组规则 进入“实例”页面中找到“安全组”。新建一个安全组 新建之后如下所示。 新建之后,并不是直接就可以用了。而是要进行绑定!这个一定要注意!!! 2. 修改安全组规则 点击实例之后,选择“操作”,更改安全组 之后在这里选择刚才创建的安全组即可。其他的VPC应该也是类似的。 被网上的教程坑了。大家注意甄


SPI通信:从原理到工程实践
我太浮躁2026/1/8

文章目录 1、概述2、什么是SPI?2.1 SPI的特点是什么? 3、SPI的历史发展3.1 SPI诞生3.2 为什么是SPI?3.3 SPI的一路升级3.3.1 标准 SPI (Standard SPI)3.3.2 Dual SPI & Quad SPI (QSPI)3.3.3 Octal SPI (OSPI) / xSPI3.3.4 eSPI (Enhanced SPI) 4、协议架构及通信原理4.1 SPI功能层级划分(非官方,但实用便于理解)4.1.1 物理层 :四线制结


Objective-C 核心语法深度解析:基本类型、集合类与代码块实战指南
奋斗理想2026/1/16

详细讲解:Objective-C 基本类型、集合类和代码块 一、基本类型详解 1.1 主要基本类型 // MyTypes.m #import <Foundation/Foundation.h> void demonstrateBasicTypes() { NSLog(@"========== 基本类型演示 =========="); // 1. BOOL 类型(实际上是 signed char) BOOL isOpen = YES; // YES = 1,


怎么理解 HttpServletRequest @Autowired注入
それども2026/1/24

在你的代码中,@Autowired(required = false) private HttpServletRequest req 的 required = false 是多余的,甚至可能带来潜在问题。以下是详细分析: 1. 为什么 required = false 是多余的? (1) HttpServletRequest 的特殊性 由 Servlet 容器(如 Tomcat)管理:HttpServletRequest 是 Web 请求的上下文对象,在 Servlet 环境中必然存在(

首页编辑器站点地图

本站内容在 CC BY-SA 4.0 协议下发布

Copyright © 2026 XYZ博客