foundry

foundryの始め方と私の使い方について

編集中です。hardhatの使い方をよく聞かれるんですが、とにかく便利なfoundryを使いましょう!公式ドキュメントがしっかりしているので、それを読めば解決すると思いますが、サクッとまとめたものから随時アップしていきます。

はじめに

hardhatで開発するよりも断然効率よく開発できるのでfoundryがおすすめです。

Foundryのインストール

https://book.getfoundry.sh/getting-started/installation

VSCode環境構築

VSCodeを使用している方は、下記のページの設定を行うのがおすすめです。

https://book.getfoundry.sh/config/vscode

Foundryの準備を始める

まずは、リポジトリを作成

forge init hello_foundry

次にパッケージのインストールなどを行なっていきます。

下記のインストールは、サブモジュール方式になります。(おすすめです!)

forge install dapphub/ds-test
forge install openzeppelin/openzeppelin-contracts
forge install chiru-labs/ERC721A
forge install transmissions11/solmate
forge install thirdweb-dev/contracts
forge install dmfxyz/murky
forge install ProjectOpenSea/operator-filter-registry

※私がよく使っているものを記載しています。

次にリマッピングを行なっていきます。

forge remappingsを実行すると、すでに適用されているリマッピングが表示されます。

@chainlink/contracts/src/=lib/contracts/node_modules/@chainlink/contracts/src/
@ds-test/=lib/contracts/lib/ds-test/src/
@openzeppelin/=lib/contracts/node_modules/@openzeppelin/
@std/=lib/contracts/lib/forge-std/src/
ERC721A/=lib/ERC721A/contracts/
OpenSea-NFT-Template/=lib/OpenSea-NFT-Template/src/
contracts/=lib/contracts/contracts/
ds-test/=lib/ds-test/src/
erc4626-tests/=lib/operator-filter-registry/lib/openzeppelin-contracts/lib/erc4626-tests/
erc721a-upgradeable/=lib/contracts/node_modules/erc721a-upgradeable/
erc721a/=lib/contracts/node_modules/erc721a/
forge-std/=lib/forge-std/src/
murky/=lib/murky/src/
openzeppelin-contracts-upgradeable/=lib/operator-filter-registry/lib/openzeppelin-contracts-upgradeable/contracts/
openzeppelin-contracts/=lib/murky/lib/openzeppelin-contracts/
operator-filter-registry/=lib/operator-filter-registry/src/
solmate/=lib/solmate/src/

フォルダ直下にremappings.txtのファイルを作成し、その中に記載することで自由にリマッピングすることができます。

テスト作る際にちょこちょこ変更しますが、基本的に下記のものは常に記載しています。

@thirdweb/=lib/contracts/contracts/
@openzeppelin/=lib/openzeppelin-contracts/
erc721a/=lib/ERC721A/
operator-filter-registry/=lib/operator-filter-registry/src/

一旦テストを行なって、正常に動くか試してみましょう。

forge build
forge test

インストールされたテストが正常にパスすることが確認できると思います。

テストする際は、下記のようにコントラクトを指定してテストすることもできます。

forge test --match-contract Counter --match-test testIncrement
forge test --match-path test/Counter.t.sol

ログ表示については、複数段階あります。必要に応じて切り替えて使ってくださいね。

Level 1 (-v)
Level 2 (-vv)
Level 3 (-vvv)
Level 4 (-vvvv)
Level 5 (-vvvvv)

たとえば、このような使い方です

forge test -vvvvv

パッケージのインストールなど

サブモジュールでのインストール

forge install openzeppelin/openzeppelin-contracts

コミットしたくない場合は、--no-commitをつける

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

パッケージのアップデート

forge update
forge update openzeppelin/openzeppelin-contracts

パッケージさよなら

forge remove lib/openzeppelin-contracts
forge remove openzeppelin/openzeppelin-contracts

バグった時はとりあえず

forge clean

コマンドまとめ

setUp

必ず毎回呼ばれる関数

試しに、console.log("setUp")と書いて動かせば、毎回setUpが呼ばれることがわかると思います

function名

通常:test<***>

失敗することをテストする場合:testFail<***>

よく使うコマンド一覧

アドレス関係

vm.addressvm.addr(uint256 privateKey);address alice = vm.addr(1);
makeAddrAndKeymakeAddrAndKey(string ***);(address alice, uint256 key) = makeAddrAndKey("alice");
vm.dealvm.deal(address who, uint256 newBalance);vm.deal(alice, 1 ether);
dealdeal(address token, address to, uint256 give);deal(address(dai), alice, 10000e18);

msg.senderやtx.origin

vm.prankvm.prank(address sender, address origin)msg.senderとtx.originの設定vm.prank(owner,anotherContract);
vm.startPrankvm.startPrank(address sender, address origin)stopPrankまでmsg.senderを変更するvm.startPrank(alice)
vm.stopPrankvm.stopPrank()vm.stopPrank()
hoaxhoax(address who, address origin, uint256 give)addressとgiveは省略可能hoax(owner, 1 ether)
startHoaxstartHoax(address who, address origin, uint256 give)startHoax(owner,anotherContract,1 ether);
changePrankchangePrank(address who)vm.stopPrank + vm.startPrankchangePrank(alice)

expect

vm.expectRevertvm.expectRevert(bytes("error message"))bytes("error message")は省略可能vm.expectRevert(bytes("You can't withdraw yet"))
vm.expectEmitvm.expectEmit( bool checkTopic1, bool checkTopic2, bool checkTopic3, bool checkData )eventがemitされるかどうかをチェック テスト側にイベントを取り込む必要あり。checkTopicは、eventの引数にindexedがあるかないかに連動vm.expectEmit(false, false, false, true);

timestamp

vm.warpvm.warp(uint256)指定したblock.timestampに移動vm.warp(3600)
skipskip(uint256)指定した時間分だけ進めるskip(1000)
rewindrewind(uint256)指定した時間分だけ戻すrewind(1000)

-foundry