Hardhatによるアップグレード可能なSolility契約の作成


スマート契約はブロックチェーンでいったん展開された意味ではありません.これは良いものですが、展開されたコントラクトがバギーであるか、以前に展開されたコントラクトの状態を失うことによって、新しい契約を展開せずに、すでに配備されているスマートコントラクトに特定の機能を追加する必要がある場合はどうなりますか.
OpenZeppelinは、スマート契約が状態損失なしで更新されることができるアップグレードを提供することによって、救助に来ます.このチュートリアルでは、HardHatとOpenZeppelinのアップグレード可能な契約を使用します.
端末を開いて入力することで新しいNPMプロジェクトを作成します
チュートリアルコードの生活here

  • Project Dependencies
  • Project
  • How Upgradable Contract Works
  • Verify Upgradable Contract
  • Upgrading a smart contract
  • Things to know when working with Upgradable Contracts
  • プロジェクト依存

    npm init --y
    

    Open the package.json file that was created when the above command was run and add the following dependencies code below to the package.json file

      "devDependencies": {
        "@nomiclabs/hardhat-ethers": "^2.0.3",
        "@nomiclabs/hardhat-etherscan": "^2.1.8",
        "@openzeppelin/hardhat-upgrades": "^1.12.0",
        "ethers": "^5.5.2",
        "hardhat": "^2.8.0"
      },
      "dependencies": {
        "dotenv": "^16.0.0"
      }
    

    Install the above dependencies by running npm i . This installs the dependencies into the project. @openzeppelin/hardhat-upgrades provides functionality for creating and deploying upgradable contracts. @nomiclabs/hardhat-etherscan is used for verifying the contract using Etherscan. @nomiclabs/hardhat-ethers allows hardhat to work with ether.js.

    プロジェクト

    Create a new hardhat project by running in the terminal:

     npx hardhat
    

    This presents us options to select a project template. Select the first option which is Create a sample project and this creates a sample project with boiler plate code.

    Create a .env file in the project directory. This file will contain our environment variable. In this project, we will need values for the following environmental variables which are:
    INFURA_API_KEY
    PRI_KEY
    ETHERSCAN_API_KEY
    INFURA_API_KEY : our API key we get from
    Infura .我々はinfuraに接続するためにこれを必要とするPRI_KEY : メタマスクのあなたのアカウントの主キー.これはトランザクションに署名するために使用されますETHERSCAN_API_KEY : EtherScanからのAPIキー.これは契約を確認するために使用されます.
    開けるhardhat-config.js 以下のコードを追加して設定します.
    require("@nomiclabs/hardhat-ethers");
    require("@openzeppelin/hardhat-upgrades");
    require("@nomiclabs/hardhat-etherscan");
    
    require('dotenv').config();
    module.exports = {
      solidity: "0.8.10",
      networks: {
        ropsten: {
          url: `https://ropsten.infura.io/v3/${process.env.INFURA_API_KEY}`,
          accounts: [process.env.PRI_KEY],
        },
        rinkeby: {
          url: `https://rinkeby.infura.io/v3/${process.env.INFURA_API_KEY}`,
          accounts: [process.env.PRI_KEY]
        }
      },
      etherscan: {
        apiKey: process.env.ETHERSCAN_API_KEY,
      },
    };
    
    プロジェクト内のコントラクトフォルダーを開き、Greeter.sol ファイル.新しいファイルを作成するCalculatorV1.sol . これは、我々が展開するスマート契約を含みますrinkeby ネットワーク
    ファイルの中CalculatorV1.sol 次のコードに置き換えます.
    pragma solidity 0.8.10;
    
    import "hardhat/console.sol";
    import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
    
    contract CalculatorV1 is Initializable {
       uint public val;
       function initialize(uint256 _val ) external initializer{
            val = _val;
        }
        function add(uint a, uint b) public pure returns (uint) {
            return a + b;
        }
       function getVal() public view returns (uint) {
            return val;
        }
    }
    
    このスマート契約は、電卓の単純な契約です.その契約はInitializable OpenZeppelin契約である契約.それはinitialize 関数は一度だけ呼び出される.アップグレード可能な契約にはコンストラクタがありませんinitialize 関数はコンストラクタとして働きます.The initializer 修飾子は、関数が一度呼び出されることを保証します.
    この契約にはパブリック変数がありますval と3つの関数initialize , add and getVal . 我々は、この契約を、我々が2009年に設定したRrinkebyネットワークに配備したいhardhat-config.js ファイル.
    スクリプトフォルダ内の新しいファイルを作成し、それを呼び出すdeploy_contract.js . このファイルは私たちの計算機契約を展開するコードを含みます.
    インサイドdeploy_contract.js file次のコードを追加します.
    //scripts/deploy_contract.js
    const { ethers, upgrades } = require("hardhat");
    
    async function main() {
       const CalculatorV1 = await ethers.getContractFactory("CalculatorV1");
        console.log("Deploying Calculator...");
        const calculator = await upgrades.deployProxy(CalculatorV1, [42], {
            initializer: "initialize",
        });
        await calculator.deployed();
        console.log("Calculator deployed to:", calculator.address);
    }
    
    main();
    
    上記のコードはethers and upgrades からhardhat . 安async 関数を作成し、関数の内部にあるethers 契約の名前(Calculatorv 1)で.The upgrades.deployProxy は、コントラクトファクトリのコントラクトパスと初期化関数をそのパラメータを渡して展開するために使用されます.
    契約書の中で覚えておいてくださいinitialize 関数val 変数.この関数は、42 関数へのパラメータとして.
    次のコードを端末で実行し、契約を配備します.
     npx hardhat run --network rinkeby scripts/deploy_contract.js
    
    数秒後、契約は
    コントラクトアドレスはコンソールにログオンします.

    アップグレード契約

    When we deployed the contract, three contracts were deployed in total. These are a Proxy contract, a Proxy Admin contract and the Implementation contract which is our CalculatorV1 . When a user interacts with the contract, he is actually interacting with the Proxy contract. The Proxy contract makes a delegateCall to our CalculatorV1 contract. For example A contract named A makes a delegateCall to a contract B calling a function in contract B . The function in B is executed in the state of variable A .

    For our upgradable contract, the Proxy contract calls the Implementation contract (CalculatorV1). The state change is made on the Proxy contract. The Proxy Admin contract is used for updating the address of the implementation contract
    inside the Proxy contract.

    アップグレード契約の検証

    When we deployed our contract, we got back the address of the Proxy contract. if we search for this address on Ether scan we are presented with a contract with name
    TransparentUpgradeableProxy
    . This contract is the Proxy contract and this will be responsible for calling the Implementation contract.

    To verify the Implementation contract and publish the contract code we have to look into the project folder and you will see a folder named .openZeppelin . Open the folder and you will find a file named rinkeby.json . This file is so named because of the network we deployed the contract to. This file was auto generated by hardhat when we ran the deployed script. Inside this file the addressees of the Implementation contract, Proxy admin and the Proxy are kept. As the contract is updated the new addresses are added to the file. Copy the address of the
    Implementation contract and proceed to the terminal for verification.
    Run this code at the terminal:
    npx hardhat verify --network rinkeby contractAddress

    Replace contract address with the Implementation address that was copied and run the code. This verifies the contract source code. We also need to verify the Proxy admin contract. Go to Etherscan and search for the Proxy contract using its address. Click on the Contract tab then click on Code tab and click on the more options. Click on is this a proxy? and then click on verify. The Proxy contract will be verified.

    Upgrading a Contract

    Create a new file inside the contract folder and name it CalculatorV2 .

    pragma solidity 0.8.10;
    
    import "hardhat/console.sol";
    import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
    
    contract CalculatorV2 is Initializable {
        uint public val;
    
        function add(uint a, uint b) public pure returns (uint) {
            return a + b;
        }
    
        function multiply(uint a, uint b) public pure returns (uint) {
            return a * b;
        }
      function getVal() public view returns (uint) {
            return val;
        }
    
    }
    

    We have added a new function to this version of the contract. The multiply function is added. To upgrade the deployed contract. Create a script inside the scripts folder and create a file upgrade_contract.js . Inside this file put the following code.

    const { ethers, upgrades } = require("hardhat");
    //the address of the deployed proxy
    const PROXY = "0xaf03a6F46Eea7386F3E5481a4756efC678a624e6";
    
    async function main() {
        const CalculatorV2 = await ethers.getContractFactory("CalculatorV2");
        console.log("Upgrading Calculator...");
        await upgrades.upgradeProxy(PROXY, CalculatorV2);
        console.log("Calculator upgraded");
    }
    
    main();
    

    The address of the implementation Proxy and the contract factory of the new version of the contract is passed as parameters to upgrades.upgradeProxy . Run the code by typing on the terminal :

     npx hardhat run --network rinkeby scripts/upgrade_contract.js
    

    This will update the address of the Implementation contract in the Proxy contract to make use of the new version deployed. Run the getVal contract to retrieve the value of the state variable val . You will notice that the value of val is still the value we initiated it to be when we deployed the first version of the contract. That is the beauty of upgradable contracts which is the preservation of variable state.

    To verify the contract, we have to perform the same steps that was used to verify the first version of the contract.

    アップグレード契約で作業するときに知るべきこと

    When working with Upgradable contracts the following points should be noted:

    • Constructor: An upgradable contract can not have a constructor . If you have code that must run when the contract is created. The code should be placed in an init function that will get called when the contract is deployed. Openaeppelin Initializable can be used to ensure a function is called once. ( initializer )
    function initialize(uint256 _val ) external initializer {
            val = _val;
    }
    

    The initialize function will be called only once because of the initializer modifier attached to it.

    • state variables : state variables in upgradable contracts once declared cannot be removed. Assuming we have a version one contract where we define the following state variables :
    uint public val;
    string public name;
    

    When deploying version two of the contract, we must ensure that version two of the contract upgrade also contain the same variable as version one in the same order as was defined in version one. The order of the variable matters. if we want to use new state variables, they are added at the bottom.

    uint public val;
    string public name;
    string public newVariableOne;
    uint public newVariableTwo;
    
    • variable initialization : only state variable declared as const and immutable can be initialize. This is because initializing a state variable will attempt to create a storage for that variable. And as we know the Implementation contract don't use its state. The Proxy contract provides the storage used by the Implementation contract.

    The value of variables declared as const are placed in the application code of the contract instead of in storage. That's why only const variable can be initialize.

    • Implementation contract can not contain code that will self destruct the contract. If a contract is self destruct and removed from the blockchain, the Proxy contract will no longer know where to look to execute functions.
    function kill() external {
         selfdestruct(payable(address(0)));
    }
    

    概要


    スマート契約をアップグレードする方法を持っているときに変更し、契約コードを改善する必要がある場合に便利になる可能性があります.読んでくれてありがとう.