solidity:部署可升级的智能合约


概要:

由于智能合约不可修改的特性,如果已经发布的智能合约出现bug,或者需要业务扩展,数据迁移等业务需求(尽管可以预先预留升级功能),往往显得缚手缚脚。
OpenZeppelin Upgrades 的出现就解决了这种问题。当合约出现问题时,通过升级你的智能合约,就像传统项目发布新版本一样,方便的给你的智能合约进行迭代升级(让你可以更安心的写bug~)。

开始

升级库

npm install --save-dev @openzeppelin/hardhat-upgrades
npm install --save-dev @nomiclabs/hardhat-ethers ethers

在 hardhat.config.js 中引入

require('@openzeppelin/hardhat-upgrades');

demo

js 脚本:
const { ethers, upgrades } = require("hardhat");
const {writeFile} = require("./tools")

// npx hardhat run scripts/sample-script.js --network localhost
async function main() {
    // 发布合约
    const Greeter = await ethers.getContractFactory("Greeter");
    const greeter = await upgrades.deployProxy(Greeter, ["Hello, Hardhat!"], { initializer: 'initialize' });
    console.log('___0', greeter.address)

    // 更新合约
    const Greeter1 = await ethers.getContractFactory("Greeter1");
    const upgraded = await upgrades.upgradeProxy(greeter.address, Greeter1);
    console.log('___1', upgraded.address)

    // 可以看到, greeter 同事具备了 greeter1 合约的能力,同时保留了 自身的属性
    const GreeterContract1 = await ethers.getContractAt("Greeter1", greeter.address);
    console.log('___3', await GreeterContract1.greet());
    console.log('___3', await GreeterContract1.greet1());

}

main()
    .then(() => process.exit(0))
    .catch(error => {
    console.error(error);
    process.exit(1);
});
solidity文件
// Greeter.sol
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.7.0;

import "hardhat/console.sol";


contract Greeter {
    string greeting;

    function initialize(string memory _greeting) public virtual {
        console.log("Deploying a Greeter with greeting:", _greeting);
        greeting = _greeting;
    }

    function greet() public view returns (string memory) {
        return greeting;
    }

    function setGreeting(string memory _greeting) public {
        console.log("Changing greeting from '%s' to '%s'", greeting, _greeting);
        greeting = _greeting;
    }
}
// Greeter1.sol
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.7.0;

import "hardhat/console.sol";


contract Greeter1 {
    string greeting;

    function initialize(string memory _greeting) public virtual {
        console.log("Deploying a Greeter1 with greeting:", _greeting);
        greeting = _greeting;
    }

    function greet() public view returns (string memory) {
        return greeting;
    }

    function greet1() public view returns (string memory) {
        return "123";
    }

    function setGreeting(string memory _greeting) public {
        console.log("Changing greeting from '%s' to '%s'", greeting, _greeting);
        greeting = _greeting;
    }
}

tips

  • 合约代码必须实现初始化函数,用参数 initializer 制定,而且不能使用 constructor,详见文档
  • 官方文档