Forge Standard Library adalah kumpulan kontrak dan perpustakaan yang bermanfaat untuk digunakan dengan Forge dan Foundry. Ini memanfaatkan kode curang Forge untuk membuat tes penulisan lebih mudah dan cepat, sekaligus meningkatkan UX kode curang.
Pelajari cara menggunakan Forge-Std dengan Foundry Book (Forge-Std Guide).
forge install foundry-rs/forge-std
Ini adalah kontrak pembantu untuk kesalahan dan pengembalian. Di Forge, kontrak ini sangat membantu untuk kode cheat expectRevert
, karena kontrak ini menyediakan semua kesalahan bawaan kompiler.
Lihat kontrak itu sendiri untuk semua kode kesalahan.
import " forge-std/Test.sol " ;
contract TestContract is Test {
ErrorsTest test;
function setUp () public {
test = new ErrorsTest ();
}
function testExpectArithmetic () public {
vm. expectRevert (stdError.arithmeticError);
test. arithmeticError ( 10 );
}
}
contract ErrorsTest {
function arithmeticError ( uint256 a ) public {
uint256 a = a - 100 ;
}
}
Ini adalah kontrak yang agak besar karena semua kelebihan beban untuk membuat UX layak. Terutama, ini adalah pembungkus record
dan accesses
kode curang. Itu selalu dapat menemukan dan menulis slot penyimpanan yang terkait dengan variabel tertentu tanpa mengetahui tata letak penyimpanan. Satu-satunya peringatan utama dalam hal ini adalah meskipun slot dapat ditemukan untuk variabel penyimpanan yang dikemas, kami tidak dapat menulis ke variabel tersebut dengan aman. Jika pengguna mencoba menulis ke slot yang dikemas, eksekusi akan menimbulkan kesalahan, kecuali jika tidak diinisialisasi ( bytes32(0)
).
Ini bekerja dengan merekam semua SLOAD
dan SSTORE
selama pemanggilan fungsi. Jika ada satu slot yang dibaca atau ditulis, slot tersebut akan segera dikembalikan. Jika tidak, di balik layar, kami mengulangi dan memeriksa masing-masing (dengan asumsi pengguna memasukkan parameter depth
). Jika variabelnya adalah sebuah struct, Anda bisa memasukkan parameter depth
yang pada dasarnya adalah kedalaman bidang.
Yaitu:
struct T {
// depth 0
uint256 a;
// depth 1
uint256 b;
}
import " forge-std/Test.sol " ;
contract TestContract is Test {
using stdStorage for StdStorage;
Storage test;
function setUp () public {
test = new Storage ();
}
function testFindExists () public {
// Lets say we want to find the slot for the public
// variable `exists`. We just pass in the function selector
// to the `find` command
uint256 slot = stdstore. target ( address (test)). sig ( " exists() " ). find ();
assertEq (slot, 0 );
}
function testWriteExists () public {
// Lets say we want to write to the slot for the public
// variable `exists`. We just pass in the function selector
// to the `checked_write` command
stdstore. target ( address (test)). sig ( " exists() " ). checked_write ( 100 );
assertEq (test. exists (), 100 );
}
// It supports arbitrary storage layouts, like assembly based storage locations
function testFindHidden () public {
// `hidden` is a random hash of a bytes, iteration through slots would
// not find it. Our mechanism does
// Also, you can use the selector instead of a string
uint256 slot = stdstore. target ( address (test)). sig (test.hidden. selector ). find ();
assertEq (slot, uint256 ( keccak256 ( " my.random.var " )));
}
// If targeting a mapping, you have to pass in the keys necessary to perform the find
// i.e.:
function testFindMapping () public {
uint256 slot = stdstore
. target ( address (test))
. sig (test.map_addr. selector )
. with_key ( address ( this ))
. find ();
// in the `Storage` constructor, we wrote that this address' value was 1 in the map
// so when we load the slot, we expect it to be 1
assertEq ( uint (vm. load ( address (test), bytes32 (slot))), 1 );
}
// If the target is a struct, you can specify the field depth:
function testFindStruct () public {
// NOTE: see the depth parameter - 0 means 0th field, 1 means 1st field, etc.
uint256 slot_for_a_field = stdstore
. target ( address (test))
. sig (test.basicStruct. selector )
. depth ( 0 )
. find ();
uint256 slot_for_b_field = stdstore
. target ( address (test))
. sig (test.basicStruct. selector )
. depth ( 1 )
. find ();
assertEq ( uint (vm. load ( address (test), bytes32 (slot_for_a_field))), 1 );
assertEq ( uint (vm. load ( address (test), bytes32 (slot_for_b_field))), 2 );
}
}
// A complex storage contract
contract Storage {
struct UnpackedStruct {
uint256 a;
uint256 b;
}
constructor () {
map_addr[ msg . sender ] = 1 ;
}
uint256 public exists = 1 ;
mapping ( address => uint256 ) public map_addr;
// mapping(address => Packed) public map_packed;
mapping ( address => UnpackedStruct) public map_struct;
mapping ( address => mapping ( address => uint256 )) public deep_map;
mapping ( address => mapping ( address => UnpackedStruct)) public deep_map_struct;
UnpackedStruct public basicStruct = UnpackedStruct ({
a: 1 ,
b: 2
});
function hidden () public view returns ( bytes32 t ) {
// an extremely hidden storage slot
bytes32 slot = keccak256 ( " my.random.var " );
assembly {
t := sload (slot)
}
}
}
Ini adalah pembungkus berbagai kode curang yang membutuhkan pembungkus agar lebih ramah pengembang. Saat ini hanya ada fungsi yang berhubungan dengan prank
. Secara umum, pengguna mungkin mengharapkan ETH dimasukkan ke dalam alamat prank
, tetapi hal ini tidak terjadi karena alasan keamanan. Secara eksplisit fungsi hoax
ini hanya boleh digunakan untuk alamat yang memiliki saldo yang diharapkan karena akan ditimpa. Jika suatu alamat sudah memiliki ETH, sebaiknya gunakan saja prank
. Jika Anda ingin mengubah saldo itu secara eksplisit, gunakan saja deal
. Jika Anda ingin melakukan keduanya, hoax
juga cocok untuk Anda.
// SPDX-License-Identifier: MIT
pragma solidity ^ 0.8.0 ;
import " forge-std/Test.sol " ;
// Inherit the stdCheats
contract StdCheatsTest is Test {
Bar test;
function setUp () public {
test = new Bar ();
}
function testHoax () public {
// we call `hoax`, which gives the target address
// eth and then calls `prank`
hoax ( address ( 1337 ));
test. bar {value: 100 }( address ( 1337 ));
// overloaded to allow you to specify how much eth to
// initialize the address with
hoax ( address ( 1337 ), 1 );
test. bar {value: 1 }( address ( 1337 ));
}
function testStartHoax () public {
// we call `startHoax`, which gives the target address
// eth and then calls `startPrank`
//
// it is also overloaded so that you can specify an eth amount
startHoax ( address ( 1337 ));
test. bar {value: 100 }( address ( 1337 ));
test. bar {value: 100 }( address ( 1337 ));
vm. stopPrank ();
test. bar ( address ( this ));
}
}
contract Bar {
function bar ( address expectedSender ) public payable {
require ( msg . sender == expectedSender, " !prank " );
}
}
Berisi berbagai pernyataan.
console.log
Penggunaannya mengikuti format yang sama dengan Hardhat. Disarankan untuk menggunakan console2.sol
seperti yang ditunjukkan di bawah ini, karena ini akan menampilkan log yang didekodekan di jejak Forge.
// import it indirectly via Test.sol
import " forge-std/Test.sol " ;
// or directly import it
import " forge-std/console2.sol " ;
...
console2. log (someValue);
Jika Anda memerlukan kompatibilitas dengan Hardhat, Anda harus menggunakan console.sol
standar. Karena bug di console.sol
, log yang menggunakan tipe uint256
atau int256
tidak akan didekodekan dengan benar di jejak Forge.
// import it indirectly via Test.sol
import " forge-std/Test.sol " ;
// or directly import it
import " forge-std/console.sol " ;
...
console. log (someValue);
Lihat pedoman kontribusi kami.
Pertama, lihat apakah jawaban atas pertanyaan Anda dapat ditemukan di buku.
Jika jawabannya tidak ada:
Jika Anda ingin berkontribusi, atau mengikuti diskusi kontributor, Anda dapat menggunakan telegram utama kami untuk mengobrol dengan kami tentang perkembangan Foundry!
Forge Standard Library ditawarkan di bawah lisensi MIT atau Apache 2.0.