Blockchain

Hardhat

Sila 2022. 11. 2. 23:53

 

1. intro

hardhat은 컨트랙트를 컴파일, 배포, 테스트 할 수 있는 truffle과 비슷한 종류의 툴이다.

 

ts를 사용할 수도 있고, depolyment script를 작성하기도 편할 뿐더러,

 

console.log 기능을 지원해 개발 과정에서 디버깅을 훨씬 편하게 해줄 수 있기 때문에

 

truffle에 비해 편리한 점이 있다.

 

2. install, setup

npm install --save-dev hardhat

(공식 개발자 문서에서는 wsl2 환경에서 사용하는 것을 추천하고 있다.)

npx hardhat

콘솔에서 다음과 같이 입력해 hardhat을 실행하면 시작화면이 출력된다.

 

 

여기에서 첫 번째 옵션 (create a JS project)를 선택하고 엔터를 치면

 

루트 디렉토리 안에 컨트랙트 솔리디티 파일과 필요한 폴더들을 생성해준다.

 

처음부터 프로젝를 만들고 싶을 때도 이를 선택해 폴더를 생성해준 후, 각 폴더의 내용물을 정리하는 식으로

 

작업을 진행하는 것이 편할 것 같다.

 

이제 몇 가지 폴더와 파일이 생성될텐데, 컨트랙트를 작성하고 컴파일, 배포를 이어나가며 각각의 역할을 살펴보도록 하자.

 

 

3. apply

1. 컨트랙트 작성

hardhat의 유용한 기능 중 하나가 컨트랙트에서 console.log처럼 메시지를 출력해주는 것이다.

 

개발 과정에서 코드에 문제가 없는지를 확인하고 버그를 수정하는데 매우 유용하다.

 

다음과 같이 contract 폴더 안에 Counter 컨트랙트를 작성해주자.

// contracts/counter.sol

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

import "hardhat/console.sol";
// 이 코드를 추가함으로써 컨트랙트에서 console.log 기능을 사용할 수 있다.

contract Counter {
    uint public count;
    address payable public owner;

    constructor(uint _initialNum) payable {
        count = _initialNum;
        owner = payable(msg.sender);
    }

    function decrease() public {
        require(count >= 1, "cannot be negative number");
        console.log("proceeding decrease");

        count--;
        console.log('current number :', count);
    }

    function increase() public {
        console.log("proceeding increase");
        count++;

        console.log("current number :" , count);
    }

    function showNum() public view returns(uint) {
        console.log("showing number...");
        return count;
    }
}

간단한 카운터 컨트랙트이지만, count 변수가 음수가 되지 않도록 숫자를 낮추는 것은 1 이상일때만

실행 가능하도록 제한을 두었다.

 

솔리디티 파일 상단에 hardhat/console.sol을 추가함으로써 우리가 js, ts 등을 이용해 개발할 때처럼

console.log 기능을 사용할 수 있다.

 

여기서는 decrease, increase 함수가 실행될 때마다 현재 count이 값이 출력되도록 console.log를 추가해주었고,

 

decrease() 함수를 실행할 때, count 값이 음수가 되지 못하게 조건문을 넣고,

 

이 조건을 만족하지 못 해 에러가 날 때, 그 이유를 출력해주도록 require문에 2번째의 매개변수로 메시지(string)를 추가해주었다.

2. compile

이제 작성한 컨트랙트를 evm이 해석할 수 있도록 컴파일을 해주도록 하자.

npx hardhat compile

터미널에 다음과 같이 입력하면 솔리디티 파일을 컴파일해 json 파일로 변환해준다.

 

이는 artifacts/contracts/[sol 파일명] 폴더에 저장된다.

 

 

폴더를 보면 json 파일이 2개 생성되었음을 알 수 있는데,

 

이 중 dbg가 없는 [sol 파일명].json을 보면 된다. truffle에 비해 파일의 길이가 짧고 간결하다.

 

3. hardhat network 사용

hardhat은 추가적인 작업 없이 간단하게 localhost에서 네트워크를 만들어 컨트랙트를 배포하고, 테스트 할 수 있게 해주는

 

기능도 가지고 있는데, 다음과 같이 터미널에 명령어를 입력하기만 하면 테스트용 네트워크를 생성할 수 있다.

npx hardhat node

 

