Tools/faucet4Testing — Step 1: 환경 + ERC20 컨트랙트
looks_oneBlockchain2026-04-25· 39분 읽기

faucet4Testing — Step 1: 환경 + ERC20 컨트랙트

EVM 로컬 개발 환경 구성과 OpenZeppelin 기반 ERC20(FaucetToken/USDT) 작성·배포·민트 단계별 가이드.

list목차(45)

Step 1: 환경 구성 + ERC20 컨트랙트 — 상세 가이드

README 개발 플랜 Step 1에 따른 순차 작업 가이드입니다.

목표: EVM 로컬 개발 환경을 만들고, 배포·호출 가능한 ERC20 컨트랙트(이름/심볼/민트 권한)를 작성한다.

산출물: Hardhat 또는 Foundry 프로젝트, ERC20 컨트랙트(이름·심볼·민트 권한), 로컬 컴파일·배포·호출 확인

추가 참고

  • C. 로컬 블록체인에서 테스트하기: 로컬 노드 상시 실행, 테스트 ETH·ERC20 민팅·전송, Remix/Ganache UI 사용법.
  • D. 브라우저 UI + 상태 저장/복원: 브라우저에서 계정·잔액 조회, Anvil --state로 상태 저장/불러오기 설계.

폴더 구조 (도구별 분리)

각 도구는 서로 다른 폴더에서 진행합니다. 둘 다 사용할 경우 두 폴더를 모두 만들고, 하나만 사용할 경우 해당 폴더만 만들면 됩니다.

도구 작업 폴더 설명
Hardhat hardhat/ Node.js 기반, JavaScript/TypeScript 스크립트·테스트
Foundry foundry/ Forge/Anvil/Cast 기반, Solidity 네이티브 테스트·배포

프로젝트 루트 기준 예시:

faucet4Testing/
├── hardhat/          # Hardhat 프로젝트 (선택 시)
│   ├── contracts/
│   ├── scripts/
│   ├── test/
│   └── hardhat.config.js
├── foundry/          # Foundry 프로젝트 (선택 시)
│   ├── src/
│   ├── script/
│   ├── test/
│   └── lib/
├── docs/
└── README.md

아래에서 Hardhat 경로와 Foundry 경로 중 하나를 선택해 해당 폴더에서만 순차 진행하면 됩니다.


공통: 사전 요구사항

Hardhat 사용 시

  • Node.js v22.10.0 이상 권장 (Hardhat 3.x 기준). v18~20 사용 시 경고가 나올 수 있음.
  • npm v9 이상 또는 yarn / pnpm
node -v   # 22.10.0 이상 권장 (Hardhat 3.x)
npm -v

Foundry 사용 시

curl -L https://foundry.paradigm.xyz | bash
foundryup
forge --version
anvil --version
cast --version

A. Hardhat 버전 — hardhat/ 폴더에서 진행

모든 명령은 프로젝트 루트가 아닌 hardhat/ 폴더에서 실행합니다.

Ethers.js / Viem: 배포·호출 예시는 EthersViem 두 가지를 모두 안내합니다. Viem만 설치된 경우(Hardhat 3 + Node Test Runner + Viem)는 A.5.1 배포 스크립트와 A.6의 Viem 호출 확인 예시를 사용하세요.


A.1 hardhat/ 폴더 생성 및 초기화

프로젝트 루트(faucet4Testing/)에서:

mkdir -p hardhat
cd hardhat
npx hardhat init
  • Create a JavaScript project (또는 TypeScript) 선택
  • 패키지 매니저·Git 옵션은 프로젝트에 맞게 선택
  • sudo는 사용하지 않는 것이 좋습니다 (권한·경로 문제를 일으킬 수 있음).

생성되는 구조:

hardhat/
├── contracts/       # Solidity 컨트랙트
├── scripts/         # 배포·유틸 스크립트
├── test/            # 테스트
├── hardhat.config.js (또는 .ts)
└── package.json

이후 단계도 현재 디렉터리가 hardhat/인지 확인한 뒤 진행합니다.

A.1.1 초기화 시 자주 나오는 오류와 해결

다음과 비슷한 메시지가 나올 수 있습니다.

오류 예시 (터미널 출력)

Need to install the following packages:
hardhat@3.x.x
Ok to proceed? (y) y

WARNING: You are using Node.js 20.17.0 which is not supported by Hardhat.
Please upgrade to 22.10.0 or a later LTS version (even major version number)

Error HHE3: No Hardhat config file found.

You can initialize a new project by running Hardhat with --init

For more info go to https://hardhat.org/HHE3 or run Hardhat with --show-stack-traces

해결 방안

원인 해결
Node.js 버전 Hardhat 3.x는 Node 22.10.0 이상을 요구합니다. node -v로 확인 후, nodejs.org에서 LTS 설치 또는 nvm use 22(nvm 사용 시)로 버전 변경.
초기화 옵션 npx hardhat -init(하이픈 하나)이 아니라 npx hardhat --init(하이픈 두 개) 또는 **npx hardhat init**으로 실행. 사용 중인 Hardhat 버전 안내에 맞춰 선택.
실행 위치 설정 파일이 없으면 "No Hardhat config file found"가 납니다. 반드시 hardhat/ 폴더 안에서 npx hardhat init(또는 --init)을 실행하세요. 루트나 다른 디렉터리에서 실행하지 않습니다.
sudo 사용 sudo npx hardhat ...는 권한·경로가 꼬일 수 있으므로 사용하지 않습니다. 전역 설치는 npm install -g hardhat 등으로 하고, 프로젝트 내에서는 npx hardhat만 사용합니다. sudo 없이 쓰려면 아래 권한 조정 참고.

sudo 없이 사용하려면 — 권한 조정

이전에 sudo npm / sudo npx를 썼다면, 전역 디렉터리나 프로젝트가 root 소유가 되어 권한 오류가 날 수 있습니다. 한 번만 아래 중 하나를 적용하면 됩니다.

  • 방법 1: 소유자 변경 (권장)
    npm 캐시·전역 prefix를 현재 사용자 소유로 바꿉니다.
    npm config get prefix로 prefix 확인 후(예: /usr/local), 해당 경로의 lib/node_modules, bin 등이 root 소유면:

    # npm 캐시
    sudo chown -R "$(whoami)" ~/.npm
    
    # 전역 설치 경로 (prefix가 /usr/local 이라면)
    sudo chown -R "$(whoami)" /usr/local/lib/node_modules
    sudo chown -R "$(whoami)" /usr/local/bin
    

    이후에는 npm install -g ..., npx ... 를 sudo 없이 사용할 수 있습니다.

  • 방법 2: chmod로 쓰기 허용
    특정 디렉터리만 그룹/others 쓰기 허용할 때. (보안상 필요한 범위만 적용하는 것이 좋습니다.)

    # 현재 프로젝트의 node_modules만 현재 사용자가 쓸 수 있게 (이미 본인 소유면 불필요)
    chmod -R u+rwX hardhat/node_modules
    
    # 전역 node_modules를 모든 사용자 쓰기 가능하게 (비추천; 가능하면 방법 1 사용)
    # sudo chmod -R 775 /usr/local/lib/node_modules
    

    프로젝트는 보통 chown -R $(whoami) hardhat 으로 폴더 소유자를 바꾸는 편이 더 안전합니다.

  • 방법 3: 사용자 전용 npm prefix 사용
    전역 설치는 사용자 홈 아래로 두면 sudo가 필요 없습니다.

    mkdir -p ~/.npm-global
    npm config set prefix ~/.npm-global
    # 셸 설정(.zshrc 등)에 추가: export PATH="$HOME/.npm-global/bin:$PATH"
    

정리: hardhat/로 이동 → npx hardhat init 또는 npx hardhat --init 실행, Node 22.10.0 이상, sudo 생략.

A.1.2 Hardhat 버전 선택 (3 Beta vs 2)

npx hardhat --init 실행 후 다음처럼 버전을 묻는 화면이 나옵니다.

? Which version of Hardhat would you like to use? …
❯ Hardhat 3 Beta (recommended for new projects)
  Hardhat 2 (older version)

각 옵션 특성

구분 Hardhat 3 Beta Hardhat 2
상태 신규 프로젝트용 권장, 2024~2025 기준 프로덕션 사용 가능 기존 안정 버전, 레거시
설정 선언형 설정(defineConfig), ESM 기본. config는 ES 모듈 필수 명령형 설정, CommonJS 가능
네트워크 명시적 네트워크 관리, 한 프로세스에서 여러 연결 동시 사용 가능 단일 hre.network 고정
테스트 Solidity 테스트(Foundry 스타일) 지원, 플러그인으로 Mocha/Node 테스트 러너 선택 Mocha + JS/TS 테스트 위주
빌드 빌드 프로파일, 타입된 artifacts 기본 생성, npm 연동 강화 기존 컴파일·아티팩트 방식
Node Node 22.10.0 이상 필요 Node 18 등 이전 버전도 동작
플러그인 훅 기반 확장, config에 플러그인·태스크 명시적 등록 extendConfig / extendEnvironment 등 기존 방식

차이 요약

  • Hardhat 3: 완전 재작성. ESM·선언형 설정·다중 네트워크·Solidity 테스트 등 새 기능이 많고, 기존 Hardhat 2 프로젝트와 설정·API가 호환되지 않음. 신규 프로젝트에 적합.
  • Hardhat 2: 예전 문서·블로그·플러그인 대부분이 이 버전 기준. 기존 프로젝트 유지·마이그레이션 없이 빠르게 시작할 때 선택.

권장

  • 새로 시작하는 Faucet4Testing처럼 신규 프로젝트Hardhat 3 Beta 선택을 권장합니다. (프롬프트에서 “Hardhat 3 Beta (recommended for new projects)”)
  • 이미 Hardhat 2로 된 레포를 유지해야 하거나, 특정 플러그인(hardhat-deploy, gas 리포터 등)이 아직 Hardhat 3를 지원하지 않으면 Hardhat 2를 선택해도 됩니다.

선택 후 생성되는 config·스크립트 형식이 버전마다 다르므로, 본 가이드의 이후 단계(배포 스크립트 등)는 선택한 버전에 맞춰 Hardhat 공식 문서를 참고해 조정하면 됩니다.

A.1.3 프로젝트 타입 선택 (Hardhat 3)

Hardhat 3 Beta 선택 후, 프로젝트 타입을 묻는 화면이 나옵니다.

? What type of project would you like to initialize?
A TypeScript Hardhat project using Node Test Runner and Viem
  A TypeScript Hardhat project using Mocha and Ethers.js
  A minimal Hardhat project

각 옵션 특성

