// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.19; import { OptimizerV3 } from "../src/OptimizerV3.sol"; import { OptimizerV3Push3 } from "../src/OptimizerV3Push3.sol"; import "forge-std/Test.sol"; /** * @title OptimizerV3Push3Test * @notice Verifies that the Push3-transpiled OptimizerV3Push3 produces * identical results to the hand-written OptimizerV3 for all test cases. * * Tests mirror OptimizerV3.t.sol to ensure equivalence. */ contract OptimizerV3Push3Test is Test { OptimizerV3 ref; // reference (hand-written) OptimizerV3Push3 push3; // transpiled from Push3 uint256[30] TAX_RATES = [uint256(1), 3, 5, 8, 12, 18, 24, 30, 40, 50, 60, 80, 100, 130, 180, 250, 320, 420, 540, 700, 920, 1200, 1600, 2000, 2600, 3400, 4400, 5700, 7500, 9700]; uint256 constant MAX_TAX = 9700; function setUp() public { ref = new OptimizerV3(); push3 = new OptimizerV3Push3(); } function _norm(uint256 taxIdx) internal view returns (uint256) { return TAX_RATES[taxIdx] * 1e18 / MAX_TAX; } function _pct(uint256 pct) internal pure returns (uint256) { return pct * 1e18 / 100; } // ---- Equivalence against reference ---- function testEquivalenceAllTaxRates() public view { uint256[7] memory staked = [uint256(0), 50, 91, 92, 95, 96, 100]; for (uint256 s = 0; s < staked.length; s++) { for (uint256 t = 0; t < 30; t++) { bool r = ref.isBullMarket(_pct(staked[s]), _norm(t)); bool p = push3.isBullMarket(_pct(staked[s]), _norm(t)); assertEq(p, r, string.concat( "Mismatch at staked=", vm.toString(staked[s]), "% taxIdx=", vm.toString(t) )); } } } function testEquivalence93to99Percent() public view { for (uint256 s = 93; s <= 99; s++) { for (uint256 t = 0; t < 30; t++) { bool r = ref.isBullMarket(_pct(s), _norm(t)); bool p = push3.isBullMarket(_pct(s), _norm(t)); assertEq(p, r, string.concat( "Mismatch at staked=", vm.toString(s), "% taxIdx=", vm.toString(t) )); } } } // ---- Direct correctness tests (mirror OptimizerV3.t.sol) ---- function testAlwaysBearAt0Percent() public view { for (uint256 t = 0; t < 30; t++) { assertFalse(push3.isBullMarket(0, _norm(t))); } } function testAlwaysBearAt91Percent() public view { for (uint256 t = 0; t < 30; t++) { assertFalse(push3.isBullMarket(_pct(91), _norm(t))); } } function testBoundary92PercentLowestTax() public view { // deltaS=8, effIdx=0 → penalty=0 < 50 → BULL assertTrue(push3.isBullMarket(_pct(92), _norm(0))); } function testBoundary92PercentTaxIdx1() public view { // deltaS=8, effIdx=1 → penalty=512*1/20=25 < 50 → BULL assertTrue(push3.isBullMarket(_pct(92), _norm(1))); } function testBoundary92PercentTaxIdx2() public view { // deltaS=8, effIdx=2 → penalty=512*2/20=51 >= 50 → BEAR assertFalse(push3.isBullMarket(_pct(92), _norm(2))); } function testAt95PercentTaxIdx7() public view { // deltaS=5, effIdx=7 → penalty=125*7/20=43 < 50 → BULL assertTrue(push3.isBullMarket(_pct(95), _norm(7))); } function testAt95PercentTaxIdx8() public view { // deltaS=5, effIdx=8 → penalty=125*8/20=50 NOT < 50 → BEAR assertFalse(push3.isBullMarket(_pct(95), _norm(8))); } function testAt97PercentHighTax() public view { // deltaS=3, effIdx=29 → penalty=27*29/20=39 < 50 → BULL assertTrue(push3.isBullMarket(_pct(97), _norm(29))); } function testAt100PercentAlwaysBull() public view { for (uint256 t = 0; t < 30; t++) { assertTrue(push3.isBullMarket(1e18, _norm(t))); } } function testEffIdxShiftAtBoundary() public view { // taxIdx=13: effIdx=13, penalty=64*13/20=41 < 50 → BULL assertTrue(push3.isBullMarket(_pct(96), _norm(13))); // taxIdx=14: effIdx=15 (shift!), penalty=64*15/20=48 < 50 → BULL assertTrue(push3.isBullMarket(_pct(96), _norm(14))); // taxIdx=15: effIdx=16, penalty=64*16/20=51 >= 50 → BEAR assertFalse(push3.isBullMarket(_pct(96), _norm(15))); } function testRevertsAbove100Percent() public { vm.expectRevert("Invalid percentage staked"); push3.isBullMarket(1e18 + 1, 0); } // ---- Fuzz ---- function testFuzzEquivalence(uint256 percentageStaked, uint256 averageTaxRate) public view { percentageStaked = bound(percentageStaked, 0, 1e18); averageTaxRate = bound(averageTaxRate, 0, 1e18); bool r = ref.isBullMarket(percentageStaked, averageTaxRate); bool p = push3.isBullMarket(percentageStaked, averageTaxRate); assertEq(p, r, "Push3 result must match reference for all inputs"); } function testFuzzNeverReverts(uint256 percentageStaked, uint256 averageTaxRate) public view { percentageStaked = bound(percentageStaked, 0, 1e18); averageTaxRate = bound(averageTaxRate, 0, 1e18); push3.isBullMarket(percentageStaked, averageTaxRate); } }