Introduction
Last year we launched create-chimera-app, a template that made it easier and faster for anyone to get started fuzzing. Now we’re launching the second iteration of this template that incorporates lessons learned from our engagements throughout the year to make it even easier for anyone to get started with fuzzing.
create-chimera-app V2 uses the following to make your fuzzing experience setting up and running invariant tests simpler:
Managers
Separating target functions
updateGhosts
modifierRevert catching for properties
In this post we’ll look into why we’ve introduced these changes and how they make your life easier by reducing the number of decisions you have to make when setting up your fuzzing suite.
What’s New
Managers
Managers are something we’ve recently adopted in-house at Recon to simplify our lives when creating fuzzing suites that need multiple actors (simulated users) and multiple assets (tokens that interact with the system).
All managers have been added to the setup-helpers repo, along with other utilities for setting up an invariant testing suite, so they can be added to any repo without having to import all of create-chimera-app as a dependency.
ActorManager
The ActorManager
contract serves as the source of truth for actors that are being used in the fuzzing suite setup.

The ActorManager
is inherited from in the Setup
contract which allows us to add all the actors we need in the setup
function:
The primary functions of interest when setting up a suite are the _addActor
function shown above which allows adding a new address as an actor that can be tracked, and the _switchActor
function which allows the fuzzer to change the actor returned by _getActor
. The _switchActor
function is exposed via a target function in the ManagersTargets
contract (switchActor
) that can be called by the fuzzer to test random combinations of actors calling state changing target functions.
To use the actors stored in the ActorManager
, you just add the asActor
modifier on all of your target function handlers which pranks as the currently set actor. For privileged functions you can use the asAdmin
modifier which calls the target functions as the tester contract (address(this)
). In our setups we typically set the tester contract as the default admin address for simplicity.
When using these modifiers however, note that they only use the prank cheatcode, so they only set the msg.sender
for the next external call. So if you have an external call before the call to the target contract in your handler you’ll need to prank directly before the call to the target contract.
Using an ActorManager
also allows us to better track state changes because we can make assertions about the state variables of a contract with the assumption that only the actors that we’ve added in the ActorManager
are able to change the system state.
An example of this would be if our target contract was an implementation of an ERC20 token. We could clamp all our TargetFunction
handlers for minting, burning, etc. to only use the actors we’ve set up in the ActorManager
.
Using the _getActors
function we can loop over all actors and test a property like “the sum of user balances is equivalent to the totalSupply
”. Since we’ve clamped our TargetFunction
handlers we know that this will prevent actors other than those in the ActorManager
from depositing and so our property will be accurately checked and the totalSupply
won’t include deposits from other actors.
AssetManager
The other manager that we’ve introduced is the AssetManager
which allows tracking all assets being used in the fuzz suite setup. Similar to the ActorManager
this serves as the source of truth for all assets used in the test suite, so no target function should be called that may transfer an asset not tracked in the AssetManager
.

AssetManager
tracks all assets in the test suite and provides utility functions for deploying and approving new assets.In our setup we just add a call to the _newAsset
function:

_newAsset
deploys an instance of the MockERC20
contract and adds it to tracking so we can access it as needed.Then to clamp (reduce possible inputs) in calls to target functions we can simply use the value returned from a call to the _getAsset
function.
The _finalizeAssetDeployment
function additionally provides us with a utility for minting our newly added token to all actors that we’ve set up and approving it to all contracts in the the system that may need to call transferFrom
on it.
We can also switch which asset gets returned by the _getAsset
function using the switch_asset
function in the ManagersTargets
contract.
Separating Target Functions
In our engagements we’ve started to prefer to separate out target functions into individual target contracts based on functionality. This allows for separation of concerns making it easier for other team members to gain context on an existing test suite because it follows a common format and keeps files easier to read.

In the example TargetFunctions
contract we can see that there are now additional AdminTargets
, DoomsdayTargets
and ManagersTargets
contracts that get inherited from:
AdminTargets
defines target functions that can only be called by an admin address using theasAdmin
modifier described above (we explicitly use the modifier even though these would be called byaddress(this)
by default for clarity).DoomsdayTargets
defines in-line tests to check for worst-case scenario type of events.ManagersTargets
defines target functions that use the_switchActor
and_switchAsset
functions from the managers to switch the currently used asset/actor and the_newAsset
function which deploys a new asset.
BeforeAfter Modifiers
We’ve also added a new modifier in the BeforeAfter
contract that allows you to more easily update these tracking variables for use in checking global properties. This is done via the updateGhosts
modifier which should be added to every target function handler before the asActor
/asAdmin
modifier.

This will subsequently update all the tracking variables defined in the BeforeAfter
contract before and after the call to the target function handler. You can then read these values in a property definition to check if they behave as expected.
Error Catching
This version also introduces the Utils contract from the helpers repo. The primary utility provided by this contract is the checkError
function which allows checking if a call to one of the target functions reverts for the reason passed in as the expected argument:

This can be added to function handlers in our TargetFunctions
contract which we can use to check if a function reverted for a particular reason:

We can then make assertions about which reverts we expect to happen which if falsified could lead to unexpected behavior. This makes detecting issues like unexpected reverts due to underflow in functions much simpler to catch.
Conclusion
We’ve used the lessons that we’ve learned in the fuzzing trenches to bring you a new and improved version of create-chimera-app that gives you a more usable invariant suite requiring less design decisions on your part.
Using a standardized MockERC20 with an AssetManager
allows you to more easily deploy and manage the assets being used in your test suite. In combination with an ActorManager
you can use this to track multiple actors in your system and ensure all properties are only checked for these actors/assets.
Separating target functions gives you greater separation of concerns and makes it easier to track different logic.
The updateGhosts
modifier makes it easier to always update BeforeAfter
variables in any state-changing target function call so you can know that global properties checked in the Properties
contract are using the correct value.
Finally, we saw how we can catch any revert from one of our target function handlers to more easily catch unexpected revert reasons.
Hopefully these improvements help you get started with invariant testing faster. If you still have questions about how to use it though, feel free to reach out to our team in the recon discord.
If you're looking to get started with invariant testing, we just released a 5 part Invariant Testing Bootcamp At Home free course on X (click the card below to view it):