Hardhat으로 업그레이드 가능한 견고성 계약 생성
OpenZeppelin은 상태 손실 없이 스마트 계약을 업데이트할 수 있는 업그레이드 가능 항목을 제공하여 구출합니다. 이 튜토리얼은 Hardhat 및 OpenZeppelin 업그레이드 가능 계약을 사용합니다.
터미널을 열고 다음을 입력하여 새 npm 프로젝트를 만듭니다. (이 튜토리얼에서는 사용자가 이미 노드와 npm을 시스템에 설치했다고 가정합니다.)
튜토리얼 코드가 살아 있음here
Project Dependencies
프로젝트 종속성
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.
.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;
}
}
이 스마트 계약은 계산기의 간단한 계약입니다. 계약은 Openzeppelin 계약인
Initializable
계약에서 상속됩니다. initialize
함수가 한 번만 호출되도록 합니다. 업그레이드 가능한 계약에는 생성자가 없으므로 initialize
함수가 생성자로 작동하고 한 번만 호출해야 합니다. initializer
수정자는 함수가 한 번 호출되도록 합니다.계약에는
val
라는 이름의 공용 변수와 initialize
, add
및 getVal
의 세 가지 함수가 있습니다. hardhat-config.js
파일에서 설정한 Rinkeby 네트워크에 이 계약을 배포하려고 합니다.scripts 폴더 안에 새 파일을 만들고 이름을 지정합니다
deploy_contract.js
. 이 파일에는 계산기 계약을 배포할 코드가 포함됩니다.deploy_contract.js
파일 안에 다음 코드를 추가합니다.//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
에서 upgrades
및 hardhat
가 필요합니다. async
함수가 생성되고 함수 내에서 계약 이름( CalculatorV1 )과 함께 ethers
를 사용하여 계약 팩토리가 검색됩니다. 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
. This contract is the
TransparentUpgradeableProxyProxy
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.
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. OpenaeppelinInitializable
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
andimmutable
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)));
}
요약
스마트 계약을 업그레이드하는 방법이 있으면 계약 코드를 변경하고 개선해야 할 때 유용할 수 있습니다. 읽어 주셔서 감사합니다...
Reference
이 문제에 관하여(Hardhat으로 업그레이드 가능한 견고성 계약 생성), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/jamiescript/creating-upgradable-solidity-contract-with-hardhat-1134텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)