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へのデプロイ
- .envファイルを作成し、RPC-URLや秘密鍵を入力(hardhatと同じ)
RINKEBY_RPC_URL=https://...
PRIVATE_KEY=abcde...
ETHERSCAN_KEY=aaaaa...
- .envファイルを読み込み
source .env
- 以下コマンド実行
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
ローカルノードの立ち上げ
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の使い方がとても参考になったのでメモ
フォーマット
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
もうちょっと自分で試してから書く