After restricting your protocol as much as possible, it’s time to ensure that it functions according to its specification. The invariant mindset (or formal verification mindset) is an excellent tool for this. This mindset focuses on identifying unbreakable conditions, or invariants, within the code. An invariant is a condition that should hold true regardless of the protocol’s state. This mindset is particularly useful for functions that perform calculations based on formulas.
Formal verification methods can be used to analytically prove that an invariant always holds. However, the biggest challenge with this mindset is identifying the invariants themselves. Having a detailed protocol specification greatly aids this process, but for many new DeFi protocols aiming for rapid launch, such specifications are often missing.
We want to emphasize that starting protocol design with a clear specification is extremely beneficial. Many issues can be detected and addressed during the specification phase. However, this approach requires additional effort and time, which may be challenging. If you don’t have time to prepare a detailed specification, you should at least compile a list of critical invariants.
The primary question for this mindset is: “
What invariants exist in the code?” As mentioned earlier, this question is best applied to functions that involve calculations, as it’s easier to identify meaningful invariants in those cases. For example, if you have a function that calculates how tokens are distributed among multiple actors, an invariant might ensure that the distribution is balanced, or that no tokens remain after distribution. The specific invariant will depend on the function’s logic, so you’ll need to define it carefully.
A common invariant example for Uniswap v4-like protocols, which stores all the balances on one contract and allows different pools to work with these balances, is a check that guarantees the number of tokens output in a swap cannot exceed the specific pool's balance. This invariant is simple to implement and can be adapted to most decentralized exchanges (DEXs), ensuring that user funds remain protected during swaps.
Another useful invariant is one that ensures the variable representing the total amount of tokens controlled by the protocol is always less than or equal to the contract’s token balance. This check uses “less than or equal” because in cases where ERC20 tokens are transferred directly to the contract’s address, a strict equality check would fail.
It’s important to note that while companies offering formal verification services handle the preparation of invariants, fuzzing techniques can also help validate them. However, fuzzing does not guarantee that invariants will hold in all protocol states. If formal verification services are out of reach at this stage, setting up fuzzing tests is essential. Tools like
Echidna or
Foundry can be highly effective here.