From 8c00656b2d3e7fda39f74d25f1f9a09263c7bc42 Mon Sep 17 00:00:00 2001 From: chefburger Date: Mon, 11 Nov 2024 15:37:05 +0800 Subject: [PATCH] fix: [hexen-r9] add overflow check for bin liquidity --- ...omCurveHookTest#test_Swap_CustomCurve.snap | 2 +- ...inHookTest#testDonateSucceedsWithHook.snap | 2 +- .../BinHookTest#testMintSucceedsWithHook.snap | 2 +- .../BinHookTest#testSwapSucceedsWithHook.snap | 2 +- .../BinMintBurnFeeHookTest#test_Mint.snap | 2 +- .../BinPoolManagerBytecodeSize.snap | 2 +- .../BinPoolManagerTest#testGasDonate.snap | 2 +- ...nPoolManagerTest#testGasMintNneBins-1.snap | 2 +- ...nPoolManagerTest#testGasMintNneBins-2.snap | 2 +- ...inPoolManagerTest#testGasMintOneBin-1.snap | 2 +- ...inPoolManagerTest#testGasMintOneBin-2.snap | 2 +- ...olManagerTest#testGasSwapMultipleBins.snap | 2 +- ...nagerTest#testGasSwapOverBigBinIdGate.snap | 2 +- ...nPoolManagerTest#testGasSwapSingleBin.snap | 2 +- ...oolManagerTest#testMintNativeCurrency.snap | 2 +- src/pool-bin/libraries/BinPool.sol | 26 ++++++++++++++++--- src/pool-bin/libraries/Constants.sol | 4 +++ test/pool-bin/libraries/BinPoolDonate.t.sol | 11 ++++++++ 18 files changed, 52 insertions(+), 19 deletions(-) diff --git a/.forge-snapshots/BinCustomCurveHookTest#test_Swap_CustomCurve.snap b/.forge-snapshots/BinCustomCurveHookTest#test_Swap_CustomCurve.snap index 6589b3a3..61b6efad 100644 --- a/.forge-snapshots/BinCustomCurveHookTest#test_Swap_CustomCurve.snap +++ b/.forge-snapshots/BinCustomCurveHookTest#test_Swap_CustomCurve.snap @@ -1 +1 @@ -142478 \ No newline at end of file +142475 \ No newline at end of file diff --git a/.forge-snapshots/BinHookTest#testDonateSucceedsWithHook.snap b/.forge-snapshots/BinHookTest#testDonateSucceedsWithHook.snap index 6c9aac79..acb72c87 100644 --- a/.forge-snapshots/BinHookTest#testDonateSucceedsWithHook.snap +++ b/.forge-snapshots/BinHookTest#testDonateSucceedsWithHook.snap @@ -1 +1 @@ -188410 \ No newline at end of file +188265 \ No newline at end of file diff --git a/.forge-snapshots/BinHookTest#testMintSucceedsWithHook.snap b/.forge-snapshots/BinHookTest#testMintSucceedsWithHook.snap index 607b6b5d..07ee066b 100644 --- a/.forge-snapshots/BinHookTest#testMintSucceedsWithHook.snap +++ b/.forge-snapshots/BinHookTest#testMintSucceedsWithHook.snap @@ -1 +1 @@ -311254 \ No newline at end of file +311495 \ No newline at end of file diff --git a/.forge-snapshots/BinHookTest#testSwapSucceedsWithHook.snap b/.forge-snapshots/BinHookTest#testSwapSucceedsWithHook.snap index 763303e6..211df2eb 100644 --- a/.forge-snapshots/BinHookTest#testSwapSucceedsWithHook.snap +++ b/.forge-snapshots/BinHookTest#testSwapSucceedsWithHook.snap @@ -1 +1 @@ -189451 \ No newline at end of file +189959 \ No newline at end of file diff --git a/.forge-snapshots/BinMintBurnFeeHookTest#test_Mint.snap b/.forge-snapshots/BinMintBurnFeeHookTest#test_Mint.snap index 4128ba06..0e89e7af 100644 --- a/.forge-snapshots/BinMintBurnFeeHookTest#test_Mint.snap +++ b/.forge-snapshots/BinMintBurnFeeHookTest#test_Mint.snap @@ -1 +1 @@ -410336 \ No newline at end of file +410577 \ No newline at end of file diff --git a/.forge-snapshots/BinPoolManagerBytecodeSize.snap b/.forge-snapshots/BinPoolManagerBytecodeSize.snap index c0501746..07534292 100644 --- a/.forge-snapshots/BinPoolManagerBytecodeSize.snap +++ b/.forge-snapshots/BinPoolManagerBytecodeSize.snap @@ -1 +1 @@ -23287 \ No newline at end of file +23548 \ No newline at end of file diff --git a/.forge-snapshots/BinPoolManagerTest#testGasDonate.snap b/.forge-snapshots/BinPoolManagerTest#testGasDonate.snap index 5deafddc..0d5a057d 100644 --- a/.forge-snapshots/BinPoolManagerTest#testGasDonate.snap +++ b/.forge-snapshots/BinPoolManagerTest#testGasDonate.snap @@ -1 +1 @@ -118691 \ No newline at end of file +118546 \ No newline at end of file diff --git a/.forge-snapshots/BinPoolManagerTest#testGasMintNneBins-1.snap b/.forge-snapshots/BinPoolManagerTest#testGasMintNneBins-1.snap index dc44a72f..2aa17409 100644 --- a/.forge-snapshots/BinPoolManagerTest#testGasMintNneBins-1.snap +++ b/.forge-snapshots/BinPoolManagerTest#testGasMintNneBins-1.snap @@ -1 +1 @@ -968475 \ No newline at end of file +970284 \ No newline at end of file diff --git a/.forge-snapshots/BinPoolManagerTest#testGasMintNneBins-2.snap b/.forge-snapshots/BinPoolManagerTest#testGasMintNneBins-2.snap index 4a863ea0..eeef0a41 100644 --- a/.forge-snapshots/BinPoolManagerTest#testGasMintNneBins-2.snap +++ b/.forge-snapshots/BinPoolManagerTest#testGasMintNneBins-2.snap @@ -1 +1 @@ -327787 \ No newline at end of file +329605 \ No newline at end of file diff --git a/.forge-snapshots/BinPoolManagerTest#testGasMintOneBin-1.snap b/.forge-snapshots/BinPoolManagerTest#testGasMintOneBin-1.snap index f71e10bd..d25d3e11 100644 --- a/.forge-snapshots/BinPoolManagerTest#testGasMintOneBin-1.snap +++ b/.forge-snapshots/BinPoolManagerTest#testGasMintOneBin-1.snap @@ -1 +1 @@ -337511 \ No newline at end of file +337752 \ No newline at end of file diff --git a/.forge-snapshots/BinPoolManagerTest#testGasMintOneBin-2.snap b/.forge-snapshots/BinPoolManagerTest#testGasMintOneBin-2.snap index 140b1ddb..ddde8d98 100644 --- a/.forge-snapshots/BinPoolManagerTest#testGasMintOneBin-2.snap +++ b/.forge-snapshots/BinPoolManagerTest#testGasMintOneBin-2.snap @@ -1 +1 @@ -140062 \ No newline at end of file +140304 \ No newline at end of file diff --git a/.forge-snapshots/BinPoolManagerTest#testGasSwapMultipleBins.snap b/.forge-snapshots/BinPoolManagerTest#testGasSwapMultipleBins.snap index 23969d6b..d1be9631 100644 --- a/.forge-snapshots/BinPoolManagerTest#testGasSwapMultipleBins.snap +++ b/.forge-snapshots/BinPoolManagerTest#testGasSwapMultipleBins.snap @@ -1 +1 @@ -173098 \ No newline at end of file +177927 \ No newline at end of file diff --git a/.forge-snapshots/BinPoolManagerTest#testGasSwapOverBigBinIdGate.snap b/.forge-snapshots/BinPoolManagerTest#testGasSwapOverBigBinIdGate.snap index 2925f2ee..7802c629 100644 --- a/.forge-snapshots/BinPoolManagerTest#testGasSwapOverBigBinIdGate.snap +++ b/.forge-snapshots/BinPoolManagerTest#testGasSwapOverBigBinIdGate.snap @@ -1 +1 @@ -179126 \ No newline at end of file +184209 \ No newline at end of file diff --git a/.forge-snapshots/BinPoolManagerTest#testGasSwapSingleBin.snap b/.forge-snapshots/BinPoolManagerTest#testGasSwapSingleBin.snap index 423d7fde..ba7cd49d 100644 --- a/.forge-snapshots/BinPoolManagerTest#testGasSwapSingleBin.snap +++ b/.forge-snapshots/BinPoolManagerTest#testGasSwapSingleBin.snap @@ -1 +1 @@ -133129 \ No newline at end of file +133637 \ No newline at end of file diff --git a/.forge-snapshots/BinPoolManagerTest#testMintNativeCurrency.snap b/.forge-snapshots/BinPoolManagerTest#testMintNativeCurrency.snap index f9388fc0..f7fe2469 100644 --- a/.forge-snapshots/BinPoolManagerTest#testMintNativeCurrency.snap +++ b/.forge-snapshots/BinPoolManagerTest#testMintNativeCurrency.snap @@ -1 +1 @@ -304550 \ No newline at end of file +304791 \ No newline at end of file diff --git a/src/pool-bin/libraries/BinPool.sol b/src/pool-bin/libraries/BinPool.sol index 8fb77084..410e2397 100644 --- a/src/pool-bin/libraries/BinPool.sol +++ b/src/pool-bin/libraries/BinPool.sol @@ -48,6 +48,7 @@ library BinPool { error BinPool__NoLiquidityToReceiveFees(); /// @dev if swap exactIn, x for y, unspecifiedToken = token y. if swap x for exact out y, unspecified token is x error BinPool__InsufficientAmountUnSpecified(); + error BinPool__MaxLiquidityPerBinExceeded(); /// @dev The state of a pool struct State { @@ -168,7 +169,15 @@ library BinPool { amountsInWithFees = amountsInWithFees.sub(pFee); } - self.reserveOfBin[swapState.activeId] = binReserves.add(amountsInWithFees).sub(amountsOutOfBin); + bytes32 newReserve = binReserves.add(amountsInWithFees).sub(amountsOutOfBin); + if ( + newReserve.getLiquidity(swapState.activeId.getPriceFromId(params.binStep)) + > Constants.MAX_LIQUIDITY_PER_BIN + ) { + revert BinPool__MaxLiquidityPerBinExceeded(); + } + + self.reserveOfBin[swapState.activeId] = newReserve; } } @@ -347,9 +356,13 @@ library BinPool { /// @dev overflow check on total reserves and the resulting liquidity uint256 price = activeId.getPriceFromId(binStep); - binReserves.add(amountIn).getLiquidity(price); + bytes32 newReserves = binReserves.add(amountIn); + uint256 liquidity = newReserves.getLiquidity(price); + if (liquidity > Constants.MAX_LIQUIDITY_PER_BIN) { + revert BinPool__MaxLiquidityPerBinExceeded(); + } - self.reserveOfBin[activeId] = binReserves.add(amountIn); + self.reserveOfBin[activeId] = newReserves; result = toBalanceDelta(-(amount0.safeInt128()), -(amount1.safeInt128())); } @@ -457,7 +470,12 @@ library BinPool { if (shares == 0 || amountsInToBin == 0) revert BinPool__ZeroShares(id); if (supply == 0) _addBinIdToTree(self, id); - self.reserveOfBin[id] = binReserves.add(amountsInToBin); + bytes32 newReserves = binReserves.add(amountsInToBin); + if (newReserves.getLiquidity(price) > Constants.MAX_LIQUIDITY_PER_BIN) { + revert BinPool__MaxLiquidityPerBinExceeded(); + } + + self.reserveOfBin[id] = newReserves; } /// @notice Subtract share from user's position and update total share supply of bin diff --git a/src/pool-bin/libraries/Constants.sol b/src/pool-bin/libraries/Constants.sol index 1f9d26bb..a2ddfe8e 100644 --- a/src/pool-bin/libraries/Constants.sol +++ b/src/pool-bin/libraries/Constants.sol @@ -11,4 +11,8 @@ library Constants { uint256 internal constant SQUARED_PRECISION = PRECISION * PRECISION; uint256 internal constant BASIS_POINT_MAX = 10_000; + + // (2^256 - 1) / (2 * log(2**128) / log(1.0001)) + uint256 internal constant MAX_LIQUIDITY_PER_BIN = + 65251743116719673010965625540244653191619923014385985379600384103134737; } diff --git a/test/pool-bin/libraries/BinPoolDonate.t.sol b/test/pool-bin/libraries/BinPoolDonate.t.sol index 9efc1ddf..5c8ebca8 100644 --- a/test/pool-bin/libraries/BinPoolDonate.t.sol +++ b/test/pool-bin/libraries/BinPoolDonate.t.sol @@ -17,11 +17,14 @@ import {PackedUint128Math} from "../../../src/pool-bin/libraries/math/PackedUint import {SafeCast} from "../../../src/pool-bin/libraries/math/SafeCast.sol"; import {BinPoolParametersHelper} from "../../../src/pool-bin/libraries/BinPoolParametersHelper.sol"; import {BinTestHelper} from "../helpers/BinTestHelper.sol"; +import {Constants} from "../../../src/pool-bin/libraries/Constants.sol"; +import {PriceHelper} from "../../../src/pool-bin/libraries/PriceHelper.sol"; contract BinPoolDonateTest is BinTestHelper { using PackedUint128Math for bytes32; using BinPoolParametersHelper for bytes32; using SafeCast for uint256; + using BinHelper for bytes32; MockVault public vault; BinPoolManager public poolManager; @@ -119,6 +122,14 @@ contract BinPoolDonateTest is BinTestHelper { addLiquidityToBin(key, poolManager, bob, activeId, 1e18, 1e18, 1e18, 1e18, ""); poolManager.getPosition(poolId, bob, activeId, 0).share; + bytes32 newReserves = PackedUint128Math.encode(1e18 + amt0, 1e18 + amt1); + uint256 price = PriceHelper.getPriceFromId(activeId, poolParam.getBinStep()); + if (newReserves.getLiquidity(price) > Constants.MAX_LIQUIDITY_PER_BIN) { + vm.expectRevert(BinPool.BinPool__MaxLiquidityPerBinExceeded.selector); + poolManager.donate(key, amt0, amt1, ""); + return; + } + poolManager.donate(key, amt0, amt1, ""); // Verify reserve after donate