diff --git a/onchain/src/OptimizerV3Push3.sol b/onchain/src/OptimizerV3Push3.sol index 78a6dd1..a24578f 100644 --- a/onchain/src/OptimizerV3Push3.sol +++ b/onchain/src/OptimizerV3Push3.sol @@ -56,8 +56,10 @@ contract OptimizerV3Push3 is IOptimizer { require(inputs[0].mantissa <= 1e18, "mantissa overflow"); // Validate that shift is 0 (future-only field, not yet supported) + // and that no mantissa is negative (uint256 cast silently wraps negatives). for (uint256 k = 0; k < 8; k++) { require(inputs[k].shift == 0, "shift not yet supported"); + require(inputs[k].mantissa >= 0, "negative mantissa"); } uint256 percentagestaked = uint256(uint256(inputs[0].mantissa)); diff --git a/onchain/test/OptimizerV3Push3.t.sol b/onchain/test/OptimizerV3Push3.t.sol index aba1183..bae7169 100644 --- a/onchain/test/OptimizerV3Push3.t.sol +++ b/onchain/test/OptimizerV3Push3.t.sol @@ -200,6 +200,30 @@ contract OptimizerV3Push3Test is Test { _assertBull(ci, as_, aw, dd); } + // ---- Negative mantissa guards ---- + + function testNegativeMantissaSlot0Reverts() public { + OptimizerInput[8] memory inp; + inp[0] = OptimizerInput({mantissa: -1, shift: 0}); + vm.expectRevert("negative mantissa"); + push3.calculateParams(inp); + } + + function testNegativeMantissaSlot1Reverts() public { + OptimizerInput[8] memory inp; + inp[0] = OptimizerInput({mantissa: int256(_pct(95)), shift: 0}); + inp[1] = OptimizerInput({mantissa: -1, shift: 0}); + vm.expectRevert("negative mantissa"); + push3.calculateParams(inp); + } + + function testNegativeMantissaSlot5Reverts() public { + OptimizerInput[8] memory inp; + inp[5] = OptimizerInput({mantissa: -1, shift: 0}); + vm.expectRevert("negative mantissa"); + push3.calculateParams(inp); + } + // ---- Fuzz ---- function testFuzzNeverReverts(uint256 percentageStaked, uint256 averageTaxRate) public view {