乐趣区

关于区块链:如何使用hardhat进行合约uups模式升级

id:BSN_2021
公众号:BSN 研习社

背景:在开发或保护 solidity 语言的智能合约时,常常会因为业务逻辑变动而变动合约内的逻辑,这就要思考在不影响以前智能合约中已上链的数据的同时,批改或扩大新的业务逻辑,所以合约第一次开发时就须要思考其自身反对可降级性能

目标:本篇文章是为了让读者疾速上手应用 hardhat 并搭配 openZeppelin 的 uups 降级模式对合约进行可降级适配以及领导后续如何进行合约降级

实用对象:实用于 BSN 凋谢联盟链武汉链(基于 ETH),也可实用于其余反对 solidity 语言的链框架

如何应用 hardhat 进行合约 uups 模式降级

  • 装置 hardhat
  • 初始化我的项目
  • 编写合约
  • 编写测试文件
  • 编辑配置文件
  • 执行我的项目
  • 参考

装置 hardhat
1. 创立空的文件夹 demo-uups 并进入文件夹外面,执行 npm init
2. 执行装置命令 npm install –save-dev hardhat
3. 装置依赖 npm install –save-dev @nomiclabs/hardhat-waffle ethereum-waffle chai @nomiclabs/hardhat-ethers ethers
4. 装置 openZeppelin 的 uups 可降级合约包 npm install –save-dev @openzeppelin/contracts-upgradeable
5. 装置测试工具 mocha npm install –save-dev mocha
初始化我的项目
执行 npx hardhat 抉择 Create a basic sample project

初始化实现后的目录构造为
编写合约
在 contracts 目录上面新建两个名称别离为 MyLogicV1 和 MyLogicV2 的.sol 文件并进行代码编写。实现 openZeppelin 的 uups 降级模式必须要引入 openZeppelin 相干库文件,且必须在 constructor()办法上加 @custom:oz-upgrades-unsafe-allow constructor 字样正文


// contract/MyLogicV1.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";

contract MyLogicV1 is Initializable, UUPSUpgradeable, OwnableUpgradeable {function initialize() initializer public {__Ownable_init();
      __UUPSUpgradeable_init();}

    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor() initializer {}

    function _authorizeUpgrade(address) internal override onlyOwner {}

    mapping(string => uint256) private logic;

    event logicSetted(string indexed _key, uint256 _value);

    function SetLogic(string memory _key, uint256 _value) external {logic[_key] = _value;
        emit logicSetted(_key, _value);
    }

    function GetLogic(string memory _key) public view returns (uint256){return logic[_key];
    }
}

// contract/MyLogicV2.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";

contract MyLogicV2 is Initializable, UUPSUpgradeable, OwnableUpgradeable {function initialize() initializer public {__Ownable_init();
      __UUPSUpgradeable_init();}

    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor() initializer {}

    function _authorizeUpgrade(address) internal override onlyOwner {}

    mapping(string => uint256) private logic;

    event logicSetted(string indexed _key, uint256 _value);

    function SetLogic(string memory _key, uint256 _value) external {logic[_key] = _value;
        emit logicSetted(_key, _value);
    }

    function GetLogic(string memory _key) public view returns (uint256){return logic[_key]+100;
    }
}

MyLogicV1 实现了一个对于 logic 的 map 的写入办法和读取办法,MyLogicV2 只是改写了 MyLogicV1 的 GetLogic 办法中的解决逻辑,使得每次返回的值加了 100

编写测试文件
在 test 目录下创立生成名称为 update-test 的.js 文件并进行代码编写


// test/update-test.ts
const {expect} = require('chai');
const {ethers, upgrades} = require('hardhat');


let myLogicV1;
let myLogicV2;

describe('uups mode upgrade', function () {it('deploys', async function () {const MyLogicV1 = await ethers.getContractFactory('MyLogicV1');
      myLogicV1 = (await upgrades.deployProxy(MyLogicV1, {kind: 'uups'}));
      console.log(myLogicV1.address);
  })
  it('myLogicV1 set', async function () {await myLogicV1.SetLogic("aa", 1);
    expect((await myLogicV1.GetLogic("aa")).toString()).to.equal('1');
  })
  it('upgrades', async function () {const MyLogicV2 = await ethers.getContractFactory('MyLogicV2');
      myLogicV2 = (await upgrades.upgradeProxy(myLogicV1, MyLogicV2));
      console.log(myLogicV2.address);
  })
  it('myLogicV2 get', async function () {expect((await myLogicV2.GetLogic("aa")).toString()).to.equal('101');
  })
})

测试文件步骤详解

  • deploys: 调用 upgrades 中的 deployProxy 办法以 uups 的形式部署 MyLogicV1 合约
  • myLogicV1 set:调用 MyLogicV1 合约中的 SetLogic 办法将值传入,并调用 GetLogic 办法确认返回值为 1
  • upgrades:调用 upgrades 中的 upgradeProxy 办法降级
  • myLogicV2 get:调用 MyLogicV2 合约中的 GetLogic 办法确认返回值为 101

编辑配置文件
编辑 hardhat.config.js 文件


require("@nomiclabs/hardhat-waffle");
require('@openzeppelin/hardhat-upgrades');

// This is a sample Hardhat task. To learn how to create your own go to
// https://hardhat.org/guides/create-task.html
task("accounts", "Prints the list of accounts", async (taskArgs, hre) => {const accounts = await hre.ethers.getSigners();

  for (const account of accounts) {console.log(account.address);
  }
});

// You need to export an object to set up your config
// Go to https://hardhat.org/config/ to learn more

/**
 * @type import('hardhat/config').HardhatUserConfig
 */
module.exports = {
  solidity: {
    version: "0.8.4",
    settings: {
      optimizer: {
        enabled: true,
        runs: 200
      }
    }
  },
  mocha: {timeout: 60000}
};

执行我的项目
1. 执行编译合约
npx hardhat compile

2. 启动本地测试节点
npx hardhat node

3. 新开一个窗口运行测试文件
npx hardhat test test/update-test.js –network localhost

测试通过,合约地址都为代理合约地址,测试阐明胜利降级改写了 GetLogic 中的解决逻辑,使得每次返回的值都在原来的根底上多加了 100

参考
Hardhat 官网文档:Overview | Hardhat | Ethereum development environment for professionals by Nomic Foundation[1]

OpenZeppelin 官网文档:Using with Upgrades – OpenZeppelin Docs[2]

References
[1] Overview | Hardhat | Ethereum development environment for professionals by Nomic Foundation: https://hardhat.org/getting-s…
[2] Using with Upgrades – OpenZeppelin Docs: https://docs.openzeppelin.com…

退出移动版