diff --git a/onchain/src/Optimizer.sol b/onchain/src/Optimizer.sol index fe7687b..54db899 100644 --- a/onchain/src/Optimizer.sol +++ b/onchain/src/Optimizer.sol @@ -248,6 +248,9 @@ contract Optimizer is Initializable, UUPSUpgradeable { virtual returns (uint256 capitalInefficiency, uint256 anchorShare, uint24 anchorWidth, uint256 discoveryDepth) { + // Guard against negative mantissa — uint256() cast silently wraps negatives. + require(inputs[0].mantissa >= 0, "negative mantissa"); + require(inputs[1].mantissa >= 0, "negative mantissa"); // Extract slots 0 and 1 (shift=0 assumed — mantissa IS the value) uint256 percentageStaked = uint256(inputs[0].mantissa); uint256 averageTaxRate = uint256(inputs[1].mantissa); diff --git a/onchain/test/Optimizer.t.sol b/onchain/test/Optimizer.t.sol index d22eef6..2b5fff5 100644 --- a/onchain/test/Optimizer.t.sol +++ b/onchain/test/Optimizer.t.sol @@ -324,6 +324,26 @@ contract OptimizerTest is Test { assertEq(w, 10, "totalWidth < 10 should be clamped to minimum of 10"); } + /** + * @notice calculateParams reverts when inputs[0].mantissa is negative + */ + function testCalculateParamsRevertsOnNegativeMantissa0() public { + OptimizerInput[8] memory inputs; + inputs[0] = OptimizerInput({mantissa: -1, shift: 0}); + vm.expectRevert("negative mantissa"); + optimizer.calculateParams(inputs); + } + + /** + * @notice calculateParams reverts when inputs[1].mantissa is negative + */ + function testCalculateParamsRevertsOnNegativeMantissa1() public { + OptimizerInput[8] memory inputs; + inputs[1] = OptimizerInput({mantissa: -1, shift: 0}); + vm.expectRevert("negative mantissa"); + optimizer.calculateParams(inputs); + } + /** * @notice Non-admin calling upgradeTo should revert with UnauthorizedAccount */