티스토리 뷰
hardhat으로 contract를 작성하고 배포해보겠다.
이 과정을 수행하기 위해선 반드시 사전에 환경설정이 완료되어야한다.
환경설정이 필요하다면 아래 링크를 참고해서 세팅해주자.
[Ethereum] hardhat 설치 및 환경설정
hardhat은 이더리움 소프트웨어 개발환경으로 스마트 컨트랙트와 DApp을 개발, 컴파일, 디버깅, 배포하기위한 완전한 개발환경을 제공한다. hardhat은 반복된 작업(like 검증 과정)을 간단한 명령어 한
jerryjerryjerry.tistory.com
Solidity 코드 작성
아래 사진은 이번 프로젝트 실행에 필요한 디렉토리 목록이다.

우선 프로젝트 경로 아래에 contracts라는 폴더를 만든 후 Box.sol 파일을 생성해준다.
Box.sol을 작성해서 기본적인 getter, setter를 제공하는 컨트랙트를 배포해 볼 것이다.
// contracts/Box.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
contract Box {
uint256 private value;
// Emitted when the stored value changes
event ValueChanged(uint256 newValue);
// Stores a new value in the contract
function store(uint256 newValue) public {
value = newValue;
emit ValueChanged(newValue);
}
// Reads the last stored value
function retrieve() public view returns (uint256) {
return value;
}
}
Box라는 contract의 상태를 초기화할 수 있는 것은 생성자가 아니라 public 접근자로 모두 접근할 수 있는 store 함수이다.
Solidity 코드 테스트 및 컴파일
작성한 코드를 로컬에서 실행시키고 테스트하기 위해선 패키지를 하나더 설치해줘야한다.
npm install --save-dev chai
작성한 Box.sol이 컴파일이 되는지 올바르게 작성됐는지
proxy를 통해 호출될 수 있는지 모듈 테스트를 진행할 것이다.
test라는 폴더를 생성한 뒤 아래 Box.js 와 Box.proxy.js 파일을 생성하고 아래처럼 작성하자
// test/Box.js
// Load dependencies
const { expect } = require('chai');
let Box;
let box;
// Start test block
describe('Box', function () {
beforeEach(async function () {
Box = await ethers.getContractFactory("Box");
box = await Box.deploy();
await box.deployed();
});
// Test case
it('retrieve returns a value previously stored', async function () {
// Store a value
await box.store(42);
// Test if the returned value is the same one
// Note that we need to use strings to compare the 256 bit integers
expect((await box.retrieve()).toString()).to.equal('42');
});
});
// test/Box.proxy.js
// Load dependencies
const { expect } = require('chai');
let Box;
let box;
// Start test block
describe('Box (proxy)', function () {
beforeEach(async function () {
Box = await ethers.getContractFactory("Box");
box = await upgrades.deployProxy(Box, [42], {initializer: 'store'});
});
// Test case
it('retrieve returns a value previously initialized', async function () {
// Test if the returned value is the same one
// Note that we need to use strings to compare the 256 bit integers
expect((await box.retrieve()).toString()).to.equal('42');
});
});
그런 다음 테스트를 실행하기 위해 아래 명령어를 입력하자
npx hardhat test
$ npx hardhat test
✔ Help us improve Hardhat with anonymous crash reports & basic usage data? (Y/n) · y
Downloading compiler 0.8.9
Compiled 1 Solidity file successfully
Box
✔ retrieve returns a value previously stored
Box (proxy)
✔ retrieve returns a value previously initialized
2 passing (2s)
에러 발생없이 Box 컨트랙트와 proxy가 잘 컴파일 됐다면 컨트랙트를 배포할 준비가 완료된 것이다.
contract 배포
Box를 배포하기 위해 배포 코드를 작성해 줄 것이다.
scripts 라는 폴더를 생성하고 아래에 deploy.js를 만들어 아래와 같이 작성해주자.
Box 컨트랙트를 인스턴스화하고, Box의 상태값을 초기화하는 함수는 store 함수, 그리고 초기값은 42를 넣는다.
// scripts/deploy.js
async function main() {
const Box = await ethers.getContractFactory("Box");
console.log("Deploying Box...");
const box = await upgrades.deployProxy(Box, [42], { initializer: 'store' });
console.log("Box deployed to:", box.address);
}
main()
.then(() => process.exit(0))
.catch(error => {
console.error(error);
process.exit(1);
});
그리고 앞에서 설정해준 .env 파일에 네트워크 정보를 hardhat.config.js에 입력해준다.
// hardhat.config.js
require("@nomiclabs/hardhat-ethers");
require('@openzeppelin/hardhat-upgrades');
require("dotenv").config();
const { ALCHEMY_URL, METAMASK_KEY } = process.env;
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);
}
});
/** @type import('hardhat/config').HardhatUserConfig */
module.exports = {
solidity: "0.8.9",
defaultNetwork: "goerli",
networks: {
hardhat: {},
goerli: {
url: ALCHEMY_URL,
accounts: [`0x${METAMASK_KEY}`]
}
}
};
드디어 작성한 Box 컨트랙트를 배포할 차례이다.
터미널에 아래와 같은 명령어를 입력해준다(프로젝트의 루트 경로에서 입력해주자)
npx hardhat run --network goerli scripts/deploy.js
배포에 성공하면 아래와 같이 주소 하나가 로그로 뜨는데
배포한 컨트랙트의 주소이므로 잘 복사해두자
$ npx hardhat run --network goerli scripts/deploy.js
Deploying Box...
Box deployed to: 0x249bEb20558DBaBec3c0b78636F839D04cC00000
contract 다루기
터미널을 하나 더 열고 콘솔을 열어 컨트랙트를 다뤄보자.
goerli 네트워크로 연결하는 명령어이다.
npx hardhat console --network goerli
그런 다음 Box 컨트랙트에 접근할 수 있는 인스턴스(proxy 역할)를 만들어 주자.
배포할 당시 Box.js에 초기값을 42를 넣어줬다.
retrieve 함수를 출력해보면 초기값 42가 나타나는 것을 확인할 수 있다.
$ npx hardhat console --network goerli
Welcome to Node.js v14.19.0.
Type ".help" for more information.
> const Box = await ethers.getContractFactory("Box")
undefined
> const box = await Box.attach("0x249bEb20558DBaBec3c0b78636F839D04cC00000")
undefined
> (await box.retrieve()).toString()
'42'
Box 컨트랙트에는 value 값을 초기화하는 store 함수도 있었다.
store 함수로 value 값을 재정의 해보자.
> await box.store(12)
{
hash: '0xcfe64a4cf4c60624e0e5482399544920de371459222d4d1750ba67c0c1500000',
type: 2,
accessList: [],
blockHash: null,
blockNumber: null,
transactionIndex: null,
confirmations: 0,
from: '0xaCb8D2dD46887C9304db1BaB6633b4fC43400000',
gasPrice: BigNumber { value: "1500000011" },
maxPriorityFeePerGas: BigNumber { value: "1500000000" },
maxFeePerGas: BigNumber { value: "1500000011" },
gasLimit: BigNumber { value: "35209" },
to: '0x249bEb20558DBaBec3c0b78636F839D04cC00000',
value: BigNumber { value: "0" },
nonce: 7,
data: '0x6057361d000000000000000000000000000000000000000000000000000000000000000c',
r: '0x7f0bd5486479cd0abb7a54cbc436673a417a2a4a635bff9016c8a024f3091f0c',
s: '0x7af04f54e65f03e8a66f4ab38f97ada9cf6b08b87bbe342a471cad8314fdeb27',
v: 0,
creates: null,
chainId: 5,
wait: [Function (anonymous)]
}
> (await box.retrieve()).toString()
'12'
store 함수는 call 함수가 아니고 상태를 변경하는 setter 함수이기에
실행시키면 반드시 트랜잭션을 일으킨다.
시간이 좀 걸리면서 위와 같이 hash와 트랜잭션 recipt 정보가 정상적으로 떴다면 올바르게 처리된 것이다.
그런다음 다시 retrieve 함수를 출력하면 상태값이 12로 잘 변경된 것이 보인다.
goerli 이더스캔에서도 확인하면 성공적으로 트랜잭션이 보내진 것을 확인할 수 있다.

여기까지 hardhat으로 간단한 컨트랙트 작성과 배포, 실행까지 해보았다.
더 많은 기능이 있는 컨트랙트를 작성하고 알맞은 proxy 코드를 만들 수 있다면
더 좋은 플랫폼 개발을 할 수 있을 것이다.
'BlockChain > Ethereum' 카테고리의 다른 글
[Ethereum] 알케미(Alchemy) API Key 발급받는 방법 (0) | 2022.08.11 |
---|---|
[Ethereum] Goerli 테스트넷 Faucet 이더 얻기 (1) | 2022.08.11 |
[Ethereum] hardhat 설치 및 환경설정 (0) | 2022.08.10 |
[Ethereum] 이더스캔(Etherscan) 보는 방법, 개발 용어 정리 (2) (1) | 2022.06.14 |
[Ethereum] EIP-1559 수수료 모델에 대하여 (0) | 2022.06.13 |