久々のブログです。菊地啓哉です。
Dev Container は便利ですよね。開発を始めようとした時に、まずは Dev Container で環境をつくるのが習慣になってきました。
今回は(気持ちは)自力で Hardhat 3 で Solidity の開発をできる Dev Container の開発環境をつくっていこうと思います。つくり方の流れを説明しているので、これをベースにカスタマイズしていただければと思います。
ついでに、Hardhat 3 で SmartContract を動かすところまでやってみます。
前提
- WSL上で Docker が使えること
- VS Code をインストール済みで、 Remote Development の拡張機能をインストール済みであること
- (パブリックネットワークにデプロイする場合)ガス代を払う為のネイティブトークンを持っている Wallet Address とその秘密鍵、RPC Endpoint
最終的にできあがる devcontainer.json
ほとんど自動でつくられるもののままでとってもシンプルですが、できあがる devcontainer.json は以下の通りです。
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/typescript-node
{
"name": "Node.js & TypeScript",
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
"image": "mcr.microsoft.com/devcontainers/typescript-node:1-22-bookworm",
// Features to add to the dev container. More info: https://containers.dev/features.
// "features": {},
// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [],
// Use 'postCreateCommand' to run commands after the container is created.
// "postCreateCommand": "yarn install",
// Configure tool-specific properties.
"customizations": {
"vscode": {
"extensions": [
"NomicFoundation.hardhat-solidity"
],
"settings": {
"[solidity]": {
"editor.formatOnSave": true
}
}
}
},
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
"remoteUser": "node"
}
Dev Container で Solidity 開発環境をつくっていく
まずは、WSL上で空の作業ディレクトリを作成し、VS Code のリモートエクスプローラーから、WSL に接続し、作業ディレクトリを開きます。
左下の背景色がある 「 >< WSL: ~」 と書かれているところをクリックして出てくるメニューから「コンテナーで再度開く」選びます。コマンドパレットから探して実行することもできます。

この先、環境によっては出てくるダイアログが違うかもしれないので参考程度に見ていただければと思いますが、
適当な starting point を選びます。Hardhat は Node.js が必要になり、TypeScript にも対応しているのでベースに「Node.js & TypeScript」を選びました。

Docker Image を選択します。特に理由が無ければ既定のもので良いと思います。

追加機能は必要なものがあれば選んで Enter または OKをクリックします。ここでは特に選択せずに OK にしました。

オプションファイルも必要であれば選んで進みます。ここも選択しませんでした。

これで、設定ファイルの devcontainer.json を .devcontainerディレクトリに作ってくれて、Dev Container で開発環境が開かれます。

Dev Container ができたということで、まず最初にターミナルで id を実行してユーザや UID/GID を確認しておきます。
UID/GID がホストの UID/GID と一致していれば、コンテナ内で作成したファイルをホストの WSL側で開けないなどの権限問題は起こらないのですが、 remoteUser に、このユーザを指定しておきます。
remoteUser を設定することで、Dev Container内で使用するユーザにホストの UID/GID が設定され、権限問題が起こらないようにしてくれるかと思います。
今の状態でログインしているユーザが root のような特権ユーザの場合には別のユーザを指定するなど、別の方法が必要になります。

Dev Container の設定を変更して、反映したいタイミングで左下の背景色がついている今度は「>< 開発コンテナー: ~」などになっているところをクリックして、「コンテナーのリビルド」を選び、リビルドします。適宜リビルドしてください。
もしくは、 devcontainer.json を変更した場合には右下などにポップアップが出てくると思いますので、そこからでもリビルドできます。

続いて、拡張機能を入れていきます。
通常、 VS Code でやるように、拡張機能のメニューから選んで追加したいものをインストールすれば使えますが、コンテナがリビルドされると入れ直しが必要になってしまうので、Dev Container の設定にも追加します。
ここでは Hardhat を作っている Nomic Foundation の Solidity 拡張を入れます。
拡張機能を見つけたらインストールして、歯車⚙から「拡張機能 ID のコピー」を選びます。
※画面からインストールするのは、コンテナのリビルドを待つのが面倒だからです。

そして、devcontainer.json で customizations.vscode.extensions の配列の中に拡張機能 ID を追加します。補完機能を使って書いていけるので、IDはダブルクォーテーションで囲むことさえ気を付ければ、特に迷わず設定できると思います。
これでコンテナーのリビルドがあっても拡張機能が入った状態になります。上記の通り手動でインストールもしていればコンテナのリビルドは不要です。
続いて、Solidity ファイルを保存した時に、フォーマットされるように設定します。
こちらも、devcontainer.json で設定することで、コンテナ内にだけ設定が適用され、コンテナがリビルドされても設定が残るようにします。
具体的には、devcontainer.json で customizations.vscode.settings["[solidity]"]["editor.formatOnSave"] を true にします。
私が少し Solidity のコードをいじった感触としてはデフォルトの設定のままで特に問題無さそうだったので、スタイルの設定は特に設定しません。
拡張機能と自動フォーマットの設定を入れたものが以下のようになります。devcontainer.json のコードはこのページの最初に記載したものです。