goerli등 다른 테스트용 네트워크에 연결할 수도 있지만 네트워크를 설정해주지 않으면 localhost 네트워크를 생성하고,

 

10000 ETH가 있는 지갑 20개를 생성해 터미널에 출력해준다.

 

4. deploy

이제 이렇게 생성한 네트워크에 컨트랙트를 배포해보자.

 

우선 script 폴더의 deploy.js 파일에서 배포 스크립트를 작성해 주어야한다.

 

우선 어떤 컨트랙트를 배포할 것인지를 알아야 하는데, 우리가 방금 솔리디티 파일을 컴파일 했을 때,

 

생성된 json 파일에 컨트랙트의 이름이 정해져 기록되었다.

 

이 contractName을 매개변수로 주고 getContractFactory 함수를 실행하면 된다.

 

여기에 더해 추가적으로 count의 초기값을 정해주는 작업이 필요하다.

// scripts/deploy.js

const hre = require("hardhat");

async function main() {
  
  const _initialNum = 2;
	// counter의 초기값을 2로 설정

  const Counter = await hre.ethers.getContractFactory("Counter");
	// 우리가 컴파일한 json 파일에 contract의 이름이 이미 지정되어있다.
	// 우리가 배포할 컨트랙트 이름을 getContractfactory의 매개변수로 주면 된다.

  const counter = await Counter.deploy(_initialNum)
      // 설정한 초기값을 deploy()이 매개변수로 넣는다.
      // 이는 contract의 생성자함수의 매개변수로 작용한다.

  await counter.deployed()
}

main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
})

 

이제 컨트랙트를 우리 개인 네트워크에 배포할 것인데, 다음과 같이 네트워크를 지정해 배포한다

npx hardhat run scripts/deploy.js --network localhost

 

배포가 되면 node가 돌아가고 있는 터미널에서 배포된 컨트랙트의 정보와 tx 정보를 출력해줄 것이다.

 

 

ca는 기억해두자.

 

5. console

hardhat도 truffle처럼 콘솔을 사용할 수 있는데, 방식이 조금 다르다.

 

truffle은 web3와 함께 사용되지만, hardhat은 ethers.js와 함께 사용된다.

 

다음과 같이 입력해 콘솔로 들어갈 수 있다. 네트워크를 지정해주는 것을 잊지 말자.

 

npx hardhat console --network localhost

 

콘솔로 진입했다면, 우리가 배포한 Counter 컨트랙트와 상호작용할 수 있다.

 

다음과 같이 Counter 컨트랙트를 변수에 지정할 수 있다.

 

변수 이름은 컨트랙트 이름과 같지 않아도 상관없다.

 

const izone = await ethers.getContractAt("Counter", "0x5fbdb2315678afecb367f032d93f642f64180aa3")

 

네트워크에 배포된 컨트랙트를 특정하려면 컨트랙트의 이름ca 두 개의 인수를 입력해주어야 한다.

 

undefined가 응답으로 나왔다면 이제부터 Counter 컨트랙트를 izone이라는 이름으로 사용할 수 있다.

 

먼저 초기값이 잘 정해졌는지 확인하기 위해 showNum() 함수를 호출해보자.

 

await izone.showNum()

 

우리가 정해준 초기값이 잘 나온다면, decrease 함수를 호출해보자.

 

await izone.decrease()

 

콘솔에서는 응답으로 이 함수의 실행이 포함된 transaction을 보여준다.

 

그와 동시에 노드가 돌아가고 있는 터미널에서도 호출된 contract와 tx 등이 보여지는데,

 

그 아래에 우리가 아까 console.log를 이용해 작성한 현재 count 값이 출력되는 것을 확인할 수 있다.

 

 

여기서 decrease 함수를 두 번 더 호출해 count를 음수로 만들 수 있는지 확인해보자.

 

await izone.decrease()

 

count = 0 인 상태에서 다시 decrease()함수를 호출하려고 하면 다음과 같은 에러를 출력해줄 것이다.

 

 

이 때도 우리가 require문을 사용할 때, 두 번째 인수로 적어준 에러 이유를 출력해주는 것을 확인할 수 있다.

 

이렇게 hardhat의 기본적인 사용법을 알아보았는데,

 

다음에는 개인 네트워크가 아닌 다른 네트워크에 연결하는 법과

 

ethers.js를 이용해 네트워크와 상호작용하는 방법을 알아보도록 하겠다.