Require Invariants: Proving inter-dependent invariants
TherequireInvariant
statements can be used to establish
crucial conditions that must persist throughout the execution of a smart contract. Let’s explore the
usefulness of the requireInvariant
statement and illustrate its application using the provided example.
invariant totalSharesLessThanDepositedAmount()
totalSupply() <= depositedAmount();
invariant totalSharesLessThanUnderlyingBalance()
totalSupply() <= underlying.balanceOf(currentContract)
{
preserved with(env e) {
require e.msg.sender != currentContract;
requireInvariant totalSharesLessThanDepositedAmount();
requireInvariant depositedAmountLessThanContractUnderlyingAsset();
}
}
rule sharesRoundingTripFavoursContract(env e) {
uint256 clientSharesBefore = balanceOf(e.msg.sender);
uint256 contractSharesBefore = balanceOf(currentContract);
requireInvariant totalSharesLessThanDepositedAmount();
require e.msg.sender != currentContract;
uint256 depositedAmount = depositedAmount();
uint256 clientAmount = withdraw(e, clientSharesBefore);
uint256 clientSharesAfter = deposit(e, clientAmount);
uint256 contractSharesAfter = balanceOf(currentContract);
assert (clientAmount == depositedAmount) => clientSharesBefore <= clientSharesAfter;
// all other states
assert (clientAmount < depositedAmount) => clientSharesBefore >= clientSharesAfter;
assert contractSharesBefore <= contractSharesAfter;
}
Importance of Require Invariants:
Ensuring Invariant Consistency:
The
totalSharesLessThanUnderlyingBalance
invariant expands the validation scope to include the underlying balance of the current contract. By utilizing the previously establishedtotalSharesLessThanDepositedAmount
invariant as a prerequisite, the specification writer ensures that the total shares in the contract remain within the limits of the underlying asset balance. ThisrequireInvariant
approach effectively eliminates counterexamples in states that have already been proven to be unattainable.
Validation Through Rules:
The
sharesRoundingTripFavoursContract
rule showcases the application of require invariants to verify the behavior of share transactions. Similarly to the invariant example, therequireInvariant
statements enable the specification writer to disregard counterexamples in states that are not relevant to the intended behavior.
In conclusion, the “Require Invariants” design pattern, as demonstrated through the provided example, offers a systematic methodology to define, validate, and uphold critical conditions within smart contract specifications. for more information, please visit the documentation.