はじめに
Solidityのコントラクト開発は、HardhatとFoundryが主流です。
この記事では、Foundryの開発フローを理解し、実践的な使い方を解説します。
インストール
Foundry は、公式のインストーラ兼バージョンマネージャーである foundryup を使ってインストールします。
foundryup のインストール
foundryupのインストールは以下のコマンドで行います。
curl -L https://foundry.paradigm.xyz | bash
Windowsの場合、Git BashやWSLが実行環境となります。
PowerShell や Command Prompt はサポートされていません。
ターミナルの再起動
以下を実行します。
source ~/.bashrc
# または
source ~/.zshrc
Foundry のインストール
以下を実行します。
foundryup
ツール
Foundry をインストールすると、以下の 4 つのツールが利用できるようになります。
| ツール | 目的 |
|---|---|
forge |
スマートコントラクトのビルド、テスト、デバッグ、デプロイ、Verify(検証) |
cast |
コントラクトとの対話、トランザクション送信、チェーンデータの取得 |
anvil |
フォーク機能を備えたローカル Ethereum ノードを起動 |
chisel |
高速なプロトタイピングのための Solidity REPL |
プロジェクトセットアップ
Foundry のプロジェクトは forge init で初期化します。
初期化後は、すぐに使える標準的なディレクトリ構成が自動的に作成されます。
プロジェクトの作成
既存のディレクトリで初期化する場合は以下を実行します。
ここでは、開発環境をVSCodeとしている為、オプションに--vscodeを付けています。
cd my_project
forge init --vscode
空でないディレクトリで初期化する場合は以下を実行します。
forge init --force --vscode
フォルダ構成
以下がフォルダ構成となります。
my_project/
├─ foundry.toml #プロジェクト設定ファイル
│
├─ src/
│ └─ Counter.sol #サンプルコントラクト
│
├─ test/
│ └─ Counter.t.sol #サンプルテスト
│
├─ script/
│ └─ Counter.s.sol #サンプルスクリプト
│
└─ lib/
└─ forge-std/ #標準ライブラリ
OpenZeppelinライブラリ追加
ERC20やERC721などのトークン標準、アクセス制御、その他の汎用機能を活用する場合は、OpenZeppelinのライブラリを追加します。
forge install を使うことでも可能ですが、ここではforge soldeer を使ってインストールします。
Soldeer の場合バージョン指定になります。
forge soldeer install @openzeppelin-contracts~5.6.1
インポートは以下のようになります。
import { ERC20 } from "@openzeppelin-contracts-5.6.1/token/ERC20/ERC20.sol";
Upgradeableの場合は以下のコマンドとなります。
forge soldeer install @openzeppelin-contracts-upgradeable~5.6.1
Forge
Forge は、Solidity スマートコントラクトの コンパイル、テスト、デプロイを行うためのツールです。
Foundry における 中核となる開発ツールです。
コントラクトのビルド
Forge は、src/ ディレクトリ内の すべての Solidity ファイルをコンパイルし、
生成された成果物(アーティファクト)を out/ ディレクトリに出力します。
forge build
コンパイラのバージョン
Forge は、コントラクト内の pragma ステートメントから 必要な Solidity のバージョンを自動検出し、対応するコンパイラを 自動的にダウンロードします。
特定のバージョンに固定したい場合は、foundry.toml で明示的に指定できます。
# foundry.toml
[profile.default]
solc_version = "0.8.28"
最適化(Optimization)
本番環境へのデプロイ時は、ガス最適化のためオプティマイザを有効化が必須です。
# foundry.toml
[profile.default]
optimizer = true
optimizer_runs = 200
via_ir = true
※:テスト環境では、via_ir を無効にしておいたほうが良いです。
テスト(Testing)
Forge は、Solidity で記述されたテストを実行します。
テストファイルは test/ ディレクトリに配置し、テスト関数名は test で始める必要があります。
forge test
テストの記述(Writing tests)
Test を継承した テストコントラクトを作成します。
主な規約(Key conventions)
- テストファイル名は
.t.solで終わる - テストコントラクトは
forge-std/Test.solを継承する - テスト関数名は
test_またはtestで始める -
setUp()は各テストの実行前に毎回呼ばれる
カバレッジ(Coverage)
カバレッジレポートを生成します。
forge coverage
トレース(Traces)
トレースは、テスト実行中に行われたすべての呼び出しをツリー構造で表示します。
これにより、実行フローを理解しやすくなり、失敗時のデバッグに役立ちます。
例:スタックトレース(Stack traces)
forge test -vvv
詳細度レベル(Verbosity levels)
-v フラグを使うことで、Forge が出力する ログの詳細度を制御できます。
| フラグ | 表示内容 |
|---|---|
| (なし) | 成功 / 失敗のサマリーのみ |
-v |
テスト名を表示 |
-vv |
テスト中に出力されたログを表示 |
-vvv |
失敗したテストのみトレースを表示 |
-vvvv |
すべてのテスト(setUp 含む)のトレースを表示 |
-vvvvv |
トレース+ストレージの変更内容を表示 |
ガス使用量のトラッキング(Gas tracking)
Forge は、コントラクトのガス使用量を追跡し、最適化の検討やガスコストの退行(regression)検知を支援します。
ガスレポート(Gas reports)
すべてのテストに対して ガスレポートを生成するには、以下を実行します。
forge test --gas-report
スクリプト(Scripting)
Forge の スクリプトは、コントラクトのデプロイやオンチェーンでのトランザクション実行を行う Solidity ファイルです。
スクリプトの構成(Script structure)
スクリプトは Script を継承し、run() 関数を実装します。
例)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
import {Script} from "forge-std/Script.sol";
import {Counter} from "../src/Counter.sol";
contract DeployScript is Script {
function run() public {
vm.startBroadcast();
Counter counter = new Counter();
counter.setNumber(42);
vm.stopBroadcast();
}
}
主な要素(Key elements)
-
forge-std/Script.solを継承する - スクリプトファイル名は
.s.solで終わる - デプロイ処理は
vm.startBroadcast()/vm.stopBroadcast()で囲む
スクリプトの実行(Running scripts)
デプロイのシミュレーション(トランザクションは送信されません)
forge script script/Deploy.s.sol
このコマンドは、実際にはトランザクションを送信せず、デプロイ処理をシミュレーション実行します。
ネットワークに対してトランザクションを送信する場合
forge script script/Deploy.s.sol --broadcast --rpc-url $RPC_URL
--broadcast を指定することで、指定した RPC エンドポイント($RPC_URL)に対して 実際にトランザクションが送信されます。
フォーマット(Formatting)
Forge には、一貫したコードスタイルを強制するためのビルトインフォーマッタが含まれています。
注:VSCodeのファイル保存で自動フォーマットをしている場合、フォーマットと合わせる必要があります。
ファイルのフォーマット
すべての Solidity ファイルをフォーマットします。
forge fmt
変更を加えずにフォーマットをチェックします。
forge fmt --check
設定(Configuration)
フォーマッタの挙動は foundry.toml で設定できます。
# foundry.toml
[fmt]
line_length = 120
tab_width = 4
bracket_spacing = true
int_types = "long"
multiline_func_header = "params_first"
quote_style = "double"
number_underscore = "thousands"
single_line_statement_blocks = "preserve"
主なオプション(Common options)
| オプション | デフォルト | 説明 |
|---|---|---|
| line_length | 120 | 1 行の最大文字数 |
| tab_width | 4 | インデントあたりのスペース数 |
| bracket_spacing | false | 中括弧内のスペース有無 ({ x } / {x})` |
| int_types | "long" | uint256 形式か uint 形式か |
| quote_style | "double" |
"string" / 'string'
|
| number_underscore | "preserve" |
1_000_000 / 1000000
|
オプションはフォーマッタのリファレンスを参照
リンティング(Linting)
Forge には、一般的な問題を検出し、ベストプラクティスを強制するためのビルトインリンターが含まれています。
リンターの実行
forge lint
設定(Configuration)
リンターのルールは foundry.toml で設定できます。
# foundry.toml
[lint]
severity = ["high", "med", "low"]
exclude_lints = ["mixed-case-function", "custom-errors"]
severity:実行する lint の重大度レベルを指定します。
exclude_lints:特定の lint ルールを無効化します。
Anvil
Anvil は、開発およびテスト向けの 高速なローカル Ethereum ノードです。
すべて メモリ内で動作し、任意の EVM 互換チェーンからのフォークをサポートしています。
※:ローカル環境でブロックチェーンノードのダミーとして利用可能です。
ローカルノードの起動
anvil
デフォルトアカウント(Default accounts)
Anvil は、開発用アカウントを 10 個生成し、それぞれに 10,000 ETH があらかじめ割り当てられます。
デフォルトのニーモニック(復元フレーズ)は以下のとおりです。
test test test test test test test test test test test junk
アカウントのカスタマイズ
以下のオプションを使って、生成されるアカウントをカスタマイズできます。
-
--accounts:生成するアカウント数を指定 -
--balance:各アカウントの残高を指定 -
--mnemonic:使用するニーモニックを指定
まとめ
開発で扱いそうな機能をひとおおり記載しました。
Foundryは非常に豊富な機能を備えているため、さらに詳しく知りたい場合は公式ドキュメントを参照してください。