47.可升级合约


文档摘要

title: 47. 可升级合约 tags: solidity proxy OpenZeppelin WTF Solidity极简入门: 47. 可升级合约 我最近在重新学 Solidity,巩固一下细节,也写一个“WTF Solidity极简入门”,供小白们使用(编程大佬可以另找教程),每周更新 1-3 讲。 推特:@0xAAScience|@WTFAcademy 社区:Discord|微信群|官网 wtf.academy 所有代码和教程开源在 github: github.com/AmazingAng/WTF-Solidity 这一讲,我们将介绍可升级合约(Upgradeable Contract)。教学用的合约由 中的合约简化而来,可能有安全问题,不要用于生产环境。

title: 47. 可升级合约 tags: - solidity - proxy - OpenZeppelin

WTF Solidity极简入门: 47. 可升级合约

我最近在重新学 Solidity,巩固一下细节,也写一个“WTF Solidity极简入门”,供小白们使用(编程大佬可以另找教程),每周更新 1-3 讲。

推特:@0xAA_Science@WTFAcademy_

社区:Discord微信群官网 wtf.academy

所有代码和教程开源在 github: github.com/AmazingAng/WTF-Solidity

这一讲,我们将介绍可升级合约(Upgradeable Contract)。教学用的合约由OpenZeppelin中的合约简化而来,可能有安全问题,不要用于生产环境。

可升级合约

如果你理解了代理合约,就很容易理解可升级合约。它就是一个可以更改逻辑合约的代理合约。

可升级模式

简单实现

下面我们实现一个简单的可升级合约,它包含3个合约:代理合约,旧的逻辑合约,和新的逻辑合约。

代理合约

这个代理合约比第46讲中的简单。我们没有在它的fallback()函数中使用内联汇编,而仅仅用了implementation.delegatecall(msg.data);。因此,回调函数没有返回值,但足够教学使用了。

它包含3个变量:

  • implementation:逻辑合约地址。
  • admin:admin地址。
  • words:字符串,可以通过逻辑合约的函数改变。

它包含3个函数:

  • 构造函数:初始化admin和逻辑合约地址。
  • fallback():回调函数,将调用委托给逻辑合约。
  • upgrade():升级函数,改变逻辑合约地址,只能由admin调用。
// SPDX-License-Identifier: MIT // wtf.academy pragma solidity ^0.8.21; // 简单的可升级合约,管理员可以通过升级函数更改逻辑合约地址,从而改变合约的逻辑。 // 教学演示用,不要用在生产环境 contract SimpleUpgrade { address public implementation; // 逻辑合约地址 address public admin; // admin地址 string public words; // 字符串,可以通过逻辑合约的函数改变 // 构造函数,初始化admin和逻辑合约地址 constructor(address _implementation){ admin = msg.sender; implementation = _implementation; } // fallback函数,将调用委托给逻辑合约 fallback() external payable { (bool success, bytes memory data) = implementation.delegatecall(msg.data); } // 升级函数,改变逻辑合约地址,只能由admin调用 function upgrade(address newImplementation) external { require(msg.sender == admin); implementation = newImplementation; } }

旧逻辑合约

这个逻辑合约包含3个状态变量,与代理合约保持一致,防止插槽冲突。它只有一个函数foo(),将代理合约中的words的值改为"old"

// 逻辑合约1 contract Logic1 { // 状态变量和proxy合约一致,防止插槽冲突 address public implementation; address public admin; string public words; // 字符串,可以通过逻辑合约的函数改变 // 改变proxy中状态变量,选择器: 0xc2985578 function foo() public{ words = "old"; } }

新逻辑合约

这个逻辑合约包含3个状态变量,与代理合约保持一致,防止插槽冲突。它只有一个函数foo(),将代理合约中的words的值改为"new"

// 逻辑合约2 contract Logic2 { // 状态变量和proxy合约一致,防止插槽冲突 address public implementation; address public admin; string public words; // 字符串,可以通过逻辑合约的函数改变 // 改变proxy中状态变量,选择器:0xc2985578 function foo() public{ words = "new"; } }

Remix实现

  1. 部署新旧逻辑合约Logic1Logic2
    https://www.aiknowledge.cn/images/WTF-Solidity/47-2.webp
    https://www.aiknowledge.cn/images/WTF-Solidity/47-3.webp

  2. 部署可升级合约SimpleUpgrade,将implementation地址指向把旧逻辑合约。
    https://www.aiknowledge.cn/images/WTF-Solidity/47-4.webp

  3. 利用选择器0xc2985578,在代理合约中调用旧逻辑合约Logic1foo()函数,将words的值改为"old"
    https://www.aiknowledge.cn/images/WTF-Solidity/47-5.webp

  4. 调用upgrade(),将implementation地址指向新逻辑合约Logic2
    https://www.aiknowledge.cn/images/WTF-Solidity/47-6.webp

  5. 利用选择器0xc2985578,在代理合约中调用新逻辑合约Logic2foo()函数,将words的值改为"new"
    https://www.aiknowledge.cn/images/WTF-Solidity/47-7.webp

总结

这一讲,我们介绍了一个简单的可升级合约。它是一个可以改变逻辑合约的代理合约,给不可更改的智能合约增加了升级功能。但是,这个合约有选择器冲突的问题,存在安全隐患。之后我们会介绍解决这一隐患的可升级合约标准:透明代理和UUPS


作者与出处
原作者: WTFAcademy
来源:AmazingAng
许可证:CC BY-NC-SA 4.0
整理: 灏天文库整理
由灏天文库结构化整理,提供目录导航、全文检索与在线阅读,便于系统化学习
发布者: 作者: WTFAcademy 转发
评论区 (0)
U