Foundry
Installation
(For Linux and MacOS Users)
Install foundryup
:
curl -L https://foundry.paradigm.xyz | bash
This will download foundryup
. Then install Foundry by running:
foundryup
Basic Commands
To start a new project with Foundry:
forge init hello_foundry
Compile Solidity code:
forge build
Run Solidity tests:
forge test -vvvv
Install Dependencies
forge install transmissions11/solmate
forge install openzeppelin/openzeppelin-contracts
forge install smartcontractkit/chainlink-brownie-contracts
Generate remappings for installed libraries
forge remappings > remappings.txt
Update Dependencies
forge update lib/<dependency-name>
Unit Testing
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.16;
import "forge-std/Test.sol";
import "../src/SampleContract.sol";
import "forge-std/console.sol";
// vm deployer address: 0xb4c79daB8f259C7Aee6E5b2Aa729821864227e84
abstract contract StateZero is Test {
SampleContract internal sampleContract;
address alice;
address bob;
function setUp() public virtual {
sampleContract = new SampleContract();
alice = address(0x1);
bob = address(0x2);
vm.label(alice, "alice");
vm.label(bob, "bob");
}
}
contract StateZeroTest is StateZero {
function testChangeStateOne() public {}
function testChangStateOneReverts() public {
vm.expectRevert(bytes("revert message"));
// call function that is intended to revert here
}
function testChangStateOneEmitsEvent() public {
vm.expectEmit(true, true, true, true);
// 1. emit the event with expected values
// 2. call function that is intended to emit the event
}
}
abstract contract StateOne is StateZero {
function setUp() public virtual override {
// run initial set-up function from StateZero
super.setUp();
// function that changes state from zero to one
sampleContract.changeStateOne();
}
}
contract StateOneTest is StateOne {
function testChangeStateTwo() public {}
}
Fuzz Testing
Fuzz testing is done by using a range of randomized possible inputs to test for edge cases, instead of just testing it with select inputs as in unit testing.
Foundry lets us do this natively.
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import "forge-std/Test.sol";
import "../src/Counter.sol";
contract CounterTest is Test {
Counter public counter;
function setUp() public {
counter = new Counter();
}
// Fuzz test for setNumber function
function testFuzz_SetNumber(uint256 x) public {
counter.setNumber(x);
assertEq(counter.number(), x);
}
// Fuzz test for multiple increment operations
function testFuzz_Increment(uint8 numberOfIncrements) public {
uint256 startingValue = counter.number();
for (uint i = 0; i < numberOfIncrements; i++) {
counter.increment();
}
assertEq(counter.number(), startingValue + numberOfIncrements);
}
// Fuzz test for combining setNumber and increment
function testFuzz_SetAndIncrement(uint256 x, uint8 incrementTimes) public {
counter.setNumber(x);
uint256 expectedValue = x;
for (uint i = 0; i < incrementTimes; i++) {
// Check for potential overflow
if (expectedValue < type(uint256).max) {
counter.increment();
expectedValue++;
} else {
// If we would overflow, don't increment anymore
break;
}
}
assertEq(counter.number(), expectedValue);
}
}
To run these fuzz tests, use:
forge test --match-test testFuzz
Additional Security
Checking Test Coverage
forge coverage --report summary
Static Analysis (Slither)
Invariant Testing
Deployment
Create .env file in root folder:
PRIVATE_KEY=
ETHERSCAN_KEY=
GOERLI_RPC_URL=
Update foundry.toml:
[rpc_endpoints]
goerli = "${GOERLI_RPC_URL}"
[etherscan]
goerli = { key = "${ETHERSCAN_KEY}" }
SampleContract.s.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.16;
import "../src/SampleContract.sol";
import "forge-std/Script.sol";
contract SampleContractScript is Script {
function setUp() public {}
function run() public {
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
vm.startBroadcast(deployerPrivateKey);
SampleContract contract = new SampleContract();
vm.stopBroadcast();
}
}
To run the above script:
forge script script/SampleContract.s.sol
To deploy SampleContract
to a live testnet, run the following in your terminal:
forge script script/SampleContract.s.sol:SampleContractScript --rpc-url $GOERLI_RPC_URL --broadcast \
Verification
To verify an existing contract:
forge verify-contract --chain-id 5 --num-of-optimizations 1000000 --watch \
--compiler-version v0.8.16+commit.fc410830 <the_contract_address> \
src/SampleContract.sol:SampleContract <your_etherscan_api_key>
Check verification status:
forge verify-check --chain-id 5 <GUID> <your_etherscan_api_key>
References:
https://book.getfoundry.sh/tutorials/solidity-scripting#deploying-our-contract
Last updated