foundry icon indicating copy to clipboard operation
foundry copied to clipboard

feat(invariant): add Optimization mode to Invariant Testing similar to Echidna

Open rappie opened this issue 2 months ago • 2 comments

Component

Forge

Describe the feature you would like

In Echidna, optimization mode is used to fuzz for the maximized return value of a given function.

Example:

    function echidna_opt_price_difference() public view returns (int256) {
        return maxPriceDifference;
    }

It basically runs the fuzzer like normal, but instead of looking for broken invariants, it stores the highest value returned by the function being tested. I often use this to determine the max error due to rounding to determine if this is acceptable, but it has many uses.

Additional context

For more info see: https://secure-contracts.com/program-analysis/echidna/advanced/optimization_mode.html

rappie avatar Oct 20 '25 11:10 rappie

@rappie from the echidna doc this seems to me like a stateless / regular fuzz test as per https://secure-contracts.com/program-analysis/echidna/advanced/optimization_mode.html#optimizing-with-echidna wonder if something like this should work:

    function test_max_price(uint256 startPrice, uint256 endPrice, uint256 startTime, uint256 endTime)
        public
        view
        returns (int256 maxPriceDifference)
    {
        if (endTime < (startTime + 900)) revert();
        if (startPrice <= endPrice) revert();

        uint256 numerator = (startPrice - endPrice) * (block.timestamp - startTime);
        uint256 denominator = endTime - startTime;
        uint256 stepDecrease = numerator / denominator;
        uint256 currentAuctionPrice = startPrice - stepDecrease;

        if (currentAuctionPrice < endPrice) {
            maxPriceDifference = int256(endPrice - currentAuctionPrice);
        }
        if (currentAuctionPrice > startPrice) {
            maxPriceDifference = int256(currentAuctionPrice - startPrice);
        }
    }

and we treat test* as optimization mode if returns int (or we could add a optimization = true config). Just want to make sure that I get this right and these can run as standalone stateless fuzz functions instead of invariant tests. Thank you!

grandizzy avatar Oct 27 '25 10:10 grandizzy

Two things:

  1. You've got it right that having a view that returns an int256 is basically all we need to do optimization
  2. We do want to be able run this as invariant test. For example, you often have a sequence where you first deposit a certain amount of tokens, and then perform an action (transfer, swap, etc.) that has rounding errors. You then want to optimize for the maximum rounding error possible. These are usually 2-5 long sequences of transactions.

rappie avatar Oct 27 '25 11:10 rappie