fix: getLiquidityParams: int256 overflow on large VWAP values (#622)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
5deb1eab0c
commit
7007e593da
2 changed files with 76 additions and 0 deletions
|
|
@ -414,6 +414,7 @@ contract Optimizer is Initializable, UUPSUpgradeable, IOptimizer {
|
||||||
// ---- Slot 2: pricePosition (also needs VWAP) ----
|
// ---- Slot 2: pricePosition (also needs VWAP) ----
|
||||||
if (vwapTracker != address(0)) {
|
if (vwapTracker != address(0)) {
|
||||||
uint256 vwapX96 = IVWAPTracker(vwapTracker).getVWAP();
|
uint256 vwapX96 = IVWAPTracker(vwapTracker).getVWAP();
|
||||||
|
require(vwapX96 <= uint256(type(int256).max), "VWAP exceeds int256 range");
|
||||||
if (vwapX96 > 0) {
|
if (vwapX96 > 0) {
|
||||||
// Convert pool tick to KRK-price space: higher tick = more expensive KRK.
|
// Convert pool tick to KRK-price space: higher tick = more expensive KRK.
|
||||||
// Uniswap convention: tick ↑ → token1 more expensive relative to token0.
|
// Uniswap convention: tick ↑ → token1 more expensive relative to token0.
|
||||||
|
|
|
||||||
|
|
@ -704,3 +704,78 @@ contract OptimizerNormalizedInputsTest is Test {
|
||||||
|
|
||||||
import { TickMath } from "@aperture/uni-v3-lib/TickMath.sol";
|
import { TickMath } from "@aperture/uni-v3-lib/TickMath.sol";
|
||||||
import { Math } from "@openzeppelin/utils/math/Math.sol";
|
import { Math } from "@openzeppelin/utils/math/Math.sol";
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// VWAP int256 overflow guard test (issue #622)
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
/// @dev Minimal mock that returns a configurable VWAP value for overflow testing.
|
||||||
|
contract ConfigurableVWAPMock {
|
||||||
|
uint256 private _vwap;
|
||||||
|
|
||||||
|
function setVWAP(uint256 v) external {
|
||||||
|
_vwap = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getVWAP() external view returns (uint256) {
|
||||||
|
return _vwap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contract OptimizerVWAPOverflowTest is Test {
|
||||||
|
OptimizerInputCapture capture;
|
||||||
|
Optimizer optimizer;
|
||||||
|
MockStake mockStake;
|
||||||
|
MockKraiken mockKraiken;
|
||||||
|
ConfigurableVWAPMock configurableVwap;
|
||||||
|
MockPool mockPool;
|
||||||
|
|
||||||
|
function setUp() public {
|
||||||
|
mockKraiken = new MockKraiken();
|
||||||
|
mockStake = new MockStake();
|
||||||
|
configurableVwap = new ConfigurableVWAPMock();
|
||||||
|
mockPool = new MockPool();
|
||||||
|
|
||||||
|
OptimizerInputCapture impl = new OptimizerInputCapture();
|
||||||
|
bytes memory initData = abi.encodeWithSelector(Optimizer.initialize.selector, address(mockKraiken), address(mockStake));
|
||||||
|
ERC1967Proxy proxy = new ERC1967Proxy(address(impl), initData);
|
||||||
|
capture = OptimizerInputCapture(address(proxy));
|
||||||
|
optimizer = Optimizer(address(proxy));
|
||||||
|
|
||||||
|
optimizer.setDataSources(address(configurableVwap), address(mockPool), address(0), false);
|
||||||
|
mockPool.setCurrentTick(0);
|
||||||
|
mockPool.setRevertOnObserve(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @notice VWAP value exceeding int256.max must revert with descriptive message.
|
||||||
|
function testRevertsWhenVWAPExceedsInt256Max() public {
|
||||||
|
configurableVwap.setVWAP(uint256(type(int256).max) + 1);
|
||||||
|
|
||||||
|
vm.expectRevert("VWAP exceeds int256 range");
|
||||||
|
optimizer.getLiquidityParams();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @notice VWAP at exactly int256.max should not revert.
|
||||||
|
function testAcceptsVWAPAtInt256Max() public {
|
||||||
|
configurableVwap.setVWAP(uint256(type(int256).max));
|
||||||
|
|
||||||
|
// Should not revert
|
||||||
|
optimizer.getLiquidityParams();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @notice VWAP at uint256.max must revert.
|
||||||
|
function testRevertsWhenVWAPIsUint256Max() public {
|
||||||
|
configurableVwap.setVWAP(type(uint256).max);
|
||||||
|
|
||||||
|
vm.expectRevert("VWAP exceeds int256 range");
|
||||||
|
optimizer.getLiquidityParams();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @notice VWAP of zero should not revert (no-op path).
|
||||||
|
function testAcceptsVWAPZero() public {
|
||||||
|
configurableVwap.setVWAP(0);
|
||||||
|
|
||||||
|
// Should not revert — zero VWAP skips the pricePosition computation
|
||||||
|
optimizer.getLiquidityParams();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue