Hardhatとは
Hardhatは、Ethereumスマートコントラクトの開発環境です。コントラクトのコンパイル、テスト、デプロイ、デバッグを一貫して行えるツールチェーンを提供します。本書では、Hardhatを使ってゼロからスマートコントラクト開発を学んでいきます。
Hardhatの主な特徴は以下のとおりです。
- ローカルネットワーク: 開発用のEthereumネットワークをローカルで起動できる
- TypeScriptサポート: 設定ファイルやテストをTypeScriptで記述できる
- 豊富なプラグイン: Etherscan検証、ガスレポートなど様々なプラグインが利用可能
- デバッグ機能: Solidityのconsole.logやスタックトレースでデバッグが容易
環境構築
Node.jsのインストール
Hardhatを使うには、Node.js(v18以上)が必要です。
# Node.jsのバージョン確認
node -v
# v18.0.0 以上であることを確認
# npmのバージョン確認
npm -v
Node.jsがインストールされていない場合は、公式サイトからLTS版をダウンロードしてインストールしてください。nvmを使ったバージョン管理もお勧めです。
# nvmを使う場合
nvm install 18
nvm use 18
プロジェクトの初期化
新しいディレクトリを作成し、Hardhatプロジェクトを初期化します。
# プロジェクトディレクトリの作成
mkdir hardhat-tutorial
cd hardhat-tutorial
# npmプロジェクトの初期化
npm init -y
# Hardhatのインストール
npm install --save-dev hardhat
# Hardhatプロジェクトの初期化
npx hardhat init
npx hardhat init を実行すると、対話形式でプロジェクトの設定を聞かれます。
「Create a TypeScript project」を選択してください。推奨されるパッケージのインストールにも同意します。
プロジェクト構成
初期化後のプロジェクト構成は以下のようになります。
hardhat.config.tsの確認
// hardhat.config.ts
import { HardhatUserConfig } from "hardhat/config";
import "@nomicfoundation/hardhat-toolbox";
const config: HardhatUserConfig = {
solidity: "0.8.20",
};
export default config;
最初のコントラクト: Greeter
サンプルの Lock.sol を削除し、シンプルな「Greeter」コントラクトを作成しましょう。これは、メッセージ(greeting)を保存・変更できるだけの簡単なコントラクトです。
// contracts/Greeter.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "hardhat/console.sol";
contract Greeter {
string private greeting;
/// @notice コントラクト作成時に初期メッセージを設定
constructor(string memory _greeting) {
console.log("Deploying Greeter with greeting:", _greeting);
greeting = _greeting;
}
/// @notice 現在のメッセージを取得する
function greet() public view returns (string memory) {
return greeting;
}
/// @notice メッセージを変更する
function setGreeting(string memory _greeting) public {
console.log(
"Changing greeting from '%s' to '%s'",
greeting,
_greeting
);
greeting = _greeting;
}
}
import "hardhat/console.sol" をインポートすることで、Solidityコード内で console.log を使えます。これはHardhatのローカルネットワークでのみ動作し、デバッグに非常に便利です。
コンパイル
コントラクトをコンパイルします。
npx hardhat compile
成功すると、artifacts/ ディレクトリにコンパイル済みのABI(Application Binary Interface)とバイトコードが生成されます。
テストの作成
Hardhatのテストは、MochaとChaiをベースに、ethers.jsを使ってコントラクトとやり取りします。
// test/Greeter.test.ts
import { expect } from "chai";
import { ethers } from "hardhat";
describe("Greeter", function () {
it("デプロイ時に設定したメッセージを返す", async function () {
// コントラクトファクトリを取得
const Greeter = await ethers.getContractFactory("Greeter");
// コントラクトをデプロイ(初期メッセージを設定)
const greeter = await Greeter.deploy("こんにちは、ブロックチェーン!");
// デプロイ完了を待つ
await greeter.waitForDeployment();
// greet()の戻り値を確認
expect(await greeter.greet()).to.equal(
"こんにちは、ブロックチェーン!"
);
});
it("メッセージを変更できる", async function () {
const Greeter = await ethers.getContractFactory("Greeter");
const greeter = await Greeter.deploy("初期メッセージ");
await greeter.waitForDeployment();
// メッセージを変更
const tx = await greeter.setGreeting("新しいメッセージ");
await tx.wait(); // トランザクションの確認を待つ
// 変更後のメッセージを確認
expect(await greeter.greet()).to.equal("新しいメッセージ");
});
it("異なるアカウントからもメッセージを変更できる", async function () {
const [owner, otherAccount] = await ethers.getSigners();
const Greeter = await ethers.getContractFactory("Greeter");
const greeter = await Greeter.deploy("初期メッセージ");
await greeter.waitForDeployment();
// 別のアカウントで接続してメッセージを変更
await greeter.connect(otherAccount).setGreeting("別のユーザーから");
expect(await greeter.greet()).to.equal("別のユーザーから");
});
});
テストを実行します。
npx hardhat test
ローカルネットワークへのデプロイ
デプロイスクリプトを作成します。
// scripts/deploy.ts
import { ethers } from "hardhat";
async function main() {
const [deployer] = await ethers.getSigners();
console.log("Deploying with account:", deployer.address);
const Greeter = await ethers.getContractFactory("Greeter");
const greeter = await Greeter.deploy("こんにちは!");
await greeter.waitForDeployment();
const address = await greeter.getAddress();
console.log("Greeter deployed to:", address);
// デプロイ後に動作確認
const greeting = await greeter.greet();
console.log("Current greeting:", greeting);
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
ローカルネットワークでデプロイを実行します。
# 方法1: Hardhatの内蔵ネットワークで一時的にデプロイ
npx hardhat run scripts/deploy.ts
# 方法2: 永続的なローカルノードを起動してデプロイ
# ターミナル1でノードを起動
npx hardhat node
# ターミナル2でデプロイ
npx hardhat run scripts/deploy.ts --network localhost
方法2では、ノードが起動している間はデプロイしたコントラクトにアクセスし続けることができます。
Hardhatコンソール
Hardhatには対話型コンソールがあり、コントラクトを直接操作できます。
npx hardhat console --network localhost
// コンソール内で実行
const Greeter = await ethers.getContractFactory("Greeter");
const greeter = await Greeter.attach("デプロイされたアドレス");
// メッセージの取得
await greeter.greet();
// => 'こんにちは!'
// メッセージの変更
await greeter.setGreeting("コンソールから変更");
await greeter.greet();
// => 'コンソールから変更'
まとめ
本章では、Hardhatの環境構築を行い、最初のSolidityコントラクト「Greeter」を作成しました。コンパイル、テスト、ローカルデプロイという基本的なワークフローを体験しました。
次章では、Solidityのイベント(Event)機能を学びます。イベントは、コントラクト内で発生した出来事をブロックチェーン上に記録し、フロントエンドから監視するために使用される重要な機能です。