Introducing forge-std v1.0.0 🧵
Strongly recommend updating your forge-std versions ASAP, as there are changes that improve fuzzing so you might find some new failing tests.
Update with `forge update lib/forge-std`
1. The `bound` method was updated.
This, along with `vm.assume` are used in fuzz tests to take an input value generated by the fuzzer and wrap it into some range.
But the old `bound` had a problem.
A big feature of the forge fuzzer it its dictionary: it takes PUSH bytes and keys/values from storage, adds them to a dictionary, then sometimes uses the values as fuzz inputs
But if the min value in your bound was non-zero, there was a bias which negated the dictionary benefits
The new bound fixes this. Now, that bias is removed, so you can safely use it without removing the dictionary benefits.
Additionally, bound now has edge biasing, so the min and max values in your bound range should be covered. Previously, this was extremely unlikely.
These changes to bound mean that after updating forge-std, you may have new test failures that you didn't have before, but that is good thing!
(Note that no changes are needed to use this, just update forge-std and the new bound will be used, then you may need to fix some tests)
With forge-std v1.0.0, the new fuzzing best practices are:
- Default to using `bound` to shape inputs: it increases the chance of testing the edges of your bound and is faster than `vm.assume`
- Use `vm.assume` to exclude specific values from fuzzing, such as an owner address
2. Standard interfaces were added.
One feature of @vyperlang that I really like is the built-in interfaces. Popular interface definitions are included so you don't need to redefine them in each project.
The lack of shared interfaces causes two issues.
The first is that it's tedious to copy/paste interface files, or install a dependency to get the interfaces
The second is that everyone declares interfaces differently, and if you have two dependencies with different IERC20 files, you may end up with some solc complaints to fix
So forge-std now includes common interface files by default for ERC-20, ERC-165, ERC-721, ERC-1155, and ERC-4626.
`import {IERC20} from "forge-std/interfaces/IERC20.sol"` is all you need
If you need an interface not currently included, feel free to PR it in.
3. A new `stdChains` variable, included by default in your scripts and tests.
This lets you do a few things:
1. Access a chain's ID with `stdChains.Mainnet.chainId`
2. Access a chain's RPC URL with `stdChains.Optimisim.rpcUrl`
3. Access a chain's name with `stdChains.ArbitrumOne.name`
Note: The default RPC URLs are included for convenience to facilitate quick tests and experimentation. Do not use them for production test suites, CI, or other heavy usage as you will be throttled and this is a disservice to others who need this endpoint.
Ensuring scripts are safe and footgun-free is important. But some cheatcodes might result in a script working as expected during tests, but not in production.
For example, if you `vm.warp` to change timestamp, and use the block timestamp to calculate an input value.
So now you can alternatively use `vmSafe` in your scripts, which is a subset of the full `vm` interface. It excludes all cheats that modify the VM state, such as warp, roll, deal, etc.
5. A few small other small features and breaking changes:
- A new `createCompute2Address` utility.
- The minimum supported pragma is now 0.6.2.
- forge-std is now more modular and better organized.
- Related to the `VmSafe` change to make scripts safer, `using stdStorage for StdStorage` will break in scripts and will need to be changed to `stdStorageSafe` (or manually import the full `stdStorage`)
And finally:
- Thanks to @PaulRBerg for helping modularize the codebase.
- Thanks to @mrnventuro for helping with `bound` improvements.
- HUGE thanks to @ZeroEkkusu00x for all the help implementing and testing these features!