この記事は2024/08/28時点の情報に基づいています
はじめに
Hardhatと双璧をなすSolidity開発ツールのFoundryですが、現在pre1.0をリリースしており、もうすぐ正式リリースとなる見込みです。
様々なプロダクトがv1.0まで開発が継続されない中、多大な支持を得て、一つ目の壁を突破しようとしています。
v1.0では様々な破壊的変更が含まれており、開発者の皆様はプロジェクトの更新が必要になる可能性があります。本記事では、主な変更点と対応策について解説します。
安定版を使用したい場合の推奨事項
Foundry 1.0のリリースに向けて、Semverに従うために複数の破壊的変更が行われます。安定版が必要な場合は、5月2日の最新のピン留めされたnightlyビルドを使用することが推奨されています。
ローカルで最新のピン留めされたnightlyを使用するには、以下のコマンドを実行してください。
shellfoundryup --version nightly-e15e33a07c0920189fc336391f538c3dad53da73
CIの場合は下記です
shell- name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 with: version: nightly-e15e33a07c0920189fc336391f538c3dad53da73
破壊的変更詳細
expectEmitの仕様変更
呼び出された直後の次の呼び出しに対してのみ機能するようになった。
solidity// 今までOKの例 function testEmitMyEvent_v2() external { cheatcodes.expectEmit(true, true, true, true); emit MyEvent(myContract.myVar(), address(this), address(this), block.timestamp); myContract.emitMyEvent(); } // 上記がアウト、理由は「myContract.myVar()」に対してexpectEmitが働くから。 // なので、このように改変する必要がある function testEmitMyEvent_v2() external { uint256 myVar = myContract.myVar(); cheatcodes.expectEmit(true, true, true, true); emit MyEvent(myVar, address(this), address(this), block.timestamp); myContract.emitMyEvent(); }
expectCallの仕様変更
これもexpectEmit同様、直後の呼び出しに対してのみ機能するようになった。
修正方法もexpectEmit同様。
expectRevertの仕様変更
これもexpectEmit同様、直後の呼び出しに対してのみ機能するようになった。
修正方法もexpectEmit同様。
-mオプションの削除
--mt
または--match-test
の非推奨エイリアスだった-m
が削除された。startPrankの仕様変更
既存のprankを上書きできるようになった
solidity// 今まで function testOldBehavior() public { vm.startPrank(address1); // address1としての操作 vm.stopPrank(); // これが必須だった vm.startPrank(address2); // address2としての操作 } // これから function testNewBehavior() public { vm.startPrank(address1); // address1としての操作 vm.startPrank(address2); // エラーにならず、直接切り替わる // address2としての操作 }
precompilesの互換性
あんまり関係なさそうだけど、precompilesがすべてのチートコードと互換性がなくなる。
プリコンパイルアドレスのストレージを直接操作しようとしてたり、プリコンパイルアドレスにETHやトークンを割り当てようとしてたりすると影響がある。
difficultyとprevrandaoチートコード
正しいEVMバージョンで使用されない場合、失敗するようになった。下記のようにevmのバージョンを明示的に設定する必要がある。
soliditypragma solidity ^0.8.0; import "forge-std/Test.sol"; contract MyTest is Test { function setUp() public { vm.evm_version("paris"); // または適切なバージョン } // テスト内容 }
もしくはfoundry.tomlで
solidityevm_version = "paris"
のようにする必要がある。
デフォルトEVMバージョンの変更
デフォルトのEVMバージョンがShanghaiになった、以前はlondonだったらしい。
parseJsonの仕様変更
以前は存在しないJSONキーにアクセスしようとした場合、エラーが発生していたが、空のバイトを返すようになった。
なので、空のバイトが帰ってくる可能を考慮してロジックを記述することも出てくるかもしれないというところ
soliditybytes memory result = vm.parseJson(json, ".nonExistentKey"); if (result.length == 0) { // キーが存在しない場合の処理 } else { // キーが存在する場合の処理 } // とか bytes memory result = vm.parseJson(json, ".possiblyNonExistentKey"); uint256 value = result.length > 0 ? abi.decode(result, (uint256)) : defaultValue; // とか
対応策まとめ
- テストコードの見直し:
- 特に
expectRevert
を使用しているテストは、外部呼び出しになっているか確認し、必要に応じて修正。
- CIの更新:
- 安定版を使用したい場合は、CIのFoundryインストール手順を更新。
- EVMバージョンの確認:
- プロジェクトで使用しているEVMチェーンがShanghai(EIP-3855)と互換性があるか確認し、必要に応じてEVMバージョンを指定。
- オプションの使用方法の見直し:
m
オプションを使用している場合は、-mt
または-match-test
に変更。
- チートコードの使用方法の確認:
expectEmit
、expectCall
、startPrank
などのチートコードの使用箇所を見直し、新しい動作に合わせて調整。
所感
upgradesのチェック機能を使って安心したいという理由だけでHardhatを使い続けていますが、最新のSolidityコンパイラが利用できなかったりと色々モヤモヤが増えてきたので、このタイミングでそろそろ乗り換えの時期かなぁとも思ったり、思わなかったり。
testコードの見直しが必須なレベルなので、Foundryを使い続ける予定の皆様はお早めにご対応ください。