Open3

Foundryメモ

ぽけなぽけな

hardhatで書かれたSolidityのチュートリアルをFoundryでやってみようとした時に調べたことメモ

デプロイスクリプトに定数を定義

contract CryptoDevsScript is Script {
    address WHITELIST_CONTRACT_ADDRESS = 0x32Bf937d425D1b1a5171750a9aC57A8856E193Bd;
    string METADATA_URL = "https://nft-collection-sneh1999.vercel.app/api/";

    function setUp() public {}

    function run() public {
        vm.startBroadcast();

        CryptoDevs cryptoDevsContract = new CryptoDevs(METADATA_URL, WHITELIST_CONTRACT_ADDRESS);

        vm.startBroadcast();
    }
}

Rinkebyへのデプロイ

  1. .envファイルを作成し、RPC-URLや秘密鍵を入力(hardhatと同じ)
RINKEBY_RPC_URL=https://...
PRIVATE_KEY=abcde...
ETHERSCAN_KEY=aaaaa...
  1. .envファイルを読み込み
source .env
  1. 以下コマンド実行
forge script script/NFT.s.sol:NFTScript --rpc-url $RINKEBY_RPC_URL --private-key $PRIVATE_KEY --broadcast

追記:veirfyしたい時はこっち?

forge create --rpc-url $RINKEBY_RPC_URL
    --private-key $PRIVATE_KEY src/MyToken.sol:MyToken
    --etherscan-api-key $ETHERSCAN_KEY 
    --verify

リソース:https://book.getfoundry.sh/tutorials/solidity-scripting

テスト時、EOAからコントラクトの関数をコールしたい時

// only the owner can transfer
function testTransferToken() public {
    // mint the token to bob's address
    erc721 = new ERC721();
    erc721.mint(bob, 0);

    // emulate bob
    vm.startPrank(bob);

    // transfer to mary
    erc721.safeTransferFrom(bob, mary, 0);

    // check to make sure mary is the new owner
    address owner_of = erc721.ownerOf(0);
    assertEq(mary, owner_of);
}
https://github.com/dabit3/foundry-cheatsheet

テスト時、ログが出ないんだけど!

forge test -vv

https://github.com/dabit3/foundry-cheatsheet

ローカルノードの立ち上げ

anvil

コントラクトの関数呼び出し

トランザクション発生しない場合

cast call コントラクトアドレス "ownerOf(uint256)(address)" 0 --rpc-url http://localhost:8545

トランザクション発生する場合

cast send --private-key 秘密鍵 コントラクトアドレス "mintTo(address)()" 引数のアドレス --rpc-url http://localhost:8545

テスト

Fuzz Testing

引数に値を設定すると、テスト実行時にランダムな値を入れたパターンをテストしてくれる

    function testMintFromGuests(uint96 amount) public {
        vm.expectRevert(bytes("Only minters can mint"));
        tokenContract.mint(bob, amount);
    }

Revertを期待する試験をしたいとき

  • コントラクト内で requireを使用している場合は、エラーメッセージをexpectRevertの引数に入れてあげたらできた
    function testAddMintersFromGuests(address addr) public {
        vm.expectRevert(bytes("Ownable: caller is not the owner"));
        vm.prank(addr);
        tokenContract.addMinter(addr);
    }
  • コントラクト内でrevertを使用している場合は、vm.expectRevert()でOK
    function testMintFromGuests(uint96 amount) public {
        vm.expectRevert();
        tokenContract.mint(bob, amount);
    }
  • コントラクト内で定義した特定のエラーを期待したい場合
    function testMintFromGuests(uint96 amount) public {
        vm.expectRevert(YourContract.CustomError.selector);
        tokenContract.mint(bob, amount);
    }

共通処理にしたいけど、全テストで実行したくない処理

  • modifierにて実装し、実行したいテストの時に付ける
  • 例えば、特定の人しかmintできない試験とmint後のTransfer試験がある時とかに便利
modifier mintedTokens() {
    tokenContract.mint(bob, 100);
    _;
}

function testMintFromInvalidAddress() public {
    vm.expertRevert(alice);
    tokenContract.mint(alice, 100);
}

function testTransfer() mintedTokens() public  {
    tokenContract.transfer(alice, 100);
    assertEq(tokenContract.balanceOf(alice), 100);
}

参考:https://github.com/chiru-labs/PBT/blob/main/test/PBTSimpleTest.sol

ぽけなぽけな

この動画のFoudryの使い方がとても参考になったのでメモ
https://www.youtube.com/watch?v=wUjYK5gwNZs

フォーマット

forge fmt

deploy.s.solのrun()をテスト時も使用する

deploy.s.sol

function run() external returns (MyERC721) {
    vm.startBroadcast();
    MyERC721 erc721 = new MyERC721();
    vm.stopBroadcast();
    return erc721;
}

test.t.sol

import {Test} from "forge-std/Test.sol";
import {DeployMyERC721} from "../script/deploy.s.sol";
import {MyERC721} from "../src/MyERC721.sol";

contract TestMyERC721 is Test {
    DeployMyERC721 deployer;
    MyERC721 erc721;

    function setup() public {
        deployer = new DeployMyERC721();
        erc721 = deployer.run();
    }
}

※テストを行うローカル環境と実際にデプロイするメインネット両方に対応しているコードを書かなきゃいけないのだけ注意

Invariant Test

もうちょっと自分で試してから書く