forge init myProject
cd myProject
cd myProject
forge install <https://github.com/library-to-import>@branch
forge remappings > remappings.txt
# ./remappings.txt
ds-test/=lib/forge-std/lib/ds-test/src/
forge-std/=lib/forge-std/src/
openzeppelin-contracts/=lib/openzeppelin-contracts/contracts
Import in contract:
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import {Ownable} from "openzeppelin-contracts/access/Ownable.sol";
contract AnyContract is Ownable {}
Set compiler version in foundry.toml
solc = "0.8.13"
Compile and log contracts sizes:
forge build --sizes
Inspect compiled contracts:
forge inspect <contract_name> <element_to_inspect>
Contract metadata:
forge inspect <contract_name> metadata
bytecode:
forge inspect <contract_name> bytecode
abi:
forge inspect <contract_name> abi
Inspect storage
can be very useful:
forge inspect <contract_name> storage --pretty
Create file in test/AnyContract.t.sol
Import the testing library in the test contract:
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import {Test} from "forge-std/Test.sol";
import {VmSafe} from "forge-std/Vm.sol";
import {AnyContract} from "src/AnyContract.sol";
contract AnyContract_test is Test {}
Add a setUp
function for "beforeAll" conditions:
{...}
import {AnyContract} from "src/AnyContract.sol";
contract AnyContract_test is Test {
function setUp() public {
AnyContract anyContract = new AnyContract();
}
}
Create an unit test with function starting with test
:
{...}
contract AnyContract_test is Test {
{...}
function test_functionName_ConditionToTest() public {}
}
Run tests with differents amount of details
forge test
forge test -vv
forge test -vvvv
Read about the Test library: Assertion cheatcodes Execution environment cheatcode
Set a network in foundry.toml
:
[rpc_endpoints]
anvil = "http://localhost:8545/"
goerli = "https://ethereum-goerli-rpc.allthatnode.com"
RPC urls can be founds on chainlist.org, consider using an API KEY with nodes providers (like Infura, POKT network, ...) for large amount of transactions or calls.
Fork the network for the whole tests:
forge test -f goerli
Fork the network only in specific tests:
{...}
contract AnyContract_test is Test {
{...}
function test_functionName_ForkCondition() public {
vm.createSelectFork("goerli");
// try by logging chain variable
emit log_named_uint("chain ID", block.chainid);
emit log_named_uint("block height", block.number);
}
}
Increase tests speed by caching calls to the forked network:
{...}
contract AnyContract_test is Test {
{...}
function test_functionName_ForkCondition() public {
// stick to a specific block number
vm.createSelectFork("goerli", 8486194);
emit log_named_uint("chain ID", block.chainid);
emit log_named_uint("block height", block.number);
}
}
Foundry will cache all calls sended to the forked network, so the next time forge test
is run, data are fetched in the cache instead of the network.
Check Foundry cache:
forge cache ls
Write scripts in script/my_script.s.sol
and import the script library:
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import "forge-std/Script.sol";
import {AnyContract} from "src/AnyContract.sol";
contract deploy is Script {}
You can add private keys in .env
and read them in the script:
DEPLOYER_ANVIL=ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
Add the "broadcast" block to sign transaction with a specific private key:
contract deploy is Script {
function run() public {
// read private key in .env
uint256 pk = vm.envUint("DEPLOYER_ANVIL");
address deployer = vm.addr(pk);
// broadcast block
vm.startBroadcast(pk);
// deploy and call contracts here
vm.stopBroadcast();
}
}
Test your script with:
forge script deploy
Foundry will execute the function run()
in your script.
Launch the local blockchain with:
anvil
Dry run your script on the network:
forge script deploy --rpc-url anvil
Dry allow to run your script and create transaction which can be checked in broadcast
Run and broadcast transaction:
forge script deploy --rpc-url anvil --broadcast
Dry run your script is always a good practice:
forge script deploy --rpc-url goerli
Then:
forge script deploy --rpc-url goerli --broadcast
forge verify-contract <address> <contract_name> --chain <chain_id> --verifier sourcify
Etherscan:
Make sure you have set the ETHERSCAN_KEY
in .env
source .env
forge verify-contract <address> <contract_name> --chain <network_alias | chain_id> $ETHERSCAN_KEY --watch
If the contract has been deployed with arguments:
cast abi-encode "constructor(address,uint256)" 0xdaab... 500000
Then add the flag --constructor-args
with the above result to the forge verify-contract
command
Contributors: Raph, xDrKush, Yamakhala, Amine