Hardhat 사용하기

Hardhat 환경에서 스마트 컨트랙트를 생성하고 배포합니다.

이번에는 Hardhat를 시용하여 스마트 컨트랙트를 생성하고 배포해 봅니다.

다음과 같이 Hardhat 프로젝트를 생성할 수 있습니다. 'npx hardhat'을 실행하면 어떤 형태의 프로젝트를 생성할 지 선택하고, 프로젝트의 루트 경로를 설정하고, .gitignore 파일을 추가할 것인지, 샘플 프로젝트에 의존성 모듈을 설치할 것인지는 선택하면 필요한 파일들을 설치하게 됩니다.

$ mkdir greeter
$ cd greeter

$ npm install --save-dev hardhat

$ npx hardhat
888    888                      888 888               888
888    888                      888 888               888
888    888                      888 888               888
8888888888  8888b.  888d888 .d88888 88888b.   8888b.  888888
888    888     "88b 888P"  d88" 888 888 "88b     "88b 888
888    888 .d888888 888    888  888 888  888 .d888888 888
888    888 888  888 888    Y88b 888 888  888 888  888 Y88b.
888    888 "Y888888 888     "Y88888 888  888 "Y888888  "Y888

👷 Welcome to Hardhat v2.13.0 👷‍

✔ What do you want to do? · Create a JavaScript project
✔ Hardhat project root: · /Users/trident/Projects/Solidity/hardhat-test
✔ Do you want to add a .gitignore? (Y/n) · y
✔ Do you want to install this sample project's dependencies with npm (hardhat @nomicfoundation/hardhat-toolbox)? (Y/n) · y

설치하고 난 후 디렉터리 구조는 다음과 같습니다. contracts에 스마트 컨트랙트가 위치하게 되고, scripts에는 배포 스크립트가 test에는 테스트 코드가 artifacts, cache에는 컴파일된 결과가 위치하게 됩니다. hardhat.config.js에 네트워크 연결 정보, 솔리디티 컴파일러 버전 정보 등을 설정합니다.

tree . -I node_modules
.
├── README.md
├── contracts
│   └── Lock.sol
├── hardhat.config.js
├── package-lock.json
├── package.json
├── scripts
│   └── deploy.js
└── test
    └── Lock.js

4 directories, 7 files

기본적으로 생성되는 Lock.sol은 다음과 같고 이를 아래와 같이 컴파일을 합니다.

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

// Uncomment this line to use console.log
// import "hardhat/console.sol";

contract Lock {
    uint public unlockTime;
    address payable public owner;

    event Withdrawal(uint amount, uint when);

    constructor(uint _unlockTime) payable {
        require(
            block.timestamp < _unlockTime,
            "Unlock time should be in the future"
        );

        unlockTime = _unlockTime;
        owner = payable(msg.sender);
    }

    function withdraw() public {
        // Uncomment this line, and the import of "hardhat/console.sol", to print a log in your terminal
        // console.log("Unlock time is %o and block timestamp is %o", unlockTime, block.timestamp);

        require(block.timestamp >= unlockTime, "You can't withdraw yet");
        require(msg.sender == owner, "You aren't the owner");

        emit Withdrawal(address(this).balance, block.timestamp);

        owner.transfer(address(this).balance);
    }
}
$ npx hardhat compile
Compiled 1 Solidity files successfully

기본적으로 생성되는 Lock.js를 이용하여 다음과 같이 테스트를 실행합니다.

$ npx hardhat test


  Lock
    Deployment
      ✔ Should set the right unlockTime (2392ms)
      ✔ Should set the right owner
      ✔ Should receive and store the funds to lock
      ✔ Should fail if the unlockTime is not in the future (46ms)
    Withdrawals
      Validations
        ✔ Should revert with the right error if called too soon
        ✔ Should revert with the right error if called from another account
        ✔ Shouldn't fail if the unlockTime has arrived and the owner calls it
      Events
        ✔ Should emit an event on withdrawals
      Transfers
        ✔ Should transfer the funds to the owner


  9 passing (3s)

스마트 컨트랙트의 배포는 기본적으로 제공되는 deploy.js를 사용합니다.

사전에 배포할 네트워크를 설정해야 하는데 아래와 같이 hardhat.config.js에 WEMIX3.0 테스트넷과 메인넷을 추가합니다. 배포에 사용할 계정 주소의 프라이빗 키를 저장한다면 쉽게 작업할 수 있어 테스트용 계정을 생성한 후 필요한 만큼의 WEMIX만 전송하여 사용하는 것이 좋습니다. MetaMask에서 프라이빗를키키 추출하는 방법은 여기서는 생략합니다.

hardhat.config.js
require("@nomicfoundation/hardhat-toolbox");

/** @type import('hardhat/config').HardhatUserConfig */
const PrivateKey = "<<Private Key>>"

module.exports = {
  solidity: "0.8.18",
  networks: {
    wemix_testnet: {
      url: "https://api.test.wemix.com",
      accounts: [`0x${PrivateKey}`],
      gasPrice: 101000000000
    },
    wemix_mainnet: {
      url: "https://api.wemix.com",
      accounts: [`0x${PrivateKey}`],
      gasPrice: 101000000000
    },
  },
};
scripts/deploy.js
// We require the Hardhat Runtime Environment explicitly here. This is optional
// but useful for running the script in a standalone fashion through `node <script>`.
//
// You can also run a script with `npx hardhat run <script>`. If you do that, Hardhat
// will compile your contracts, add the Hardhat Runtime Environment's members to the
// global scope, and execute the script.
const hre = require("hardhat");

async function main() {
  const currentTimestampInSeconds = Math.round(Date.now() / 1000);
  const unlockTime = currentTimestampInSeconds + 60;

  const lockedAmount = hre.ethers.utils.parseEther("0.001");

  const Lock = await hre.ethers.getContractFactory("Lock");
  const lock = await Lock.deploy(unlockTime, { value: lockedAmount });

  await lock.deployed();

  console.log(
    `Lock with ${ethers.utils.formatEther(
      lockedAmount
    )}ETH and unlock timestamp ${unlockTime} deployed to ${lock.address}`
  );
}

// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.
main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});

준비가 다 되었으면 다음과 같은 명령을 실행하여 배포합니다. 성공적으로 배포되면 사전 설정된 로그에 따라 스마트 컨트랙트 주소가 출력됩니다.

$ npx hardhat run scripts/deploy.js --network wemix_testnet
Lock with 0.001ETH and unlock timestamp 1679558487 deployed to 0x813fbbbeF30f343da37332293546dc854CCecF