구분 Node Test Runner + Viem Mocha + Ethers.js Minimal
테스트 러너 Node.js 내장 테스트 러너 Mocha 없음(직접 추가)
블록체인 라이브러리 Viem Ethers.js 없음(직접 추가)
언어 TypeScript TypeScript 최소(TS/JS 설정 본인 선택)
의존성 적음, Node 내장 활용 Mocha·Ethers 추가 최소
테스트 속도 보통 더 빠름 플러그인·설정에 따라 다름
문서·예제 늘어나는 추세 매우 많음(기존 Hardhat 예제 대부분)

옵션별 설명

  • Node Test Runner + Viem

    • Node Test Runner: Node.js 18+ 내장 테스트 러너. describe/it/test 지원, 별도 Mocha 설치 없이 테스트 실행 가능.
    • Viem: TypeScript 네이티브, 가벼운 Ethereum 클라이언트. 최신 Hardhat/Next.js 생태계에서 많이 사용.
    • Hardhat 3에서 첫 번째(권장) 옵션으로 나오는 구성입니다.
  • Mocha + Ethers.js

    • Mocha: 오래 쓰인 테스트 프레임워크. 블록체인 튜토리얼·블로그 예제가 대부분 이 조합.
    • Ethers.js: 스마트 컨트랙트 연동에 널리 쓰이는 라이브러리. README에도 “ethers v6 또는 viem”으로 명시되어 있음.
    • 기존 자료를 참고하며 진행하기 좋습니다.
  • A minimal Hardhat project

    • 테스트 러너·Ethers/Viem 등 아무것도 포함하지 않은 최소 템플릿.
    • 본인이 테스트 도구·라이브러리를 골라서 넣고 싶을 때 선택합니다.

권장

  • Faucet4Testing처럼 새로 시작하는 프로젝트에서는 “A TypeScript Hardhat project using Node Test Runner and Viem” 선택을 권장합니다. Hardhat 3 기본 권장 구성이고, 이후 Step 4 백엔드에서 viem을 쓸 때 설정을 맞추기 쉽습니다.
  • 기존 Ethers.js 예제·문서를 많이 참고할 예정이면 **“A TypeScript Hardhat project using Mocha and Ethers.js”**를 선택해도 됩니다. README의 “ethers v6 또는 viem” 중 Ethers 쪽에 맞춥니다.
  • Minimal은 테스트·라이브러리를 직접 구성하고 싶을 때만 선택하면 됩니다.

선택한 타입에 따라 생성되는 hardhat.config, test/, scripts/ 구조와 사용하는 API(viem vs ethers)가 달라지므로, 본 가이드의 배포 스크립트·테스트 예시는 선택한 조합에 맞게 import·문법만 바꿔서 사용하면 됩니다.


A.2 OpenZeppelin Contracts 설치

hardhat/ 디렉터리에서:

npm install @openzeppelin/contracts

(또는 yarn add @openzeppelin/contracts / pnpm add @openzeppelin/contracts)


A.3 ERC20 컨트랙트 작성

파일: hardhat/contracts/FaucetToken.sol

요구사항: 이름(name), 심볼(symbol), 민트 권한(owner만 mint 가능)

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

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract FaucetToken is ERC20, Ownable {
    constructor(
        string memory name_,
        string memory symbol_,
        address initialOwner_
    ) ERC20(name_, symbol_) Ownable(initialOwner_) {}

    /// @notice 민트 권한: owner만 호출 가능
    function mint(address to, uint256 amount) external onlyOwner {
        _mint(to, amount);
    }
}
  • name_, symbol_: 예) "Faucet Test Token", "FAUCET"
  • initialOwner_: 민트 권한을 가질 주소 (배포자 또는 Faucet 서버 주소)

A.4 컴파일

hardhat/에서:

npx hardhat compile

artifacts/, cache/가 생성되면 성공입니다.

A.4.1 artifacts/cache/ 폴더

컴파일 후 생성되는 두 폴더의 역할과 확인할 사항입니다.

