Merge pull request 'fix: Catch block skips clamping that try block applies (#1019)' (#1118) from fix/issue-1019 into master

This commit is contained in:
johba 2026-03-22 16:06:02 +01:00
commit 311b8192f6
2 changed files with 44 additions and 1 deletions

View file

@ -249,7 +249,16 @@ contract LiquidityManager is ThreePositionStrategy, PriceOracle {
_setPositions(currentTick, params);
} catch {
// Fallback to safe bear-mode defaults if optimizer fails
// Fallback to safe bear-mode defaults if optimizer fails.
// Defence-in-depth: assert bear constants satisfy the same bounds
// that the try-path clamps to. These are compile-time constants so
// the asserts can never fire at runtime, but they guard against a
// future edit to IOptimizer.sol that pushes a value out of range.
assert(BEAR_CAPITAL_INEFFICIENCY <= MAX_PARAM_SCALE);
assert(BEAR_ANCHOR_SHARE <= MAX_PARAM_SCALE);
assert(BEAR_ANCHOR_WIDTH <= MAX_ANCHOR_WIDTH);
assert(BEAR_DISCOVERY_DEPTH <= MAX_PARAM_SCALE);
PositionParams memory defaultParams = PositionParams({
capitalInefficiency: BEAR_CAPITAL_INEFFICIENCY,
anchorShare: BEAR_ANCHOR_SHARE,

View file

@ -28,6 +28,7 @@ import { ThreePositionStrategy } from "../src/abstracts/ThreePositionStrategy.so
import "../src/helpers/UniswapHelpers.sol";
import "../test/mocks/MockOptimizer.sol";
import "../test/mocks/ConfigurableOptimizer.sol";
import { BEAR_CAPITAL_INEFFICIENCY, BEAR_ANCHOR_SHARE, BEAR_ANCHOR_WIDTH, BEAR_DISCOVERY_DEPTH } from "../src/IOptimizer.sol";
import { TestEnvironment } from "./helpers/TestBase.sol";
import { UniSwapHelper } from "./helpers/UniswapTestBase.sol";
@ -1100,6 +1101,39 @@ contract LiquidityManagerTest is UniSwapHelper {
assertGt(anchorLiq, 0, "ANCHOR position should have been created with fallback params");
}
/**
* @notice Catch-path clamping: verify bear defaults satisfy the same bounds
* that the try-path clamps to, and that all three positions are deployed.
* Covers issue #1019 defence-in-depth assertions in catch block.
*/
function testOptimizerFallback_BearDefaultsWithinClampingBounds() public {
// --- Static check: bear constants are within clamping ceilings ---
uint256 MAX_PARAM_SCALE = 10 ** 18;
uint24 MAX_ANCHOR_WIDTH = 1233;
assertLe(BEAR_CAPITAL_INEFFICIENCY, MAX_PARAM_SCALE, "BEAR_CAPITAL_INEFFICIENCY exceeds MAX_PARAM_SCALE");
assertLe(BEAR_ANCHOR_SHARE, MAX_PARAM_SCALE, "BEAR_ANCHOR_SHARE exceeds MAX_PARAM_SCALE");
assertLe(BEAR_ANCHOR_WIDTH, MAX_ANCHOR_WIDTH, "BEAR_ANCHOR_WIDTH exceeds MAX_ANCHOR_WIDTH");
assertLe(BEAR_DISCOVERY_DEPTH, MAX_PARAM_SCALE, "BEAR_DISCOVERY_DEPTH exceeds MAX_PARAM_SCALE");
// --- Runtime check: catch block deploys all three positions ---
RevertingOptimizer revertingOpt = new RevertingOptimizer();
TestEnvironment env = new TestEnvironment(feeDestination);
(,,,,, LiquidityManager _lm,,) = env.setupEnvironmentWithOptimizer(DEFAULT_TOKEN0_IS_WETH, address(revertingOpt));
vm.prank(RECENTER_CALLER);
_lm.recenter();
// All three position stages must have non-zero liquidity
(uint128 floorLiq,,) = _lm.positions(ThreePositionStrategy.Stage.FLOOR);
(uint128 anchorLiq,,) = _lm.positions(ThreePositionStrategy.Stage.ANCHOR);
(uint128 discoveryLiq,,) = _lm.positions(ThreePositionStrategy.Stage.DISCOVERY);
assertGt(floorLiq, 0, "FLOOR position missing in catch path");
assertGt(anchorLiq, 0, "ANCHOR position missing in catch path");
assertGt(discoveryLiq, 0, "DISCOVERY position missing in catch path");
}
/**
* @notice When feeDestination == address(lm), recenter must not transfer fees externally
* and _getOutstandingSupply must not double-subtract LM-held KRK.