diff --git a/.forge-snapshots/BinFungiblePositionManager_AddLiquidityTest#testAddLiquidityOutsideActiveId_ExistingId.snap b/.forge-snapshots/BinFungiblePositionManager_AddLiquidityTest#testAddLiquidityOutsideActiveId_ExistingId.snap index 263a0be..c7f5ae1 100644 --- a/.forge-snapshots/BinFungiblePositionManager_AddLiquidityTest#testAddLiquidityOutsideActiveId_ExistingId.snap +++ b/.forge-snapshots/BinFungiblePositionManager_AddLiquidityTest#testAddLiquidityOutsideActiveId_ExistingId.snap @@ -1 +1 @@ -79699 \ No newline at end of file +80435 \ No newline at end of file diff --git a/.forge-snapshots/BinFungiblePositionManager_AddLiquidityTest#testAddLiquidityOutsideActiveId_NewId.snap b/.forge-snapshots/BinFungiblePositionManager_AddLiquidityTest#testAddLiquidityOutsideActiveId_NewId.snap index a1860b7..e6ce0fc 100644 --- a/.forge-snapshots/BinFungiblePositionManager_AddLiquidityTest#testAddLiquidityOutsideActiveId_NewId.snap +++ b/.forge-snapshots/BinFungiblePositionManager_AddLiquidityTest#testAddLiquidityOutsideActiveId_NewId.snap @@ -1 +1 @@ -627067 \ No newline at end of file +627803 \ No newline at end of file diff --git a/.forge-snapshots/BinFungiblePositionManager_AddLiquidityTest#testAddLiquidityWithActiveId.snap b/.forge-snapshots/BinFungiblePositionManager_AddLiquidityTest#testAddLiquidityWithActiveId.snap index 9266ff3..699b4c7 100644 --- a/.forge-snapshots/BinFungiblePositionManager_AddLiquidityTest#testAddLiquidityWithActiveId.snap +++ b/.forge-snapshots/BinFungiblePositionManager_AddLiquidityTest#testAddLiquidityWithActiveId.snap @@ -1 +1 @@ -918147 \ No newline at end of file +919685 \ No newline at end of file diff --git a/.forge-snapshots/BinFungiblePositionManager_AddLiquidityTest#testAddLiquidityWithActiveId_WithHook.snap b/.forge-snapshots/BinFungiblePositionManager_AddLiquidityTest#testAddLiquidityWithActiveId_WithHook.snap index e7bfb55..aa8f421 100644 --- a/.forge-snapshots/BinFungiblePositionManager_AddLiquidityTest#testAddLiquidityWithActiveId_WithHook.snap +++ b/.forge-snapshots/BinFungiblePositionManager_AddLiquidityTest#testAddLiquidityWithActiveId_WithHook.snap @@ -1 +1 @@ -1268812 \ No newline at end of file +1270351 \ No newline at end of file diff --git a/.forge-snapshots/BinFungiblePositionManager_RemoveLiquidityTest#testRemoveLiquidityOutsideActiveId_ThreeBins.snap b/.forge-snapshots/BinFungiblePositionManager_RemoveLiquidityTest#testRemoveLiquidityOutsideActiveId_ThreeBins.snap index 8bb86be..97f0b74 100644 --- a/.forge-snapshots/BinFungiblePositionManager_RemoveLiquidityTest#testRemoveLiquidityOutsideActiveId_ThreeBins.snap +++ b/.forge-snapshots/BinFungiblePositionManager_RemoveLiquidityTest#testRemoveLiquidityOutsideActiveId_ThreeBins.snap @@ -1 +1 @@ -80824 \ No newline at end of file +80759 \ No newline at end of file diff --git a/.forge-snapshots/BinFungiblePositionManager_RemoveLiquidityTest#testRemoveLiquidityWithActiveId_ThreeBins.snap b/.forge-snapshots/BinFungiblePositionManager_RemoveLiquidityTest#testRemoveLiquidityWithActiveId_ThreeBins.snap index 6403666..399f6c8 100644 --- a/.forge-snapshots/BinFungiblePositionManager_RemoveLiquidityTest#testRemoveLiquidityWithActiveId_ThreeBins.snap +++ b/.forge-snapshots/BinFungiblePositionManager_RemoveLiquidityTest#testRemoveLiquidityWithActiveId_ThreeBins.snap @@ -1 +1 @@ -117826 \ No newline at end of file +117761 \ No newline at end of file diff --git a/.forge-snapshots/BinFungiblePositionManager_RemoveLiquidityTest#testRemoveLiquidity_Half.snap b/.forge-snapshots/BinFungiblePositionManager_RemoveLiquidityTest#testRemoveLiquidity_Half.snap index 796527c..09f64a0 100644 --- a/.forge-snapshots/BinFungiblePositionManager_RemoveLiquidityTest#testRemoveLiquidity_Half.snap +++ b/.forge-snapshots/BinFungiblePositionManager_RemoveLiquidityTest#testRemoveLiquidity_Half.snap @@ -1 +1 @@ -114631 \ No newline at end of file +114566 \ No newline at end of file diff --git a/.forge-snapshots/BinFungibleTokenTest#testBatchTransferFrom_FromOwner.snap b/.forge-snapshots/BinFungibleTokenTest#testBatchTransferFrom_FromOwner.snap index 4ec87e2..9a9457a 100644 --- a/.forge-snapshots/BinFungibleTokenTest#testBatchTransferFrom_FromOwner.snap +++ b/.forge-snapshots/BinFungibleTokenTest#testBatchTransferFrom_FromOwner.snap @@ -1 +1 @@ -29390 \ No newline at end of file +9490 \ No newline at end of file diff --git a/.forge-snapshots/BinFungibleTokenTest#testMint.snap b/.forge-snapshots/BinFungibleTokenTest#testMint.snap index a7b7eae..8396f64 100644 --- a/.forge-snapshots/BinFungibleTokenTest#testMint.snap +++ b/.forge-snapshots/BinFungibleTokenTest#testMint.snap @@ -1 +1 @@ -43594 \ No newline at end of file +43591 \ No newline at end of file diff --git a/.forge-snapshots/BinSwapRouterTest#testExactInputSingle_DifferentRecipient.snap b/.forge-snapshots/BinSwapRouterTest#testExactInputSingle_DifferentRecipient.snap index 27fc416..b135fe5 100644 --- a/.forge-snapshots/BinSwapRouterTest#testExactInputSingle_DifferentRecipient.snap +++ b/.forge-snapshots/BinSwapRouterTest#testExactInputSingle_DifferentRecipient.snap @@ -1 +1 @@ -138752 \ No newline at end of file +139491 \ No newline at end of file diff --git a/.forge-snapshots/BinSwapRouterTest#testExactInputSingle_EthPool_SwapEthForToken.snap b/.forge-snapshots/BinSwapRouterTest#testExactInputSingle_EthPool_SwapEthForToken.snap index 83e1219..08fe6d7 100644 --- a/.forge-snapshots/BinSwapRouterTest#testExactInputSingle_EthPool_SwapEthForToken.snap +++ b/.forge-snapshots/BinSwapRouterTest#testExactInputSingle_EthPool_SwapEthForToken.snap @@ -1 +1 @@ -131484 \ No newline at end of file +132217 \ No newline at end of file diff --git a/.forge-snapshots/BinSwapRouterTest#testExactInputSingle_EthPool_SwapTokenForEth.snap b/.forge-snapshots/BinSwapRouterTest#testExactInputSingle_EthPool_SwapTokenForEth.snap index 3b92e4c..c2823a1 100644 --- a/.forge-snapshots/BinSwapRouterTest#testExactInputSingle_EthPool_SwapTokenForEth.snap +++ b/.forge-snapshots/BinSwapRouterTest#testExactInputSingle_EthPool_SwapTokenForEth.snap @@ -1 +1 @@ -136297 \ No newline at end of file +137030 \ No newline at end of file diff --git a/.forge-snapshots/BinSwapRouterTest#testExactInputSingle_SwapForY_1.snap b/.forge-snapshots/BinSwapRouterTest#testExactInputSingle_SwapForY_1.snap index 25487f4..770caaf 100644 --- a/.forge-snapshots/BinSwapRouterTest#testExactInputSingle_SwapForY_1.snap +++ b/.forge-snapshots/BinSwapRouterTest#testExactInputSingle_SwapForY_1.snap @@ -1 +1 @@ -132258 \ No newline at end of file +132997 \ No newline at end of file diff --git a/.forge-snapshots/BinSwapRouterTest#testExactInputSingle_SwapForY_2.snap b/.forge-snapshots/BinSwapRouterTest#testExactInputSingle_SwapForY_2.snap index 1c2c5d6..2b75ed0 100644 --- a/.forge-snapshots/BinSwapRouterTest#testExactInputSingle_SwapForY_2.snap +++ b/.forge-snapshots/BinSwapRouterTest#testExactInputSingle_SwapForY_2.snap @@ -1 +1 @@ -132333 \ No newline at end of file +133072 \ No newline at end of file diff --git a/.forge-snapshots/BinSwapRouterTest#testExactInput_MultiHopDifferentRecipient.snap b/.forge-snapshots/BinSwapRouterTest#testExactInput_MultiHopDifferentRecipient.snap index 56e73ca..d329a65 100644 --- a/.forge-snapshots/BinSwapRouterTest#testExactInput_MultiHopDifferentRecipient.snap +++ b/.forge-snapshots/BinSwapRouterTest#testExactInput_MultiHopDifferentRecipient.snap @@ -1 +1 @@ -167590 \ No newline at end of file +168327 \ No newline at end of file diff --git a/.forge-snapshots/BinSwapRouterTest#testExactOutputSingle_DifferentRecipient.snap b/.forge-snapshots/BinSwapRouterTest#testExactOutputSingle_DifferentRecipient.snap index c7af699..b0f96a7 100644 --- a/.forge-snapshots/BinSwapRouterTest#testExactOutputSingle_DifferentRecipient.snap +++ b/.forge-snapshots/BinSwapRouterTest#testExactOutputSingle_DifferentRecipient.snap @@ -1 +1 @@ -144076 \ No newline at end of file +144815 \ No newline at end of file diff --git a/.forge-snapshots/BinSwapRouterTest#testExactOutputSingle_SwapForY_1.snap b/.forge-snapshots/BinSwapRouterTest#testExactOutputSingle_SwapForY_1.snap index 5229aeb..0a85f16 100644 --- a/.forge-snapshots/BinSwapRouterTest#testExactOutputSingle_SwapForY_1.snap +++ b/.forge-snapshots/BinSwapRouterTest#testExactOutputSingle_SwapForY_1.snap @@ -1 +1 @@ -137582 \ No newline at end of file +138321 \ No newline at end of file diff --git a/.forge-snapshots/BinSwapRouterTest#testExactOutputSingle_SwapForY_2.snap b/.forge-snapshots/BinSwapRouterTest#testExactOutputSingle_SwapForY_2.snap index 6a89ac0..2d25ced 100644 --- a/.forge-snapshots/BinSwapRouterTest#testExactOutputSingle_SwapForY_2.snap +++ b/.forge-snapshots/BinSwapRouterTest#testExactOutputSingle_SwapForY_2.snap @@ -1 +1 @@ -137669 \ No newline at end of file +138408 \ No newline at end of file diff --git a/.forge-snapshots/BinSwapRouterTest#testExactOutput_MultiHopDifferentRecipient.snap b/.forge-snapshots/BinSwapRouterTest#testExactOutput_MultiHopDifferentRecipient.snap index 99b08db..87ede0d 100644 --- a/.forge-snapshots/BinSwapRouterTest#testExactOutput_MultiHopDifferentRecipient.snap +++ b/.forge-snapshots/BinSwapRouterTest#testExactOutput_MultiHopDifferentRecipient.snap @@ -1 +1 @@ -172111 \ No newline at end of file +172844 \ No newline at end of file diff --git a/.forge-snapshots/BinSwapRouterTest#testExactOutput_SingleHop.snap b/.forge-snapshots/BinSwapRouterTest#testExactOutput_SingleHop.snap index 6f0720b..625b341 100644 --- a/.forge-snapshots/BinSwapRouterTest#testExactOutput_SingleHop.snap +++ b/.forge-snapshots/BinSwapRouterTest#testExactOutput_SingleHop.snap @@ -1 +1 @@ -130040 \ No newline at end of file +130773 \ No newline at end of file diff --git a/.forge-snapshots/CLSwapRouterTest#ExactInput.snap b/.forge-snapshots/CLSwapRouterTest#ExactInput.snap index 282df43..db307b1 100644 --- a/.forge-snapshots/CLSwapRouterTest#ExactInput.snap +++ b/.forge-snapshots/CLSwapRouterTest#ExactInput.snap @@ -1 +1 @@ -250131 \ No newline at end of file +250868 \ No newline at end of file diff --git a/.forge-snapshots/CLSwapRouterTest#ExactInputSingle.snap b/.forge-snapshots/CLSwapRouterTest#ExactInputSingle.snap index 9bb7eff..7486ea9 100644 --- a/.forge-snapshots/CLSwapRouterTest#ExactInputSingle.snap +++ b/.forge-snapshots/CLSwapRouterTest#ExactInputSingle.snap @@ -1 +1 @@ -180878 \ No newline at end of file +181611 \ No newline at end of file diff --git a/.forge-snapshots/CLSwapRouterTest#ExactOutput.snap b/.forge-snapshots/CLSwapRouterTest#ExactOutput.snap index ec89d67..94328a6 100644 --- a/.forge-snapshots/CLSwapRouterTest#ExactOutput.snap +++ b/.forge-snapshots/CLSwapRouterTest#ExactOutput.snap @@ -1 +1 @@ -256134 \ No newline at end of file +256867 \ No newline at end of file diff --git a/.forge-snapshots/CLSwapRouterTest#ExactOutputSingle.snap b/.forge-snapshots/CLSwapRouterTest#ExactOutputSingle.snap index c80c086..07848a8 100644 --- a/.forge-snapshots/CLSwapRouterTest#ExactOutputSingle.snap +++ b/.forge-snapshots/CLSwapRouterTest#ExactOutputSingle.snap @@ -1 +1 @@ -180439 \ No newline at end of file +181172 \ No newline at end of file diff --git a/.forge-snapshots/NonfungiblePositionManager#collect.snap b/.forge-snapshots/NonfungiblePositionManager#collect.snap index e0baa67..971e5bb 100644 --- a/.forge-snapshots/NonfungiblePositionManager#collect.snap +++ b/.forge-snapshots/NonfungiblePositionManager#collect.snap @@ -1 +1 @@ -199252 \ No newline at end of file +199079 \ No newline at end of file diff --git a/.forge-snapshots/NonfungiblePositionManager#decreaseLiquidity.snap b/.forge-snapshots/NonfungiblePositionManager#decreaseLiquidity.snap index 46bd735..ad94799 100644 --- a/.forge-snapshots/NonfungiblePositionManager#decreaseLiquidity.snap +++ b/.forge-snapshots/NonfungiblePositionManager#decreaseLiquidity.snap @@ -1 +1 @@ -75212 \ No newline at end of file +75144 \ No newline at end of file diff --git a/.forge-snapshots/NonfungiblePositionManager#increaseLiquidity.snap b/.forge-snapshots/NonfungiblePositionManager#increaseLiquidity.snap index 05044df..342683f 100644 --- a/.forge-snapshots/NonfungiblePositionManager#increaseLiquidity.snap +++ b/.forge-snapshots/NonfungiblePositionManager#increaseLiquidity.snap @@ -1 +1 @@ -80908 \ No newline at end of file +82440 \ No newline at end of file diff --git a/.forge-snapshots/NonfungiblePositionManager#mint.snap b/.forge-snapshots/NonfungiblePositionManager#mint.snap index e3830e7..be92d9f 100644 --- a/.forge-snapshots/NonfungiblePositionManager#mint.snap +++ b/.forge-snapshots/NonfungiblePositionManager#mint.snap @@ -1 +1 @@ -624939 \ No newline at end of file +626477 \ No newline at end of file diff --git a/lib/forge-std b/lib/forge-std index 2f11269..bb4ceea 160000 --- a/lib/forge-std +++ b/lib/forge-std @@ -1 +1 @@ -Subproject commit 2f112697506eab12d433a65fdc31a639548fe365 +Subproject commit bb4ceea94d6f10eeb5b41dc2391c6c8bf8e734ef diff --git a/lib/openzeppelin-contracts b/lib/openzeppelin-contracts index 9329cfa..dc44c9f 160000 --- a/lib/openzeppelin-contracts +++ b/lib/openzeppelin-contracts @@ -1 +1 @@ -Subproject commit 9329cfacd4c7d20bcb43d772d947ff9e39b65df9 +Subproject commit dc44c9f1a4c3b10af99492eed84f83ed244203f6 diff --git a/lib/pancake-v4-core b/lib/pancake-v4-core index 4d2c745..7dbd664 160000 --- a/lib/pancake-v4-core +++ b/lib/pancake-v4-core @@ -1 +1 @@ -Subproject commit 4d2c74569471bbc3e70c24c933d908ef3d2acbbb +Subproject commit 7dbd664d1d6025668774bd170bd1de3bdf2141ef diff --git a/lib/solmate b/lib/solmate index e0e9ff0..c892309 160000 --- a/lib/solmate +++ b/lib/solmate @@ -1 +1 @@ -Subproject commit e0e9ff05d8aa5c7c48465511f85a6efdf5d5c30d +Subproject commit c892309933b25c03d32b1b0d674df7ae292ba925 diff --git a/package.json b/package.json index 9568ff3..fbfce70 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "test": "forge test", "dev": "forge test -vvv -w", "snapshot": "rm -fr .forge-snapshots && forge test", - "prettier": "forge fmt contracts/ && forge fmt test/ && forge fmt script/", + "prettier": "forge fmt src/ && forge fmt test/ && forge fmt script/", "prettier-check": "forge fmt --check" } } diff --git a/src/SwapRouterBase.sol b/src/SwapRouterBase.sol index a689b44..80bc67e 100644 --- a/src/SwapRouterBase.sol +++ b/src/SwapRouterBase.sol @@ -40,7 +40,7 @@ abstract contract SwapRouterBase is ISwapRouterBase { function _payAndSettle(Currency currency, address msgSender, int128 settleAmount) internal virtual { _pay(currency, msgSender, address(vault), uint256(uint128(settleAmount))); - vault.settle(currency); + vault.settleAndMintRefund(currency, msgSender); } function _pay(Currency currency, address payer, address recipient, uint256 amount) internal virtual; diff --git a/src/pool-bin/BinFungiblePositionManager.sol b/src/pool-bin/BinFungiblePositionManager.sol index 2f7c1ea..a24f53b 100644 --- a/src/pool-bin/BinFungiblePositionManager.sol +++ b/src/pool-bin/BinFungiblePositionManager.sol @@ -227,14 +227,14 @@ contract BinFungiblePositionManager is function _settleDeltas(address user, PoolKey memory poolKey, BalanceDelta delta) internal { if (delta.amount0() > 0) { pay(poolKey.currency0, user, address(vault), uint256(int256(delta.amount0()))); - vault.settle(poolKey.currency0); + vault.settleAndMintRefund(poolKey.currency0, user); } else if (delta.amount0() < 0) { vault.take(poolKey.currency0, user, uint128(-delta.amount0())); } if (delta.amount1() > 0) { pay(poolKey.currency1, user, address(vault), uint256(int256(delta.amount1()))); - vault.settle(poolKey.currency1); + vault.settleAndMintRefund(poolKey.currency1, user); } else if (delta.amount1() < 0) { vault.take(poolKey.currency1, user, uint128(-delta.amount1())); } diff --git a/src/pool-cl/base/LiquidityManagement.sol b/src/pool-cl/base/LiquidityManagement.sol index 9d34c57..b4dd4bb 100644 --- a/src/pool-cl/base/LiquidityManagement.sol +++ b/src/pool-cl/base/LiquidityManagement.sol @@ -113,13 +113,13 @@ abstract contract LiquidityManagement is CLPeripheryImmutableState, PeripheryPay function settleDeltas(address sender, PoolKey memory poolKey, BalanceDelta delta) internal { if (delta.amount0() > 0) { pay(poolKey.currency0, sender, address(vault), uint256(int256(delta.amount0()))); - vault.settle(poolKey.currency0); + vault.settleAndMintRefund(poolKey.currency0, sender); } else if (delta.amount0() < 0) { vault.take(poolKey.currency0, sender, uint128(-delta.amount0())); } if (delta.amount1() > 0) { pay(poolKey.currency1, sender, address(vault), uint256(int256(delta.amount1()))); - vault.settle(poolKey.currency1); + vault.settleAndMintRefund(poolKey.currency1, sender); } else if (delta.amount1() < 0) { vault.take(poolKey.currency1, sender, uint128(-delta.amount1())); } diff --git a/test/pool-bin/BinFungiblePositionManager_AddLiquidity.t.sol b/test/pool-bin/BinFungiblePositionManager_AddLiquidity.t.sol index da26689..b7f0df8 100644 --- a/test/pool-bin/BinFungiblePositionManager_AddLiquidity.t.sol +++ b/test/pool-bin/BinFungiblePositionManager_AddLiquidity.t.sol @@ -538,4 +538,41 @@ contract BinFungiblePositionManager_AddLiquidityTest is Test, GasSnapshot, Liqui deadline: block.timestamp + 600 }); } + + function testSettleAndMintRefund() public { + // transfer excess token to vault + uint256 excessTokenAmount = 1 ether; + address hacker = address(1); + token0.mint(hacker, excessTokenAmount); + vm.startPrank(hacker); + token0.transfer(address(vault), excessTokenAmount); + vm.stopPrank(); + + // pre-test, verify alice has 1e18 token0 and token1 + token0.mint(alice, 1 ether); + token1.mint(alice, 1 ether); + assertEq(token0.balanceOf(alice), 1 ether); + assertEq(token1.balanceOf(alice), 1 ether); + + vm.startPrank(alice); + uint24[] memory binIds = getBinIds(activeId, 3); + IBinFungiblePositionManager.AddLiquidityParams memory params; + params = _getAddParams(key1, binIds, 1 ether, 1 ether, activeId, alice); + (,, uint256[] memory tokenIds,) = binFungiblePositionManager.addLiquidity(params); + + for (uint256 i; i < tokenIds.length; i++) { + (Currency curr0, Currency curr1, uint24 fee, uint24 binId) = + binFungiblePositionManager.positions(tokenIds[i]); + assertEq(Currency.unwrap(curr0), Currency.unwrap(key1.currency0)); + assertEq(Currency.unwrap(curr1), Currency.unwrap(key1.currency1)); + assertEq(fee, key1.fee); + assertEq(binId, binIds[i]); + } + + // check currency balance in vault + { + uint256 currency0Balance = vault.balanceOf(alice, currency0); + assertEq(currency0Balance, excessTokenAmount, "Unexpected currency0 balance in vault"); + } + } } diff --git a/test/pool-bin/BinSwapRouter.t.sol b/test/pool-bin/BinSwapRouter.t.sol index 7749e89..cf8a07a 100644 --- a/test/pool-bin/BinSwapRouter.t.sol +++ b/test/pool-bin/BinSwapRouter.t.sol @@ -766,6 +766,47 @@ contract BinSwapRouterTest is Test, GasSnapshot, LiquidityParamsHelper { assertEq(token0.balanceOf(alice), abi.decode(result[0], (uint256))); } + function testSettleAndMintRefund() public { + // transfer excess token to vault + uint256 excessTokenAmount = 1 ether; + address hacker = address(1); + token0.mint(hacker, excessTokenAmount); + vm.startPrank(hacker); + token0.transfer(address(vault), excessTokenAmount); + vm.stopPrank(); + + vm.startPrank(alice); + token0.mint(alice, 1 ether); + + ISwapRouterBase.PathKey[] memory path = new ISwapRouterBase.PathKey[](1); + path[0] = ISwapRouterBase.PathKey({ + intermediateCurrency: Currency.wrap(address(token1)), + fee: key.fee, + hooks: key.hooks, + hookData: new bytes(0), + poolManager: key.poolManager, + parameters: key.parameters + }); + + uint256 amountOut = router.exactInput( + IBinSwapRouterBase.V4BinExactInputParams({ + currencyIn: Currency.wrap(address(token0)), + path: path, + recipient: alice, + amountIn: 1 ether, + amountOutMinimum: 0 + }), + block.timestamp + 60 + ); + assertEq(token1.balanceOf(alice), amountOut); + + // check currency balance in vault + { + uint256 currency0Balance = vault.balanceOf(alice, Currency.wrap(address(token0))); + assertEq(currency0Balance, excessTokenAmount, "Unexpected currency0 balance in vault"); + } + } + // function testExactOutput_InsufficientAmountOut() public { // //todo: in order to simulate this error, require // // 1. hooks at beforeSwap do something funny on the pool resulting in actual amountOut lesser diff --git a/test/pool-cl/CLSwapRouter.t.sol b/test/pool-cl/CLSwapRouter.t.sol index f95b64b..0604313 100644 --- a/test/pool-cl/CLSwapRouter.t.sol +++ b/test/pool-cl/CLSwapRouter.t.sol @@ -689,6 +689,40 @@ contract CLSwapRouterTest is TokenFixture, Test, GasSnapshot { snapEnd(); } + function testSettleAndMintRefund() external { + // transfer excess token to vault + uint256 excessTokenAmount = 1 ether; + address hacker = address(1); + MockERC20(Currency.unwrap(currency0)).mint(hacker, excessTokenAmount); + vm.startPrank(hacker); + MockERC20(Currency.unwrap(currency0)).transfer(address(vault), excessTokenAmount); + vm.stopPrank(); + + uint256 amountOut = router.exactInputSingle( + ICLSwapRouterBase.V4CLExactInputSingleParams({ + poolKey: poolKey0, + zeroForOne: true, + recipient: makeAddr("recipient"), + amountIn: 0.01 ether, + amountOutMinimum: 0, + sqrtPriceLimitX96: 0, + hookData: new bytes(0) + }), + block.timestamp + 100 + ); + + uint256 received = IERC20(Currency.unwrap(currency1)).balanceOf(makeAddr("recipient")); + assertEq(received, amountOut); + // considering slippage and fee, tolerance is 1% + assertApproxEqAbs(amountOut, 1 ether, amountOut / 100); + + // check currency balance in vault + { + uint256 currency0Balance = vault.balanceOf(address(this), currency0); + assertEq(currency0Balance, excessTokenAmount, "Unexpected currency0 balance in vault"); + } + } + // allow refund of ETH receive() external payable {} } diff --git a/test/pool-cl/NonFungiblePositionManager.t.sol b/test/pool-cl/NonFungiblePositionManager.t.sol index c6afb61..8989238 100644 --- a/test/pool-cl/NonFungiblePositionManager.t.sol +++ b/test/pool-cl/NonFungiblePositionManager.t.sol @@ -2624,4 +2624,98 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { assertEq(tokenOwed1, 1000000000000000000, "Unexpected tokenOwed1"); } } + + function testSettleAndMintRefund() external { + PoolKey memory key = PoolKey({ + currency0: currency0, + currency1: currency1, + hooks: IHooks(address(0)), + poolManager: poolManager, + fee: uint24(3000), + // 0 ~ 15 hookRegistrationMap = nil + // 16 ~ 24 tickSpacing = 1 + parameters: bytes32(uint256(0x10000)) + }); + + int24 tickLower = 46053; + int24 tickUpper = 46055; + uint256 amount0Desired = 1 ether; + uint256 amount1Desired = 2 ether; + + // transfer excess token to vault + uint256 excessTokenAmount = 1 ether; + IERC20(Currency.unwrap(currency0)).transfer(address(vault), excessTokenAmount); + + { + uint160 sqrtPriceX96 = uint160(10 * FixedPoint96.Q96); + poolManager.initialize(key, sqrtPriceX96, new bytes(0)); + + uint128 liquidityExpected = LiquidityAmounts.getLiquidityForAmounts( + sqrtPriceX96, + TickMath.getSqrtRatioAtTick(tickLower), + TickMath.getSqrtRatioAtTick(tickUpper), + amount0Desired, + amount1Desired + ); + + vm.expectEmit(true, true, true, false); + emit IncreaseLiquidity(1, liquidityExpected, 0, 0); + } + + (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1) = nonfungiblePoolManager.mint( + INonfungiblePositionManager.MintParams({ + poolKey: key, + tickLower: tickLower, + tickUpper: tickUpper, + amount0Desired: amount0Desired, + amount1Desired: amount1Desired, + amount0Min: 0, + amount1Min: 0, + recipient: address(this), + deadline: type(uint256).max + }) + ); + + // token consumed + { + uint256 token0Left = IERC20(Currency.unwrap(currency0)).balanceOf(address(this)); + uint256 token1Left = IERC20(Currency.unwrap(currency1)).balanceOf(address(this)); + // 1.982e16 roughly 0.02 ether, need to add 1 ether which is unexpected currency0 amount + assertEq(1000 ether - token0Left - excessTokenAmount, 19824513708386292, "Unexpected currency0 consumed"); + assertEq(amount0, 19824513708386292, "Actual consumed currency0 mismatch"); + // 1e18 i.e. 2 ether, make sense because price is 100 + assertEq(1000 ether - token1Left, 2000000000000000000, "Unexpected currency1 consumed"); + assertEq(amount1, 2000000000000000000, "Actual consumed currency1 mismatch"); + } + + // check currency balance in vault + { + uint256 currency0Balance = vault.balanceOf(address(this), currency0); + assertEq(currency0Balance, excessTokenAmount, "Unexpected currency0 balance in vault"); + } + + // tick lower and tick upper + { + (,,,,, int24 _tickLower, int24 _tickUpper,,,,,) = nonfungiblePoolManager.positions(1); + assertEq(_tickLower, tickLower, "Unexpected tickLower"); + assertEq(_tickUpper, tickUpper, "Unexpected tickUpper"); + } + + // token id starts from 1 + assertEq(tokenId, 1, "Unexpected tokenId"); + + assertEq(liquidity, 3982750054135827175977, "Liquidity from mint and liquidity from raw calculation mismatch"); + assertEq(poolManager.getLiquidity(key.toId()), 3982750054135827175977, "Unexpected liquidity for the pool"); + assertEq( + poolManager.getLiquidity(key.toId(), address(nonfungiblePoolManager), 46053, 46055), + 3982750054135827175977, + "Unexpected liquidity for current position" + ); + + assertEq( + nonfungiblePoolManager.balanceOf(address(this)), 1, "Unexpected balance of the position owner after mint" + ); + + assertEq(nonfungiblePoolManager.ownerOf(tokenId), address(this), "Unexpected owner of the position"); + } }