在“智能合约升级原理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)DEPLOY WITH PROXY:   使用代理部署

(2)UPGRADE WITH PROXY: 使用代理升级

(3)PUBLISH TO IPFS:             发布到IPFS上

我们选择使用第一项,将自动创建代理合约,并使用代理合约部署业务合约。

智能合约架构 智能合约原理_区块链

点击Deploy按钮后弹出提示框,含义是部署一个实现合约,连接到一个ERC1967代理合约,并且完成实现合约的初始化。直接点击“Proceed”。

智能合约架构 智能合约原理_数据_02

 

部署成功,在debug区域看到两笔交易,在左侧栏看到两个合约地址:

智能合约架构 智能合约原理_智能合约架构_03

BOX合约地址: 0xf8e81D47203A594245E36C48e151709F0C19fBe8

代理合约地址: 0xD7ACd2a9FD159E69Bb102A1ca21C9a3e3A5F771B

3测试代理合约

现在我们来测试下代理合约,看看是否能读写一个基本数据:

调用代理合约的store方法, 设置value=100

调用代理合约的retrieve方法, value=100

结果是成功的。这里就证实了我们只用操作代理合约就好,不再直接操作业务合约啦!

智能合约架构 智能合约原理_智能合约_04

4 升级业务合约

Box合约做个小小改动:

function retrieve() public view returns (uint256) { 
  
    // 修改方法 
  
    return value + 100; 
  
}

在部署页面,Deploy按钮下方有三个可选项,升级就选择第二项:

(2)UPGRADE WITH PROXY: 使用代理升级

第二项下方的“Use last deployed ERC1967 contract”,含义是使用最后一次部署的代理合约,这样就不用你手动填写代理合约地址啦。

智能合约架构 智能合约原理_数据_05

部署成功, 左侧任务栏看到又多出了两个合约地址,一个是新版本的Box合约,一个是原来的ERC1967代理合约,这个代理合约的地址是不会改变的。

智能合约架构 智能合约原理_智能合约架构_06

读取代理合约的retrieve方法,得到的数据是200,表明新的业务合约代码生效,原有数据也被保留下来了。

智能合约架构 智能合约原理_1024程序员节_07

大功告成,成功的实现了业务合约的UUPS升级测试。