• 基于 Openzeppelin 的可升级合约解决方案的注意事项


    基于 Openzeppelin 的可升级合约解决方案的注意事项

    注意事项

    构造函数

    在编写可升级合约时请不要使用构造函数contructor(),我们知道可升级合约运行时逻辑与数据分离的,合约数据保存在代理合约中,我们编写的合约是逻辑合约,当合约部署时,逻辑合约调用contructor()初始化的数据是逻辑合约的,代理合约中的数据并没有被初始化,所以是无效的。
    包括全局变量声明时赋值初始值,因为这种做法相当于在构造函数contructor()中设置这些值。

    父类合约初始化

    如果MyContract继承自合约 BaseContract, 那么BaseContract合约的初始化函数 initialize() 的modifier(修饰器) 必须使用 onlyInitializing,比如:

    contract BaseContract is Initializable {
        uint256 public y;
    
        function initialize() public onlyInitializing {
            y = 42;
        }
    }
    // BaseContract 继承自 Initializable,这里无需重复显式继承 Initializable
    contract MyContract is BaseContract {
        int storageValue;
    
        // modifier(修饰器) initializer 可以确保initialize只会被调用一次
        function initialize(int initValue) public initializer {
            BaseContract.initialize();
            storageValue = initValue;
        }
        ......
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    声明状态变量

    • 声明状态变量时,不能对其赋值 初始值

    像这样做是错误的

    contract MyContract is Initializable {
        int storageValue = 666; //初始值无效
    
        function initialize() public initializer {
        }
        ......
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    因为这种做法相当于在构造函数contructor()中设置这些值,因此不适用于可升级的约定。

    • 定义常量状态变量仍然可以

    像这样是可以的

    contract MyContract is Initializable {
        // constant 常量关键字
        int public constant storageValue = 666; //有效
    
        function initialize() public initializer {
        }
        ......
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在合约代码中创建新合约实例

    • 不要在合约中创建新的合约实例,创建出的合约是不可升级的

    像这样,即使MyContract是可升级的,但 ERC20 实例不可升级

    import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
    
    contract MyContract is Initializable {
        ERC20 public token;
        
        function initialize() public initializer {
            token = new ERC20("Test", "TST"); // 这个合约是不可升级的
        }
        ......
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 如果你希望在MyContract引用其他可升级合约,那么将已部署好的可升级合约实例注入到MyContract中,是很好的解决方案

    像这样

    import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
    
    contract MyContract is Initializable {
        IERC20Upgradeable public token;
    
        function initialize(IERC20Upgradeable _token) public initializer {
            token = _token;
        }
        ......
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    升级合约时声明状态变量

    在编写合约的新版本时,无论是由于新功能还是 bug 修复,还有一个额外的限制需要遵守:不能更改合约状态变量的声明顺序,也不能更改其类型。

    比如当前版本(V1)合约状态变量布局

    contract MyContractV1 {
        uint256 private x;
        string private y;
    }
    
    • 1
    • 2
    • 3
    • 4

    那么在编写新版本合约时,请避免一下错误操作:

    • 更改变量的类型

    这是错误的

    contract MyContractV2 {
        string private x;
        string private y;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 更改声明它们的顺序

    这是错误的

    contract MyContractV2 {
        string private y;
        uint256 private x;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 在现有变量之前引入一个新变量

    这是错误的

    contract MyContractV2 {
        bytes private a;
        uint256 private x;
        string private y;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    这是正确的

    contract MyContractV2 {
        uint256 private x;
        string private y;
        bytes private a;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 删除现有变量

    这是错误的

    contract MyContractV2 {
        string private y;
    }
    
    • 1
    • 2
    • 3

    参考文档:
    Openzeppelin编写可升级合约:https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable

  • 相关阅读:
    全网最详细中英文ChatGPT接口文档(五)30分钟快速入门ChatGPT——手把手示例教程:如何建立一个人工智能回答关于您的网站问题,小白也可学
    typescript+webpack构建一个js库
    有数BI开发
    Vue.js核心技术解析与uni-app跨平台实战开发学习笔记 第7章 Vue.js高级进阶 7.8 slot插槽
    使用canal订阅mysql的binlog,springboot使用canal订阅mysql的binlog
    开放原子开源基金会秘书长孙文龙:要打造以开发者为本的开源服务平台
    本地pycharm连接到远程服务器(超级详细)
    Trie字典树
    [QCM6125][Android13] 默认允许使用usb权限
    adb常用命令总结[持续更新]
  • 原文地址:https://blog.csdn.net/Lyon_Nee/article/details/125523306