🙌

Forgeを使ったスマートコントラクトの構築について

2023/01/17に公開

※当記事はこちらの記事を翻訳したものです。
https://blog.thirdweb.com/guides/get-started-with-forge-and-thirdweb/

Foundryは、開発者がプロジェクトを構築し、スマートコントラクトをテストすることができるEthereum開発フレームワークです。ForgeはFoundryに同梱されているツールで、Solidityでテストを記述することができます。

Paradigm の開発者によって Rust で書かれた Foundry は、Hardhat やその他のテスト環境に代わる、非常に高速なテスト環境です。

このガイドでは、thirdweb と Foundry を使用して Solidity プロジェクトをセットアップしますので、まずはFoundry Book の手順に従ってFoundry のインストールを行ってください。

新しい Forge プロジェクトを作成する

まず、新しいディレクトリを作成し、次のコマンドを実行します。

npx thirdweb@latest create contract

プロジェクトをセットアップするための一連の質問が出るので、それに返答していきます。

  • プロジェクトに名前を付ける
  • フレームワークとして Forge を選択する
  • 基本コントラクトとして ERC721 を選択する
  • オプションの拡張機能として Drop を選択する

最後に、以下をプロジェクト ディレクトリ内で実行します。

forge clean && foundryup && forge update

すべてがインストールされたら、プロジェクト ディレクトリに移動し、Solidity を書き始めましょう。

コントラクトのセットアップ

src フォルダーにある Contract.sol ファイル内にコントラクトを記述します。

最初は、次のようなコードになります。

pragma solidity ^0.8.13;

import "@thirdweb-dev/contracts/base/ERC721Drop.sol";

contract Contract is ERC721Drop {
    constructor(
        string memory _name,
        string memory _symbol,
        address _royaltyRecipient,
        uint128 _royaltyBps,
        address _primarySaleRecipient
    )
        ERC721Drop(
            _name,
            _symbol,
            _royaltyRecipient,
            _royaltyBps,
            _primarySaleRecipient
        )
    {}
}

今回テストを行う、標準的なERC721のmint関数を作成しましょう。

以下の関数をコントラクトに追加してください。

function mint(address _to, uint256 _amount) external {
    require(_amount > 0, 'You must mint at least one token!');
    _safeMint(_to, _amount);
}

この関数は、

  • _to: トークンをミントするアドレス
  • _amount: ミントするトークンの数

という2つの引数を受け取ります。また、mintを呼び出す際に、少なくとも1つのトークンをミントしていることを確認するrequire文も追加しています。

テストの作成

コントラクトが書けたので、Forgeを使ってmint関数をテストしてみましょう。

Foundry の利点の 1 つは、Solidity でコントラクトを記述するだけでなく、Solidity を使用してテストできるため、開発者のワークフローが単純化されることです。

Contract.t.sol 内に、

  • テストするコントラクト
  • 標準ライブラリのテストコントラクト(こちらは初めから書かれていると思います)

の両方のインポートが必要であることを確認します。

import "forge-std/Test.sol";
import "../src/Contract.sol";

次に、Test.solからインポートされたTestコントラクトを継承したContractTestという新しいコントラクトを作成します。

まず、setUp()関数内でContractのインスタンスを作成します。

また、コントラクトをテストするためのアドレスが必要です。このために、Forge標準ライブラリヘルパー関数makeAddrを使用します。

contract ContractTest is Test {
    Contract drop;
    address testAddr = makeAddr("Test");

    function setUp() public {
        drop = new Contract("MintTest", "MT", testAddr, 500, testAddr);
    }  
}

ここでは次の2つのテストを書きます。

  • testDropWithZeroTokens():
    0個のトークンをミントしようとした場合にトランザクションがリバートされる
  • testDropWithTokensMinted():
    0個より多いトークンをミントしようとした場合に正しい数のトークンがミントされる
function testDropWithZeroTokens() public {
	drop.mint(testAddr, 0);
}

function testDropWithTwoTokens() public {
	drop.mint(testAddr, 2);
}

テストが合格するかを確認するには、次のコマンドを実行します。

forge test

なんと、テストの1つが失敗してしまいました!

テストを実行すると、コンソールにエラーが表示され、ひとつは合格、もうひとつは不合格と表示されます。
テストを書くときには、トランザクションがリバートされることを想定してもテストがパスするように設計することが重要です。

testDropWithZeroTokens 関数は、0トークンで mint を呼び出したので、リバートされました。

 

このテストに合格するためには、Forge のチートコードである vm.expectRevert(<insert error message>) を使用し、引数として提供されたエラーメッセージがコントラクト内の revert 文のメッセージと一致することを確認します。

この関数の動作は、次の呼び出し時に、与えられたエラーメッセージか戻ってくるかどうかを確認することです。したがって、この行を mint を呼び出す直前に追加すれば、テストは両方とも成功するはずです。
 

最後に、テスト関数内でmintを呼び出した後、トークンの残高が正確かどうかをチェックしてみましょう。

assertEq(param_1, param_2)を利用することで、パラメータ1とパラメータ2が等しいかどうかをチェックし、等しくない場合はエラーを返します。mint を呼び出したら、両方のテストに以下を追加してみましょう。

assertEq(drop.balanceOf(testAddr), <expected balance>)

最終的にテストコントラクト Contract.t.sol は以下のようになります。

pragma solidity ^0.8.13;

import "forge-std/Test.sol";
import "../src/Contract.sol";

contract ContractTest is Test {
    Contract drop;
    
    address testAddr = makeAddr("Test");

    function setUp() public {
        drop = new Contract("MintTest", "MT", testAddr, 500, testAddr);
    }

    function testDropWithZeroTokens() public{
        vm.expectRevert("You must mint at least one token!");
        drop.mint(testAddr, 0);
        assertEq(drop.balanceOf(testAddr), 0);
    }   
    function testDropWithTokensMinted() public{
        drop.mint(testAddr, 2);
        assertEq(drop.balanceOf(testAddr), 2);
    } 
}

テストを再実行すると、両方とも合格しました。

デプロイする

mint関数のテストが終わったので、このコントラクトをデプロイしてみましょう! 次のコマンドを実行します。

npx thirdweb@latest deploy

コンストラクタのパラメータを入力します。

これでダッシュボードが開き、コントラクトの表示・管理ができるようになります。

forgeでテスト済みの新しいERC721 Dropコントラクトのデプロイ、おめでとうございます 🎉

質問がある場合や、あなたの素晴らしいプロジェクトを私たちと共有したい場合は、私たちのDiscordにぜひ飛び込んで来てください!

Discussion