(三)solidity代理调用

(三)solidity代理调用

viEcho Lv5

代理调用代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// Note:deploy this contract first
contract B {
// Note: storage layout must be the same sa contract A
// 状态变量的声明顺序必须和A一致
uint256 public num;
address public sender;
uint256 public value;

function setVars(uint256 _num)public payable{
num = 2 * _num;
sender = msg.sender;
value = msg.value;
}
}

contract A{
uint256 public num;
address public sender;
uint256 public value;

//代理调用B的函数
function setVars(address _contract, uint256 _num) public payable {
// _contract.delegatecall(abi.encodeWithSignature("setVars(uint256",_num));

// 使用函数选择器的好处是,不用写死函数名及参数名在字符串中,只需要B修改后就会同步更新过来
(bool success,bytes memory data) = _contract.delegatecall(abi.encodeWithSelector(B.setVars.selector, _num));
require (success,"delegatecall failed");
}
}

delegatecall的原理

如上代码中演示:

  • elegatecall 会执行目标合约(B)的代码,但使用调用合约(A)的存储空间
  • 执行环境(包括 msg.sender 和 msg.value )保持不变
  • 相当于把B合约的代码”借”到A合约的环境中执行

实际执行的过程是:

  • 当A合约通过 delegatecall 调用B合约的 setVars 函数时
  • 虽然执行的是B合约的代码逻辑,但所有状态变量的读写操作都发生在A合约的存储空间
  • 因此B合约的状态变量不会改变,改变的只是A合约的状态变量

代理调用,存储布局必须要一致:

  • 两个合约的状态变量声明顺序必须完全一致
  • 这是因为 delegatecall 通过存储槽位置来访问变量,而不是变量名

代理调用的实际应用场景

A. 可升级合约模式(Upgradeable Contracts)

  • 通过代理合约保存状态数据
  • 逻辑合约可以升级替换而不影响存储数据
  • 这是OpenZeppelin等标准库实现的可升级合约基础

B. 库合约调用(Library Calls)

  • 复用库合约的代码逻辑
  • 但保持调用合约的存储上下文
  • 节省部署gas费用

C. 多签钱包实现

  • 如Gnosis Safe等智能合约钱包
  • 通过代理调用执行交易但保持钱包状态

D. 模块化设计

  • 将不同功能拆分为独立合约
  • 通过代理调用组合功能
  • 类似插件系统架构

E. Gas优化

  • 复用已部署合约的代码
  • 避免重复部署相同逻辑
  • 特别适合多个合约共享相同功能的情况
  • Title: (三)solidity代理调用
  • Author: viEcho
  • Created at : 2025-05-02 16:53:05
  • Updated at : 2025-05-11 16:33:28
  • Link: https://viecho.github.io/2025/0502/solidity-proxy-call.html
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments