From 84999fc90acf1aeb8152f98da775b955ca3cc9c6 Mon Sep 17 00:00:00 2001 From: JulesCrown Date: Thu, 18 Jul 2024 16:50:23 +0200 Subject: [PATCH] better overflow protection --- onchain/src/LiquidityManager.sol | 59 +++++++++++++++++--------------- 1 file changed, 32 insertions(+), 27 deletions(-) diff --git a/onchain/src/LiquidityManager.sol b/onchain/src/LiquidityManager.sol index 0085a6f..1e719a4 100644 --- a/onchain/src/LiquidityManager.sol +++ b/onchain/src/LiquidityManager.sol @@ -51,6 +51,8 @@ contract LiquidityManager { // creating a security margin for attacks on liquidity uint256 internal constant MIN_CAPITAL_INEFFICIENCY = 100; // 120 = 20% uint256 internal constant MAX_CAPITAL_INEFFICIENCY = 200; + // used to double-check price with uni oracle + uint32 internal constant PRICE_STABILITY_INTERVAL = 300; // 5 minutes in seconds // the address of the Uniswap V3 factory address private immutable factory; @@ -330,6 +332,26 @@ contract LiquidityManager { } } + function _recordVolumeAndPrice(uint256 currentPrice, uint256 fee) internal { + // assuming FEE is 1% + uint256 volume = fee * 100; + uint256 volumeWeightedPrice = currentPrice * volume; + // Check for potential overflow. 10**70 is close to 2^256 + if (cumulativeVolumeWeightedPrice > 10**70) { + uint256 zipFactor = 10**35; + uint256 desiredPrecision = 10**5; + while (zipFactor * desiredPrecision > cumulativeVolume) { + zipFactor /= desiredPrecision; + } + // Handle overflow: zip historic trade data + cumulativeVolumeWeightedPrice = cumulativeVolumeWeightedPrice / zipFactor; + // cumulativeVolume should be well higer than zipFactor + cumulativeVolume = cumulativeVolume / zipFactor; + } + cumulativeVolumeWeightedPrice += volumeWeightedPrice; + cumulativeVolume += volume; + } + function _scrape() internal { uint256 fee0 = 0; uint256 fee1 = 0; @@ -350,6 +372,7 @@ contract LiquidityManager { fee0 += collected0 - amount0; fee1 += collected1 - amount1; if (i == uint256(Stage.ANCHOR)) { + // the historic archor position is only an approximation for the price int24 tick = token0isWeth ? -1 * (position.tickLower + ANCHOR_SPACING): position.tickUpper - ANCHOR_SPACING; currentPrice = tickToPrice(tick); } @@ -361,17 +384,7 @@ contract LiquidityManager { if (fee0 > 0) { if (token0isWeth) { IERC20(address(weth)).transfer(feeDestination, fee0); - uint256 volume = fee0 * 100; - uint256 volumeWeightedPrice = currentPrice * volume; - // Check for potential overflow - if (cumulativeVolumeWeightedPrice > type(uint256).max - volumeWeightedPrice) { - // Handle overflow: reset - cumulativeVolumeWeightedPrice = volumeWeightedPrice; - cumulativeVolume = volume; - } else { - cumulativeVolumeWeightedPrice += volumeWeightedPrice; - cumulativeVolume += volume; - } + _recordVolumeAndPrice(currentPrice, fee0); } else { IERC20(address(harb)).transfer(feeDestination, fee0); } @@ -381,35 +394,27 @@ contract LiquidityManager { IERC20(address(harb)).transfer(feeDestination, fee1); } else { IERC20(address(weth)).transfer(feeDestination, fee1); - uint256 volume = fee1 * 100; - uint256 volumeWeightedPrice = currentPrice * volume; - // Check for potential overflow - if (cumulativeVolumeWeightedPrice > type(uint256).max - volumeWeightedPrice) { - // Handle overflow: reset - cumulativeVolumeWeightedPrice = volumeWeightedPrice; - cumulativeVolume = volume; - } else { - cumulativeVolumeWeightedPrice += volumeWeightedPrice; - cumulativeVolume += volume; - } + _recordVolumeAndPrice(currentPrice, fee1); } } } function _isPriceStable(int24 currentTick) internal view returns (bool) { - uint32 timeInterval = 300; // 5 minutes in seconds uint32[] memory secondsAgo = new uint32[](2); - secondsAgo[0] = timeInterval; // 5 minutes ago + secondsAgo[0] = PRICE_STABILITY_INTERVAL; // 5 minutes ago secondsAgo[1] = 0; // current block timestamp int56 tickCumulativeDiff; int24 averageTick; try pool.observe(secondsAgo) returns (int56[] memory tickCumulatives, uint160[] memory) { tickCumulativeDiff = tickCumulatives[1] - tickCumulatives[0]; - averageTick = int24(tickCumulativeDiff / int56(int32(timeInterval))); + averageTick = int24(tickCumulativeDiff / int56(int32(PRICE_STABILITY_INTERVAL))); } catch { - // TODO: - return false; + // try with a higher timeframe + secondsAgo[0] = PRICE_STABILITY_INTERVAL * 200; + (int56[] memory tickCumulatives, ) = pool.observe(secondsAgo); + tickCumulativeDiff = tickCumulatives[1] - tickCumulatives[0]; + averageTick = int24(tickCumulativeDiff / int56(int32(PRICE_STABILITY_INTERVAL))); } return (currentTick >= averageTick - MAX_TICK_DEVIATION && currentTick <= averageTick + MAX_TICK_DEVIATION);