artifacts/ — 컴파일 결과물

  • Solidity 컴파일 최종 결과가 저장되는 폴더. 스크립트·테스트·프론트엔드에서 컨트랙트 연동 시 사용합니다.
  • 포함 내용: 컨트랙트별 ContractName.json (ABI, 바이트코드, 메타데이터), artifacts.d.ts(TypeScript 타입), build-info/*.json.
  • 체크: 수정한 컨트랙트에 대해 contracts/.../ContractName.json 존재 여부, JSON 안에 abi 포함 여부, .gitignoreartifacts/ 등록 여부.

cache/ — 컴파일 캐시

  • 증분 컴파일용 캐시. 변경 없는 컨트랙트는 재컴파일하지 않고 이전 결과를 재사용해 빌드 시간을 줄입니다.
  • 포함 내용: compile-cache.json(소스 → 아티팩트 경로 매핑), build-info/, 테스트용 빌드는 test-artifacts/ 등.
  • 체크: 소스 수정 후 compile 시 캐시 갱신 여부. 컴파일이 꼬이면 npx hardhat clean 후 다시 npx hardhat compile. .gitignorecache/ 등록.
폴더 기능 확인 포인트
artifacts/ 컴파일 결과물(ABI, 바이트코드). 배포·연동 시 사용 컨트랙트별 JSON·ABI 존재, .gitignore 등록
cache/ 증분 컴파일 캐시 문제 시 hardhat clean 후 재컴파일, .gitignore 등록

둘 다 생성되는 것이 정상이며, 커밋하지 않아도 됩니다.


A.5 로컬 노드 실행 및 배포

터미널 1hardhat/에서 로컬 노드 기동:

cd hardhat
npx hardhat node

터미널 2 — 배포 스크립트 작성 후 실행.

Ethers.js가 설치된 경우 — 아래 CommonJS 예시 사용. Viem만 설치된 경우(Hardhat 3 + Node Test Runner + Viem)는 A.5.1 Viem 배포 스크립트를 사용하세요.

파일: hardhat/scripts/deploy.js (Ethers 사용 시)

const hre = require("hardhat");

async function main() {
  const [deployer] = await hre.ethers.getSigners();
  const name = "Faucet Test Token";
  const symbol = "FAUCET";

  const FaucetToken = await hre.ethers.getContractFactory("FaucetToken");
  const token = await FaucetToken.deploy(name, symbol, deployer.address);
  await token.waitForDeployment();

  const address = await token.getAddress();
  console.log("FaucetToken deployed to:", address);
  console.log("Owner (minter):", deployer.address);
}

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

A.5.1 Viem만 설치된 경우 — 배포 스크립트

Hardhat 3 + @nomicfoundation/hardhat-toolbox-viem (Ethers 없이 Viem만) 사용 시 아래처럼 작성합니다. ESM·TypeScript 기준입니다.

파일: hardhat/scripts/deploy.ts

import { network } from "hardhat";

async function main() {
  const { viem, networkName } = await network.connect();
  const [walletClient] = await viem.getWalletClients();
  const deployerAddress = walletClient!.account.address;

  const name = "Faucet Test Token";
  const symbol = "FAUCET";

  console.log(`Deploying FaucetToken to ${networkName}...`);
  const token = await viem.deployContract("FaucetToken", [
    name,
    symbol,
    deployerAddress,
  ]);
  console.log("FaucetToken deployed to:", token.address);
  console.log("Owner (minter):", deployerAddress);
}

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

배포 실행 (터미널 2, hardhat/에서):

  • Viem: npx hardhat run scripts/deploy.ts --network localhost
  • Ethers: npx hardhat run scripts/deploy.js --network localhost

A.6 호출 확인

같은 로컬 노드에서 balanceOf, mint 동작을 확인합니다.

A.6.0 Hardhat console이란 — 목적과 할 수 있는 작업

npx hardhat console --network localhost로 들어가는 Hardhat console은 다음을 위한 대화형 REPL입니다.

구분 설명
목적 로컬 노드(localhost)에 연결된 상태에서, 스크립트 파일 없이 한 줄씩 컨트랙트 배포·호출·조회를 시험해 보는 도구입니다.
목표 Step 1에서는 배포된 FaucetToken에 대해 mint, balanceOf를 호출해, 토큰 지급·잔액 조회가 정상 동작하는지 빠르게 확인하는 것입니다.

할 수 있는 작업 예시

  • 연결된 네트워크에서 지갑(계정) 목록 가져오기
  • 컨트랙트 배포 또는 이미 배포된 컨트랙트 인스턴스 가져오기
  • view 호출 (예: balanceOf) — 가스 없이 조회
  • 트랜잭션 보내기 (예: mint) — 서명 후 전송

주의: ss, help, mint처럼 변수명만 입력하면 “Uncaught ReferenceError: xxx is not defined”가 납니다. console에서는 사용 가능한 전역 객체(예: Ethers 사용 시 ethers, hre)를 통해 호출해야 합니다.
Viem만 설치된 프로젝트에서는 console에 ethers가 없을 수 있어, 아래 Viem만 설치된 경우처럼 스크립트(verify-deploy.ts)로 확인하는 것을 권장합니다.


Ethers.js가 설치된 경우 — Hardhat 콘솔 사용.

hardhat/에서:

npx hardhat console --network localhost

콘솔 내 (주소는 배포 시 출력된 값으로 교체):

const FaucetToken = await ethers.getContractFactory("FaucetToken");
const token = await FaucetToken.attach("DEPLOYED_CONTRACT_ADDRESS");
const [owner] = await ethers.getSigners();

await token.mint(owner.address, ethers.parseEther("1000"));
const balance = await token.balanceOf(owner.address);
console.log("Balance:", ethers.formatEther(balance)); // 1000.0

Viem만 설치된 경우 — 콘솔 대신 스크립트로 확인합니다. 배포 시 출력한 컨트랙트 주소를 DEPLOYED_CONTRACT_ADDRESS 자리에 넣고 실행하세요.

파일: hardhat/scripts/verify-deploy.ts

import { network } from "hardhat";

async function main() {
  const { viem } = await network.connect();
  const [walletClient] = await viem.getWalletClients();
  const ownerAddress = walletClient!.account.address;
  const tokenAddress = "DEPLOYED_CONTRACT_ADDRESS" as `0x${string}`;

  const token = await viem.getContractAt("FaucetToken", tokenAddress);
  await token.write.mint([ownerAddress, 1000n * 10n ** 18n]);
  const balance = await token.read.balanceOf([ownerAddress]);
  console.log("Balance:", (Number(balance) / 1e18).toString());
}

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

실행: npx hardhat run scripts/verify-deploy.ts --network localhost

owner가 아닌 주소로 mint 호출 시 revert되는지도 확인하면 좋습니다.


B. Foundry 버전 — foundry/ 폴더에서 진행

모든 명령은 foundry/ 폴더에서 실행합니다.


B.1 foundry/ 폴더 생성 및 초기화

프로젝트 루트에서:

mkdir -p foundry
cd foundry
forge init
  • --no-git 옵션을 쓰면 상위가 이미 Git 저장소일 때 서브폴더만 초기화됩니다: forge init --no-git

생성되는 구조:

foundry/
├── src/           # Solidity 컨트랙트
├── script/        # 배포 스크립트
├── test/          # Solidity 테스트
├── lib/           # 의존성 (OpenZeppelin 등)
├── foundry.toml
└── .gitignore

B.2 OpenZeppelin Contracts 설치

foundry/에서:

forge install OpenZeppelin/openzeppelin-contracts --no-commit

lib/openzeppelin-contracts/가 생깁니다.

remappings 설정foundry.toml에 다음이 있는지 확인합니다. 없으면 추가합니다.

remappings = [
    "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/"
]

(또는 remappings.txt 파일에 한 줄: @openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/)


B.3 ERC20 컨트랙트 작성

파일: foundry/src/FaucetToken.sol

Hardhat과 동일한 로직, import만 Foundry remapping에 맞게 사용합니다.

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

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract FaucetToken is ERC20, Ownable {
    constructor(
        string memory name_,
        string memory symbol_,
        address initialOwner_
    ) ERC20(name_, symbol_) Ownable(initialOwner_) {}

    /// @notice 민트 권한: owner만 호출 가능
    function mint(address to, uint256 amount) external onlyOwner {
        _mint(to, amount);
    }
}

B.4 컴파일

foundry/에서:

forge build

out/ 디렉터리가 생성되면 성공입니다.


B.5 로컬 노드(Anvil) 실행 및 배포

터미널 1foundry/에서 Anvil 기동:

cd foundry
anvil
  • 기본 RPC: http://127.0.0.1:8545
  • 콘솔에 출력되는 테스트 개인키 중 하나를 복사해 둡니다 (예: 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80).

배포 스크립트 작성

파일: foundry/script/Deploy.s.sol

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

import "forge-std/Script.sol";
import "../src/FaucetToken.sol";

contract DeployScript is Script {
    function run() external {
        // Anvil 기본 첫 번째 계정 키 (로컬 전용)
        uint256 deployerPrivateKey = 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80;
        address deployer = vm.addr(deployerPrivateKey);

        vm.startBroadcast(deployerPrivateKey);

        FaucetToken token = new FaucetToken(
            "Faucet Test Token",
            "FAUCET",
            deployer
        );

        vm.stopBroadcast();
        console.log("FaucetToken deployed to:", address(token));
        console.log("Owner (minter):", deployer);
    }
}

forge-std가 없다면 먼저 설치합니다 (foundry/에서):

forge install foundry-rs/forge-std --no-commit

터미널 2 — 배포 실행 (foundry/에서):

forge script script/Deploy.s.sol:DeployScript \
  --rpc-url http://127.0.0.1:8545 \
  --broadcast
  • 스크립트에서 vm.startBroadcast(deployerPrivateKey)에 테스트 키를 넣었으므로 --private-key는 생략 가능합니다.
  • 배포 후 콘솔에 찍힌 컨트랙트 주소를 복사해 둡니다.

B.6 호출 확인 (cast)

배포된 컨트랙트 주소를 CONTRACT_ADDRESS로 넣어 실행합니다.

mint 호출 (Anvil 첫 번째 계정이 owner이므로):

cast send CONTRACT_ADDRESS "mint(address,uint256)" YOUR_ADDRESS 1000000000000000000000 \
  --rpc-url http://127.0.0.1:8545 \
  --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80

balanceOf 조회:

cast call CONTRACT_ADDRESS "balanceOf(address)(uint256)" YOUR_ADDRESS --rpc-url http://127.0.0.1:8545

YOUR_ADDRESS는 Anvil 첫 계정 주소로 하려면:

cast wallet address --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80

이 주소로 mint한 뒤 balanceOf가 1000e18으로 나오면 정상 동작입니다.


C. 로컬 블록체인에서 테스트하기

로컬에서 계속 켜 둔 블록체인을 쓰고, 테스트 ETHERC20 민팅·전송을 하며, 필요하면 GUI로도 확인하는 방법을 정리합니다.

C.1 하고 싶은 것 정리

목표 내용
1. 로컬 블록체인 프로세스 상시 실행 Ganache처럼 한 번 띄워 두고 개발·테스트에 사용
2. 테스트 ETH + ERC20 민팅·전송 로컬 환경에서 토큰 발급·전송 시나리오 구현
3. 터미널 + GUI 터미널 스크립트뿐 아니라 화면에서 잔액·트랜잭션 확인

아래는 Hardhat 노드를 기본으로 하고, GUI는 Remix 또는 Ganache UI로 보완하는 구성입니다.

C.2 로컬 블록체인 프로세스 — "Ganache처럼" 계속 실행

C.2.1 Hardhat Node (권장 — 이미 사용 중인 경우)

프로젝트에서 쓰는 로컬 노드입니다. 한 터미널에서 계속 켜 두면 Ganache처럼 동작합니다.

cd hardhat
npx hardhat node
  • RPC URL: http://127.0.0.1:8545
  • 계정: 20개 생성, 각 계정에 10,000 ETH 지급 (테스트용)
  • 특징: 터미널 전용. 재시작하면 체인 상태 초기화(스냅샷 저장 없음).

노드를 백그라운드로 두고 싶다면:

npx hardhat node &

또는 tmux / screen 안에서 실행해 두면 됩니다.

참고: 이미 8545 포트를 쓰는 프로세스가 있으면 EADDRINUSE가 납니다. 그때는 lsof -i :8545로 PID를 확인한 뒤 kill <PID>로 종료하거나, 이미 떠 있는 노드를 그대로 사용하면 됩니다.

C.2.2 Anvil (Foundry)

Foundry를 쓰는 경우:

cd foundry
anvil
  • RPC: http://127.0.0.1:8545
  • 계정·개인키가 터미널에 출력되며, ETH가 지급된 상태로 시작합니다.

C.2.3 Ganache UI (GUI로 로컬 체인 관리)

데스크톱 앱으로 로컬 체인을 띄우고, 잔액·트랜잭션·블록을 화면에서 볼 수 있습니다.

  • 다운로드: Ganache 공식 에서 Ganache (GUI) 설치
  • 사용: 앱 실행 → "Quickstart" 또는 "New Workspace" → 로컬 체인 시작
  • RPC: 보통 http://127.0.0.1:7545 (설정에서 확인)
  • 계정: 기본 10개, ETH 지급. 개인키·주소를 GUI에서 복사 가능
  • 상태 저장: Workspace로 저장하면 나중에 다시 열어 이어서 사용 가능

Hardhat에서 이 체인을 쓰려면 hardhat.config에 네트워크를 추가합니다 (아래 C.6 참고).

C.3 테스트 ETH + ERC20 민팅·전송 (로컬 환경)

C.3.1 테스트 ETH

  • Hardhat node / Anvil: 실행 시 생성되는 계정에 이미 테스트 ETH가 들어 있음. 별도 faucet 없이 사용.
  • Ganache: 앱이 지급한 ETH를 그대로 사용.

즉, "로컬 블록체인 프로세스"를 띄우기만 하면 테스트 ETH는 준비된 상태입니다.

C.3.2 ERC20 민팅·전송

이미 있는 FaucetToken 컨트랙트를 로컬 노드에 배포한 뒤, owner가 mint → transfer 하면 됩니다.

터미널에서 (스크립트)

  • 배포: npx hardhat run scripts/deploy.ts --network localhost
    → FaucetToken 배포, owner는 배포한 계정.
  • mint: verify-deploy.ts처럼 token.write.mint([주소, amount]) 호출.
  • transfer: 컨트랙트의 transfer(to, amount)를 스크립트나 다음 GUI에서 호출.

GUI에서 (Remix)

  • Remix에서 로컬 노드에 연결(아래 C.4.1) → "Deploy"로 FaucetToken 배포
    → "At Address"로 이미 배포된 주소 붙여넣기 → mint, transfer, balanceOf 버튼으로 호출.

정리하면:

  1. 로컬 노드 한 번 실행 (Hardhat node / Anvil / Ganache)
  2. FaucetToken 배포 (스크립트 또는 Remix)
  3. owner로 mint(받을주소, 금액) 호출
  4. transfer(받을주소, 금액) 또는 Remix에서 버튼으로 전송

C.4 GUI 환경에서 쓰기

C.4.1 Remix IDE — 로컬 노드 연결

Remix는 브라우저 기반 IDE라서, 로컬 노드를 "외부 RPC"로 붙이면 배포·호출을 전부 GUI로 할 수 있습니다.

  1. Remix 열기: https://remix.ethereum.org
  2. 로컬 노드 실행: 터미널에서 npx hardhat node (또는 Anvil / Ganache) 실행해 두기.
  3. Remix에서 연결:
    • 왼쪽 "Deploy and run transactions" 플러그인 선택
    • Environment에서 "External Http Provider" 또는 "Hardhat Provider" 선택
    • URLhttp://127.0.0.1:8545 입력 (Ganache면 http://127.0.0.1:7545 등)
    • "Connect" 또는 "OK"
  4. 계정: Hardhat/Anvil/Ganache에서 쓰는 계정들이 드롭다운에 나타납니다. 선택해서 사용.
  5. 컨트랙트:
    • "Compile"에서 FaucetToken 컴파일 (또는 Hardhat에서 컴파일한 ABI/Bytecode 복사)
    • "Deploy"에서 생성자 인자(name, symbol, owner) 넣고 배포
    • 배포된 컨트랙트 주소로 "At Address"에 넣으면 mint, transfer, balanceOf 등을 버튼으로 호출 가능.

이렇게 하면 로컬 블록체인 + 테스트 ETH + ERC20 민팅·전송GUI(Remix) 로 구현할 수 있습니다.

C.4.2 Ganache UI

  • 로컬 체인 자체를 GUI로 실행·관리하고 싶을 때 사용.
  • 계정 목록, 잔액, 블록, 트랜잭션을 데스크톱 앱에서 확인.
  • 컨트랙트 배포·호출은 Remix에서 "External Http Provider"로 Ganache RPC(예: 7545)를 넣어서 하면 됩니다.

C.4.3 MetaMask + 로컬 노드 (선택)

  • MetaMask에서 Custom RPC 추가:
    네트워크 추가 → RPC URL http://127.0.0.1:8545 (또는 Ganache 7545)
    체인 ID는 Hardhat 31337, Ganache는 설정값 확인.
  • Hardhat node 출력된 개인키로 계정 import → Remix나 dApp에서 그 계정으로 트랜잭션 서명 가능.

C.5 한 번에 정리 — 추천 흐름

터미널만 쓰는 경우

  1. npx hardhat node 로 로컬 노드 계속 실행
  2. npx hardhat run scripts/deploy.ts --network localhost 로 FaucetToken 배포
  3. verify-deploy.ts 등으로 mint / balanceOf 확인
  4. 필요 시 스크립트에서 transfer 호출

GUI까지 쓰는 경우

  1. 로컬 노드 실행 (npx hardhat node 또는 Ganache UI)
  2. Remix 열기 → External Http Provider http://127.0.0.1:8545 연결
  3. Remix에서 FaucetToken 배포 후 "At Address"로 붙여 넣기
  4. mint, transfer, balanceOf를 Remix 버튼으로 실행하며 확인

Ganache UI를 쓰는 경우

  1. Ganache 앱에서 Workspace 생성 후 로컬 체인 시작
  2. Hardhat config에 ganache 네트워크 추가 (host, port, network_id)
  3. npx hardhat run scripts/deploy.ts --network ganache 로 배포
  4. Remix에서 Ganache RPC(예: 7545)로 연결해 같은 식으로 배포·호출

C.6 Hardhat config 예시 (Ganache 사용 시)

Ganache UI를 쓰면서 Hardhat 스크립트로 배포하려면 hardhat.config.ts에 예를 들어 다음을 추가합니다.

// 예: Ganache 기본 포트 7545
networks: {
  localhost: { url: "http://127.0.0.1:8545" },
  ganache: {
    url: "http://127.0.0.1:7545",
    chainId: 1337,  // Ganache 기본값, 필요 시 맞춤
  },
},

그다음:

npx hardhat run scripts/deploy.ts --network ganache

C.7 요약 (로컬 블록체인 테스트)

하고 싶은 것 방법
1. 로컬 블록체인 상시 실행 npx hardhat node 한 터미널에 계속 실행. 또는 Ganache UI로 실행.
2. 테스트 ETH + ERC20 민팅·전송 노드 실행 시 계정에 ETH 있음. FaucetToken 배포 후 mint/transfer는 스크립트 또는 Remix에서 호출.
3. 터미널 + GUI 터미널: Hardhat 스크립트. GUI: Remix(External Http Provider로 localhost 연결) 또는 Ganache UI로 체인·계정 확인.

D. 브라우저 UI + 상태 저장/복원

목표: 브라우저에서 로컬 체인을 보고, 실행한 내용(상태)을 저장했다가 다시 실행할 때 그대로 불러와 쓰기.

D.1 하고 싶은 것 정리

항목 내용
브라우저에서 보기 계정 목록, 잔액, 블록, 트랜잭션 등을 웹 화면에서 확인
상태 저장 노드를 종료할 때 현재 체인 상태(계정·잔액·컨트랙트·스토리지 등)를 파일로 저장
상태 불러오기 다시 실행할 때 저장한 파일을 읽어서, 그 시점 그대로 이어서 사용

D.2 "상태 저장/불러오기" — Hardhat vs Anvil

브라우저 UI와 백엔드는 RPC(JSON-RPC) 로만 노드에 붙습니다.
그래서 Hardhat node를 쓰든 Anvil(Foundry) 을 쓰든, 로컬에서 실행 중인 노드의 주소(예: http://127.0.0.1:8545)만 맞추면 같은 브라우저·백엔드로 연결해서 볼 수 있습니다.
차이는 "그 노드가 상태를 파일로 저장/복원해 주는지" 여부뿐입니다.

Hardhat node

  • 기본 동작: 종료하면 메모리만 사용하므로 상태가 전부 사라짐.
  • 공식적으로 "종료 시 파일로 덤프 → 다음에 그 파일로 복원" 기능은 없음.
  • 테스트용 스냅샷(takeSnapshot / restore)은 같은 프로세스 안에서만 동작.

Anvil (Foundry)

  • --state <경로> 옵션 하나로 "불러오기 + 저장" 모두 가능.
    • 실행 시: 해당 경로에 파일이 있으면 그 상태로 시작.
    • 종료 시: 현재 상태를 그 경로에 저장.
  • 예: anvil --state ./data/state.json
    • 첫 실행: 빈 체인으로 시작 → 종료 시 ./data/state.json 생성.
    • 이후 실행: ./data/state.json이 있으면 그 상태로 시작.

정리

  • "저장했다가 다시 실행할 때 그대로 불러와 쓰기"를 하려면 Anvil + --state 를 쓰는 구성이 적합합니다.
  • Hardhat은 컴파일·스크립트(배포 등) 에만 쓰고, "실제로 돌리는 노드 + 상태 유지"는 Anvil을 쓰는 방식으로 가면 됩니다.

D.3 전체 구성 제안

[ 브라우저 ]  ←→  [ 백엔드(선택) ]  ←→  [ Anvil (--state) ]
     │                    │                      │
     │   HTTP/WS          │   RPC 8545           │  state.json
     │                    │   또는 프록시         │  저장/로드
     └────────────────────┴──────────────────────┘
  • Anvil: anvil --state ./data/state.json 으로 실행.
    RPC: http://127.0.0.1:8545 (기본).
    종료 시 ./data/state.json에 상태 저장, 다음 실행 시 자동 로드.
  • 브라우저 UI:
    http://localhost:8545 로 직접 붙이거나,
    CORS/보안을 위해 백엔드에서 RPC를 프록시해도 됨.
  • 백엔드(선택):
    Anvil 프로세스 시작/종료(--state 경로 지정),
    "지금 상태 저장" 버튼 → Anvil 종료(자동 덤프) 후 같은 옵션으로 재시작 등 자동화 가능.

D.4 브라우저 UI에서 할 일 (최소)

  • 연결: RPC URL 입력 또는 기본 http://127.0.0.1:8545 사용.
  • 계정: eth_accounts + eth_getBalance 로 목록·잔액 표시.
  • 블록: eth_blockNumber + eth_getBlockByNumber 로 최근 N개 표시.
  • 트랜잭션: 블록에서 transactions 보거나 eth_getTransactionByHash 로 상세 표시.
  • (선택) "상태 저장" 버튼: 백엔드가 있으면 "Anvil 종료(덤프) 후 재시작" 트리거.
    백엔드 없이 쓰면: "Anvil을 --state 옵션으로 띄우고, 종료할 때 Ctrl+C로 끄면 자동 저장" 안내만 해도 됨.

D.5 상태 저장/불러오기 사용 방법 (Anvil)

터미널에서 직접

# 프로젝트 루트 또는 원하는 디렉터리
mkdir -p data
anvil --state ./data/state.json
  • 첫 실행: 빈 체인 + 기본 계정들. 작업 후 Ctrl+C로 종료 → ./data/state.json 생성.
  • 다음 실행: 같은 명령 다시 실행 → ./data/state.json 이 있으면 저장된 상태 그대로 로드.

포트를 바꾸고 싶을 때

anvil --state ./data/state.json --port 8546

브라우저 UI에서 RPC URL을 http://127.0.0.1:8546 으로 맞추면 됩니다.

D.6 Hardhat이 필요한 경우

  • 컨트랙트 컴파일: 지금처럼 hardhat/ 에서 npx hardhat compile 그대로 사용.
  • 배포 스크립트: Hardhat에서 네트워크만 Anvil로 연결.
    예: hardhat.configanvil: { url: "http://127.0.0.1:8545" } 추가 후
    npx hardhat run scripts/deploy.ts --network anvil
    → Anvil 위에 FaucetToken 등 배포. 이 상태도 Anvil 종료 시 --state 파일에 함께 저장됨.

즉, "만들고 배포하는 건 Hardhat, 돌리고 저장/불러오기는 Anvil" 로 나누면 됩니다.

D.7 구현 단계 제안

단계 내용
1 Anvil 상태 저장 확인: anvil --state ./data/state.json 로 띄우고, FaucetToken 배포 후 종료 → 재실행해서 잔액·컨트랙트가 그대로인지 확인.
2 브라우저 UI (최소): HTML+JS 또는 React/Vue 등으로 http://127.0.0.1:8545 에 연결해 계정·잔액·블록 목록만 표시. (ethers/viem으로 RPC 호출.)
3 (선택) 백엔드: Node/Express 등에서 Anvil 자식 프로세스로 anvil --state ... 실행/종료 API, 필요 시 RPC 프록시.
4 (선택) UI에서 "노드 시작/종료": 백엔드가 있으면 버튼으로 Anvil 시작·종료(저장) 트리거.

D.8 요약 (브라우저 UI + 상태 저장)

  • 브라우저에서 보기: RPC(http://127.0.0.1:8545)로 연결하는 웹 페이지 하나면 충분. 계정·잔액·블록·트랜잭션 조회만 해도 "가나슈처럼 보는" 수준 구현 가능.
  • 실행 내용 저장 후 다시 불러오기: Anvil--state <파일경로> 로 가능. Hardhat node는 이 기능이 없으므로, "상태 유지"가 필요하면 Anvil을 쓰고, Hardhat은 컴파일·배포용으로만 쓰는 구성을 권장합니다.
  • 난이도: 브라우저 UI만 먼저 만들면 1~2일, Anvil --state 로 저장/불러오기까지 포함해도 터미널 사용법만 정리하면 바로 적용 가능합니다.

Step 1 완료 체크리스트

  • Hardhat 경로: hardhat/에 프로젝트가 있고, contracts/FaucetToken.sol에 name/symbol/민트 권한 구현됨
    또는 Foundry 경로: foundry/에 프로젝트가 있고, src/FaucetToken.sol에 동일 요구사항 구현됨
  • OpenZeppelin ERC20 + Ownable 기반으로 owner만 mint 가능함
  • npx hardhat compile(Hardhat) 또는 forge build(Foundry)로 컴파일 성공
  • 로컬 노드(Hardhat node / Anvil)에서 배포 후 balanceOf, mint 호출로 동작 확인함
  • README "로컬 실행 방법"에 사용한 경로(hardhat/ 또는 foundry/)와 컴파일·배포 명령 한 줄 이상 정리해 두면 좋음
  • (선택) 로컬 노드 상시 실행·Remix/Ganache 연결 등은 C. 로컬 블록체인에서 테스트하기 참고
  • (선택) 브라우저 UI에서 계정·잔액 보기, 상태 저장 후 재실행이 필요하면 D. 브라우저 UI + 상태 저장/복원 참고

위가 완료되면 Step 2(테스트·배포 스크립트)로 진행하면 됩니다.

More in Blockchain