在“智能合约升级原理01—起源”中介绍了合约升级的主要有三种方式:

  1. Diamond Implementation
  2. Transparent Implementation
  3. UUPS Implementation

我们将只关注最常用的Transparent和UUPS,本文通过一个代码示例学习UUPS方式,最后还将给出合约升级的注意事项。 示例说明:第一个版本的业务合约Box,内部一个value数据,实现读、取两个方法。第二个版本Box,稍微修改下代码。部署后将得到代理合约地址,供应用端访问该地址。业务合约Box版本升级并不会引起代理合约地址变动,因此应用端的访问地址是稳定不变的。 1合约代码 // SPDX-License-Identifier: MIT pragma solidity >=0.4.22 <0.9.0; import “@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol”; import “@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol”; import “@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol”; contract Box is Initializable, OwnableUpgradeable, UUPSUpgradeable { function initialize() public initializer { __Ownable_init(); __UUPSUpgradeable_init(); } uint256 private value; event ValueChanged(uint256 value); function store(uint256 _value) public { value = _value; emit ValueChanged(_value); } function retrieve() public view returns (uint256) { return value; } function _authorizeUpgrade(address newImplementation) internal onlyOwner override {} } 2部署智能合约在Remix中创建新的合约文件Box.sol,编译器版本指定为0.8.14,编译通过。在部署页面,Deploy按钮下方有三个可选项:(1)DEPLOYWITHPROXY: 使用代理部署(2)UPGRADEWITHPROXY:使用代理升级(3)PUBLISHTO IPFS:发布到IPFS上我们选择使用第一项,将自动创建代理合约,并使用代理合约部署业务合约。点击Deploy按钮后弹出提示框,含义是部署一个实现合约,连接到一个ERC1967代理合约,并且完成实现合约的初始化。直接点击“Proceed”。 部署成功,在debug区域看到两笔交易,在左侧栏看到两个合约地址:BOX合约地址:0xf8e81D47203A594245E36C48e151709F0C19fBe8代理合约地址:0xD7ACd2a9FD159E69Bb102A1ca21C9a3e3A5F771B 3测试代理合约现在我们来测试下代理合约,看看是否能读写一个基本数据:调用代理合约的store方法, 设置value=100调用代理合约的retrieve方法, value=100结果是成功的。这里就证实了我们只用操作代理合约就好,不再直接操作业务合约啦! 4 升级业务合约Box合约做个小小改动: function retrieve() public view returns (uint256) { // 修改方法 return value + 100; }在部署页面,Deploy按钮下方有三个可选项,升级就选择第二项:(2)UPGRADEWITHPROXY:使用代理升级第二项下方的“UselastdeployedERC1967 contract”,含义是使用最后一次部署的代理合约,这样就不用你手动填写代理合约地址啦。部署成功,左侧任务栏看到又多出了两个合约地址,一个是新版本的Box合约,一个是原来的ERC1967代理合约,这个代理合约的地址是不会改变的。读取代理合约的retrieve方法,得到的数据是200,表明新的业务合约代码生效,原有数据也被保留下来了。大功告成,成功的实现了业务合约的UUPS升级测试。