Foundry workspace built around seven small contracts. The interesting part is the test suite: unit tests, fuzz tests, and stateful invariant suites with handlers. The full suite runs in under 200ms across 51 tests, including invariant runs that exercise thousands of random call sequences.
- Foundry (forge, cast, anvil)
- Solidity 0.8.24
- forge-std + native cheatcodes
| Tool | Install |
|---|---|
| Foundry | curl -L https://foundry.paradigm.xyz | bash then foundryup |
forge install
forge build
forge test -vvFor gas snapshots and reports:
forge snapshot
forge test --gas-reportFormatting and the gas baseline are enforced in CI:
forge fmt --check
forge snapshot --check| Contract | Description | Test types |
|---|---|---|
src/Counter.sol |
Trivial counter | unit, fuzz |
src/Token.sol |
Minimal ERC20-style token | unit, fuzz |
src/NFT.sol |
Minimal ERC721-style NFT | unit, fuzz |
src/Vault.sol |
ETH vault | unit, fuzz, invariant |
src/Staking.sol |
ETH staking | unit, fuzz, invariant |
src/TimeLock.sol |
Queue/execute/cancel timelock | unit, fuzz |
src/Ownable.sol |
Two-step ownership mixin | unit |
Tests live in test/. The invariant handler in test/VaultInvariant.t.sol constrains the random call surface (bounded amounts, a fixed actor set) so invariants converge instead of bouncing off reverts, and it tracks ghost variables to cross-check the contract's own accounting.
| Concern | Hardhat | Foundry |
|---|---|---|
| Test language | JS / TS | Solidity |
| Test runtime | Node + ethers | Native Rust EVM |
| Fuzzing | plugin | built in, 256 runs default |
| Invariants | plugin | built in (invariant_*) |
| Cheatcodes | none | vm.prank, vm.warp, vm.expectRevert |
| Cold mainnet fork | seconds | hundreds of ms |
For Solidity-heavy work, the native test language alone is worth it. No context-switching between two languages on every assertion.