Berikut adalah tutorial untuk menggunakan Foundry dalam membuat Proof of Concept (PoC) terhadap bug leads. Tutorial ini mencakup langkah-langkah instalasi, konfigurasi, penggunaan remappings, dan pengujian PoC. Tutorial ini juga memberikan petunjuk untuk membuat PoC untuk bug yang sebenarnya.
buat folder baru silakan buat degnan nama foundrytest
Inisialisasi Proyek Foundry:
forge init --template immunefi-team/forge-poc-templates --branch default
forge init
Edit file foundry.toml dan tambahkan baris berikut:
[default]
src = 'src'
out = 'out'
libs = ['lib']
chain_id = 1
eth_rpc_url = 'https://eth-mainnet.alchemyapi.io/v2/{your_alchemy_api_key}'
block_number = 14812830
etherscan_api_key = '{your_etherscan_api_key}'
Pastikan untuk menyertakan etherscan_api_key untuk kejelasan trace saat menjalankan forge test dalam mode verbose.
Jalankan perintah berikut untuk mendapatkan sumber proyek: (lewati jika sudah installasi)
npm i -g ethereum-sources-downloader
jika sudah melakukan instalasi langsung menuju step berikut Dapatkan sumber proyek yang dikerjakan dengan menjalankan perintah berikut, Ini akan membuat direktori lib/StakingV3 yang berisi sumber-sumber kontrak yang dikerjakan dan dependensinya.
ethereum-sources-downloader etherscan 0x5B705d7c6362A73fD56D5bCedF09f4E40C2d3670 lib
Jalankan perintah remappings:
forge remappings
maka akan muncul
StakingV3/=lib/StakingV3/
ds-test/=lib/forge-std/lib/ds-test/src/
forge-std/=lib/forge-std/src/
Pastikan bahwa remappings mencakup direktori StakingV3/ dan @openzeppelin/.
pada folder src,test terdapat file berbeda Counter.t.sol Counter.s.sol
Dalam struktur proyek Foundry, folder src dan test memiliki peran dan jenis file yang berberda antara Counter.t.sol dan Counter.s.sol:
Fungsi Utama: File ini digunakan untuk menulis dan menjalankan pengujian atau tes terhadap kontrak smart dalam proyek.
Fungsi Utama: File ini digunakan untuk menuliskan dan mendefinisikan kontrak smart atau skrip yang akan dijalankan pada jaringan blockchain.
Untuk menangani masalah dengan dependensi dari @openzeppelin, gunakan fitur remappings di Foundry. Buat file remappings.txt di root proyek dan tambahkan baris berikut selanjutnya buat fil remappings.txt dan didalamnya isi pada baris pertama.
//remappings.txt
@openzeppelin/=lib/StakingV3/StakingV3/@openzeppelin/
selanjutnya adalah jalankan
forge test
@openzeppelin/=lib/StakingV3/StakingV3/@openzeppelin/
StakingV3/=lib/StakingV3/
ds-test/=lib/forge-std/lib/ds-test/src/
forge-std/=lib/forge-std/src/
Gunakan fitur Foundry untuk mengubah state yang terkait dengan tampilan kontrak. Berikut contoh token dalam setup tes: bah pada Counter.t.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
import "forge-std/Test.sol";
import "StakingV3/StakingV3/contracts/staking/StakingV3/StakingV3.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
contract CounterTest is Test {
IERC20 yopToken = IERC20(0xAE1eaAE3F627AAca434127644371b67B18444051);
StakingV3 staking = StakingV3(0x5B705d7c6362A73fD56D5bCedF09f4E40C2d3670);
address attacker = address(1);
using stdStorage for StdStorage;
function writeTokenBalance(
address who,
IERC20 token,
uint256 amt
) internal {
stdstore
.target(address(token))
.sig(token.balanceOf.selector)
.with_key(who)
.checked_write(amt);
}
function setUp() public {
writeTokenBalance(attacker, yopToken, 500 ether);
}
function testExample() public {
vm.startPrank(attacker);
uint8 lock_duration_months = 1;
yopToken.approve(address(staking), 500 ether);
staking.stake(500 ether, lock_duration_months);
}
}
Jalankan test dengan perintah:
forge test
output
[⠑] Compiling...
[⠘] Compiling 18 files with 0.8.24
[⠒] Compiling 59 files with 0.8.9
[⠃] Solc 0.8.24 finished in 8.19s
[⠑] Solc 0.8.9 finished in 11.98s
Compiler run successful with warnings:
Warning (5667): Unused function parameter. Remove or comment out the variable name to silence this warning.
--> lib/StakingV3/StakingV3/contracts/staking/StakingV3/StakingV3Base.sol:261:5:
|
261 | address _operator,
| ^^^^^^^^^^^^^^^^^
Running 1 test for test/Counter.t.sol:CounterTest
[PASS] testExample() (gas: 336381)
Test result: ok. 1 passed; 0 failed; 0 skipped; finished in 42.87s
Pastikan untuk menyesuaikan alamat token dan kontrak staking sesuai dengan proyek yang sedang diuji.
atau gunakan perintah dengan output lebih detail dengan tambahan -v,-vv,-vvv,-vvvv(maksimal adalah 4 v) contoh
forge test -vvvv
[⠰] Compiling...
No files changed, compilation skipped
Running 1 test for test/Counter.t.sol:CounterTest
[PASS] testExample() (gas: 336381)
Traces:
[336381] CounterTest::testExample()
├─ [0] VM::startPrank(0x0000000000000000000000000000000000000001)
│ └─ ← ()
├─ [24562] YOP::approve(ERC1967Proxy: [0x5B705d7c6362A73fD56D5bCedF09f4E40C2d3670], 500000000000000000000 [5e20])
│ ├─ emit Approval(owner: 0x0000000000000000000000000000000000000001, spender: ERC1967Proxy: [0x5B705d7c6362A73fD56D5bCedF09f4E40C2d3670], value: 500000000000000000000 [5e20])
│ └─ ← true
├─ [320925] ERC1967Proxy::stake(500000000000000000000 [5e20], 1)
│ ├─ [316024] StakingV3::stake(500000000000000000000 [5e20], 1) [delegatecall]
│ │ ├─ [2531] YOP::balanceOf(0x0000000000000000000000000000000000000001) [staticcall]
│ │ │ └─ ← 500000000000000000000 [5e20]
│ │ ├─ [29858] AccessControlManager::hasAccess(0x0000000000000000000000000000000000000001, ERC1967Proxy: [0x5B705d7c6362A73fD56D5bCedF09f4E40C2d3670]) [staticcall]
│ │ │ ├─ [8684] SanctionsListAccessControl::blockedAccess(0x0000000000000000000000000000000000000001, ERC1967Proxy: [0x5B705d7c6362A73fD56D5bCedF09f4E40C2d3670]) [staticcall]
│ │ │ │ ├─ [2923] SanctionsList::isSanctioned(0x0000000000000000000000000000000000000001) [staticcall]
│ │ │ │ │ └─ ← false
│ │ │ │ └─ ← false
│ │ │ ├─ [5003] AllowAnyAccessControl::hasAccess(0x0000000000000000000000000000000000000001, ERC1967Proxy: [0x5B705d7c6362A73fD56D5bCedF09f4E40C2d3670]) [staticcall]
│ │ │ │ └─ ← true
│ │ │ └─ ← true
│ │ ├─ [98455] ERC1967Proxy::calculateStakingRewards(237)
│ │ │ ├─ [93560] YOPRewardsV2::calculateStakingRewards(237) [delegatecall]
│ │ │ │ ├─ [899] ERC1967Proxy::totalWorkingSupply() [staticcall]
│ │ │ │ │ ├─ [504] StakingV3::totalWorkingSupply() [delegatecall]
│ │ │ │ │ │ └─ ← 1653128834947760 [1.653e15]
│ │ │ │ │ └─ ← 1653128834947760 [1.653e15]
│ │ │ │ ├─ [1448] ERC1967Proxy::workingBalanceOfStake(237) [staticcall]
│ │ │ │ │ ├─ [1050] StakingV3::workingBalanceOfStake(237) [delegatecall]
│ │ │ │ │ │ └─ ← 0
│ │ │ │ │ └─ ← 0
│ │ │ │ └─ ← ()
│ │ │ └─ ← ()
│ │ ├─ [13655] YOP::transferFrom(0x0000000000000000000000000000000000000001, ERC1967Proxy: [0x5B705d7c6362A73fD56D5bCedF09f4E40C2d3670], 500000000000000000000 [5e20])
│ │ │ ├─ emit Transfer(from: 0x0000000000000000000000000000000000000001, to: ERC1967Proxy: [0x5B705d7c6362A73fD56D5bCedF09f4E40C2d3670], value: 500000000000000000000 [5e20])
│ │ │ ├─ emit Approval(owner: 0x0000000000000000000000000000000000000001, spender: ERC1967Proxy: [0x5B705d7c6362A73fD56D5bCedF09f4E40C2d3670], value: 0)
│ │ │ └─ ← true
│ │ ├─ emit TransferSingle(operator: 0x0000000000000000000000000000000000000001, from: 0x0000000000000000000000000000000000000000, to: 0x0000000000000000000000000000000000000001, id: 237, value: 1)
│ │ ├─ emit Staked(_user: 0x0000000000000000000000000000000000000001, _tokenId: 237, _amount: 500000000000000000000 [5e20], _lockPeriod: 1, _startTime: 1706880347 [1.706e9])
│ │ └─ ← 237
│ └─ ← 237
└─ ← ()
Test result: ok. 1 passed; 0 failed; 0 skipped; finished in 38.30s
Ran 1 test suites: 1 tests passed, 0 failed, 0 skipped (1 total tests)
bantuan command gunakan
forge help
maka akan muncul
forge help
Build, test, fuzz, debug and deploy Solidity contracts
Usage: forge <COMMAND>
Commands:
bind Generate Rust bindings for smart contracts
build Build the project's smart contracts [aliases: b, compile]
cache Manage the Foundry cache
clean Remove the build artifacts and cache directories [aliases: cl]
completions Generate shell completions script [aliases: com]
config Display the current config [aliases: co]
coverage Generate coverage reports
create Deploy a smart contract [aliases: c]
debug Debugs a single smart contract as a script [aliases: d]
doc Generate documentation for the project
flatten Flatten a source file and all of its imports into one file [aliases: f]
fmt Format Solidity source files
geiger Detects usage of unsafe cheat codes in a project and its dependencies
generate Generate scaffold files
generate-fig-spec Generate Fig autocompletion spec [aliases: fig]
help Print this message or the help of the given subcommand(s)
init Create a new Forge project
inspect Get specialized information about a smart contract [aliases: in]
install Install one or multiple dependencies [aliases: i]
remappings Get the automatically inferred remappings for the project [aliases: re]
remove Remove one or multiple dependencies [aliases: rm]
script Run a smart contract as a script, building transactions that can be sent onchain
selectors Function selector utilities [aliases: se]
snapshot Create a snapshot of each test's gas usage [aliases: s]
test Run the project's tests [aliases: t]
tree Display a tree visualization of the project's dependency graph [aliases: tr]
update Update one or multiple dependencies [aliases: u]
verify-check Check verification status on Etherscan [aliases: vc]
verify-contract Verify smart contracts on Etherscan [aliases: v]
Options:
-h, --help Print help
-V, --version Print version
Find more information in the book: http://book.getfoundry.sh/reference/forge/forge.html