これで簡易的ではありますが、Solidity の開発環境ができました。
Hardhat 3 で開発する
続いて、Hardhat のチュートリアルに従って、Solidity の開発準備を進めていきます。
Hardhat 3 初期化
Dev Container で Node.js を使えるようにしているはずなので、以下のコマンドで初期化します。
チュートリアルには npm, pnpm, Yarn での実行方法が書かれているので、お好きなもので進めてください。ここでは、 npm で進めます。
npx hardhat --init
実行時点では Hardhat 3 はβ版となっていましたが、気にせず進んでいきます。幾つか質問されますが、適宜変更しつつ、進めていきます。(ここでは全てデフォルトでインストールしました。)

以下のようなディレクトリ構成となりました。

hardhat.comfig.ts:プロジェクトの設定。 Solidity のコンパイラのバージョン、ネットワーク設定、プラグインやプロジェクトで使用するタスクなどが設定されています。contracts:Solidity の SmartContract を保存します。.t.solという拡張子で Solidity で書かれたテストファイルを保存することもできます。test:TypeScript で書かれたテストを保存します。Solidity で書かれたテストファイルをここに保存することもできます。ignition: SmartContract をどのようにデプロイするかを定義する Hardhat Ignition を保存します。また、デプロイ情報が保存されます。scripts: ワークフローを自動化するスクリプトを保存します。スクリプトは Hardhat runtime にフルアクセスでき、プラグインの使用、ネットワークへの接続、コントラクトのデプロイなどができます。
Hardhat 3 の基本的な使い方
build
npx hardhat build
テスト
Solidity で書かれているテスト(拡張子: .t.sol)と TypeScript で書かれたテストの全てが実行されます。
npx hardhat test
# Solidity のテストだけ実行する場合は npx hardhat test solidity
# TypeScript のテストだけ実行する場合は npx hardhat test nodejs
ローカルで実行
まずはローカルの Hardhat node を起動します。
npx hardhat node
Hardhat node が起動し、ログが出力されるようになります。続いて、Dev Container で別のターミナルを開き、初期化時に自動で作成された ignition/modules/Counter.ts を使って localhost のネットワークに Contract をデプロイします。
npx hardhat ignition deploy ignition/modules/Counter.ts --network localhost
hardhat console
プログラムから localhost ネットワーク上の Contract を操作しても良いのですが、ここでは対話型で Contract を操作してみます。
npx hardhat console --network localhost
このコマンドを実行すると、対話型で Node.js のコマンドを実行できるようになります。ということで以下のようにいろいろと操作してみます。
// connection
const connection = await hre.network.connect()
// viem
const viem = connection.viem
// Contract Address
// ignition/deployments/chain-31337/deployed_addresses.json か npx hardhat node のログからコピーする
const address = "0x5fbdb2315678afecb367f032d93f642f64180aa3" // 例
// wallet
const wallet = await viem.getWalletClient()
// 先にデプロイしたContract
const counter = await viem.getContractAt("Counter", address)
// publicなプロパティの x を読み取る
// public property に対応する view関数を勝手に作ってくれる
await counter.read.x()
// 5n
// increment : x がインクリメントされる
await counter.write.inc()
// 再度読み取る
await counter.read.x()
// 6n
Sepolia へのデプロイ
デフォルトで作られている hardhat.config.ts では、ネットワーク設定の sepolia のところで、 configVariable というものが使われています。これは、Hardhat 3 が提供する、設定変数を扱うことのできる仕組みなので、これを使ってみましょう。
Sepolia の RPC Endpoint を設定します。
npx hardhat keystore set SEPOLIA_RPC_URL
初回の実行では設定するパスワードの入力が求められ、続けて打ち間違いが無いように再入力が求められます。その後、 RPC Endpoint として設定する値を入力します。
続いて、 Sepolia で処理を実行するための Wallet の秘密鍵を設定します。
npx hardhat keystore set SEPOLIA_PRIVATE_KEY
パスワードを聞かれ、先ほどと同じ値を入力し、設定する値を入力します。
これで準備ができましたので、Sepolia にデプロイします。
npx hardhat ignition deploy ignition/modules/Counter.ts --network sepolia
これで ignition のプログラムが実行され、Sepolia にデプロイされたかと思います。
まとめ
Solidity 開発のために Dev Container 構築と Hardhat 3 の準備と簡単な使い方をご紹介しました。
Dev Container の設定についてはポチポチするのが中心で、あまり覚えたりコピペする必要が無いようになっているかと思います。
Hardhat は久しぶりに触ったら新しいものがいろいろありましたが、少し触った感覚だと使いやすい印象でした。ローカルのネットワークで簡単に SmartContract を動かせるので開発や検証に便利だと思います。
またかきます
またね

