diff --git a/script/06_DeployCLMigrator.s.sol b/script/06_DeployCLMigrator.s.sol deleted file mode 100644 index a0feeb8..0000000 --- a/script/06_DeployCLMigrator.s.sol +++ /dev/null @@ -1,57 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.24; - -import "forge-std/Script.sol"; -import {BaseScript} from "./BaseScript.sol"; -import {IVault} from "infinity-core/src/interfaces/IVault.sol"; -import {IAllowanceTransfer} from "permit2/src/interfaces/IAllowanceTransfer.sol"; -import {CLMigrator} from "../src/pool-cl/CLMigrator.sol"; -import {Create3Factory} from "pancake-create3-factory/src/Create3Factory.sol"; -import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; - -/** - * Pre-req: foundry on stable (1.0) otherwise verify will fail: ref https://github.com/foundry-rs/foundry/issues/9698 - * - * Step 1: Deploy - * forge script script/06_DeployCLMigrator.s.sol:DeployCLMigratorScript -vvv \ - * --rpc-url $RPC_URL \ - * --broadcast \ - * --slow \ - * --verify - */ -contract DeployCLMigratorScript is BaseScript { - function getDeploymentSalt() public pure override returns (bytes32) { - return keccak256("INFINITY-PERIPHERY/CLMigrator/1.0.0"); - } - - function run() public { - Create3Factory factory = Create3Factory(getAddressFromConfig("create3Factory")); - - uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - vm.startBroadcast(deployerPrivateKey); - - address weth = getAddressFromConfig("weth"); - emit log_named_address("WETH", weth); - - address clPositionManager = getAddressFromConfig("clPositionManager"); - emit log_named_address("CLPositionManager", clPositionManager); - - address permit2 = getAddressFromConfig("permit2"); - emit log_named_address("Permit2", permit2); - - /// @dev prepare the payload to transfer ownership from deployer to real owner - bytes memory afterDeploymentExecutionPayload = - abi.encodeWithSelector(Ownable.transferOwnership.selector, getAddressFromConfig("owner")); - - bytes memory creationCode = abi.encodePacked( - type(CLMigrator).creationCode, abi.encode(weth, clPositionManager, IAllowanceTransfer(permit2)) - ); - address clMigrator = factory.deploy( - getDeploymentSalt(), creationCode, keccak256(creationCode), 0, afterDeploymentExecutionPayload, 0 - ); - - emit log_named_address("CLMigrator", address(clMigrator)); - - vm.stopBroadcast(); - } -} diff --git a/script/07_DeployBinMigrator.s.sol b/script/07_DeployBinMigrator.s.sol deleted file mode 100644 index c9a8efb..0000000 --- a/script/07_DeployBinMigrator.s.sol +++ /dev/null @@ -1,57 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.24; - -import "forge-std/Script.sol"; -import {BaseScript} from "./BaseScript.sol"; -import {IVault} from "infinity-core/src/interfaces/IVault.sol"; -import {IAllowanceTransfer} from "permit2/src/interfaces/IAllowanceTransfer.sol"; -import {BinMigrator} from "../src/pool-bin/BinMigrator.sol"; -import {Create3Factory} from "pancake-create3-factory/src/Create3Factory.sol"; -import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; - -/** - * Pre-req: foundry on stable (1.0) otherwise verify will fail: ref https://github.com/foundry-rs/foundry/issues/9698 - * - * Step 1: Deploy - * forge script script/07_DeployBinMigrator.s.sol:DeployBinMigratorScript -vvv \ - * --rpc-url $RPC_URL \ - * --broadcast \ - * --slow \ - * --verify - */ -contract DeployBinMigratorScript is BaseScript { - function getDeploymentSalt() public pure override returns (bytes32) { - return keccak256("INFINITY-PERIPHERY/BinMigrator/1.0.0"); - } - - function run() public { - Create3Factory factory = Create3Factory(getAddressFromConfig("create3Factory")); - - uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - vm.startBroadcast(deployerPrivateKey); - - address weth = getAddressFromConfig("weth"); - emit log_named_address("WETH", weth); - - address binPositionManager = getAddressFromConfig("binPositionManager"); - emit log_named_address("BinPositionManager", binPositionManager); - - address permit2 = getAddressFromConfig("permit2"); - emit log_named_address("Permit2", permit2); - - /// @dev prepare the payload to transfer ownership from deployer to real owner - bytes memory afterDeploymentExecutionPayload = - abi.encodeWithSelector(Ownable.transferOwnership.selector, getAddressFromConfig("owner")); - - bytes memory creationCode = abi.encodePacked( - type(BinMigrator).creationCode, abi.encode(weth, binPositionManager, IAllowanceTransfer(permit2)) - ); - address binMigrator = factory.deploy( - getDeploymentSalt(), creationCode, keccak256(creationCode), 0, afterDeploymentExecutionPayload, 0 - ); - - emit log_named_address("BinMigrator", address(binMigrator)); - - vm.stopBroadcast(); - } -} diff --git a/script/config/base-mainnet.json b/script/config/base-mainnet.json index 8fbdc3e..fcbb4e2 100644 --- a/script/config/base-mainnet.json +++ b/script/config/base-mainnet.json @@ -13,8 +13,6 @@ "binPositionManager": "0x3D311D6283Dd8aB90bb0031835C8e606349e2850", "clQuoter": "0xd0737C9762912dD34c3271197E362Aa736Df0926", "binQuoter": "0xc631f4b0fc2dd68ad45f74b2942628db117dd359", - "clMigrator": "0xF1A6B9b2b4Aa5623a490E2CF2E23B265334478e1", - "binMigrator": "0x0bcf19dFB2B776d8c8F9fA79A9d57c00fbA9557d", "factoryV3": "0x0BFbCF9fa4f9C56B0F40a671Ad40E0805A091865", "factoryV2": "0x02a84c1b3BBD7401a5f7fa98a384EBC70bB5749E", "factoryStable": "0x64D74e1EAAe3176744b5767b93B7Bee39Cf7898F", diff --git a/script/config/bsc-mainnet.json b/script/config/bsc-mainnet.json index 4da193d..cbea782 100644 --- a/script/config/bsc-mainnet.json +++ b/script/config/bsc-mainnet.json @@ -13,8 +13,6 @@ "binPositionManager": "0x3D311D6283Dd8aB90bb0031835C8e606349e2850", "clQuoter": "0xd0737C9762912dD34c3271197E362Aa736Df0926", "binQuoter": "0xC631f4B0Fc2Dd68AD45f74B2942628db117dD359", - "clMigrator": "0xF1A6B9b2b4Aa5623a490E2CF2E23B265334478e1", - "binMigrator": "0x0bcf19dFB2B776d8c8F9fA79A9d57c00fbA9557d", "factoryV3": "0x0BFbCF9fa4f9C56B0F40a671Ad40E0805A091865", "factoryV2": "0xcA143Ce32Fe78f1f7019d7d551a6402fC5350c73", "factoryStable": "0x25a55f9f2279A54951133D503490342b50E5cd15", diff --git a/script/config/bsc-testnet.json b/script/config/bsc-testnet.json index c932ae0..297190c 100644 --- a/script/config/bsc-testnet.json +++ b/script/config/bsc-testnet.json @@ -13,8 +13,6 @@ "binPositionManager": "0x68B834232da911c787bcF782CED84ec5d36909a7", "clQuoter": "0x5d544D0ad627a72d7Fb53c22D8888663FC5d5B0d", "binQuoter": "0x82E7741E3DE763692785cfDB536D168B1226c4d5", - "clMigrator": "0x8637035016cbF8E38519c9A1362b1fDDcA1c1A91.", - "binMigrator": "0x3bA139617F8318ca7638b2470DA660653FB5F486", "factoryV3": "0x0BFbCF9fa4f9C56B0F40a671Ad40E0805A091865", "factoryV2": "0x6725F303b657a9451d8BA641348b6761A6CC7a17", "factoryStable": "0xe6A00f8b819244e8Ab9Ea930e46449C2F20B6609", diff --git a/script/config/ethereum-mainnet.json b/script/config/ethereum-mainnet.json index 623f7ca..0dc118e 100755 --- a/script/config/ethereum-mainnet.json +++ b/script/config/ethereum-mainnet.json @@ -11,8 +11,6 @@ "binPositionManager": "0x", "clQuoter:": "0x", "binQuoter": "0x", - "clMigrator": "0x", - "binMigrator": "0x", "clTickLens": "0x", "binPositionManagerHelper": "0x" } diff --git a/script/config/ethereum-sepolia.json b/script/config/ethereum-sepolia.json index 41cb99d..98b72fd 100755 --- a/script/config/ethereum-sepolia.json +++ b/script/config/ethereum-sepolia.json @@ -11,8 +11,6 @@ "binPositionManager": "0x21015eF9927e06b7Fc19D986A214e449Aa22FF7d", "clQuoter": "0x6B71bA938100FD313Be08E680639900E0cfE3d74", "binQuoter": "0x4DcaF8f6040B12a2C6149956Cc2e36FA3b7a60b9", - "clMigrator": "0x32D31C2020868057E0ba5876c1710962978d3EC3", - "binMigrator": "0x1A9A9e622dB78075fED5Cf77F382aA74CB657517.", "factoryV3": "0x0BFbCF9fa4f9C56B0F40a671Ad40E0805A091865", "factoryV2": "0x1bdc540dEB9Ed1fA29964DeEcCc524A8f5e2198e", "factoryStable": "0x000000000000000000000000000000000000dEaD", diff --git a/snapshots/BinMigratorFromPancakeswapV2Test.json b/snapshots/BinMigratorFromPancakeswapV2Test.json deleted file mode 100644 index 6486edf..0000000 --- a/snapshots/BinMigratorFromPancakeswapV2Test.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "testMigrateFromV2IncludingInit": "1176442", - "testMigrateFromV2WithoutInit": "1046490", - "testMigrateFromV2WithoutNativeToken": "1087310" -} \ No newline at end of file diff --git a/snapshots/BinMigratorFromPancakeswapV3Test.json b/snapshots/BinMigratorFromPancakeswapV3Test.json deleted file mode 100644 index 1565d59..0000000 --- a/snapshots/BinMigratorFromPancakeswapV3Test.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "BinMigratorBytecode size": "15187", - "testMigrateFromV3IncludingInit": "1248658", - "testMigrateFromV3WithoutInit": "1118687", - "testMigrateFromV3WithoutNativeToken": "1153511" -} \ No newline at end of file diff --git a/snapshots/BinMigratorFromUniswapV2Test.json b/snapshots/BinMigratorFromUniswapV2Test.json deleted file mode 100644 index 6486edf..0000000 --- a/snapshots/BinMigratorFromUniswapV2Test.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "testMigrateFromV2IncludingInit": "1176442", - "testMigrateFromV2WithoutInit": "1046490", - "testMigrateFromV2WithoutNativeToken": "1087310" -} \ No newline at end of file diff --git a/snapshots/BinMigratorFromUniswapV3Test.json b/snapshots/BinMigratorFromUniswapV3Test.json deleted file mode 100644 index 51ba712..0000000 --- a/snapshots/BinMigratorFromUniswapV3Test.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "BinMigratorBytecode size": "15187", - "testMigrateFromV3IncludingInit": "1246640", - "testMigrateFromV3WithoutInit": "1116669", - "testMigrateFromV3WithoutNativeToken": "1151493" -} \ No newline at end of file diff --git a/snapshots/BinPositionManagerHelperTest.json b/snapshots/BinPositionManagerHelperTest.json index 2ce0891..0c389f3 100644 --- a/snapshots/BinPositionManagerHelperTest.json +++ b/snapshots/BinPositionManagerHelperTest.json @@ -1,8 +1,8 @@ { "BinPositionManagerHelper size": "8702", - "test_addLiquidities_existingPool": "1044057", - "test_addLiquidities_existingPool_bobReceiver": "1044057", - "test_addLiquidities_existingPool_nativeToken": "962326", - "test_addLiquidities_newPool": "1168114", - "test_addLiquidities_newPool_WithPermit": "1227164" + "test_addLiquidities_existingPool": "1049529", + "test_addLiquidities_existingPool_bobReceiver": "1049529", + "test_addLiquidities_existingPool_nativeToken": "955490", + "test_addLiquidities_newPool": "1176746", + "test_addLiquidities_newPool_WithPermit": "1247500" } \ No newline at end of file diff --git a/snapshots/CLMigratorFromPancakeswapV2Test.json b/snapshots/CLMigratorFromPancakeswapV2Test.json deleted file mode 100644 index bbf53fb..0000000 --- a/snapshots/CLMigratorFromPancakeswapV2Test.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "testCLMigrateFromV2IncludingInit": "789904", - "testCLMigrateFromV2WithoutInit": "658970", - "testCLMigrateFromV2WithoutNativeToken": "729264" -} \ No newline at end of file diff --git a/snapshots/CLMigratorFromPancakeswapV3Test.json b/snapshots/CLMigratorFromPancakeswapV3Test.json deleted file mode 100644 index a3c5130..0000000 --- a/snapshots/CLMigratorFromPancakeswapV3Test.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "CLMigratorBytecode size": "13782", - "testCLMigrateFromV3IncludingInit": "839793", - "testCLMigrateFromV3WithoutInit": "708840", - "testCLMigrateFromV3WithoutNativeToken": "752181" -} \ No newline at end of file diff --git a/snapshots/CLMigratorFromUniswapV2Test.json b/snapshots/CLMigratorFromUniswapV2Test.json deleted file mode 100644 index d5fd9eb..0000000 --- a/snapshots/CLMigratorFromUniswapV2Test.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "testCLMigrateFromV2IncludingInit": "789916", - "testCLMigrateFromV2WithoutInit": "658982", - "testCLMigrateFromV2WithoutNativeToken": "729264" -} \ No newline at end of file diff --git a/snapshots/CLMigratorFromUniswapV3Test.json b/snapshots/CLMigratorFromUniswapV3Test.json deleted file mode 100644 index 8377ef5..0000000 --- a/snapshots/CLMigratorFromUniswapV3Test.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "CLMigratorBytecode size": "13782", - "testCLMigrateFromV3IncludingInit": "837775", - "testCLMigrateFromV3WithoutInit": "706822", - "testCLMigrateFromV3WithoutNativeToken": "750163" -} \ No newline at end of file diff --git a/src/MixedQuoter.sol b/src/MixedQuoter.sol index 98fb971..39b8d63 100644 --- a/src/MixedQuoter.sol +++ b/src/MixedQuoter.sol @@ -65,11 +65,7 @@ contract MixedQuoter is IMixedQuoter, IPancakeV3SwapCallback, Multicall { */ /// @inheritdoc IPancakeV3SwapCallback - function pancakeV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes memory data) - external - view - override - { + function pancakeV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes memory data) external view override { require(amount0Delta > 0 || amount1Delta > 0); // swaps entirely within 0-liquidity regions are not supported (address tokenIn, address tokenOut, uint24 fee) = abi.decode(data, (address, address, uint24)); V3SmartRouterHelper.verifyCallback(factoryV3, tokenIn, tokenOut, fee); @@ -144,7 +140,8 @@ contract MixedQuoter is IMixedQuoter, IPancakeV3SwapCallback, Multicall { ? (zeroForOne ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1) : params.sqrtPriceLimitX96, abi.encode(params.tokenIn, params.tokenOut, params.fee) - ) {} catch (bytes memory reason) { + ) {} + catch (bytes memory reason) { gasEstimate = gasBefore - gasleft(); return handleV3Revert(reason, pool, gasEstimate); } @@ -273,11 +270,7 @@ contract MixedQuoter is IMixedQuoter, IPancakeV3SwapCallback, Multicall { if (!withContext) { (amountIn,,, gasEstimateForCurAction) = quoteExactInputSingleV3( QuoteExactInputSingleV3Params({ - tokenIn: tokenIn, - tokenOut: tokenOut, - amountIn: amountIn, - fee: fee, - sqrtPriceLimitX96: 0 + tokenIn: tokenIn, tokenOut: tokenOut, amountIn: amountIn, fee: fee, sqrtPriceLimitX96: 0 }) ); } else { @@ -292,11 +285,7 @@ contract MixedQuoter is IMixedQuoter, IPancakeV3SwapCallback, Multicall { amountIn += accAmountIn; (swapAmountOut,,, gasEstimateForCurAction) = quoteExactInputSingleV3( QuoteExactInputSingleV3Params({ - tokenIn: tokenIn, - tokenOut: tokenOut, - amountIn: amountIn, - fee: fee, - sqrtPriceLimitX96: 0 + tokenIn: tokenIn, tokenOut: tokenOut, amountIn: amountIn, fee: fee, sqrtPriceLimitX96: 0 }) ); MixedQuoterRecorder.setPoolSwapTokenAccumulation(poolHash, amountIn, swapAmountOut, zeroForOne); @@ -383,10 +372,7 @@ contract MixedQuoter is IMixedQuoter, IPancakeV3SwapCallback, Multicall { if (!withContext) { (amountIn, gasEstimateForCurAction) = quoteExactInputSingleStable( QuoteExactInputSingleStableParams({ - tokenIn: tokenIn, - tokenOut: tokenOut, - amountIn: amountIn, - flag: 2 + tokenIn: tokenIn, tokenOut: tokenOut, amountIn: amountIn, flag: 2 }) ); } else { @@ -400,10 +386,7 @@ contract MixedQuoter is IMixedQuoter, IPancakeV3SwapCallback, Multicall { amountIn += accAmountIn; (swapAmountOut, gasEstimateForCurAction) = quoteExactInputSingleStable( QuoteExactInputSingleStableParams({ - tokenIn: tokenIn, - tokenOut: tokenOut, - amountIn: amountIn, - flag: 2 + tokenIn: tokenIn, tokenOut: tokenOut, amountIn: amountIn, flag: 2 }) ); MixedQuoterRecorder.setPoolSwapTokenAccumulation(poolHash, amountIn, swapAmountOut, zeroForOne); @@ -415,10 +398,7 @@ contract MixedQuoter is IMixedQuoter, IPancakeV3SwapCallback, Multicall { // params[actionIndex] is zero bytes (amountIn, gasEstimateForCurAction) = quoteExactInputSingleStable( QuoteExactInputSingleStableParams({ - tokenIn: tokenIn, - tokenOut: tokenOut, - amountIn: amountIn, - flag: 3 + tokenIn: tokenIn, tokenOut: tokenOut, amountIn: amountIn, flag: 3 }) ); } else { diff --git a/src/base/BaseMigrator.sol b/src/base/BaseMigrator.sol deleted file mode 100644 index de4373f..0000000 --- a/src/base/BaseMigrator.sol +++ /dev/null @@ -1,216 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -// Copyright (C) 2024 PancakeSwap -pragma solidity ^0.8.0; - -import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol"; -import {SafeTransferLib, ERC20} from "solmate/src/utils/SafeTransferLib.sol"; -import {IPancakePair} from "../interfaces/external/IPancakePair.sol"; -import {IV3NonfungiblePositionManager} from "../interfaces/external/IV3NonfungiblePositionManager.sol"; -import {IWETH9} from "../interfaces/external/IWETH9.sol"; -import {Multicall} from "./Multicall.sol"; -import {Currency, CurrencyLibrary} from "infinity-core/src/types/Currency.sol"; -import {Owner} from "infinity-core/src/Owner.sol"; -import {SelfPermitERC721} from "./SelfPermitERC721.sol"; -import {IBaseMigrator} from "../interfaces/IBaseMigrator.sol"; -import {IPositionManagerPermit2} from "../interfaces/IPositionManagerPermit2.sol"; -import {IAllowanceTransfer} from "permit2/src/interfaces/IAllowanceTransfer.sol"; -import {Permit2Forwarder} from "./Permit2Forwarder.sol"; - -contract BaseMigrator is IBaseMigrator, Permit2Forwarder, Multicall, SelfPermitERC721, Owner { - using SafeCast for uint256; - using SafeTransferLib for ERC20; - - address public immutable WETH9; - IAllowanceTransfer public immutable positionManagerPermit2; - - /// @notice Theoretically, it is possible that permit2 of positionManager is not the same as the permit2 in migrator - /// @dev So let us keep positionManagerPermit2 and permit2 in migrator as separate variables - constructor(address _WETH9, address _positionManager, IAllowanceTransfer _permit2) Permit2Forwarder(_permit2) { - WETH9 = _WETH9; - positionManagerPermit2 = IPositionManagerPermit2(_positionManager).permit2(); - } - - /// @notice refund native ETH to caller - /// This is useful when the caller sends more ETH then he specifies in arguments - function refundETH() external payable override { - if (address(this).balance > 0) CurrencyLibrary.NATIVE.transfer(msg.sender, address(this).balance); - } - - /// @notice compare if tokens from v2 pair are the same as token0/token1. Revert with - /// `TOKEN_NOT_MATCH` if tokens does not match - /// @param v2Pair the address of v2 pair - /// @param token0 token0 of infinity poolKey - /// @param token1 token1 of infinity poolKey - /// @return shouldReversePair if the order of tokens from v2 pair is different from infinity pair (only when WETH is involved) - function checkTokensOrderAndMatchFromV2(address v2Pair, Currency token0, Currency token1) - internal - view - returns (bool shouldReversePair) - { - address token0V2 = IPancakePair(v2Pair).token0(); - address token1V2 = IPancakePair(v2Pair).token1(); - return _checkIfTokenPairMatchAndOrder(token0V2, token1V2, token0, token1); - } - - /// @notice compare if tokens from v3 pool are the same as token0/token1. Revert with - /// `TOKEN_NOT_MATCH` if tokens does not match - /// @param nfp the address of v3#nfp - /// @param tokenId the tokenId of v3 pool - /// @param token0 token0 of infinity poolKey - /// @param token1 token1 of infinity poolKey - /// @return shouldReversePair if the order of tokens from v3 pool is different from infinity pair (only when WETH is involved) - function checkTokensOrderAndMatchFromV3(address nfp, uint256 tokenId, Currency token0, Currency token1) - internal - view - returns (bool shouldReversePair) - { - (,, address token0V3, address token1V3,,,,,,,,) = IV3NonfungiblePositionManager(nfp).positions(tokenId); - return _checkIfTokenPairMatchAndOrder(token0V3, token1V3, token0, token1); - } - - /// @notice withdraw liquidity from v2 pool (fee will always be included) - /// It may revert if amount0/amount1 received is less than expected - /// @param v2PoolParams the parameters to withdraw liquidity from v2 pool - /// @param shouldReversePair if the order of tokens from v2 pair is different from infinity pair (only when WETH is involved) - /// @return amount0Received the actual amount of token0 received (in order of infinity pool) - /// @return amount1Received the actual amount of token1 received (in order of infinity pool) - function withdrawLiquidityFromV2(V2PoolParams calldata v2PoolParams, bool shouldReversePair) - internal - returns (uint256 amount0Received, uint256 amount1Received) - { - // burn v2 liquidity to this address - permit2.transferFrom(msg.sender, v2PoolParams.pair, uint160(v2PoolParams.migrateAmount), v2PoolParams.pair); - (amount0Received, amount1Received) = IPancakePair(v2PoolParams.pair).burn(address(this)); - - // same price slippage check as v3 - if (amount0Received < v2PoolParams.amount0Min || amount1Received < v2PoolParams.amount1Min) { - revert INSUFFICIENT_AMOUNTS_RECEIVED(); - } - - /// @notice the order may mismatch with infinity pool when WETH is invovled - /// the following check makes sure that the output always match the order of infinity pool - if (shouldReversePair) { - (amount0Received, amount1Received) = (amount1Received, amount0Received); - } - } - - /// @notice withdraw liquidity from v3 pool and collect fee if specified in `v3PoolParams` - /// It may revert if the caller is not the owner of the token or amount0/amount1 received is less than expected - /// @param v3PoolParams the parameters to withdraw liquidity from v3 pool - /// @param shouldReversePair if the order of tokens from v3 pool is different from infinity pair (only when WETH is involved) - /// @return amount0Received the actual amount of token0 received (in order of infinity pool) - /// @return amount1Received the actual amount of token1 received (in order of infinity pool) - function withdrawLiquidityFromV3(V3PoolParams calldata v3PoolParams, bool shouldReversePair) - internal - returns (uint256 amount0Received, uint256 amount1Received) - { - IV3NonfungiblePositionManager nfp = IV3NonfungiblePositionManager(v3PoolParams.nfp); - uint256 tokenId = v3PoolParams.tokenId; - ///@dev make sure the caller is the owner of the token - /// otherwise once the token is approved to migrator, anyone can steal money through this function - if (msg.sender != nfp.ownerOf(tokenId)) { - revert NOT_TOKEN_OWNER(); - } - - /// @notice decrease liquidity from v3#nfp, make sure migrator has been approved - IV3NonfungiblePositionManager.DecreaseLiquidityParams memory decreaseLiquidityParams = - IV3NonfungiblePositionManager.DecreaseLiquidityParams({ - tokenId: tokenId, - liquidity: v3PoolParams.liquidity, - amount0Min: v3PoolParams.amount0Min, - amount1Min: v3PoolParams.amount1Min, - deadline: v3PoolParams.deadline - }); - (amount0Received, amount1Received) = nfp.decreaseLiquidity(decreaseLiquidityParams); - - /// @notice collect tokens from v3#nfp (including fee if necessary) - IV3NonfungiblePositionManager.CollectParams memory collectParams = IV3NonfungiblePositionManager.CollectParams({ - tokenId: tokenId, - recipient: address(this), - amount0Max: v3PoolParams.collectFee ? type(uint128).max : amount0Received.toUint128(), - amount1Max: v3PoolParams.collectFee ? type(uint128).max : amount1Received.toUint128() - }); - (amount0Received, amount1Received) = nfp.collect(collectParams); - - /// @notice the order may mismatch with infinity pool when WETH is invovled - /// the following check makes sure that the output always match the order of infinity pool - if (shouldReversePair) { - (amount0Received, amount1Received) = (amount1Received, amount0Received); - } - } - - /// @notice receive extra tokens from user if specifies in arguments and normalize all the WETH to native ETH - function batchAndNormalizeTokens(Currency currency0, Currency currency1, uint256 extraAmount0, uint256 extraAmount1) - internal - { - ERC20 token0 = ERC20(Currency.unwrap(currency0)); - ERC20 token1 = ERC20(Currency.unwrap(currency1)); - - if (extraAmount0 > 0) { - if (currency0.isNative() && msg.value == 0) { - // we assume that user wants to send WETH - permit2.transferFrom(msg.sender, address(this), uint160(extraAmount0), WETH9); - } else if (!currency0.isNative()) { - permit2.transferFrom(msg.sender, address(this), uint160(extraAmount0), address(token0)); - } - } - - /// @dev token1 cant be NATIVE - if (extraAmount1 > 0) { - permit2.transferFrom(msg.sender, address(this), uint160(extraAmount1), address(token1)); - } - - if (extraAmount0 != 0 || extraAmount1 != 0) { - emit ExtraFundsAdded(address(token0), address(token1), extraAmount0, extraAmount1); - } - - // even if user sends native ETH, we still need to unwrap the part from source pool - if (currency0.isNative()) { - uint256 wethBalance = ERC20(WETH9).balanceOf(address(this)); - if (wethBalance > 0) IWETH9(WETH9).withdraw(wethBalance); - } - } - - /// @notice approve the maximum amount of token in permit2 if the current allowance is insufficient for following operations - function permit2ApproveMaxIfNeeded(Currency currency, address to, uint256 amount) internal { - ERC20 token = ERC20(Currency.unwrap(currency)); - if (token.allowance(address(this), address(positionManagerPermit2)) >= amount) { - return; - } - token.safeApprove(address(positionManagerPermit2), type(uint256).max); - // permit2 will not reduce allowance when value is type(uint160).max - positionManagerPermit2.approve(Currency.unwrap(currency), to, type(uint160).max, type(uint48).max); - } - - /// @notice Check and revert if tokens from both v2/v3 and infinity pair does not match - /// Return true if match but v2v3Token1 is WETH which should be ETH in infinity pair - /// @param v2v3Token0 token0 from v2/v3 pair - /// @param v2v3Token1 token1 from v2/v3 pair - /// @param infiToken0 token0 from infinity pair - /// @param infiToken1 token1 from infinity pair - /// @return shouldReversePair if the order of tokens from v2/v3 pair is different from infinity pair (only when WETH is involved) - function _checkIfTokenPairMatchAndOrder( - address v2v3Token0, - address v2v3Token1, - Currency infiToken0, - Currency infiToken1 - ) private view returns (bool shouldReversePair) { - if (infiToken0.isNative() && v2v3Token0 == WETH9) { - if (Currency.unwrap(infiToken1) != v2v3Token1) { - revert TOKEN_NOT_MATCH(); - } - } else if (infiToken0.isNative() && v2v3Token1 == WETH9) { - if (Currency.unwrap(infiToken1) != v2v3Token0) { - revert TOKEN_NOT_MATCH(); - } - shouldReversePair = true; - } else { - /// @dev the order of token0 and token1 is always sorted - /// v2: https://github.com/pancakeswap/pancake-swap-core-v2/blob/38aad83854a46a82ea0e31988ff3cddb2bffb71a/contracts/PancakeFactory.sol#L27 - /// v3: https://github.com/pancakeswap/pancake-v3-contracts/blob/5cc479f0c5a98966c74d94700057b8c3ca629afd/projects/v3-core/contracts/PancakeV3Factory.sol#L66 - if (Currency.unwrap(infiToken0) != v2v3Token0 || Currency.unwrap(infiToken1) != v2v3Token1) { - revert TOKEN_NOT_MATCH(); - } - } - } -} diff --git a/src/interfaces/IERC721Permit.sol b/src/interfaces/IERC721Permit.sol index c190b1c..96b65bf 100644 --- a/src/interfaces/IERC721Permit.sol +++ b/src/interfaces/IERC721Permit.sol @@ -21,7 +21,5 @@ interface IERC721Permit is IERC721 { /// @param v Must produce valid secp256k1 signature from the holder along with `r` and `s` /// @param r Must produce valid secp256k1 signature from the holder along with `v` and `s` /// @param s Must produce valid secp256k1 signature from the holder along with `r` and `v` - function permit(address spender, uint256 tokenId, uint256 deadline, uint8 v, bytes32 r, bytes32 s) - external - payable; + function permit(address spender, uint256 tokenId, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external payable; } diff --git a/src/interfaces/ISelfPermit.sol b/src/interfaces/ISelfPermit.sol index 301904a..1681194 100644 --- a/src/interfaces/ISelfPermit.sol +++ b/src/interfaces/ISelfPermit.sol @@ -12,9 +12,7 @@ interface ISelfPermit { /// @param v Must produce valid secp256k1 signature from the holder along with `r` and `s` /// @param r Must produce valid secp256k1 signature from the holder along with `v` and `s` /// @param s Must produce valid secp256k1 signature from the holder along with `r` and `v` - function selfPermit(address token, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) - external - payable; + function selfPermit(address token, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external payable; /// @notice Permits this contract to spend a given token from `msg.sender` /// @dev The `owner` is always msg.sender and the `spender` is always address(this). diff --git a/src/libraries/CalldataDecoder.sol b/src/libraries/CalldataDecoder.sol index 32f0666..65b749b 100644 --- a/src/libraries/CalldataDecoder.sol +++ b/src/libraries/CalldataDecoder.sol @@ -143,11 +143,7 @@ library CalldataDecoder { } /// @dev equivalent to: abi.decode(params, (Currency, uint256)) in calldata - function decodeCurrencyAndUint256(bytes calldata params) - internal - pure - returns (Currency currency, uint256 amount) - { + function decodeCurrencyAndUint256(bytes calldata params) internal pure returns (Currency currency, uint256 amount) { assembly ("memory-safe") { if lt(params.length, 0x40) { mstore(0, SLICE_ERROR_SELECTOR) diff --git a/src/libraries/external/V3SmartRouterHelper.sol b/src/libraries/external/V3SmartRouterHelper.sol index 484986f..2224025 100644 --- a/src/libraries/external/V3SmartRouterHelper.sol +++ b/src/libraries/external/V3SmartRouterHelper.sol @@ -12,7 +12,6 @@ library V3SmartRouterHelper { /** * Stable ************************************************* */ - // get the pool info in stable swap function getStableInfo(address stableSwapFactory, address input, address output, uint256 flag) internal diff --git a/src/pool-bin/BinMigrator.sol b/src/pool-bin/BinMigrator.sol deleted file mode 100644 index b231655..0000000 --- a/src/pool-bin/BinMigrator.sol +++ /dev/null @@ -1,176 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -// Copyright (C) 2024 PancakeSwap -pragma solidity 0.8.26; - -import {BaseMigrator, IV3NonfungiblePositionManager} from "../base/BaseMigrator.sol"; -import {IBinMigrator, PoolKey} from "./interfaces/IBinMigrator.sol"; -import {IBinPositionManager} from "./interfaces/IBinPositionManager.sol"; -import {IAllowanceTransfer} from "permit2/src/interfaces/IAllowanceTransfer.sol"; -import {Currency, CurrencyLibrary} from "infinity-core/src/types/Currency.sol"; -import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol"; -import {Plan, Planner} from "../libraries/Planner.sol"; -import {Actions} from "../libraries/Actions.sol"; -import {ActionConstants} from "../libraries/ActionConstants.sol"; -import {ReentrancyLock} from "../base/ReentrancyLock.sol"; - -contract BinMigrator is IBinMigrator, BaseMigrator, ReentrancyLock { - using SafeCast for uint256; - - IBinPositionManager public immutable binPositionManager; - - constructor(address _WETH9, address _binPositionManager, IAllowanceTransfer _permit2) - BaseMigrator(_WETH9, _binPositionManager, _permit2) - { - binPositionManager = IBinPositionManager(_binPositionManager); - } - - /// @inheritdoc IBinMigrator - function migrateFromV2( - V2PoolParams calldata v2PoolParams, - InfiBinPoolParams calldata infiPoolParams, - uint256 extraAmount0, - uint256 extraAmount1 - ) external payable override isNotLocked whenNotPaused { - bool shouldReversePair = checkTokensOrderAndMatchFromV2( - v2PoolParams.pair, infiPoolParams.poolKey.currency0, infiPoolParams.poolKey.currency1 - ); - (uint256 amount0Received, uint256 amount1Received) = withdrawLiquidityFromV2(v2PoolParams, shouldReversePair); - - /// @notice if user manually specify the price range, they might need to send extra token - batchAndNormalizeTokens( - infiPoolParams.poolKey.currency0, infiPoolParams.poolKey.currency1, extraAmount0, extraAmount1 - ); - - uint256 amount0Input = amount0Received + extraAmount0; - uint256 amount1Input = amount1Received + extraAmount1; - IBinPositionManager.BinAddLiquidityParams memory addLiquidityParams = IBinPositionManager.BinAddLiquidityParams({ - poolKey: infiPoolParams.poolKey, - amount0: amount0Input.toUint128(), - amount1: amount1Input.toUint128(), - amount0Max: infiPoolParams.amount0Max, - amount1Max: infiPoolParams.amount1Max, - activeIdDesired: infiPoolParams.activeIdDesired, - idSlippage: infiPoolParams.idSlippage, - deltaIds: infiPoolParams.deltaIds, - distributionX: infiPoolParams.distributionX, - distributionY: infiPoolParams.distributionY, - to: infiPoolParams.to, - hookData: infiPoolParams.hookData - }); - - (uint256 amount0Consumed, uint256 amount1Consumed) = - _addLiquidityToTargetPool(addLiquidityParams, infiPoolParams.deadline); - - // refund if necessary, ETH is supported by CurrencyLib - unchecked { - if (amount0Input > amount0Consumed) { - infiPoolParams.poolKey.currency0.transfer(infiPoolParams.to, amount0Input - amount0Consumed); - } - if (amount1Input > amount1Consumed) { - infiPoolParams.poolKey.currency1.transfer(infiPoolParams.to, amount1Input - amount1Consumed); - } - } - } - - /// @inheritdoc IBinMigrator - function migrateFromV3( - V3PoolParams calldata v3PoolParams, - InfiBinPoolParams calldata infiPoolParams, - uint256 extraAmount0, - uint256 extraAmount1 - ) external payable override isNotLocked whenNotPaused { - bool shouldReversePair = checkTokensOrderAndMatchFromV3( - v3PoolParams.nfp, v3PoolParams.tokenId, infiPoolParams.poolKey.currency0, infiPoolParams.poolKey.currency1 - ); - (uint256 amount0Received, uint256 amount1Received) = withdrawLiquidityFromV3(v3PoolParams, shouldReversePair); - - /// @notice if user manually specify the price range, they need to send extra token - batchAndNormalizeTokens( - infiPoolParams.poolKey.currency0, infiPoolParams.poolKey.currency1, extraAmount0, extraAmount1 - ); - - uint256 amount0Input = amount0Received + extraAmount0; - uint256 amount1Input = amount1Received + extraAmount1; - IBinPositionManager.BinAddLiquidityParams memory addLiquidityParams = IBinPositionManager.BinAddLiquidityParams({ - poolKey: infiPoolParams.poolKey, - amount0: amount0Input.toUint128(), - amount1: amount1Input.toUint128(), - amount0Max: infiPoolParams.amount0Max, - amount1Max: infiPoolParams.amount1Max, - activeIdDesired: infiPoolParams.activeIdDesired, - idSlippage: infiPoolParams.idSlippage, - deltaIds: infiPoolParams.deltaIds, - distributionX: infiPoolParams.distributionX, - distributionY: infiPoolParams.distributionY, - to: infiPoolParams.to, - hookData: infiPoolParams.hookData - }); - (uint256 amount0Consumed, uint256 amount1Consumed) = - _addLiquidityToTargetPool(addLiquidityParams, infiPoolParams.deadline); - - // refund if necessary, ETH is supported by CurrencyLib - unchecked { - if (amount0Input > amount0Consumed) { - infiPoolParams.poolKey.currency0.transfer(infiPoolParams.to, amount0Input - amount0Consumed); - } - if (amount1Input > amount1Consumed) { - infiPoolParams.poolKey.currency1.transfer(infiPoolParams.to, amount1Input - amount1Consumed); - } - } - } - - /// @dev adding liquidity to target bin pool, collect surplus ETH if necessary - /// @param params bin position manager add liquidity params - /// @param deadline the deadline for the transaction - /// @return amount0Consumed the actual amount of token0 consumed - /// @return amount1Consumed the actual amount of token1 consumed - function _addLiquidityToTargetPool(IBinPositionManager.BinAddLiquidityParams memory params, uint256 deadline) - internal - returns (uint128 amount0Consumed, uint128 amount1Consumed) - { - /// @dev currency1 cant be NATIVE - bool nativePair = params.poolKey.currency0.isNative(); - if (!nativePair) { - permit2ApproveMaxIfNeeded(params.poolKey.currency0, address(binPositionManager), params.amount0); - } - permit2ApproveMaxIfNeeded(params.poolKey.currency1, address(binPositionManager), params.amount1); - - uint256 currency0BalanceBefore = params.poolKey.currency0.balanceOfSelf(); - uint256 currency1BalanceBefore = params.poolKey.currency1.balanceOfSelf(); - - Plan memory planner = Planner.init(); - planner.add(Actions.BIN_ADD_LIQUIDITY, abi.encode(params)); - planner.add(Actions.SETTLE_PAIR, abi.encode(params.poolKey.currency0, params.poolKey.currency1)); - // need to sweep native token - if (nativePair) { - planner.add(Actions.SWEEP, abi.encode(params.poolKey.currency0, ActionConstants.MSG_SENDER)); - } - - binPositionManager.modifyLiquidities{value: nativePair ? params.amount0 : 0}(planner.encode(), deadline); - - uint256 currency0BalanceAfter = params.poolKey.currency0.balanceOfSelf(); - uint256 currency1BalanceAfter = params.poolKey.currency1.balanceOfSelf(); - - // If the binPositionManager already holds a balance of native tokens, then currency0BalanceAfter could be greater than currency0BalanceBefore due to the sweep action - // it means users can get a discount if there is an unexpected native balance in the binPositionManager - if (currency0BalanceBefore > currency0BalanceAfter) { - amount0Consumed = (currency0BalanceBefore - currency0BalanceAfter).toUint128(); - } - // normal tokens will not have this case , but it is better to check - if (currency1BalanceBefore > currency1BalanceAfter) { - amount1Consumed = (currency1BalanceBefore - currency1BalanceAfter).toUint128(); - } - } - - /// @inheritdoc IBinMigrator - /// @notice Planned to be batched with migration operations through multicall to save gas - function initializePool(PoolKey memory poolKey, uint24 activeId) external payable override { - return binPositionManager.initializePool(poolKey, activeId); - } - - receive() external payable { - if (msg.sender != address(binPositionManager) && msg.sender != WETH9) { - revert INVALID_ETHER_SENDER(); - } - } -} diff --git a/src/pool-bin/interfaces/IBinMigrator.sol b/src/pool-bin/interfaces/IBinMigrator.sol deleted file mode 100644 index ac141ac..0000000 --- a/src/pool-bin/interfaces/IBinMigrator.sol +++ /dev/null @@ -1,61 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.24; - -import {PoolKey} from "infinity-core/src/types/PoolKey.sol"; -import {IBaseMigrator} from "../../interfaces/IBaseMigrator.sol"; -import {IV3NonfungiblePositionManager} from "../../interfaces/external/IV3NonfungiblePositionManager.sol"; - -interface IBinMigrator is IBaseMigrator { - /// @notice same fields as IBinRouterBase.BinAddLiquidityParams - /// except amount0/amount1 which will be calculated by migrator - struct InfiBinPoolParams { - PoolKey poolKey; - uint128 amount0Max; - uint128 amount1Max; - uint256 activeIdDesired; - uint256 idSlippage; - int256[] deltaIds; - uint256[] distributionX; - uint256[] distributionY; - address to; - uint256 deadline; - // hookData will flow to hook's beforeMint/ afterMint - bytes hookData; - } - - /// @notice Migrate liquidity from v2 to infinity - /// @param v2PoolParams ncessary info for removing liqudity the source v2 pool - /// @param infiPoolParams necessary info for adding liquidity the target infinity bin-pool - /// @param extraAmount0 the extra amount of token0 that user wants to add (optional, usually 0) - /// if pool token0 is ETH and msg.value == 0, WETH will be taken from sender. - /// Otherwise if pool token0 is ETH and msg.value !=0, method will assume user have sent extraAmount0 in msg.value - /// @param extraAmount1 the extra amount of token1 that user wants to add (optional, usually 0) - function migrateFromV2( - V2PoolParams calldata v2PoolParams, - InfiBinPoolParams calldata infiPoolParams, - // extra funds to be added - uint256 extraAmount0, - uint256 extraAmount1 - ) external payable; - - /// @notice Migrate liquidity from v3 to infinity - /// @param v3PoolParams ncessary info for removing liqudity the source v3 pool - /// @param infiPoolParams necessary info for adding liquidity the target infinity bin-pool - /// @param extraAmount0 the extra amount of token0 that user wants to add (optional, usually 0) - /// if pool token0 is ETH and msg.value == 0, WETH will be taken from sender. - /// Otherwise if pool token0 is ETH and msg.value !=0, method will assume user have sent extraAmount0 in msg.value - /// @param extraAmount1 the extra amount of token1 that user wants to add (optional, usually 0) - function migrateFromV3( - V3PoolParams calldata v3PoolParams, - InfiBinPoolParams calldata infiPoolParams, - // extra funds to be added - uint256 extraAmount0, - uint256 extraAmount1 - ) external payable; - - /// @notice Initialize a pool for a given pool key, the function will forwards the call to the BinPoolManager - /// @dev Call this when the pool does not exist and is not initialized - /// @param poolKey The pool key - /// @param activeId The active id of the pool - function initializePool(PoolKey memory poolKey, uint24 activeId) external payable; -} diff --git a/src/pool-bin/interfaces/IBinQuoter.sol b/src/pool-bin/interfaces/IBinQuoter.sol index 302dcb1..b00456e 100644 --- a/src/pool-bin/interfaces/IBinQuoter.sol +++ b/src/pool-bin/interfaces/IBinQuoter.sol @@ -40,9 +40,7 @@ interface IBinQuoter is IQuoter { /// exactAmount The desired input amount /// @return amountOut The output quote for the exactIn swap /// @return gasEstimate Estimated gas units used for the swap - function quoteExactInput(QuoteExactParams memory params) - external - returns (uint256 amountOut, uint256 gasEstimate); + function quoteExactInput(QuoteExactParams memory params) external returns (uint256 amountOut, uint256 gasEstimate); /// @notice Returns the delta amounts for a given exact output swap of a single pool /// @param params The params for the quote, encoded as `QuoteExactSingleParams` @@ -63,7 +61,5 @@ interface IBinQuoter is IQuoter { /// exactAmount The desired output amount /// @return amountIn The input quote for the exactOut swap /// @return gasEstimate Estimated gas units used for the swap - function quoteExactOutput(QuoteExactParams memory params) - external - returns (uint256 amountIn, uint256 gasEstimate); + function quoteExactOutput(QuoteExactParams memory params) external returns (uint256 amountIn, uint256 gasEstimate); } diff --git a/src/pool-cl/CLMigrator.sol b/src/pool-cl/CLMigrator.sol deleted file mode 100644 index 1000dda..0000000 --- a/src/pool-cl/CLMigrator.sol +++ /dev/null @@ -1,188 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -// Copyright (C) 2024 PancakeSwap -pragma solidity 0.8.26; - -import {TickMath} from "infinity-core/src/pool-cl/libraries/TickMath.sol"; -import {PoolIdLibrary} from "infinity-core/src/types/PoolId.sol"; -import {SqrtPriceMath} from "infinity-core/src/pool-cl/libraries/SqrtPriceMath.sol"; -import {BaseMigrator, IV3NonfungiblePositionManager} from "../base/BaseMigrator.sol"; -import {ICLMigrator, PoolKey} from "./interfaces/ICLMigrator.sol"; -import {ICLPoolManager} from "infinity-core/src/pool-cl/interfaces/ICLPoolManager.sol"; -import {IAllowanceTransfer} from "permit2/src/interfaces/IAllowanceTransfer.sol"; -import {LiquidityAmounts} from "./libraries/LiquidityAmounts.sol"; -import {ICLPositionManager} from "./interfaces/ICLPositionManager.sol"; -import {Actions} from "../libraries/Actions.sol"; -import {Plan, Planner} from "../libraries/Planner.sol"; -import {ReentrancyLock} from "../base/ReentrancyLock.sol"; - -contract CLMigrator is ICLMigrator, BaseMigrator, ReentrancyLock { - ICLPositionManager public immutable clPositionManager; - ICLPoolManager public immutable clPoolManager; - - constructor(address _WETH9, address _clPositionManager, IAllowanceTransfer _permit2) - BaseMigrator(_WETH9, _clPositionManager, _permit2) - { - clPositionManager = ICLPositionManager(_clPositionManager); - clPoolManager = clPositionManager.clPoolManager(); - } - - /// @inheritdoc ICLMigrator - function migrateFromV2( - V2PoolParams calldata v2PoolParams, - InfiCLPoolParams calldata infiPoolParams, - uint256 extraAmount0, - uint256 extraAmount1 - ) external payable override isNotLocked whenNotPaused { - bool shouldReversePair = checkTokensOrderAndMatchFromV2( - v2PoolParams.pair, infiPoolParams.poolKey.currency0, infiPoolParams.poolKey.currency1 - ); - - (uint256 amount0Received, uint256 amount1Received) = withdrawLiquidityFromV2(v2PoolParams, shouldReversePair); - - /// @notice if user manually specify the price range, they might need to send extra token - batchAndNormalizeTokens( - infiPoolParams.poolKey.currency0, infiPoolParams.poolKey.currency1, extraAmount0, extraAmount1 - ); - - uint256 amount0In = amount0Received + extraAmount0; - uint256 amount1In = amount1Received + extraAmount1; - MintParams memory mintParams = MintParams({ - poolKey: infiPoolParams.poolKey, - tickLower: infiPoolParams.tickLower, - tickUpper: infiPoolParams.tickUpper, - amount0In: uint128(amount0In), - amount1In: uint128(amount1In), - liquidityMin: infiPoolParams.liquidityMin, - recipient: infiPoolParams.recipient, - hookData: infiPoolParams.hookData - }); - (uint256 amount0Consumed, uint256 amount1Consumed) = - _addLiquidityToTargetPool(mintParams, infiPoolParams.deadline); - - // refund if necessary, ETH is supported by CurrencyLib - unchecked { - if (amount0In > amount0Consumed) { - infiPoolParams.poolKey.currency0.transfer(infiPoolParams.recipient, amount0In - amount0Consumed); - } - if (amount1In > amount1Consumed) { - infiPoolParams.poolKey.currency1.transfer(infiPoolParams.recipient, amount1In - amount1Consumed); - } - } - } - - /// @inheritdoc ICLMigrator - function migrateFromV3( - V3PoolParams calldata v3PoolParams, - InfiCLPoolParams calldata infiPoolParams, - uint256 extraAmount0, - uint256 extraAmount1 - ) external payable override isNotLocked whenNotPaused { - bool shouldReversePair = checkTokensOrderAndMatchFromV3( - v3PoolParams.nfp, v3PoolParams.tokenId, infiPoolParams.poolKey.currency0, infiPoolParams.poolKey.currency1 - ); - (uint256 amount0Received, uint256 amount1Received) = withdrawLiquidityFromV3(v3PoolParams, shouldReversePair); - - /// @notice if user manually specify the price range, they need to send extra token - batchAndNormalizeTokens( - infiPoolParams.poolKey.currency0, infiPoolParams.poolKey.currency1, extraAmount0, extraAmount1 - ); - - uint256 amount0In = amount0Received + extraAmount0; - uint256 amount1In = amount1Received + extraAmount1; - MintParams memory mintParams = MintParams({ - poolKey: infiPoolParams.poolKey, - tickLower: infiPoolParams.tickLower, - tickUpper: infiPoolParams.tickUpper, - amount0In: uint128(amount0In), - amount1In: uint128(amount1In), - liquidityMin: infiPoolParams.liquidityMin, - recipient: infiPoolParams.recipient, - hookData: infiPoolParams.hookData - }); - (uint256 amount0Consumed, uint256 amount1Consumed) = - _addLiquidityToTargetPool(mintParams, infiPoolParams.deadline); - - // refund if necessary, ETH is supported by CurrencyLib - unchecked { - if (amount0In > amount0Consumed) { - infiPoolParams.poolKey.currency0.transfer(infiPoolParams.recipient, amount0In - amount0Consumed); - } - if (amount1In > amount1Consumed) { - infiPoolParams.poolKey.currency1.transfer(infiPoolParams.recipient, amount1In - amount1Consumed); - } - } - } - - /// @dev adding liquidity to target cl pool, collect surplus ETH if necessary - /// @param params cl position manager add liquidity params - /// @param deadline the deadline for the transaction - /// @return amount0Consumed the actual amount of token0 consumed - /// @return amount1Consumed the actual amount of token1 consumed - function _addLiquidityToTargetPool(MintParams memory params, uint256 deadline) - internal - returns (uint256 amount0Consumed, uint256 amount1Consumed) - { - /// @dev currency1 cant be NATIVE - bool nativePair = params.poolKey.currency0.isNative(); - if (!nativePair) { - permit2ApproveMaxIfNeeded(params.poolKey.currency0, address(clPositionManager), params.amount0In); - } - permit2ApproveMaxIfNeeded(params.poolKey.currency1, address(clPositionManager), params.amount1In); - - (uint160 sqrtPriceX96, int24 activeTick,,) = clPoolManager.getSlot0(params.poolKey.toId()); - uint160 sqrtRatioAX96 = TickMath.getSqrtRatioAtTick(params.tickLower); - uint160 sqrtRatioBX96 = TickMath.getSqrtRatioAtTick(params.tickUpper); - uint128 liquidity = LiquidityAmounts.getLiquidityForAmounts( - sqrtPriceX96, sqrtRatioAX96, sqrtRatioBX96, params.amount0In, params.amount1In - ); - - if (liquidity < params.liquidityMin) { - revert INSUFFICIENT_LIQUIDITY(); - } - - // Calculate amt0/amt1 from liquidity, similar to CLPool modifyLiquidity logic - if (activeTick < params.tickLower) { - amount0Consumed = SqrtPriceMath.getAmount0Delta(sqrtRatioAX96, sqrtRatioBX96, liquidity, true); - } else if (activeTick < params.tickUpper) { - amount0Consumed = SqrtPriceMath.getAmount0Delta(sqrtPriceX96, sqrtRatioBX96, liquidity, true); - amount1Consumed = SqrtPriceMath.getAmount1Delta(sqrtRatioAX96, sqrtPriceX96, liquidity, true); - } else { - amount1Consumed = SqrtPriceMath.getAmount1Delta(sqrtRatioAX96, sqrtPriceX96, liquidity, true); - } - - Plan memory planner = Planner.init(); - planner.add( - Actions.CL_MINT_POSITION, - abi.encode( - params.poolKey, - params.tickLower, - params.tickUpper, - uint256(liquidity), - params.amount0In, - params.amount1In, - params.recipient, - params.hookData - ) - ); - bytes memory lockData = planner.finalizeModifyLiquidityWithSettlePair(params.poolKey); - - clPositionManager.modifyLiquidities{value: nativePair ? amount0Consumed : 0}(lockData, deadline); - } - - /// @inheritdoc ICLMigrator - /// @notice Planned to be batched with migration operations through multicall to save gas - function initializePool(PoolKey memory poolKey, uint160 sqrtPriceX96) - external - payable - override - returns (int24 tick) - { - return clPositionManager.initializePool(poolKey, sqrtPriceX96); - } - - receive() external payable { - if (msg.sender != address(clPositionManager) && msg.sender != WETH9) { - revert INVALID_ETHER_SENDER(); - } - } -} diff --git a/src/pool-cl/CLPositionManager.sol b/src/pool-cl/CLPositionManager.sol index 843f396..f564032 100644 --- a/src/pool-cl/CLPositionManager.sol +++ b/src/pool-cl/CLPositionManager.sol @@ -452,10 +452,7 @@ contract CLPositionManager is (liquidityDelta, feesAccrued) = clPoolManager.modifyLiquidity( poolKey, ICLPoolManager.ModifyLiquidityParams({ - tickLower: info.tickLower(), - tickUpper: info.tickUpper(), - liquidityDelta: liquidityChange, - salt: salt + tickLower: info.tickLower(), tickUpper: info.tickUpper(), liquidityDelta: liquidityChange, salt: salt }), hookData ); @@ -494,11 +491,7 @@ contract CLPositionManager is } /// @inheritdoc ICLPositionManager - function getPoolAndPositionInfo(uint256 tokenId) - public - view - returns (PoolKey memory poolKey, CLPositionInfo info) - { + function getPoolAndPositionInfo(uint256 tokenId) public view returns (PoolKey memory poolKey, CLPositionInfo info) { info = positionInfo[tokenId]; poolKey = poolKeys[info.poolId()]; } diff --git a/src/pool-cl/CLRouterBase.sol b/src/pool-cl/CLRouterBase.sol index ed51ded..031eec3 100644 --- a/src/pool-cl/CLRouterBase.sol +++ b/src/pool-cl/CLRouterBase.sol @@ -30,8 +30,8 @@ abstract contract CLRouterBase is ICLRouterBase, DeltaResolver { _getFullCredit(params.zeroForOne ? params.poolKey.currency0 : params.poolKey.currency1).toUint128(); } uint128 amountOut = _swapExactPrivate( - params.poolKey, params.zeroForOne, -int256(uint256(amountIn)), params.hookData - ).toUint128(); + params.poolKey, params.zeroForOne, -int256(uint256(amountIn)), params.hookData + ).toUint128(); if (amountOut < params.amountOutMinimum) { revert IInfinityRouter.TooLittleReceived(params.amountOutMinimum, amountOut); } @@ -70,9 +70,10 @@ abstract contract CLRouterBase is ICLRouterBase, DeltaResolver { amountOut = _getFullDebt(params.zeroForOne ? params.poolKey.currency1 : params.poolKey.currency0).toUint128(); } - uint128 amountIn = ( - -_swapExactPrivate(params.poolKey, params.zeroForOne, int256(uint256(amountOut)), params.hookData) - ).toUint128(); + uint128 amountIn = (-_swapExactPrivate( + params.poolKey, params.zeroForOne, int256(uint256(amountOut)), params.hookData + )) + .toUint128(); if (amountIn > params.amountInMaximum) { revert IInfinityRouter.TooMuchRequested(params.amountInMaximum, amountIn); } @@ -95,11 +96,10 @@ abstract contract CLRouterBase is ICLRouterBase, DeltaResolver { pathKey = params.path[i - 1]; (PoolKey memory poolKey, bool oneForZero) = pathKey.getPoolAndSwapDirection(currencyOut); // The output delta will always be negative, except for when interacting with certain hook pools - amountIn = ( - uint256( + amountIn = (uint256( -int256(_swapExactPrivate(poolKey, !oneForZero, int256(uint256(amountOut)), pathKey.hookData)) - ) - ).toUint128(); + )) + .toUint128(); amountOut = amountIn; currencyOut = pathKey.intermediateCurrency; diff --git a/src/pool-cl/base/CLNotifier.sol b/src/pool-cl/base/CLNotifier.sol index 3963f53..2efde7a 100644 --- a/src/pool-cl/base/CLNotifier.sol +++ b/src/pool-cl/base/CLNotifier.sol @@ -95,8 +95,9 @@ abstract contract CLNotifier is ICLNotifier { // remove the subscriber delete subscriber[tokenId]; - bool success = - _call(_subscriber, abi.encodeCall(ICLSubscriber.notifyBurn, (tokenId, owner, info, liquidity, feesAccrued))); + bool success = _call( + _subscriber, abi.encodeCall(ICLSubscriber.notifyBurn, (tokenId, owner, info, liquidity, feesAccrued)) + ); if (!success) { _subscriber.bubbleUpAndRevertWith(ICLSubscriber.notifyBurn.selector, BurnNotificationReverted.selector); diff --git a/src/pool-cl/base/ERC721Permit.sol b/src/pool-cl/base/ERC721Permit.sol index 8ff7194..590f819 100644 --- a/src/pool-cl/base/ERC721Permit.sol +++ b/src/pool-cl/base/ERC721Permit.sol @@ -93,7 +93,8 @@ abstract contract ERC721Permit is ERC721, IERC721Permit, EIP712, UnorderedNonce } function _isApprovedOrOwner(address spender, uint256 tokenId) internal view returns (bool) { - return spender == ownerOf(tokenId) || getApproved[tokenId] == spender - || isApprovedForAll[ownerOf(tokenId)][spender]; + return + spender == ownerOf(tokenId) || getApproved[tokenId] == spender + || isApprovedForAll[ownerOf(tokenId)][spender]; } } diff --git a/src/pool-cl/interfaces/ICLMigrator.sol b/src/pool-cl/interfaces/ICLMigrator.sol deleted file mode 100644 index 08e7e63..0000000 --- a/src/pool-cl/interfaces/ICLMigrator.sol +++ /dev/null @@ -1,71 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.24; - -import {PoolKey} from "infinity-core/src/types/PoolKey.sol"; -import {IBaseMigrator} from "../../interfaces/IBaseMigrator.sol"; -import {IV3NonfungiblePositionManager} from "../../interfaces/external/IV3NonfungiblePositionManager.sol"; - -interface ICLMigrator is IBaseMigrator { - error INSUFFICIENT_LIQUIDITY(); - - /// @notice same fields as INonfungiblePositionManager.MintParams - /// except amount0Desired/amount1Desired which will be calculated by migrator - struct InfiCLPoolParams { - PoolKey poolKey; - int24 tickLower; - int24 tickUpper; - uint256 liquidityMin; - address recipient; - uint256 deadline; - // hookData will flow to hook's beforeAddLiquidity/ afterAddLiquidity - bytes hookData; - } - - struct MintParams { - PoolKey poolKey; - int24 tickLower; - int24 tickUpper; - uint128 amount0In; - uint128 amount1In; - uint256 liquidityMin; - address recipient; - bytes hookData; - } - - /// @notice Migrate liquidity from v2 to infinity - /// @param v2PoolParams ncessary info for removing liqudity the source v2 pool - /// @param infiPoolParams necessary info for adding liquidity the target infinity cl-pool - /// @param extraAmount0 the extra amount of token0 that user wants to add (optional, usually 0) - /// if pool token0 is ETH and msg.value == 0, WETH will be taken from sender. - /// Otherwise if pool token0 is ETH and msg.value !=0, method will assume user have sent extraAmount0 in msg.value - /// @param extraAmount1 the extra amount of token1 that user wants to add (optional, usually 0) - function migrateFromV2( - V2PoolParams calldata v2PoolParams, - InfiCLPoolParams calldata infiPoolParams, - // extra funds to be added - uint256 extraAmount0, - uint256 extraAmount1 - ) external payable; - - /// @notice Migrate liquidity from v3 to infinity - /// @param v3PoolParams ncessary info for removing liqudity the source v3 pool - /// @param infiPoolParams necessary info for adding liquidity the target infinity cl-pool - /// @param extraAmount0 the extra amount of token0 that user wants to add (optional, usually 0) - /// if pool token0 is ETH and msg.value == 0, WETH will be taken from sender. - /// Otherwise if pool token0 is ETH and msg.value !=0, method will assume user have sent extraAmount0 in msg.value - /// @param extraAmount1 the extra amount of token1 that user wants to add (optional, usually 0) - function migrateFromV3( - V3PoolParams calldata v3PoolParams, - InfiCLPoolParams calldata infiPoolParams, - // extra funds to be added - uint256 extraAmount0, - uint256 extraAmount1 - ) external payable; - - /// @notice Initialize a pool for a given pool key, the function will forwards the call to the CLPoolManager - /// @dev Call this when the pool does not exist and is not initialized. - /// @param poolKey The pool key - /// @param sqrtPriceX96 The initial sqrt price of the pool - /// @return tick Pool tick - function initializePool(PoolKey memory poolKey, uint160 sqrtPriceX96) external payable returns (int24 tick); -} diff --git a/src/pool-cl/interfaces/ICLQuoter.sol b/src/pool-cl/interfaces/ICLQuoter.sol index 5549627..5044fd0 100644 --- a/src/pool-cl/interfaces/ICLQuoter.sol +++ b/src/pool-cl/interfaces/ICLQuoter.sol @@ -40,9 +40,7 @@ interface ICLQuoter is IQuoter { /// exactAmount The desired input amount /// @return amountOut The output quote for the exactIn swap /// @return gasEstimate Estimated gas units used for the swap - function quoteExactInput(QuoteExactParams memory params) - external - returns (uint256 amountOut, uint256 gasEstimate); + function quoteExactInput(QuoteExactParams memory params) external returns (uint256 amountOut, uint256 gasEstimate); /// @notice Returns the delta amounts for a given exact output swap of a single pool /// @param params The params for the quote, encoded as `QuoteExactSingleParams` @@ -63,7 +61,5 @@ interface ICLQuoter is IQuoter { /// exactAmount The desired output amount /// @return amountIn The input quote for the exactOut swap /// @return gasEstimate Estimated gas units used for the swap - function quoteExactOutput(QuoteExactParams memory params) - external - returns (uint256 amountIn, uint256 gasEstimate); + function quoteExactOutput(QuoteExactParams memory params) external returns (uint256 amountIn, uint256 gasEstimate); } diff --git a/src/pool-cl/lens/CLQuoter.sol b/src/pool-cl/lens/CLQuoter.sol index 35bddb7..769a2f7 100644 --- a/src/pool-cl/lens/CLQuoter.sol +++ b/src/pool-cl/lens/CLQuoter.sol @@ -52,10 +52,7 @@ contract CLQuoter is ICLQuoter, BaseInfinityQuoter { } /// @inheritdoc ICLQuoter - function quoteExactInput(QuoteExactParams memory params) - external - returns (uint256 amountOut, uint256 gasEstimate) - { + function quoteExactInput(QuoteExactParams memory params) external returns (uint256 amountOut, uint256 gasEstimate) { uint256 gasBefore = gasleft(); try vault.lock(abi.encodeCall(this._quoteExactInput, (params))) {} catch (bytes memory reason) { @@ -80,10 +77,7 @@ contract CLQuoter is ICLQuoter, BaseInfinityQuoter { } /// @inheritdoc ICLQuoter - function quoteExactOutput(QuoteExactParams memory params) - external - returns (uint256 amountIn, uint256 gasEstimate) - { + function quoteExactOutput(QuoteExactParams memory params) external returns (uint256 amountIn, uint256 gasEstimate) { uint256 gasBefore = gasleft(); try vault.lock(abi.encodeCall(this._quoteExactOutput, (params))) {} catch (bytes memory reason) { diff --git a/src/pool-cl/lens/TickLens.sol b/src/pool-cl/lens/TickLens.sol index 2ea774a..334a30b 100644 --- a/src/pool-cl/lens/TickLens.sol +++ b/src/pool-cl/lens/TickLens.sol @@ -59,9 +59,7 @@ contract TickLens is ITickLens { int24 populatedTick = ((int24(tickBitmapIndex) << 8) + int24(int256(i))) * tickSpacing; Tick.Info memory tickInfo = poolManager.getPoolTickInfo(id, populatedTick); populatedTicks[--numberOfPopulatedTicks] = PopulatedTick({ - tick: populatedTick, - liquidityNet: tickInfo.liquidityNet, - liquidityGross: tickInfo.liquidityGross + tick: populatedTick, liquidityNet: tickInfo.liquidityNet, liquidityGross: tickInfo.liquidityGross }); } } diff --git a/src/pool-cl/libraries/CLPositionInfoLibrary.sol b/src/pool-cl/libraries/CLPositionInfoLibrary.sol index fe82a8a..4c30272 100644 --- a/src/pool-cl/libraries/CLPositionInfoLibrary.sol +++ b/src/pool-cl/libraries/CLPositionInfoLibrary.sol @@ -96,11 +96,10 @@ library CLPositionInfoLibrary { { bytes25 _poolId = bytes25(PoolId.unwrap(_poolKey.toId())); assembly { - info := - or( - or(and(MASK_UPPER_200_BITS, _poolId), shl(TICK_UPPER_OFFSET, and(MASK_24_BITS, _tickUpper))), - shl(TICK_LOWER_OFFSET, and(MASK_24_BITS, _tickLower)) - ) + info := or( + or(and(MASK_UPPER_200_BITS, _poolId), shl(TICK_UPPER_OFFSET, and(MASK_24_BITS, _tickUpper))), + shl(TICK_LOWER_OFFSET, and(MASK_24_BITS, _tickLower)) + ) } } } diff --git a/src/pool-cl/libraries/LiquidityAmounts.sol b/src/pool-cl/libraries/LiquidityAmounts.sol index 3b561c3..b0bccf6 100644 --- a/src/pool-cl/libraries/LiquidityAmounts.sol +++ b/src/pool-cl/libraries/LiquidityAmounts.sol @@ -56,7 +56,9 @@ library LiquidityAmounts { uint256 amount0, uint256 amount1 ) internal pure returns (uint128 liquidity) { - if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96); + if (sqrtRatioAX96 > sqrtRatioBX96) { + (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96); + } if (sqrtRatioX96 <= sqrtRatioAX96) { liquidity = getLiquidityForAmount0(sqrtRatioAX96, sqrtRatioBX96, amount0); @@ -116,7 +118,9 @@ library LiquidityAmounts { uint160 sqrtRatioBX96, uint128 liquidity ) internal pure returns (uint256 amount0, uint256 amount1) { - if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96); + if (sqrtRatioAX96 > sqrtRatioBX96) { + (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96); + } if (sqrtRatioX96 <= sqrtRatioAX96) { amount0 = getAmount0ForLiquidity(sqrtRatioAX96, sqrtRatioBX96, liquidity); diff --git a/test/MixedQuoter.t.sol b/test/MixedQuoter.t.sol index 371e4ec..9721039 100644 --- a/test/MixedQuoter.t.sol +++ b/test/MixedQuoter.t.sol @@ -846,10 +846,7 @@ contract MixedQuoterTest is (uint256 _amountOut, uint256 _gasEstimate) = clQuoter.quoteExactInputSingle( IQuoter.QuoteExactSingleParams({ - poolKey: poolKey, - zeroForOne: true, - exactAmount: 1 ether, - hookData: ZERO_BYTES + poolKey: poolKey, zeroForOne: true, exactAmount: 1 ether, hookData: ZERO_BYTES }) ); assertEq(_amountOut, amountOut); @@ -1036,10 +1033,7 @@ contract MixedQuoterTest is (uint256 _amountOut, uint256 _gasEstimate) = clQuoter.quoteExactInputSingle( IQuoter.QuoteExactSingleParams({ - poolKey: poolKey, - zeroForOne: false, - exactAmount: 1 ether, - hookData: ZERO_BYTES + poolKey: poolKey, zeroForOne: false, exactAmount: 1 ether, hookData: ZERO_BYTES }) ); assertEq(_amountOut, amountOut); @@ -1072,10 +1066,7 @@ contract MixedQuoterTest is (uint256 _amountOut, uint256 _gasEstimate) = clQuoter.quoteExactInputSingle( IQuoter.QuoteExactSingleParams({ - poolKey: poolKeyWithWETH, - zeroForOne: true, - exactAmount: 1 ether, - hookData: ZERO_BYTES + poolKey: poolKeyWithWETH, zeroForOne: true, exactAmount: 1 ether, hookData: ZERO_BYTES }) ); assertEq(_amountOut, amountOut); @@ -1102,10 +1093,7 @@ contract MixedQuoterTest is (uint256 _amountOut, uint256 _gasEstimate) = binQuoter.quoteExactInputSingle( IQuoter.QuoteExactSingleParams({ - poolKey: binPoolKey, - zeroForOne: true, - exactAmount: 1 ether, - hookData: ZERO_BYTES + poolKey: binPoolKey, zeroForOne: true, exactAmount: 1 ether, hookData: ZERO_BYTES }) ); assertEq(_amountOut, amountOut); @@ -1285,10 +1273,7 @@ contract MixedQuoterTest is (uint256 _amountOut, uint256 _gasEstimate) = binQuoter.quoteExactInputSingle( IQuoter.QuoteExactSingleParams({ - poolKey: binPoolKey, - zeroForOne: false, - exactAmount: 1 ether, - hookData: ZERO_BYTES + poolKey: binPoolKey, zeroForOne: false, exactAmount: 1 ether, hookData: ZERO_BYTES }) ); assertEq(_amountOut, amountOut); diff --git a/test/Multicall.t.sol b/test/Multicall.t.sol index 8591c27..c2b8ae8 100644 --- a/test/Multicall.t.sol +++ b/test/Multicall.t.sol @@ -29,8 +29,9 @@ contract MulticallTest is Test { function test_multicall_firstRevert() public { bytes[] memory calls = new bytes[](2); - calls[0] = - abi.encodeWithSelector(MockMulticall(multicall).functionThatRevertsWithString.selector, "First call failed"); + calls[0] = abi.encodeWithSelector( + MockMulticall(multicall).functionThatRevertsWithString.selector, "First call failed" + ); calls[1] = abi.encodeWithSelector(MockMulticall(multicall).functionThatReturnsTuple.selector, 1, 2); vm.expectRevert("First call failed"); diff --git a/test/helpers/DeployStableSwapHelper.sol b/test/helpers/DeployStableSwapHelper.sol index 84e58dd..7be27bc 100644 --- a/test/helpers/DeployStableSwapHelper.sol +++ b/test/helpers/DeployStableSwapHelper.sol @@ -41,8 +41,12 @@ contract DeployStableSwapHelper is Script { address pancakeStableSwapLPFactory; bytes32 salt = keccak256(abi.encodePacked(msg.sender, block.timestamp)); assembly ("memory-safe") { - pancakeStableSwapLPFactory := - create2(0, add(pancakeStableSwapLPFactoryBytecode, 32), mload(pancakeStableSwapLPFactoryBytecode), salt) + pancakeStableSwapLPFactory := create2( + 0, + add(pancakeStableSwapLPFactoryBytecode, 32), + mload(pancakeStableSwapLPFactoryBytecode), + salt + ) } return pancakeStableSwapLPFactory; @@ -55,13 +59,12 @@ contract DeployStableSwapHelper is Script { address pancakeStableSwapTwoPoolDeployer; bytes32 salt = keccak256(abi.encodePacked(msg.sender, block.timestamp)); assembly ("memory-safe") { - pancakeStableSwapTwoPoolDeployer := - create2( - 0, - add(pancakeStableSwapTwoPoolDeployerBytecode, 32), - mload(pancakeStableSwapTwoPoolDeployerBytecode), - salt - ) + pancakeStableSwapTwoPoolDeployer := create2( + 0, + add(pancakeStableSwapTwoPoolDeployerBytecode, 32), + mload(pancakeStableSwapTwoPoolDeployerBytecode), + salt + ) } return pancakeStableSwapTwoPoolDeployer; @@ -74,13 +77,12 @@ contract DeployStableSwapHelper is Script { address pancakeStableSwapThreePoolDeployer; bytes32 salt = keccak256(abi.encodePacked(msg.sender, block.timestamp)); assembly ("memory-safe") { - pancakeStableSwapThreePoolDeployer := - create2( - 0, - add(pancakeStableSwapThreePoolDeployerBytecode, 32), - mload(pancakeStableSwapThreePoolDeployerBytecode), - salt - ) + pancakeStableSwapThreePoolDeployer := create2( + 0, + add(pancakeStableSwapThreePoolDeployerBytecode, 32), + mload(pancakeStableSwapThreePoolDeployerBytecode), + salt + ) } return pancakeStableSwapThreePoolDeployer; diff --git a/test/helpers/PancakeV3Router.sol b/test/helpers/PancakeV3Router.sol index 411d704..bb1fea6 100644 --- a/test/helpers/PancakeV3Router.sol +++ b/test/helpers/PancakeV3Router.sol @@ -68,15 +68,16 @@ contract PancakeV3Router { bool zeroForOne = tokenIn < tokenOut; - (int256 amount0, int256 amount1) = getPool(tokenIn, tokenOut, fee).swap( - recipient, - zeroForOne, - int256(amountIn), - sqrtPriceLimitX96 == 0 - ? (zeroForOne ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1) - : sqrtPriceLimitX96, - data - ); + (int256 amount0, int256 amount1) = getPool(tokenIn, tokenOut, fee) + .swap( + recipient, + zeroForOne, + int256(amountIn), + sqrtPriceLimitX96 == 0 + ? (zeroForOne ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1) + : sqrtPriceLimitX96, + data + ); return uint256(-(zeroForOne ? amount1 : amount0)); } diff --git a/test/libraries/CalldataDecoder.t.sol b/test/libraries/CalldataDecoder.t.sol index a589bd4..09ad08f 100644 --- a/test/libraries/CalldataDecoder.t.sol +++ b/test/libraries/CalldataDecoder.t.sol @@ -142,10 +142,7 @@ contract CalldataDecoderTest is Test { decoder.decodeCurrencyPairAndAddress(invalidParams); } - function test_fuzz_decodeCurrencyAddressAndUint256(Currency _currency, address _addr, uint256 _amount) - public - view - { + function test_fuzz_decodeCurrencyAddressAndUint256(Currency _currency, address _addr, uint256 _amount) public view { bytes memory params = abi.encode(_currency, _addr, _amount); (Currency currency, address addr, uint256 amount) = decoder.decodeCurrencyAddressAndUint256(params); diff --git a/test/mocks/MockBaseActionsRouter.sol b/test/mocks/MockBaseActionsRouter.sol index 2761c11..fa83baf 100644 --- a/test/mocks/MockBaseActionsRouter.sol +++ b/test/mocks/MockBaseActionsRouter.sol @@ -58,55 +58,107 @@ contract MockBaseActionsRouter is BaseActionsRouter { return address(0xdeadbeef); } - function _clSwap(bytes calldata /* params **/ ) internal { + function _clSwap( + bytes calldata /* params **/ + ) + internal + { clSwapCount++; } - function _binSwap(bytes calldata /* params **/ ) internal { + function _binSwap( + bytes calldata /* params **/ + ) + internal + { binSwapCount++; } - function _clIncreaseLiquidity(bytes calldata /* params **/ ) internal { + function _clIncreaseLiquidity( + bytes calldata /* params **/ + ) + internal + { clIncreaseLiqCount++; } - function _binAddLiquidity(bytes calldata /* params **/ ) internal { + function _binAddLiquidity( + bytes calldata /* params **/ + ) + internal + { binAddLiqCount++; } - function _clDecreaseLiquidity(bytes calldata /* params **/ ) internal { + function _clDecreaseLiquidity( + bytes calldata /* params **/ + ) + internal + { clDecreaseLiqCount++; } - function _binRemoveLiquidity(bytes calldata /* params **/ ) internal { + function _binRemoveLiquidity( + bytes calldata /* params **/ + ) + internal + { binRemoveLiqCount++; } - function _clDonate(bytes calldata /* params **/ ) internal { + function _clDonate( + bytes calldata /* params **/ + ) + internal + { clDonateCount++; } - function _binDonate(bytes calldata /* params **/ ) internal { + function _binDonate( + bytes calldata /* params **/ + ) + internal + { binDonateCount++; } - function _settle(bytes calldata /* params **/ ) internal { + function _settle( + bytes calldata /* params **/ + ) + internal + { settleCount++; } - function _take(bytes calldata /* params **/ ) internal { + function _take( + bytes calldata /* params **/ + ) + internal + { takeCount++; } - function _mint6909(bytes calldata /* params **/ ) internal { + function _mint6909( + bytes calldata /* params **/ + ) + internal + { mintCount++; } - function _burn6909(bytes calldata /* params **/ ) internal { + function _burn6909( + bytes calldata /* params **/ + ) + internal + { burnCount++; } - function _clear(bytes calldata /* params **/ ) internal { + function _clear( + bytes calldata /* params **/ + ) + internal + { clearCount++; } diff --git a/test/mocks/MockReentrantPositionManager.sol b/test/mocks/MockReentrantPositionManager.sol deleted file mode 100644 index 72a5b6e..0000000 --- a/test/mocks/MockReentrantPositionManager.sol +++ /dev/null @@ -1,131 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.20; - -import {Test} from "forge-std/Test.sol"; -import {PoolKey} from "infinity-core/src/types/PoolKey.sol"; -import {Currency} from "infinity-core/src/types/Currency.sol"; -import {IHooks} from "infinity-core/src/interfaces/IHooks.sol"; -import {IPoolManager} from "infinity-core/src/interfaces/IPoolManager.sol"; -import {ICLPoolManager} from "infinity-core/src/pool-cl/interfaces/ICLPoolManager.sol"; -import {IAllowanceTransfer} from "permit2/src/interfaces/IAllowanceTransfer.sol"; -import {IBinMigrator, IBaseMigrator} from "../../src/pool-bin/interfaces/IBinMigrator.sol"; -import {ICLMigrator} from "../../src/pool-cl/interfaces/ICLMigrator.sol"; - -/// @title MockReentrantPositionManager -/// @notice This contract is used to test reentrancy in PositionManager -/// @dev Can add more reentrant types if needed -contract MockReentrantPositionManager is Test { - IBinMigrator public binMigrator; - ICLMigrator public clMigrator; - IAllowanceTransfer public immutable permit2; - - // CLMigrator need to query this in constructor - ICLPoolManager public clPoolManager; - - enum ReentrantType { - BinMigrateFromV3, - BinMigrateFromV2, - CLMigrateFromV3, - CLMigrateFromV2 - } - - ReentrantType public reentrantType; - - constructor(IAllowanceTransfer _permit2) { - permit2 = _permit2; - } - - // need to set the binMigrator after binMigrator is deployed - function setBinMigrator(IBinMigrator _migrator) external { - binMigrator = _migrator; - } - - // need to set clMigrator after clMigrator is deployed - function setCLMigrator(ICLMigrator _migrator) external { - clMigrator = _migrator; - } - - // need to set clPoolManager after MockReentrantPositionManager is deployed - function setCLPoolMnager(ICLPoolManager _clPoolManager) external { - clPoolManager = _clPoolManager; - } - - function setRenentrantType(ReentrantType _type) external { - reentrantType = _type; - } - - function modifyLiquidities(bytes calldata, uint256) external payable { - IBinMigrator.InfiBinPoolParams memory infiBinPoolParams = _generateMockInfiBinPoolParams(); - - ICLMigrator.InfiCLPoolParams memory infiCLPoolParams = _generateMockInfiCLPoolParams(); - - IBaseMigrator.V3PoolParams memory v3PoolParams = _generateMockV3PoolParams(); - - IBaseMigrator.V2PoolParams memory v2PoolParams = _generateMockV2PoolParams(); - // Mock data can fulfill the requirement because it will trigger ContractLocked revert before any operations are executed - if (reentrantType == ReentrantType.BinMigrateFromV2) { - binMigrator.migrateFromV2(v2PoolParams, infiBinPoolParams, 0, 0); - } else if (reentrantType == ReentrantType.BinMigrateFromV3) { - binMigrator.migrateFromV3(v3PoolParams, infiBinPoolParams, 0, 0); - } else if (reentrantType == ReentrantType.CLMigrateFromV2) { - clMigrator.migrateFromV2(v2PoolParams, infiCLPoolParams, 0, 0); - } else if (reentrantType == ReentrantType.CLMigrateFromV3) { - clMigrator.migrateFromV3(v3PoolParams, infiCLPoolParams, 0, 0); - } - } - - function _generateMockPoolKey() internal returns (PoolKey memory) { - return PoolKey({ - currency0: Currency.wrap(makeAddr("currency0")), - currency1: Currency.wrap(makeAddr("currency1")), - hooks: IHooks(makeAddr("hook")), - poolManager: IPoolManager(makeAddr("pm")), - fee: 100, - parameters: hex"1022" - }); - } - - function _generateMockInfiBinPoolParams() internal returns (IBinMigrator.InfiBinPoolParams memory) { - return IBinMigrator.InfiBinPoolParams({ - poolKey: _generateMockPoolKey(), - amount0Max: type(uint128).max, - amount1Max: type(uint128).max, - activeIdDesired: 0, - idSlippage: 0, - deltaIds: new int256[](0), - distributionX: new uint256[](0), - distributionY: new uint256[](0), - to: address(0), - deadline: 0, - hookData: new bytes(0) - }); - } - - function _generateMockInfiCLPoolParams() internal returns (ICLMigrator.InfiCLPoolParams memory) { - return ICLMigrator.InfiCLPoolParams({ - poolKey: _generateMockPoolKey(), - tickLower: 0, - tickUpper: 0, - liquidityMin: 0, - recipient: address(0), - deadline: 0, - hookData: new bytes(0) - }); - } - - function _generateMockV3PoolParams() internal pure returns (IBaseMigrator.V3PoolParams memory) { - return IBaseMigrator.V3PoolParams({ - nfp: address(0), - tokenId: 0, - liquidity: 0, - amount0Min: 0, - amount1Min: 0, - collectFee: false, - deadline: 0 - }); - } - - function _generateMockV2PoolParams() internal pure returns (IBaseMigrator.V2PoolParams memory) { - return IBaseMigrator.V2PoolParams({pair: address(0), migrateAmount: 0, amount0Min: 0, amount1Min: 0}); - } -} diff --git a/test/pool-bin/BinPositionManager_ModifyLiquidites.t.sol b/test/pool-bin/BinPositionManager_ModifyLiquidites.t.sol index 15181c7..ff51139 100644 --- a/test/pool-bin/BinPositionManager_ModifyLiquidites.t.sol +++ b/test/pool-bin/BinPositionManager_ModifyLiquidites.t.sol @@ -880,9 +880,7 @@ contract BinPositionManager_ModifyLiquidityTest is BinLiquidityHelper, TokenFixt ); } - function _test_transferLiquidityToken_revertIfVaultLocked(uint256[] memory ids, uint256[] memory amounts) - external - { + function _test_transferLiquidityToken_revertIfVaultLocked(uint256[] memory ids, uint256[] memory amounts) external { vm.expectRevert(IPositionManager.VaultMustBeUnlocked.selector); binPm.batchTransferFrom(address(this), makeAddr("someone"), ids, amounts); } @@ -909,19 +907,19 @@ contract BinPositionManager_ModifyLiquidityTest is BinLiquidityHelper, TokenFixt fotKey, binIds, uint128(amountAfterTransfer), uint128(amountAfterTransfer), activeId, address(this) ); - IBinPositionManager.BinAddLiquidityFromDeltasParams memory param = IBinPositionManager - .BinAddLiquidityFromDeltasParams({ - poolKey: _param.poolKey, - amount0Max: _param.amount0Max, - amount1Max: _param.amount1Max, - activeIdDesired: _param.activeIdDesired, - idSlippage: _param.idSlippage, - deltaIds: _param.deltaIds, - distributionX: _param.distributionX, - distributionY: _param.distributionY, - to: _param.to, - hookData: _param.hookData - }); + IBinPositionManager.BinAddLiquidityFromDeltasParams memory param = + IBinPositionManager.BinAddLiquidityFromDeltasParams({ + poolKey: _param.poolKey, + amount0Max: _param.amount0Max, + amount1Max: _param.amount1Max, + activeIdDesired: _param.activeIdDesired, + idSlippage: _param.idSlippage, + deltaIds: _param.deltaIds, + distributionX: _param.distributionX, + distributionY: _param.distributionY, + to: _param.to, + hookData: _param.hookData + }); planner.add(Actions.BIN_ADD_LIQUIDITY_FROM_DELTAS, abi.encode(param)); bytes memory plan = planner.encode(); @@ -958,19 +956,19 @@ contract BinPositionManager_ModifyLiquidityTest is BinLiquidityHelper, TokenFixt IBinPositionManager.BinAddLiquidityParams memory _param = _getAddParams(key1, binIds, uint128(amountToSend), uint128(amountToSend), activeId, address(this)); - IBinPositionManager.BinAddLiquidityFromDeltasParams memory param = IBinPositionManager - .BinAddLiquidityFromDeltasParams({ - poolKey: _param.poolKey, - amount0Max: _param.amount0Max, - amount1Max: _param.amount1Max, - activeIdDesired: _param.activeIdDesired, - idSlippage: _param.idSlippage, - deltaIds: _param.deltaIds, - distributionX: _param.distributionX, - distributionY: _param.distributionY, - to: _param.to, - hookData: _param.hookData - }); + IBinPositionManager.BinAddLiquidityFromDeltasParams memory param = + IBinPositionManager.BinAddLiquidityFromDeltasParams({ + poolKey: _param.poolKey, + amount0Max: _param.amount0Max, + amount1Max: _param.amount1Max, + activeIdDesired: _param.activeIdDesired, + idSlippage: _param.idSlippage, + deltaIds: _param.deltaIds, + distributionX: _param.distributionX, + distributionY: _param.distributionY, + to: _param.to, + hookData: _param.hookData + }); planner.add(Actions.BIN_ADD_LIQUIDITY_FROM_DELTAS, abi.encode(param)); bytes memory plan = planner.encode(); @@ -1025,19 +1023,19 @@ contract BinPositionManager_ModifyLiquidityTest is BinLiquidityHelper, TokenFixt IBinPositionManager.BinAddLiquidityParams memory _param = _getAddParams( fotKey, binIds, uint128(amount0AfterTransfer), uint128(amount1AfterTransfer), activeId, address(this) ); - IBinPositionManager.BinAddLiquidityFromDeltasParams memory param = IBinPositionManager - .BinAddLiquidityFromDeltasParams({ - poolKey: _param.poolKey, - amount0Max: _param.amount0Max, - amount1Max: _param.amount1Max, - activeIdDesired: _param.activeIdDesired, - idSlippage: _param.idSlippage, - deltaIds: _param.deltaIds, - distributionX: _param.distributionX, - distributionY: _param.distributionY, - to: _param.to, - hookData: _param.hookData - }); + IBinPositionManager.BinAddLiquidityFromDeltasParams memory param = + IBinPositionManager.BinAddLiquidityFromDeltasParams({ + poolKey: _param.poolKey, + amount0Max: _param.amount0Max, + amount1Max: _param.amount1Max, + activeIdDesired: _param.activeIdDesired, + idSlippage: _param.idSlippage, + deltaIds: _param.deltaIds, + distributionX: _param.distributionX, + distributionY: _param.distributionY, + to: _param.to, + hookData: _param.hookData + }); planner.add(Actions.BIN_ADD_LIQUIDITY_FROM_DELTAS, abi.encode(param)); bytes memory plan = planner.encode(); diff --git a/test/pool-bin/BinPositionManager_MultiCall.t.sol b/test/pool-bin/BinPositionManager_MultiCall.t.sol index 8b0b9c2..0b845ed 100644 --- a/test/pool-bin/BinPositionManager_MultiCall.t.sol +++ b/test/pool-bin/BinPositionManager_MultiCall.t.sol @@ -32,12 +32,7 @@ import {BaseActionsRouter} from "../../src/base/BaseActionsRouter.sol"; import {Permit2SignatureHelpers} from "../shared/Permit2SignatureHelpers.sol"; import {IWETH9} from "../../src/interfaces/external/IWETH9.sol"; -contract BinPositionManager_MultiCallTest is - Permit2SignatureHelpers, - BinLiquidityHelper, - TokenFixture, - DeployPermit2 -{ +contract BinPositionManager_MultiCallTest is Permit2SignatureHelpers, BinLiquidityHelper, TokenFixture, DeployPermit2 { using BinPoolParametersHelper for bytes32; using SafeCast for uint256; using BinTokenLibrary for PoolId; diff --git a/test/pool-bin/BinQuoter.t.sol b/test/pool-bin/BinQuoter.t.sol index 43c8616..57378f5 100644 --- a/test/pool-bin/BinQuoter.t.sol +++ b/test/pool-bin/BinQuoter.t.sol @@ -166,10 +166,7 @@ contract BinQuoterTest is Test, BinLiquidityHelper, DeployPermit2 { (uint256 _amountOut, uint256 _gasEstimate) = quoter.quoteExactInputSingle( IQuoter.QuoteExactSingleParams({ - poolKey: key3, - zeroForOne: true, - exactAmount: 1 ether, - hookData: new bytes(0) + poolKey: key3, zeroForOne: true, exactAmount: 1 ether, hookData: new bytes(0) }) ); @@ -211,10 +208,7 @@ contract BinQuoterTest is Test, BinLiquidityHelper, DeployPermit2 { (uint256 _amountOut, uint256 _gasEstimate) = quoter.quoteExactInputSingle( IQuoter.QuoteExactSingleParams({ - poolKey: key3, - zeroForOne: false, - exactAmount: 1 ether, - hookData: new bytes(0) + poolKey: key3, zeroForOne: false, exactAmount: 1 ether, hookData: new bytes(0) }) ); @@ -273,9 +267,7 @@ contract BinQuoterTest is Test, BinLiquidityHelper, DeployPermit2 { (uint256 _amountOut, uint256 _gasEstimate) = quoter.quoteExactInput( IQuoter.QuoteExactParams({ - exactCurrency: Currency.wrap(address(token0)), - path: quoter_path, - exactAmount: 1 ether + exactCurrency: Currency.wrap(address(token0)), path: quoter_path, exactAmount: 1 ether }) ); @@ -349,9 +341,7 @@ contract BinQuoterTest is Test, BinLiquidityHelper, DeployPermit2 { (uint256 _amountOut, uint256 _gasEstimate) = quoter.quoteExactInput( IQuoter.QuoteExactParams({ - exactCurrency: Currency.wrap(address(token0)), - path: quoter_path, - exactAmount: 1 ether + exactCurrency: Currency.wrap(address(token0)), path: quoter_path, exactAmount: 1 ether }) ); @@ -389,10 +379,7 @@ contract BinQuoterTest is Test, BinLiquidityHelper, DeployPermit2 { (uint256 _amountIn, uint256 _gasEstimate) = quoter.quoteExactOutputSingle( IQuoter.QuoteExactSingleParams({ - poolKey: key, - zeroForOne: true, - exactAmount: 0.5 ether, - hookData: new bytes(0) + poolKey: key, zeroForOne: true, exactAmount: 0.5 ether, hookData: new bytes(0) }) ); @@ -433,10 +420,7 @@ contract BinQuoterTest is Test, BinLiquidityHelper, DeployPermit2 { (uint256 _amountIn, uint256 _gasEstimate) = quoter.quoteExactOutputSingle( IQuoter.QuoteExactSingleParams({ - poolKey: key, - zeroForOne: false, - exactAmount: 0.5 ether, - hookData: new bytes(0) + poolKey: key, zeroForOne: false, exactAmount: 0.5 ether, hookData: new bytes(0) }) ); @@ -502,9 +486,7 @@ contract BinQuoterTest is Test, BinLiquidityHelper, DeployPermit2 { (uint256 _amountIn, uint256 _gasEstimate) = quoter.quoteExactOutput( IQuoter.QuoteExactParams({ - exactCurrency: Currency.wrap(address(token1)), - path: quoter_path, - exactAmount: 0.5 ether + exactCurrency: Currency.wrap(address(token1)), path: quoter_path, exactAmount: 0.5 ether }) ); @@ -583,9 +565,7 @@ contract BinQuoterTest is Test, BinLiquidityHelper, DeployPermit2 { (uint256 _amountIn, uint256 _gasEstimate) = quoter.quoteExactOutput( IQuoter.QuoteExactParams({ - exactCurrency: Currency.wrap(address(token2)), - path: quoter_path, - exactAmount: 0.5 ether + exactCurrency: Currency.wrap(address(token2)), path: quoter_path, exactAmount: 0.5 ether }) ); @@ -642,10 +622,7 @@ contract BinQuoterTest is Test, BinLiquidityHelper, DeployPermit2 { quoter._quoteExactInputSingle( IQuoter.QuoteExactSingleParams({ - poolKey: key3, - zeroForOne: true, - exactAmount: 1 ether, - hookData: new bytes(0) + poolKey: key3, zeroForOne: true, exactAmount: 1 ether, hookData: new bytes(0) }) ); } @@ -661,10 +638,7 @@ contract BinQuoterTest is Test, BinLiquidityHelper, DeployPermit2 { ); quoter.quoteExactOutputSingle( IQuoter.QuoteExactSingleParams({ - poolKey: key, - zeroForOne: true, - exactAmount: 20 ether, - hookData: new bytes(0) + poolKey: key, zeroForOne: true, exactAmount: 20 ether, hookData: new bytes(0) }) ); } diff --git a/test/pool-bin/libraries/BinCalldataDecoder.t.sol b/test/pool-bin/libraries/BinCalldataDecoder.t.sol index bef7d97..f3cb450 100644 --- a/test/pool-bin/libraries/BinCalldataDecoder.t.sol +++ b/test/pool-bin/libraries/BinCalldataDecoder.t.sol @@ -89,10 +89,7 @@ contract BinCalldataDecoderTest is Test { function test_decodeBinSwapExactInParams_outOfBounds() public { PathKey[] memory path = new PathKey[](0); IInfinityRouter.BinSwapExactInputParams memory _swapParams = IBinRouterBase.BinSwapExactInputParams({ - currencyIn: Currency.wrap(makeAddr("currencyIn")), - path: path, - amountIn: 1 ether, - amountOutMinimum: 1 ether + currencyIn: Currency.wrap(makeAddr("currencyIn")), path: path, amountIn: 1 ether, amountOutMinimum: 1 ether }); /// @dev params.length is 192 as abi.encode adds 32 bytes for dynamic field. However ether.js doesn't add 32 bytes @@ -123,11 +120,7 @@ contract BinCalldataDecoderTest is Test { function test_fuzz_decodeBinSwapExactInSingleParams_outOfBounds(PoolKey memory key) public { IInfinityRouter.BinSwapExactInputSingleParams memory _swapParams = IBinRouterBase.BinSwapExactInputSingleParams({ - poolKey: key, - swapForY: true, - amountIn: 1 ether, - amountOutMinimum: 1 ether, - hookData: "" + poolKey: key, swapForY: true, amountIn: 1 ether, amountOutMinimum: 1 ether, hookData: "" }); /// @dev params.length is 384 as abi.encode adds 32 bytes for dynamic field. However ether.js doesn't add 32 bytes @@ -191,13 +184,8 @@ contract BinCalldataDecoderTest is Test { } function test_fuzz_decodeBinSwapExactOutSingleParams_outOfBounds(PoolKey memory key) public { - IInfinityRouter.BinSwapExactOutputSingleParams memory _swapParams = IBinRouterBase - .BinSwapExactOutputSingleParams({ - poolKey: key, - swapForY: true, - amountOut: 1 ether, - amountInMaximum: 1 ether, - hookData: "" + IInfinityRouter.BinSwapExactOutputSingleParams memory _swapParams = IBinRouterBase.BinSwapExactOutputSingleParams({ + poolKey: key, swapForY: true, amountOut: 1 ether, amountInMaximum: 1 ether, hookData: "" }); /// @dev params.length is 384 as abi.encode adds 32 bytes for dynamic field. However ether.js doesn't add 32 bytes diff --git a/test/pool-bin/migrator/BinMigratorFromPancakeswapV2.t.sol b/test/pool-bin/migrator/BinMigratorFromPancakeswapV2.t.sol deleted file mode 100644 index ed6d4ad..0000000 --- a/test/pool-bin/migrator/BinMigratorFromPancakeswapV2.t.sol +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.24; - -import "forge-std/Test.sol"; -import {BinMigratorFromV2} from "./BinMigratorFromV2.sol"; - -contract BinMigratorFromPancakeswapV2Test is BinMigratorFromV2 { - function _getBytecodePath() internal pure override returns (string memory) { - // Create a Pancakeswap V2 pair - // relative to the root of the project - // https://etherscan.io/address/0x1097053Fd2ea711dad45caCcc45EfF7548fCB362#code - return "./test/bin/pcsV2Factory.bytecode"; - } - - function _getContractName() internal pure override returns (string memory) { - return "BinMigratorFromPancakeswapV2Test"; - } -} diff --git a/test/pool-bin/migrator/BinMigratorFromPancakeswapV3.t.sol b/test/pool-bin/migrator/BinMigratorFromPancakeswapV3.t.sol deleted file mode 100644 index 4246ee3..0000000 --- a/test/pool-bin/migrator/BinMigratorFromPancakeswapV3.t.sol +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.24; - -import "forge-std/Test.sol"; -import {BinMigratorFromV3} from "./BinMigratorFromV3.sol"; - -contract BinMigratorFromPancakeswapV3Test is BinMigratorFromV3 { - function _getDeployerBytecodePath() internal pure override returns (string memory) { - // https://etherscan.io/address/0x41ff9AA7e16B8B1a8a8dc4f0eFacd93D02d071c9#code - return "./test/bin/pcsV3Deployer.bytecode"; - } - - function _getFactoryBytecodePath() internal pure override returns (string memory) { - // https://etherscan.io/address/0x0BFbCF9fa4f9C56B0F40a671Ad40E0805A091865#code - return "./test/bin/pcsV3Factory.bytecode"; - } - - function _getNfpmBytecodePath() internal pure override returns (string memory) { - // https://etherscan.io/address/0x46A15B0b27311cedF172AB29E4f4766fbE7F4364#code - return "./test/bin/pcsV3Nfpm.bytecode"; - } - - function _getContractName() internal pure override returns (string memory) { - return "BinMigratorFromPancakeswapV3Test"; - } -} diff --git a/test/pool-bin/migrator/BinMigratorFromUniswapV2.t.sol b/test/pool-bin/migrator/BinMigratorFromUniswapV2.t.sol deleted file mode 100644 index 9173c32..0000000 --- a/test/pool-bin/migrator/BinMigratorFromUniswapV2.t.sol +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.24; - -import "forge-std/Test.sol"; -import {BinMigratorFromV2} from "./BinMigratorFromV2.sol"; - -contract BinMigratorFromUniswapV2Test is BinMigratorFromV2 { - function _getBytecodePath() internal pure override returns (string memory) { - // Create a Uniswap V2 pair - // relative to the root of the project - // https://etherscan.io/address/0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f#code - return "./test/bin/uniV2Factory.bytecode"; - } - - function _getContractName() internal pure override returns (string memory) { - return "BinMigratorFromUniswapV2Test"; - } -} diff --git a/test/pool-bin/migrator/BinMigratorFromUniswapV3.t.sol b/test/pool-bin/migrator/BinMigratorFromUniswapV3.t.sol deleted file mode 100644 index 488131e..0000000 --- a/test/pool-bin/migrator/BinMigratorFromUniswapV3.t.sol +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.24; - -import "forge-std/Test.sol"; -import {BinMigratorFromV3} from "./BinMigratorFromV3.sol"; - -contract BinMigratorFromUniswapV3Test is BinMigratorFromV3 { - function _getDeployerBytecodePath() internal pure override returns (string memory) { - return ""; - } - - function _getFactoryBytecodePath() internal pure override returns (string memory) { - // https://etherscan.io/address/0x1F98431c8aD98523631AE4a59f267346ea31F984#code - return "./test/bin/uniV3Factory.bytecode"; - } - - function _getNfpmBytecodePath() internal pure override returns (string memory) { - // https://etherscan.io/address/0xC36442b4a4522E871399CD717aBDD847Ab11FE88#code - return "./test/bin/uniV3Nfpm.bytecode"; - } - - function _getContractName() internal pure override returns (string memory) { - return "BinMigratorFromUniswapV3Test"; - } -} diff --git a/test/pool-bin/migrator/BinMigratorFromV2.sol b/test/pool-bin/migrator/BinMigratorFromV2.sol deleted file mode 100644 index d40bbf6..0000000 --- a/test/pool-bin/migrator/BinMigratorFromV2.sol +++ /dev/null @@ -1,1148 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.24; - -import "forge-std/Test.sol"; -import {OldVersionHelper} from "../../helpers/OldVersionHelper.sol"; -import {IPancakePair} from "../../../src/interfaces/external/IPancakePair.sol"; -import {WETH} from "solmate/src/tokens/WETH.sol"; -import {MockERC20} from "solmate/src/test/utils/mocks/MockERC20.sol"; -import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import {BinMigrator} from "../../../src/pool-bin/BinMigrator.sol"; -import {IBinMigrator, IBaseMigrator} from "../../../src/pool-bin/interfaces/IBinMigrator.sol"; -import {IBinPositionManager} from "../../../src/pool-bin/interfaces/IBinPositionManager.sol"; -import {BinPositionManager} from "../../../src/pool-bin/BinPositionManager.sol"; -import {IVault} from "infinity-core/src/interfaces/IVault.sol"; -import {Vault} from "infinity-core/src/Vault.sol"; -import {IBinPoolManager} from "infinity-core/src/pool-bin/interfaces/IBinPoolManager.sol"; -import {BinPoolManager} from "infinity-core/src/pool-bin/BinPoolManager.sol"; -import {PoolKey} from "infinity-core/src/types/PoolKey.sol"; -import {BinPoolParametersHelper} from "infinity-core/src/pool-bin/libraries/BinPoolParametersHelper.sol"; -import {Currency} from "infinity-core/src/types/Currency.sol"; -import {IPoolManager} from "infinity-core/src/interfaces/IPoolManager.sol"; -import {IHooks} from "infinity-core/src/interfaces/IHooks.sol"; -import {PoolId, PoolIdLibrary} from "infinity-core/src/types/PoolId.sol"; -import {BinLiquidityHelper} from "../helper/BinLiquidityHelper.sol"; -import {BinTokenLibrary} from "../../../src/pool-bin/libraries/BinTokenLibrary.sol"; -import {Plan, Planner} from "../../../src/libraries/Planner.sol"; -import {Actions} from "../../../src/libraries/Actions.sol"; -import {SafeCallback} from "../../../src/base/SafeCallback.sol"; -import {IAllowanceTransfer} from "permit2/src/interfaces/IAllowanceTransfer.sol"; -import {DeployPermit2} from "permit2/test/utils/DeployPermit2.sol"; -import {MockReentrantPositionManager} from "../../mocks/MockReentrantPositionManager.sol"; -import {ReentrancyLock} from "../../../src/base/ReentrancyLock.sol"; -import {Permit2ApproveHelper} from "../../helpers/Permit2ApproveHelper.sol"; -import {Permit2SignatureHelpers} from "../../shared/Permit2SignatureHelpers.sol"; -import {Permit2Forwarder} from "../../../src/base/Permit2Forwarder.sol"; -import {IPositionManager} from "../../../src/interfaces/IPositionManager.sol"; -import {Pausable} from "infinity-core/src/base/Pausable.sol"; -import {MockBinMigratorHook} from "./mocks/MockBinMigratorHook.sol"; -import {IWETH9} from "../../../src/interfaces/external/IWETH9.sol"; - -interface IPancakeV2LikePairFactory { - function createPair(address tokenA, address tokenB) external returns (address pair); -} - -abstract contract BinMigratorFromV2 is - OldVersionHelper, - BinLiquidityHelper, - DeployPermit2, - Permit2ApproveHelper, - Permit2SignatureHelpers -{ - using BinPoolParametersHelper for bytes32; - using BinTokenLibrary for PoolId; - - // 1 tokenX = 1 tokenY - uint24 public constant ACTIVE_BIN_ID = 2 ** 23; - - WETH weth; - MockERC20 token0; - MockERC20 token1; - - Vault vault; - BinPoolManager poolManager; - BinPositionManager binPm; - IAllowanceTransfer permit2; - IBinMigrator migrator; - PoolKey poolKey; - PoolKey poolKeyWithoutNativeToken; - MockBinMigratorHook binMigratorHook; - - IPancakeV2LikePairFactory v2Factory; - IPancakePair v2Pair; - IPancakePair v2PairWithoutNativeToken; - bytes32 PERMIT2_DOMAIN_SEPARATOR; - - function _getBytecodePath() internal pure virtual returns (string memory); - - function _getContractName() internal pure virtual returns (string memory); - - function setUp() public { - weth = new WETH(); - token0 = new MockERC20("Token0", "TKN0", 18); - token1 = new MockERC20("Token1", "TKN1", 18); - (token0, token1) = token0 < token1 ? (token0, token1) : (token1, token0); - - // init infinity nfpm & migrator - vault = new Vault(); - poolManager = new BinPoolManager(IVault(address(vault))); - vault.registerApp(address(poolManager)); - permit2 = IAllowanceTransfer(deployPermit2()); - binPm = new BinPositionManager( - IVault(address(vault)), IBinPoolManager(address(poolManager)), permit2, IWETH9(address(0)) - ); - migrator = new BinMigrator(address(weth), address(binPm), permit2); - binMigratorHook = new MockBinMigratorHook(); - - PERMIT2_DOMAIN_SEPARATOR = permit2.DOMAIN_SEPARATOR(); - - poolKey = PoolKey({ - // WETH after migration will be native token - currency0: Currency.wrap(address(0)), - currency1: Currency.wrap(address(token0)), - hooks: IHooks(address(binMigratorHook)), - poolManager: poolManager, - fee: 0, - parameters: bytes32(uint256(binMigratorHook.getHooksRegistrationBitmap())).setBinStep(1) - }); - - poolKeyWithoutNativeToken = PoolKey({ - currency0: Currency.wrap(address(token0)), - currency1: Currency.wrap(address(token1)), - hooks: IHooks(address(0)), - poolManager: poolManager, - fee: 0, - parameters: bytes32(0).setBinStep(1) - }); - - // make sure the contract has enough balance - // WETH: 100 ether - // Token: 100 ether - // ETH: 90 ether - deal(address(this), 1000 ether); - weth.deposit{value: 100 ether}(); - token0.mint(address(this), 100 ether); - token1.mint(address(this), 100 ether); - - v2Factory = IPancakeV2LikePairFactory(createContractThroughBytecode(_getBytecodePath())); - v2Pair = IPancakePair(v2Factory.createPair(address(weth), address(token0))); - v2PairWithoutNativeToken = IPancakePair(v2Factory.createPair(address(token0), address(token1))); - } - - function testMigrateFromV2_Owner() public { - // casted as owner/transferOwnership not in IBinMigrator interface - BinMigrator _migrator = BinMigrator(payable(address(migrator))); - assertEq(_migrator.owner(), address(this)); - - address alice = makeAddr("alice"); - _migrator.transferOwnership(alice); - - assertEq(_migrator.owner(), alice); - } - - function testMigrateFromV2_WhenPaused() public { - // 1. mint some liquidity to the v2 pair - _mintV2Liquidity(v2Pair); - uint256 lpTokenBefore = v2Pair.balanceOf(address(this)); - - // 2. make sure migrator can transfer user's v2 lp token - permit2ApproveWithSpecificAllowance( - address(this), permit2, address(v2Pair), address(migrator), lpTokenBefore, uint160(lpTokenBefore) - ); - - // 3. initialize the pool - migrator.initializePool(poolKey, ACTIVE_BIN_ID); - - IBaseMigrator.V2PoolParams memory v2PoolParams = IBaseMigrator.V2PoolParams({ - pair: address(v2Pair), - migrateAmount: lpTokenBefore, - // minor precision loss is acceptable - amount0Min: 9.999 ether, - amount1Min: 9.999 ether - }); - - IBinPositionManager.BinAddLiquidityParams memory params = - _getAddParams(poolKey, getBinIds(ACTIVE_BIN_ID, 3), 10 ether, 10 ether, ACTIVE_BIN_ID, address(this)); - - IBinMigrator.InfiBinPoolParams memory infiBinPoolParams = IBinMigrator.InfiBinPoolParams({ - poolKey: params.poolKey, - amount0Max: params.amount0Max, - amount1Max: params.amount1Max, - activeIdDesired: params.activeIdDesired, - idSlippage: params.idSlippage, - deltaIds: params.deltaIds, - distributionX: params.distributionX, - distributionY: params.distributionY, - to: params.to, - deadline: block.timestamp + 1, - hookData: new bytes(0) - }); - - // pre-req: pause - BinMigrator _migrator = BinMigrator(payable(address(migrator))); - _migrator.pause(); - - // 4. migrateFromV2 - vm.expectRevert(Pausable.EnforcedPause.selector); - migrator.migrateFromV2(v2PoolParams, infiBinPoolParams, 0, 0); - } - - function testMigrateFromV2_HookData() public { - // 1. mint some liquidity to the v2 pair - _mintV2Liquidity(v2Pair); - uint256 lpTokenBefore = v2Pair.balanceOf(address(this)); - - // 2. make sure migrator can transfer user's v2 lp token - permit2ApproveWithSpecificAllowance( - address(this), permit2, address(v2Pair), address(migrator), lpTokenBefore, uint160(lpTokenBefore) - ); - - // 3. initialize the pool - migrator.initializePool(poolKey, ACTIVE_BIN_ID); - - IBaseMigrator.V2PoolParams memory v2PoolParams = IBaseMigrator.V2PoolParams({ - pair: address(v2Pair), - migrateAmount: lpTokenBefore, - // minor precision loss is acceptable - amount0Min: 9.999 ether, - amount1Min: 9.999 ether - }); - - IBinPositionManager.BinAddLiquidityParams memory params = - _getAddParams(poolKey, getBinIds(ACTIVE_BIN_ID, 3), 10 ether, 10 ether, ACTIVE_BIN_ID, address(this)); - - bytes memory hookData = abi.encode(32); - IBinMigrator.InfiBinPoolParams memory infiBinPoolParams = IBinMigrator.InfiBinPoolParams({ - poolKey: params.poolKey, - amount0Max: params.amount0Max, - amount1Max: params.amount1Max, - activeIdDesired: params.activeIdDesired, - idSlippage: params.idSlippage, - deltaIds: params.deltaIds, - distributionX: params.distributionX, - distributionY: params.distributionY, - to: params.to, - deadline: block.timestamp + 1, - hookData: hookData - }); - - // 4. migrateFromV2 - migrator.migrateFromV2(v2PoolParams, infiBinPoolParams, 0, 0); - - // assert hookData flown to hook - assertEq(binMigratorHook.hookData(), hookData); - } - - function testMigrateFromV2ReentrancyLockRevert() public { - MockReentrantPositionManager reentrantPM = new MockReentrantPositionManager(permit2); - migrator = new BinMigrator(address(weth), address(reentrantPM), permit2); - reentrantPM.setBinMigrator(migrator); - reentrantPM.setRenentrantType(MockReentrantPositionManager.ReentrantType.BinMigrateFromV2); - // 1. mint some liquidity to the v2 pair - _mintV2Liquidity(v2Pair); - uint256 lpTokenBefore = v2Pair.balanceOf(address(this)); - assertGt(lpTokenBefore, 0); - - // 2. make sure migrator can transfer user's v2 lp token - permit2ApproveWithSpecificAllowance( - address(this), permit2, address(v2Pair), address(migrator), lpTokenBefore, uint160(lpTokenBefore) - ); - - // 3. initialize the pool - poolManager.initialize(poolKey, ACTIVE_BIN_ID); - - IBaseMigrator.V2PoolParams memory v2PoolParams = IBaseMigrator.V2PoolParams({ - pair: address(v2Pair), - migrateAmount: lpTokenBefore, - // minor precision loss is acceptable - amount0Min: 9.999 ether, - amount1Min: 9.999 ether - }); - - IBinPositionManager.BinAddLiquidityParams memory params = - _getAddParams(poolKey, getBinIds(ACTIVE_BIN_ID, 3), 10 ether, 10 ether, ACTIVE_BIN_ID, address(this)); - - IBinMigrator.InfiBinPoolParams memory infiBinPoolParams = IBinMigrator.InfiBinPoolParams({ - poolKey: params.poolKey, - amount0Max: params.amount0Max, - amount1Max: params.amount1Max, - activeIdDesired: params.activeIdDesired, - idSlippage: params.idSlippage, - deltaIds: params.deltaIds, - distributionX: params.distributionX, - distributionY: params.distributionY, - to: params.to, - deadline: block.timestamp + 1, - hookData: new bytes(0) - }); - - vm.expectRevert(ReentrancyLock.ContractLocked.selector); - migrator.migrateFromV2(v2PoolParams, infiBinPoolParams, 0, 0); - } - - function testMigrateFromV2IncludingInit() public { - // 1. mint some liquidity to the v2 pair - _mintV2Liquidity(v2Pair); - uint256 lpTokenBefore = v2Pair.balanceOf(address(this)); - assertGt(lpTokenBefore, 0); - - // 2. make sure migrator can transfer user's v2 lp token - permit2ApproveWithSpecificAllowance( - address(this), permit2, address(v2Pair), address(migrator), lpTokenBefore, uint160(lpTokenBefore) - ); - - IBaseMigrator.V2PoolParams memory v2PoolParams = IBaseMigrator.V2PoolParams({ - pair: address(v2Pair), - migrateAmount: lpTokenBefore, - // minor precision loss is acceptable - amount0Min: 9.999 ether, - amount1Min: 9.999 ether - }); - - IBinPositionManager.BinAddLiquidityParams memory params = - _getAddParams(poolKey, getBinIds(ACTIVE_BIN_ID, 3), 10 ether, 10 ether, ACTIVE_BIN_ID, address(this)); - - IBinMigrator.InfiBinPoolParams memory infiBinPoolParams = IBinMigrator.InfiBinPoolParams({ - poolKey: params.poolKey, - amount0Max: params.amount0Max, - amount1Max: params.amount1Max, - activeIdDesired: params.activeIdDesired, - idSlippage: params.idSlippage, - deltaIds: params.deltaIds, - distributionX: params.distributionX, - distributionY: params.distributionY, - to: params.to, - deadline: block.timestamp + 1, - hookData: new bytes(0) - }); - - // 3. multicall, combine initialize and migrateFromV2 - bytes[] memory data = new bytes[](2); - data[0] = abi.encodeWithSelector(migrator.initializePool.selector, poolKey, ACTIVE_BIN_ID, bytes("")); - data[1] = abi.encodeWithSelector(migrator.migrateFromV2.selector, v2PoolParams, infiBinPoolParams, 0, 0); - - migrator.multicall(data); - vm.snapshotGasLastCall("testMigrateFromV2IncludingInit"); - - // necessary checks - // v2 pair should be burned already - assertEq(v2Pair.balanceOf(address(this)), 0); - - // make sure liuqidty is minted to the correct pool - assertApproxEqAbs(address(vault).balance, 10 ether, 0.000001 ether); - assertApproxEqAbs(token0.balanceOf(address(vault)), 10 ether, 0.000001 ether); - - uint256 positionId0 = poolKey.toId().toTokenId(ACTIVE_BIN_ID - 1); - uint256 positionId1 = poolKey.toId().toTokenId(ACTIVE_BIN_ID); - uint256 positionId2 = poolKey.toId().toTokenId(ACTIVE_BIN_ID + 1); - uint256 positionId3 = poolKey.toId().toTokenId(ACTIVE_BIN_ID + 2); - assertGt(binPm.balanceOf(address(this), positionId0), 0); - assertGt(binPm.balanceOf(address(this), positionId1), 0); - assertGt(binPm.balanceOf(address(this), positionId2), 0); - assertEq(binPm.balanceOf(address(this), positionId3), 0); - - (PoolKey memory _poolKey, uint24 binId) = binPm.positions(positionId0); - assertEq(PoolId.unwrap(_poolKey.toId()), PoolId.unwrap(poolKey.toId())); - assertEq(Currency.unwrap(_poolKey.currency0), address(0)); - assertEq(Currency.unwrap(_poolKey.currency1), address(token0)); - assertEq(_poolKey.fee, 0); - assertEq(binId, ACTIVE_BIN_ID - 1); - - (_poolKey, binId) = binPm.positions(positionId1); - assertEq(PoolId.unwrap(_poolKey.toId()), PoolId.unwrap(poolKey.toId())); - assertEq(Currency.unwrap(_poolKey.currency0), address(0)); - assertEq(Currency.unwrap(_poolKey.currency1), address(token0)); - assertEq(_poolKey.fee, 0); - assertEq(binId, ACTIVE_BIN_ID); - - (_poolKey, binId) = binPm.positions(positionId2); - assertEq(PoolId.unwrap(_poolKey.toId()), PoolId.unwrap(poolKey.toId())); - assertEq(Currency.unwrap(_poolKey.currency0), address(0)); - assertEq(Currency.unwrap(_poolKey.currency1), address(token0)); - assertEq(_poolKey.fee, 0); - assertEq(binId, ACTIVE_BIN_ID + 1); - - vm.expectRevert(IPositionManager.InvalidTokenID.selector); - binPm.positions(positionId3); - } - - function testMigrateFromV2TokenMismatch() public { - // 1. mint some liquidity to the v2 pair - _mintV2Liquidity(v2Pair); - uint256 lpTokenBefore = v2Pair.balanceOf(address(this)); - assertGt(lpTokenBefore, 0); - - // 2. make sure migrator can transfer user's v2 lp token - permit2ApproveWithSpecificAllowance( - address(this), permit2, address(v2Pair), address(migrator), lpTokenBefore, uint160(lpTokenBefore) - ); - - IBaseMigrator.V2PoolParams memory v2PoolParams = IBaseMigrator.V2PoolParams({ - pair: address(v2Pair), - migrateAmount: lpTokenBefore, - // minor precision loss is acceptable - amount0Min: 9.999 ether, - amount1Min: 9.999 ether - }); - - IBinPositionManager.BinAddLiquidityParams memory params = - _getAddParams(poolKey, getBinIds(ACTIVE_BIN_ID, 3), 10 ether, 10 ether, ACTIVE_BIN_ID, address(this)); - - // v2 weth, token0 - // infinity ETH, token1 - PoolKey memory poolKeyMismatch = poolKey; - poolKeyMismatch.currency1 = Currency.wrap(address(token1)); - IBinMigrator.InfiBinPoolParams memory infiBinPoolParams = IBinMigrator.InfiBinPoolParams({ - poolKey: poolKeyMismatch, - amount0Max: params.amount0Max, - amount1Max: params.amount1Max, - activeIdDesired: params.activeIdDesired, - idSlippage: params.idSlippage, - deltaIds: params.deltaIds, - distributionX: params.distributionX, - distributionY: params.distributionY, - to: params.to, - deadline: block.timestamp + 1, - hookData: new bytes(0) - }); - - // 3. multicall, combine initialize and migrateFromV2 - bytes[] memory data = new bytes[](2); - data[0] = abi.encodeWithSelector(migrator.initializePool.selector, poolKeyMismatch, ACTIVE_BIN_ID, bytes("")); - data[1] = abi.encodeWithSelector(migrator.migrateFromV2.selector, v2PoolParams, infiBinPoolParams, 0, 0); - vm.expectRevert(); - migrator.multicall(data); - - { - // v2 weth, token0 - // infinity token0, token1 - poolKeyMismatch.currency0 = Currency.wrap(address(token0)); - poolKeyMismatch.currency1 = Currency.wrap(address(token1)); - infiBinPoolParams.poolKey = poolKeyMismatch; - data = new bytes[](2); - data[0] = - abi.encodeWithSelector(migrator.initializePool.selector, poolKeyMismatch, ACTIVE_BIN_ID, bytes("")); - data[1] = abi.encodeWithSelector(migrator.migrateFromV2.selector, v2PoolParams, infiBinPoolParams, 0, 0); - vm.expectRevert(); - migrator.multicall(data); - } - } - - function testMigrateFromV2WithoutInit() public { - // 1. mint some liquidity to the v2 pair - _mintV2Liquidity(v2Pair); - uint256 lpTokenBefore = v2Pair.balanceOf(address(this)); - assertGt(lpTokenBefore, 0); - - // 2. make sure migrator can transfer user's v2 lp token - permit2ApproveWithSpecificAllowance( - address(this), permit2, address(v2Pair), address(migrator), lpTokenBefore, uint160(lpTokenBefore) - ); - - // 3. initialize the pool - migrator.initializePool(poolKey, ACTIVE_BIN_ID); - - IBaseMigrator.V2PoolParams memory v2PoolParams = IBaseMigrator.V2PoolParams({ - pair: address(v2Pair), - migrateAmount: lpTokenBefore, - // minor precision loss is acceptable - amount0Min: 9.999 ether, - amount1Min: 9.999 ether - }); - - IBinPositionManager.BinAddLiquidityParams memory params = - _getAddParams(poolKey, getBinIds(ACTIVE_BIN_ID, 3), 10 ether, 10 ether, ACTIVE_BIN_ID, address(this)); - - IBinMigrator.InfiBinPoolParams memory infiBinPoolParams = IBinMigrator.InfiBinPoolParams({ - poolKey: params.poolKey, - amount0Max: params.amount0Max, - amount1Max: params.amount1Max, - activeIdDesired: params.activeIdDesired, - idSlippage: params.idSlippage, - deltaIds: params.deltaIds, - distributionX: params.distributionX, - distributionY: params.distributionY, - to: params.to, - deadline: block.timestamp + 1, - hookData: new bytes(0) - }); - - // 4. migrateFromV2 - migrator.migrateFromV2(v2PoolParams, infiBinPoolParams, 0, 0); - vm.snapshotGasLastCall("testMigrateFromV2WithoutInit"); - - // necessary checks - // v2 pair should be burned already - assertEq(v2Pair.balanceOf(address(this)), 0); - - // make sure liuqidty is minted to the correct pool - assertApproxEqAbs(address(vault).balance, 10 ether, 0.000001 ether); - assertApproxEqAbs(token0.balanceOf(address(vault)), 10 ether, 0.000001 ether); - - uint256 positionId0 = poolKey.toId().toTokenId(ACTIVE_BIN_ID - 1); - uint256 positionId1 = poolKey.toId().toTokenId(ACTIVE_BIN_ID); - uint256 positionId2 = poolKey.toId().toTokenId(ACTIVE_BIN_ID + 1); - uint256 positionId3 = poolKey.toId().toTokenId(ACTIVE_BIN_ID + 2); - assertGt(binPm.balanceOf(address(this), positionId0), 0); - assertGt(binPm.balanceOf(address(this), positionId1), 0); - assertGt(binPm.balanceOf(address(this), positionId2), 0); - assertEq(binPm.balanceOf(address(this), positionId3), 0); - - (PoolKey memory _poolKey, uint24 binId) = binPm.positions(positionId0); - assertEq(PoolId.unwrap(_poolKey.toId()), PoolId.unwrap(poolKey.toId())); - assertEq(Currency.unwrap(_poolKey.currency0), address(0)); - assertEq(Currency.unwrap(_poolKey.currency1), address(token0)); - assertEq(_poolKey.fee, 0); - assertEq(binId, ACTIVE_BIN_ID - 1); - - (_poolKey, binId) = binPm.positions(positionId1); - assertEq(PoolId.unwrap(_poolKey.toId()), PoolId.unwrap(poolKey.toId())); - assertEq(Currency.unwrap(_poolKey.currency0), address(0)); - assertEq(Currency.unwrap(_poolKey.currency1), address(token0)); - assertEq(_poolKey.fee, 0); - assertEq(binId, ACTIVE_BIN_ID); - - (_poolKey, binId) = binPm.positions(positionId2); - assertEq(PoolId.unwrap(_poolKey.toId()), PoolId.unwrap(poolKey.toId())); - assertEq(Currency.unwrap(_poolKey.currency0), address(0)); - assertEq(Currency.unwrap(_poolKey.currency1), address(token0)); - assertEq(_poolKey.fee, 0); - assertEq(binId, ACTIVE_BIN_ID + 1); - - vm.expectRevert(IPositionManager.InvalidTokenID.selector); - binPm.positions(positionId3); - } - - function testMigrateFromV2WithoutNativeToken() public { - // 1. mint some liquidity to the v2 pair - _mintV2Liquidity(v2PairWithoutNativeToken); - uint256 lpTokenBefore = v2PairWithoutNativeToken.balanceOf(address(this)); - assertGt(lpTokenBefore, 0); - - // 2. make sure migrator can transfer user's v2 lp token - permit2ApproveWithSpecificAllowance( - address(this), - permit2, - address(v2PairWithoutNativeToken), - address(migrator), - lpTokenBefore, - uint160(lpTokenBefore) - ); - - // 3. initialize the pool - migrator.initializePool(poolKeyWithoutNativeToken, ACTIVE_BIN_ID); - - IBaseMigrator.V2PoolParams memory v2PoolParams = IBaseMigrator.V2PoolParams({ - pair: address(v2PairWithoutNativeToken), - migrateAmount: lpTokenBefore, - // minor precision loss is acceptable - amount0Min: 9.999 ether, - amount1Min: 9.999 ether - }); - - IBinPositionManager.BinAddLiquidityParams memory params = _getAddParams( - poolKeyWithoutNativeToken, getBinIds(ACTIVE_BIN_ID, 3), 10 ether, 10 ether, ACTIVE_BIN_ID, address(this) - ); - - IBinMigrator.InfiBinPoolParams memory infiBinPoolParams = IBinMigrator.InfiBinPoolParams({ - poolKey: params.poolKey, - amount0Max: params.amount0Max, - amount1Max: params.amount1Max, - activeIdDesired: params.activeIdDesired, - idSlippage: params.idSlippage, - deltaIds: params.deltaIds, - distributionX: params.distributionX, - distributionY: params.distributionY, - to: params.to, - deadline: block.timestamp + 1, - hookData: new bytes(0) - }); - - // 4. migrate from v2 to infinity - migrator.migrateFromV2(v2PoolParams, infiBinPoolParams, 0, 0); - vm.snapshotGasLastCall("testMigrateFromV2WithoutNativeToken"); - - // necessary checks - // v2 pair should be burned already - assertEq(v2PairWithoutNativeToken.balanceOf(address(this)), 0); - - // make sure liuqidty is minted to the correct pool - assertApproxEqAbs(token0.balanceOf(address(vault)), 10 ether, 0.000001 ether); - assertApproxEqAbs(token1.balanceOf(address(vault)), 10 ether, 0.000001 ether); - - uint256 positionId0 = poolKeyWithoutNativeToken.toId().toTokenId(ACTIVE_BIN_ID - 1); - uint256 positionId1 = poolKeyWithoutNativeToken.toId().toTokenId(ACTIVE_BIN_ID); - uint256 positionId2 = poolKeyWithoutNativeToken.toId().toTokenId(ACTIVE_BIN_ID + 1); - uint256 positionId3 = poolKeyWithoutNativeToken.toId().toTokenId(ACTIVE_BIN_ID + 2); - assertGt(binPm.balanceOf(address(this), positionId0), 0); - assertGt(binPm.balanceOf(address(this), positionId1), 0); - assertGt(binPm.balanceOf(address(this), positionId2), 0); - assertEq(binPm.balanceOf(address(this), positionId3), 0); - - (PoolKey memory _poolKey, uint24 binId) = binPm.positions(positionId0); - assertEq(PoolId.unwrap(_poolKey.toId()), PoolId.unwrap(poolKeyWithoutNativeToken.toId())); - assertEq(Currency.unwrap(_poolKey.currency0), address(token0)); - assertEq(Currency.unwrap(_poolKey.currency1), address(token1)); - assertEq(_poolKey.fee, 0); - assertEq(binId, ACTIVE_BIN_ID - 1); - - (_poolKey, binId) = binPm.positions(positionId1); - assertEq(PoolId.unwrap(_poolKey.toId()), PoolId.unwrap(poolKeyWithoutNativeToken.toId())); - assertEq(Currency.unwrap(_poolKey.currency0), address(token0)); - assertEq(Currency.unwrap(_poolKey.currency1), address(token1)); - assertEq(_poolKey.fee, 0); - assertEq(binId, ACTIVE_BIN_ID); - - (_poolKey, binId) = binPm.positions(positionId2); - assertEq(PoolId.unwrap(_poolKey.toId()), PoolId.unwrap(poolKeyWithoutNativeToken.toId())); - assertEq(Currency.unwrap(_poolKey.currency0), address(token0)); - assertEq(Currency.unwrap(_poolKey.currency1), address(token1)); - assertEq(_poolKey.fee, 0); - assertEq(binId, ACTIVE_BIN_ID + 1); - - vm.expectRevert(IPositionManager.InvalidTokenID.selector); - binPm.positions(positionId3); - } - - function testMigrateFromV2AddExtraAmount() public { - // 1. mint some liquidity to the v2 pair - _mintV2Liquidity(v2Pair); - uint256 lpTokenBefore = v2Pair.balanceOf(address(this)); - assertGt(lpTokenBefore, 0); - - // 2. make sure migrator can transfer user's v2 lp token - permit2ApproveWithSpecificAllowance( - address(this), permit2, address(v2Pair), address(migrator), lpTokenBefore, uint160(lpTokenBefore) - ); - - // 3. initialize the pool - migrator.initializePool(poolKey, ACTIVE_BIN_ID); - - IBaseMigrator.V2PoolParams memory v2PoolParams = IBaseMigrator.V2PoolParams({ - pair: address(v2Pair), - migrateAmount: lpTokenBefore, - // minor precision loss is acceptable - amount0Min: 9.999 ether, - amount1Min: 9.999 ether - }); - - IBinPositionManager.BinAddLiquidityParams memory params = - _getAddParams(poolKey, getBinIds(ACTIVE_BIN_ID, 3), 10 ether, 10 ether, ACTIVE_BIN_ID, address(this)); - - IBinMigrator.InfiBinPoolParams memory infiBinPoolParams = IBinMigrator.InfiBinPoolParams({ - poolKey: params.poolKey, - amount0Max: params.amount0Max, - amount1Max: params.amount1Max, - activeIdDesired: params.activeIdDesired, - idSlippage: params.idSlippage, - deltaIds: params.deltaIds, - distributionX: params.distributionX, - distributionY: params.distributionY, - to: params.to, - deadline: block.timestamp + 1, - hookData: new bytes(0) - }); - - uint256 balance0Before = address(this).balance; - uint256 balance1Before = token0.balanceOf(address(this)); - - permit2ApproveWithSpecificAllowance( - address(this), permit2, address(token0), address(migrator), 20 ether, 20 ether - ); - // 4. migrate from v2 to infinity - migrator.migrateFromV2{value: 20 ether}(v2PoolParams, infiBinPoolParams, 20 ether, 20 ether); - - // necessary checks - // consumed extra 20 ether from user - assertApproxEqAbs(balance0Before - address(this).balance, 20 ether, 0.000001 ether); - assertEq(balance1Before - token0.balanceOf(address(this)), 20 ether); - // WETH balance unchanged - assertEq(weth.balanceOf(address(this)), 90 ether); - - // v2 pair should be burned already - assertEq(v2Pair.balanceOf(address(this)), 0); - - // make sure liuqidty is minted to the correct pool - assertApproxEqAbs(address(vault).balance, 30 ether, 0.000001 ether); - assertApproxEqAbs(token0.balanceOf(address(vault)), 30 ether, 0.000001 ether); - - uint256 positionId0 = poolKey.toId().toTokenId(ACTIVE_BIN_ID - 1); - uint256 positionId1 = poolKey.toId().toTokenId(ACTIVE_BIN_ID); - uint256 positionId2 = poolKey.toId().toTokenId(ACTIVE_BIN_ID + 1); - uint256 positionId3 = poolKey.toId().toTokenId(ACTIVE_BIN_ID + 2); - assertGt(binPm.balanceOf(address(this), positionId0), 0); - assertGt(binPm.balanceOf(address(this), positionId1), 0); - assertGt(binPm.balanceOf(address(this), positionId2), 0); - assertEq(binPm.balanceOf(address(this), positionId3), 0); - - (PoolKey memory _poolKey, uint24 binId) = binPm.positions(positionId0); - assertEq(PoolId.unwrap(_poolKey.toId()), PoolId.unwrap(poolKey.toId())); - assertEq(Currency.unwrap(_poolKey.currency0), address(0)); - assertEq(Currency.unwrap(_poolKey.currency1), address(token0)); - assertEq(_poolKey.fee, 0); - assertEq(binId, ACTIVE_BIN_ID - 1); - - (_poolKey, binId) = binPm.positions(positionId1); - assertEq(PoolId.unwrap(_poolKey.toId()), PoolId.unwrap(poolKey.toId())); - assertEq(Currency.unwrap(_poolKey.currency0), address(0)); - assertEq(Currency.unwrap(_poolKey.currency1), address(token0)); - assertEq(_poolKey.fee, 0); - assertEq(binId, ACTIVE_BIN_ID); - - (_poolKey, binId) = binPm.positions(positionId2); - assertEq(PoolId.unwrap(_poolKey.toId()), PoolId.unwrap(poolKey.toId())); - assertEq(Currency.unwrap(_poolKey.currency0), address(0)); - assertEq(Currency.unwrap(_poolKey.currency1), address(token0)); - assertEq(_poolKey.fee, 0); - assertEq(binId, ACTIVE_BIN_ID + 1); - - vm.expectRevert(IPositionManager.InvalidTokenID.selector); - binPm.positions(positionId3); - } - - function testMigrateFromV2AddExtraAmountThroughWETH() public { - // 1. mint some liquidity to the v2 pair - _mintV2Liquidity(v2Pair); - uint256 lpTokenBefore = v2Pair.balanceOf(address(this)); - assertGt(lpTokenBefore, 0); - - // 2. make sure migrator can transfer user's v2 lp token - - permit2ApproveWithSpecificAllowance( - address(this), permit2, address(v2Pair), address(migrator), lpTokenBefore, uint160(lpTokenBefore) - ); - - // 3. initialize the pool - migrator.initializePool(poolKey, ACTIVE_BIN_ID); - - IBaseMigrator.V2PoolParams memory v2PoolParams = IBaseMigrator.V2PoolParams({ - pair: address(v2Pair), - migrateAmount: lpTokenBefore, - // minor precision loss is acceptable - amount0Min: 9.999 ether, - amount1Min: 9.999 ether - }); - - IBinPositionManager.BinAddLiquidityParams memory params = - _getAddParams(poolKey, getBinIds(ACTIVE_BIN_ID, 3), 10 ether, 10 ether, ACTIVE_BIN_ID, address(this)); - - IBinMigrator.InfiBinPoolParams memory infiBinPoolParams = IBinMigrator.InfiBinPoolParams({ - poolKey: params.poolKey, - amount0Max: params.amount0Max, - amount1Max: params.amount1Max, - activeIdDesired: params.activeIdDesired, - idSlippage: params.idSlippage, - deltaIds: params.deltaIds, - distributionX: params.distributionX, - distributionY: params.distributionY, - to: params.to, - deadline: block.timestamp + 1, - hookData: new bytes(0) - }); - - uint256 balance0Before = address(this).balance; - uint256 balance1Before = token0.balanceOf(address(this)); - - permit2ApproveWithSpecificAllowance( - address(this), permit2, address(weth), address(migrator), 20 ether, 20 ether - ); - permit2ApproveWithSpecificAllowance( - address(this), permit2, address(token0), address(migrator), 20 ether, 20 ether - ); - // 4. migrate from v2 to infinity, not sending ETH denotes pay by WETH - migrator.migrateFromV2(v2PoolParams, infiBinPoolParams, 20 ether, 20 ether); - - // necessary checks - // consumed extra 20 ether from user - // native token balance unchanged - assertApproxEqAbs(balance0Before - address(this).balance, 0 ether, 0.000001 ether); - assertEq(balance1Before - token0.balanceOf(address(this)), 20 ether); - // consumed 20 ether WETH - assertEq(weth.balanceOf(address(this)), 70 ether); - - // v2 pair should be burned already - assertEq(v2Pair.balanceOf(address(this)), 0); - - // make sure liuqidty is minted to the correct pool - assertApproxEqAbs(address(vault).balance, 30 ether, 0.000001 ether); - assertApproxEqAbs(token0.balanceOf(address(vault)), 30 ether, 0.000001 ether); - - uint256 positionId0 = poolKey.toId().toTokenId(ACTIVE_BIN_ID - 1); - uint256 positionId1 = poolKey.toId().toTokenId(ACTIVE_BIN_ID); - uint256 positionId2 = poolKey.toId().toTokenId(ACTIVE_BIN_ID + 1); - uint256 positionId3 = poolKey.toId().toTokenId(ACTIVE_BIN_ID + 2); - assertGt(binPm.balanceOf(address(this), positionId0), 0); - assertGt(binPm.balanceOf(address(this), positionId1), 0); - assertGt(binPm.balanceOf(address(this), positionId2), 0); - assertEq(binPm.balanceOf(address(this), positionId3), 0); - - (PoolKey memory _poolKey, uint24 binId) = binPm.positions(positionId0); - assertEq(PoolId.unwrap(_poolKey.toId()), PoolId.unwrap(poolKey.toId())); - assertEq(Currency.unwrap(_poolKey.currency0), address(0)); - assertEq(Currency.unwrap(_poolKey.currency1), address(token0)); - assertEq(_poolKey.fee, 0); - assertEq(binId, ACTIVE_BIN_ID - 1); - - (_poolKey, binId) = binPm.positions(positionId1); - assertEq(PoolId.unwrap(_poolKey.toId()), PoolId.unwrap(poolKey.toId())); - assertEq(Currency.unwrap(_poolKey.currency0), address(0)); - assertEq(Currency.unwrap(_poolKey.currency1), address(token0)); - assertEq(_poolKey.fee, 0); - assertEq(binId, ACTIVE_BIN_ID); - - (_poolKey, binId) = binPm.positions(positionId2); - assertEq(PoolId.unwrap(_poolKey.toId()), PoolId.unwrap(poolKey.toId())); - assertEq(Currency.unwrap(_poolKey.currency0), address(0)); - assertEq(Currency.unwrap(_poolKey.currency1), address(token0)); - assertEq(_poolKey.fee, 0); - assertEq(binId, ACTIVE_BIN_ID + 1); - - vm.expectRevert(IPositionManager.InvalidTokenID.selector); - binPm.positions(positionId3); - } - - function testMigrateFromV2Refund() public { - // 1. mint some liquidity to the v2 pair - _mintV2Liquidity(v2Pair, 10 ether, 10 ether); - uint256 lpTokenBefore = v2Pair.balanceOf(address(this)); - assertGt(lpTokenBefore, 0); - - // 2. make sure migrator can transfer user's v2 lp token - permit2ApproveWithSpecificAllowance( - address(this), permit2, address(v2Pair), address(migrator), lpTokenBefore, uint160(lpTokenBefore) - ); - - // 3. initialize the pool - migrator.initializePool(poolKey, ACTIVE_BIN_ID); - - IBaseMigrator.V2PoolParams memory v2PoolParams = IBaseMigrator.V2PoolParams({ - pair: address(v2Pair), - migrateAmount: lpTokenBefore, - // the order of token0 and token1 respect to the pair - // but may mismatch the order of infinity pool key when WETH is invovled - amount0Min: 9.99 ether, - amount1Min: 9.99 ether - }); - - IBinPositionManager.BinAddLiquidityParams memory params = - _getAddParams(poolKey, getBinIds(ACTIVE_BIN_ID, 3), 10 ether, 10 ether, ACTIVE_BIN_ID, address(this)); - - int256[] memory deltaIds = new int256[](2); - deltaIds[0] = params.deltaIds[0]; - deltaIds[1] = params.deltaIds[1]; - - uint256[] memory distributionX = new uint256[](2); - distributionX[0] = params.distributionX[0]; - distributionX[1] = params.distributionX[1]; - - uint256[] memory distributionY = new uint256[](2); - distributionY[0] = params.distributionY[0]; - distributionY[1] = params.distributionY[1]; - - // delete the last distribution point so that the refund is triggered - // we expect to get 50% of tokenX back - // (0, 50%) (50%, 50%) (50%, 0) => (0, 50%) (50%, 50%) - IBinMigrator.InfiBinPoolParams memory infiBinPoolParams = IBinMigrator.InfiBinPoolParams({ - poolKey: params.poolKey, - amount0Max: params.amount0Max, - amount1Max: params.amount1Max, - activeIdDesired: params.activeIdDesired, - idSlippage: params.idSlippage, - deltaIds: deltaIds, - distributionX: distributionX, - distributionY: distributionY, - to: params.to, - deadline: block.timestamp + 1, - hookData: new bytes(0) - }); - - uint256 balance0Before = address(this).balance; - uint256 balance1Before = token0.balanceOf(address(this)); - - // 4. migrate from v2 to infinity, not sending ETH denotes pay by WETH - migrator.migrateFromV2(v2PoolParams, infiBinPoolParams, 0, 0); - - // necessary checks - // refund 5 ether in the form of native token - assertApproxEqAbs(address(this).balance - balance0Before, 5 ether, 0.000001 ether); - assertEq(balance1Before - token0.balanceOf(address(this)), 0 ether); - // WETH balance unchanged - assertEq(weth.balanceOf(address(this)), 90 ether); - - // v2 pair should be burned already - assertEq(v2Pair.balanceOf(address(this)), 0); - - // make sure liuqidty is minted to the correct pool - assertApproxEqAbs(address(vault).balance, 5 ether, 0.000001 ether); - assertApproxEqAbs(token0.balanceOf(address(vault)), 10 ether, 0.000001 ether); - - uint256 positionId0 = poolKey.toId().toTokenId(ACTIVE_BIN_ID - 1); - uint256 positionId1 = poolKey.toId().toTokenId(ACTIVE_BIN_ID); - uint256 positionId2 = poolKey.toId().toTokenId(ACTIVE_BIN_ID + 1); - uint256 positionId3 = poolKey.toId().toTokenId(ACTIVE_BIN_ID + 2); - assertGt(binPm.balanceOf(address(this), positionId0), 0); - assertGt(binPm.balanceOf(address(this), positionId1), 0); - assertEq(binPm.balanceOf(address(this), positionId2), 0); - assertEq(binPm.balanceOf(address(this), positionId3), 0); - - (PoolKey memory _poolKey, uint24 binId) = binPm.positions(positionId0); - assertEq(PoolId.unwrap(_poolKey.toId()), PoolId.unwrap(poolKey.toId())); - assertEq(Currency.unwrap(_poolKey.currency0), address(0)); - assertEq(Currency.unwrap(_poolKey.currency1), address(token0)); - assertEq(_poolKey.fee, 0); - assertEq(binId, ACTIVE_BIN_ID - 1); - - (_poolKey, binId) = binPm.positions(positionId1); - assertEq(PoolId.unwrap(_poolKey.toId()), PoolId.unwrap(poolKey.toId())); - assertEq(Currency.unwrap(_poolKey.currency0), address(0)); - assertEq(Currency.unwrap(_poolKey.currency1), address(token0)); - assertEq(_poolKey.fee, 0); - assertEq(binId, ACTIVE_BIN_ID); - - vm.expectRevert(IPositionManager.InvalidTokenID.selector); - binPm.positions(positionId2); - - vm.expectRevert(IPositionManager.InvalidTokenID.selector); - binPm.positions(positionId3); - } - - function testMigrateFromV2RefundNonNativeToken() public { - // 1. mint some liquidity to the v2 pair - _mintV2Liquidity(v2PairWithoutNativeToken, 10 ether, 10 ether); - uint256 lpTokenBefore = v2PairWithoutNativeToken.balanceOf(address(this)); - assertGt(lpTokenBefore, 0); - - // 2. make sure migrator can transfer user's v2 lp token - permit2ApproveWithSpecificAllowance( - address(this), - permit2, - address(v2PairWithoutNativeToken), - address(migrator), - lpTokenBefore, - uint160(lpTokenBefore) - ); - - // 3. initialize the pool - migrator.initializePool(poolKeyWithoutNativeToken, ACTIVE_BIN_ID); - - IBaseMigrator.V2PoolParams memory v2PoolParams = IBaseMigrator.V2PoolParams({ - pair: address(v2PairWithoutNativeToken), - migrateAmount: lpTokenBefore, - // the order of token0 and token1 respect to the pair - // but may mismatch the order of infinity pool key when WETH is invovled - amount0Min: 9.999 ether, - amount1Min: 9.999 ether - }); - - IBinPositionManager.BinAddLiquidityParams memory params = _getAddParams( - poolKeyWithoutNativeToken, getBinIds(ACTIVE_BIN_ID, 3), 10 ether, 10 ether, ACTIVE_BIN_ID, address(this) - ); - - int256[] memory deltaIds = new int256[](2); - deltaIds[0] = params.deltaIds[0]; - deltaIds[1] = params.deltaIds[1]; - - uint256[] memory distributionX = new uint256[](2); - distributionX[0] = params.distributionX[0]; - distributionX[1] = params.distributionX[1]; - - uint256[] memory distributionY = new uint256[](2); - distributionY[0] = params.distributionY[0]; - distributionY[1] = params.distributionY[1]; - - // delete the last distribution point so that the refund is triggered - // we expect to get 50% of tokenX back - // (0, 50%) (50%, 50%) (50%, 0) => (0, 50%) (50%, 50%) - IBinMigrator.InfiBinPoolParams memory infiBinPoolParams = IBinMigrator.InfiBinPoolParams({ - poolKey: params.poolKey, - amount0Max: params.amount0Max, - amount1Max: params.amount1Max, - activeIdDesired: params.activeIdDesired, - idSlippage: params.idSlippage, - deltaIds: deltaIds, - distributionX: distributionX, - distributionY: distributionY, - to: params.to, - deadline: block.timestamp + 1, - hookData: new bytes(0) - }); - - uint256 balance0Before = token0.balanceOf(address(this)); - uint256 balance1Before = token1.balanceOf(address(this)); - - // 4. migrate from v2 to infinity - migrator.migrateFromV2(v2PoolParams, infiBinPoolParams, 0, 0); - - // necessary checks - - // refund 5 ether of token0 - assertApproxEqAbs(token0.balanceOf(address(this)) - balance0Before, 5 ether, 0.000001 ether); - assertEq(balance1Before - token1.balanceOf(address(this)), 0 ether); - // WETH balance unchanged - assertEq(weth.balanceOf(address(this)), 100 ether); - - // v2 pair should be burned already - assertEq(v2PairWithoutNativeToken.balanceOf(address(this)), 0); - - // make sure liuqidty is minted to the correct pool - assertApproxEqAbs(token0.balanceOf(address(vault)), 5 ether, 0.000001 ether); - assertApproxEqAbs(token1.balanceOf(address(vault)), 10 ether, 0.000001 ether); - - uint256 positionId0 = poolKeyWithoutNativeToken.toId().toTokenId(ACTIVE_BIN_ID - 1); - uint256 positionId1 = poolKeyWithoutNativeToken.toId().toTokenId(ACTIVE_BIN_ID); - uint256 positionId2 = poolKeyWithoutNativeToken.toId().toTokenId(ACTIVE_BIN_ID + 1); - uint256 positionId3 = poolKeyWithoutNativeToken.toId().toTokenId(ACTIVE_BIN_ID + 2); - assertGt(binPm.balanceOf(address(this), positionId0), 0); - assertGt(binPm.balanceOf(address(this), positionId1), 0); - assertEq(binPm.balanceOf(address(this), positionId2), 0); - assertEq(binPm.balanceOf(address(this), positionId3), 0); - - (PoolKey memory _poolKey, uint24 binId) = binPm.positions(positionId0); - assertEq(PoolId.unwrap(_poolKey.toId()), PoolId.unwrap(poolKeyWithoutNativeToken.toId())); - assertEq(Currency.unwrap(_poolKey.currency0), address(token0)); - assertEq(Currency.unwrap(_poolKey.currency1), address(token1)); - assertEq(_poolKey.fee, 0); - assertEq(binId, ACTIVE_BIN_ID - 1); - - (_poolKey, binId) = binPm.positions(positionId1); - assertEq(PoolId.unwrap(_poolKey.toId()), PoolId.unwrap(poolKeyWithoutNativeToken.toId())); - assertEq(Currency.unwrap(_poolKey.currency0), address(token0)); - assertEq(Currency.unwrap(_poolKey.currency1), address(token1)); - assertEq(_poolKey.fee, 0); - assertEq(binId, ACTIVE_BIN_ID); - - vm.expectRevert(IPositionManager.InvalidTokenID.selector); - binPm.positions(positionId2); - - vm.expectRevert(IPositionManager.InvalidTokenID.selector); - binPm.positions(positionId3); - } - - function testMigrateFromV2ThroughOffchainSign() public { - // 1. mint some liquidity to the v2 pair - _mintV2Liquidity(v2Pair); - uint256 lpTokenBefore = v2Pair.balanceOf(address(this)); - assertGt(lpTokenBefore, 0); - - // 2. make sure migrator can transfer user's v2 lp token - - (address userAddr, uint256 userPrivateKey) = makeAddrAndKey("user"); - - // 2.a transfer the lp token to the user - v2Pair.transfer(userAddr, lpTokenBefore); - - uint256 ddl = block.timestamp + 100; - // 2.b prepare the hash - bytes32 structHash = keccak256( - abi.encode( - keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"), - userAddr, - address(permit2), - lpTokenBefore, - v2Pair.nonces(address(permit2)), - ddl - ) - ); - bytes32 hash = keccak256(abi.encodePacked("\x19\x01", v2Pair.DOMAIN_SEPARATOR(), structHash)); - - // 2.c generate the signature - (uint8 v, bytes32 r, bytes32 s) = vm.sign(userPrivateKey, hash); - - IAllowanceTransfer.PermitSingle memory permit = - defaultERC20PermitAllowance(address(v2Pair), uint160(lpTokenBefore), type(uint48).max, 0); - permit.spender = address(migrator); - bytes memory sig = getPermitSignature(permit, userPrivateKey, PERMIT2_DOMAIN_SEPARATOR); - - IBaseMigrator.V2PoolParams memory v2PoolParams = IBaseMigrator.V2PoolParams({ - pair: address(v2Pair), - migrateAmount: lpTokenBefore, - // minor precision loss is acceptable - amount0Min: 9.999 ether, - amount1Min: 9.999 ether - }); - - IBinPositionManager.BinAddLiquidityParams memory params = - _getAddParams(poolKey, getBinIds(ACTIVE_BIN_ID, 3), 10 ether, 10 ether, ACTIVE_BIN_ID, address(this)); - - IBinMigrator.InfiBinPoolParams memory infiBinPoolParams = IBinMigrator.InfiBinPoolParams({ - poolKey: params.poolKey, - amount0Max: params.amount0Max, - amount1Max: params.amount1Max, - activeIdDesired: params.activeIdDesired, - idSlippage: params.idSlippage, - deltaIds: params.deltaIds, - distributionX: params.distributionX, - distributionY: params.distributionY, - to: params.to, - deadline: block.timestamp + 1, - hookData: new bytes(0) - }); - - // 3. multicall, combine permit2.permit, initialize and migrateFromV2 - bytes[] memory data = new bytes[](3); - data[0] = abi.encodeWithSelector(migrator.initializePool.selector, poolKey, ACTIVE_BIN_ID, bytes("")); - data[1] = abi.encodeWithSelector(Permit2Forwarder.permit.selector, userAddr, permit, sig); - data[2] = abi.encodeWithSelector(migrator.migrateFromV2.selector, v2PoolParams, infiBinPoolParams, 0, 0); - vm.startPrank(userAddr); - v2Pair.permit(userAddr, address(permit2), lpTokenBefore, ddl, v, r, s); - migrator.multicall(data); - vm.stopPrank(); - - // necessary checks - // v2 pair should be burned already - assertEq(v2Pair.balanceOf(address(this)), 0); - - // make sure liuqidty is minted to the correct pool - assertApproxEqAbs(address(vault).balance, 10 ether, 0.000001 ether); - assertApproxEqAbs(token0.balanceOf(address(vault)), 10 ether, 0.000001 ether); - - uint256 positionId0 = poolKey.toId().toTokenId(ACTIVE_BIN_ID - 1); - uint256 positionId1 = poolKey.toId().toTokenId(ACTIVE_BIN_ID); - uint256 positionId2 = poolKey.toId().toTokenId(ACTIVE_BIN_ID + 1); - uint256 positionId3 = poolKey.toId().toTokenId(ACTIVE_BIN_ID + 2); - assertGt(binPm.balanceOf(address(this), positionId0), 0); - assertGt(binPm.balanceOf(address(this), positionId1), 0); - assertGt(binPm.balanceOf(address(this), positionId2), 0); - assertEq(binPm.balanceOf(address(this), positionId3), 0); - - (PoolKey memory _poolKey, uint24 binId) = binPm.positions(positionId0); - assertEq(PoolId.unwrap(_poolKey.toId()), PoolId.unwrap(poolKey.toId())); - assertEq(Currency.unwrap(_poolKey.currency0), address(0)); - assertEq(Currency.unwrap(_poolKey.currency1), address(token0)); - assertEq(_poolKey.fee, 0); - assertEq(binId, ACTIVE_BIN_ID - 1); - - (_poolKey, binId) = binPm.positions(positionId1); - assertEq(PoolId.unwrap(_poolKey.toId()), PoolId.unwrap(poolKey.toId())); - assertEq(Currency.unwrap(_poolKey.currency0), address(0)); - assertEq(Currency.unwrap(_poolKey.currency1), address(token0)); - assertEq(_poolKey.fee, 0); - assertEq(binId, ACTIVE_BIN_ID); - - (_poolKey, binId) = binPm.positions(positionId2); - assertEq(PoolId.unwrap(_poolKey.toId()), PoolId.unwrap(poolKey.toId())); - assertEq(Currency.unwrap(_poolKey.currency0), address(0)); - assertEq(Currency.unwrap(_poolKey.currency1), address(token0)); - assertEq(_poolKey.fee, 0); - assertEq(binId, ACTIVE_BIN_ID + 1); - - vm.expectRevert(IPositionManager.InvalidTokenID.selector); - binPm.positions(positionId3); - } - - function _mintV2Liquidity(IPancakePair pair) public { - IERC20(pair.token0()).transfer(address(pair), 10 ether); - IERC20(pair.token1()).transfer(address(pair), 10 ether); - - pair.mint(address(this)); - } - - function _mintV2Liquidity(IPancakePair pair, uint256 amount0, uint256 amount1) public { - IERC20(pair.token0()).transfer(address(pair), amount0); - IERC20(pair.token1()).transfer(address(pair), amount1); - - pair.mint(address(this)); - } - - receive() external payable {} -} diff --git a/test/pool-bin/migrator/BinMigratorFromV3.sol b/test/pool-bin/migrator/BinMigratorFromV3.sol deleted file mode 100644 index 3725169..0000000 --- a/test/pool-bin/migrator/BinMigratorFromV3.sol +++ /dev/null @@ -1,1440 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.24; - -import "forge-std/Test.sol"; -import {OldVersionHelper} from "../../helpers/OldVersionHelper.sol"; -import {IPancakePair} from "../../../src/interfaces/external/IPancakePair.sol"; -import {WETH} from "solmate/src/tokens/WETH.sol"; -import {MockERC20} from "solmate/src/test/utils/mocks/MockERC20.sol"; -import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import {BinMigrator} from "../../../src/pool-bin/BinMigrator.sol"; -import {IBinMigrator, IBaseMigrator} from "../../../src/pool-bin/interfaces/IBinMigrator.sol"; -import {IBinPositionManager} from "../../../src/pool-bin/interfaces/IBinPositionManager.sol"; -import {BinPositionManager} from "../../../src/pool-bin/BinPositionManager.sol"; -import {IVault} from "infinity-core/src/interfaces/IVault.sol"; -import {Vault} from "infinity-core/src/Vault.sol"; -import {IBinPoolManager} from "infinity-core/src/pool-bin/interfaces/IBinPoolManager.sol"; -import {BinPoolManager} from "infinity-core/src/pool-bin/BinPoolManager.sol"; -import {PoolKey} from "infinity-core/src/types/PoolKey.sol"; -import {BinPoolParametersHelper} from "infinity-core/src/pool-bin/libraries/BinPoolParametersHelper.sol"; -import {Currency} from "infinity-core/src/types/Currency.sol"; -import {IPoolManager} from "infinity-core/src/interfaces/IPoolManager.sol"; -import {IHooks} from "infinity-core/src/interfaces/IHooks.sol"; -import {PoolId, PoolIdLibrary} from "infinity-core/src/types/PoolId.sol"; -import {BinLiquidityHelper} from "../helper/BinLiquidityHelper.sol"; -import {BinTokenLibrary} from "../../../src/pool-bin/libraries/BinTokenLibrary.sol"; -import {Plan, Planner} from "../../../src/libraries/Planner.sol"; -import {Actions} from "../../../src/libraries/Actions.sol"; -import {SafeCallback} from "../../../src/base/SafeCallback.sol"; -import {IAllowanceTransfer} from "permit2/src/interfaces/IAllowanceTransfer.sol"; -import {DeployPermit2} from "permit2/test/utils/DeployPermit2.sol"; -import {IV3NonfungiblePositionManager} from "../../../src/interfaces/external/IV3NonfungiblePositionManager.sol"; -import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; -import {PackedUint128Math} from "infinity-core/src/pool-bin/libraries/math/PackedUint128Math.sol"; -import {MockReentrantPositionManager} from "../../mocks/MockReentrantPositionManager.sol"; -import {ReentrancyLock} from "../../../src/base/ReentrancyLock.sol"; -import {Permit2ApproveHelper} from "../../helpers/Permit2ApproveHelper.sol"; -import {IPositionManager} from "../../../src/interfaces/IPositionManager.sol"; -import {Pausable} from "infinity-core/src/base/Pausable.sol"; -import {MockBinMigratorHook} from "./mocks/MockBinMigratorHook.sol"; -import {IWETH9} from "../../../src/interfaces/external/IWETH9.sol"; - -interface IPancakeV3LikePairFactory { - function createPool(address tokenA, address tokenB, uint24 fee) external returns (address pool); -} - -abstract contract BinMigratorFromV3 is OldVersionHelper, BinLiquidityHelper, DeployPermit2, Permit2ApproveHelper { - using BinPoolParametersHelper for bytes32; - using PackedUint128Math for bytes32; - using BinTokenLibrary for PoolId; - - error ContractSizeTooLarge(uint256 diff); - - uint160 public constant INIT_SQRT_PRICE = 79228162514264337593543950336; - // 1 tokenX = 1 tokenY - uint24 public constant ACTIVE_BIN_ID = 2 ** 23; - - WETH weth; - MockERC20 token0; - MockERC20 token1; - - Vault vault; - BinPoolManager poolManager; - BinPositionManager binPm; - IAllowanceTransfer permit2; - IBinMigrator migrator; - PoolKey poolKey; - PoolKey poolKeyWithoutNativeToken; - MockBinMigratorHook binMigratorHook; - - IPancakeV3LikePairFactory v3Factory; - IV3NonfungiblePositionManager v3Nfpm; - - function _getDeployerBytecodePath() internal pure virtual returns (string memory); - function _getFactoryBytecodePath() internal pure virtual returns (string memory); - function _getNfpmBytecodePath() internal pure virtual returns (string memory); - - function _getContractName() internal pure virtual returns (string memory); - - function setUp() public { - weth = new WETH(); - token0 = new MockERC20("Token0", "TKN0", 18); - token1 = new MockERC20("Token1", "TKN1", 18); - (token0, token1) = token0 < token1 ? (token0, token1) : (token1, token0); - - // init infinity nfpm & migrator - vault = new Vault(); - poolManager = new BinPoolManager(IVault(address(vault))); - vault.registerApp(address(poolManager)); - permit2 = IAllowanceTransfer(deployPermit2()); - binPm = new BinPositionManager( - IVault(address(vault)), IBinPoolManager(address(poolManager)), permit2, IWETH9(address(0)) - ); - migrator = new BinMigrator(address(weth), address(binPm), permit2); - binMigratorHook = new MockBinMigratorHook(); - - poolKey = PoolKey({ - // WETH after migration will be native token - currency0: Currency.wrap(address(0)), - currency1: Currency.wrap(address(token0)), - hooks: IHooks(address(binMigratorHook)), - poolManager: poolManager, - fee: 0, - parameters: bytes32(uint256(binMigratorHook.getHooksRegistrationBitmap())).setBinStep(1) - }); - - poolKeyWithoutNativeToken = PoolKey({ - currency0: Currency.wrap(address(token0)), - currency1: Currency.wrap(address(token1)), - hooks: IHooks(address(0)), - poolManager: poolManager, - fee: 0, - parameters: bytes32(0).setBinStep(1) - }); - - // make sure the contract has enough balance - // WETH: 100 ether - // Token: 100 ether - // ETH: 90 ether - deal(address(this), 1000 ether); - weth.deposit{value: 100 ether}(); - token0.mint(address(this), 100 ether); - token1.mint(address(this), 100 ether); - - // pcs v3 - if (bytes(_getDeployerBytecodePath()).length != 0) { - address deployer = createContractThroughBytecode(_getDeployerBytecodePath()); - v3Factory = IPancakeV3LikePairFactory( - createContractThroughBytecode(_getFactoryBytecodePath(), toBytes32(address(deployer))) - ); - (bool success,) = deployer.call(abi.encodeWithSignature("setFactoryAddress(address)", address(v3Factory))); - require(success, "setFactoryAddress failed"); - v3Nfpm = IV3NonfungiblePositionManager( - createContractThroughBytecode( - _getNfpmBytecodePath(), - toBytes32(deployer), - toBytes32(address(v3Factory)), - toBytes32(address(weth)), - 0 - ) - ); - } else { - v3Factory = IPancakeV3LikePairFactory(createContractThroughBytecode(_getFactoryBytecodePath())); - - v3Nfpm = IV3NonfungiblePositionManager( - createContractThroughBytecode( - _getNfpmBytecodePath(), toBytes32(address(v3Factory)), toBytes32(address(weth)), 0 - ) - ); - } - - // make sure v3Nfpm has allowance - weth.approve(address(v3Nfpm), type(uint256).max); - token0.approve(address(v3Nfpm), type(uint256).max); - token1.approve(address(v3Nfpm), type(uint256).max); - } - - function test_bytecodeSize() public { - vm.snapshotValue("BinMigratorBytecode size", address(migrator).code.length); - - if (vm.envExists("FOUNDRY_PROFILE") && address(migrator).code.length > 24576) { - revert ContractSizeTooLarge(address(migrator).code.length - 24576); - } - } - - function testMigrateFromV3_WhenPaused() public { - // 1. mint some liquidity to the v3 pool - _mintV3Liquidity(address(weth), address(token0)); - (,,,,,,, uint128 liquidityFromV3Before,,,,) = v3Nfpm.positions(1); - - // 2. make sure migrator can transfer user's v3 lp token - v3Nfpm.approve(address(migrator), 1); - - // 3. initialize the pool - migrator.initializePool(poolKey, ACTIVE_BIN_ID); - - IBaseMigrator.V3PoolParams memory v3PoolParams = IBaseMigrator.V3PoolParams({ - nfp: address(v3Nfpm), - tokenId: 1, - liquidity: liquidityFromV3Before, - amount0Min: 9.9 ether, - amount1Min: 9.9 ether, - collectFee: false, - deadline: block.timestamp + 100 - }); - - IBinPositionManager.BinAddLiquidityParams memory params = - _getAddParams(poolKey, getBinIds(ACTIVE_BIN_ID, 3), 10 ether, 10 ether, ACTIVE_BIN_ID, address(this)); - - IBinMigrator.InfiBinPoolParams memory infiBinPoolParams = IBinMigrator.InfiBinPoolParams({ - poolKey: params.poolKey, - amount0Max: params.amount0Max, - amount1Max: params.amount1Max, - activeIdDesired: params.activeIdDesired, - idSlippage: params.idSlippage, - deltaIds: params.deltaIds, - distributionX: params.distributionX, - distributionY: params.distributionY, - to: params.to, - deadline: block.timestamp + 1, - hookData: new bytes(0) - }); - - // pre-req: pause - BinMigrator _migrator = BinMigrator(payable(address(migrator))); - _migrator.pause(); - - // 4. migrateFromV3 directly given pool has been initialized - vm.expectRevert(Pausable.EnforcedPause.selector); - migrator.migrateFromV3(v3PoolParams, infiBinPoolParams, 0, 0); - } - - function testMigrateFromV3_HookData() public { - // 1. mint some liquidity to the v3 pool - _mintV3Liquidity(address(weth), address(token0)); - (,,,,,,, uint128 liquidityFromV3Before,,,,) = v3Nfpm.positions(1); - - // 2. make sure migrator can transfer user's v3 lp token - v3Nfpm.approve(address(migrator), 1); - - // 3. initialize the pool - migrator.initializePool(poolKey, ACTIVE_BIN_ID); - - IBaseMigrator.V3PoolParams memory v3PoolParams = IBaseMigrator.V3PoolParams({ - nfp: address(v3Nfpm), - tokenId: 1, - liquidity: liquidityFromV3Before, - amount0Min: 9.9 ether, - amount1Min: 9.9 ether, - collectFee: false, - deadline: block.timestamp + 100 - }); - - IBinPositionManager.BinAddLiquidityParams memory params = - _getAddParams(poolKey, getBinIds(ACTIVE_BIN_ID, 3), 10 ether, 10 ether, ACTIVE_BIN_ID, address(this)); - - bytes memory hookData = abi.encode(32); - IBinMigrator.InfiBinPoolParams memory infiBinPoolParams = IBinMigrator.InfiBinPoolParams({ - poolKey: params.poolKey, - amount0Max: params.amount0Max, - amount1Max: params.amount1Max, - activeIdDesired: params.activeIdDesired, - idSlippage: params.idSlippage, - deltaIds: params.deltaIds, - distributionX: params.distributionX, - distributionY: params.distributionY, - to: params.to, - deadline: block.timestamp + 1, - hookData: hookData - }); - - // 4. migrateFromV3 directly given pool has been initialized - migrator.migrateFromV3(v3PoolParams, infiBinPoolParams, 0, 0); - - // assert hookData flown to hook - assertEq(binMigratorHook.hookData(), hookData); - } - - function testMigrateFromV3ReentrancyLockRevert() public { - MockReentrantPositionManager reentrantPM = new MockReentrantPositionManager(permit2); - migrator = new BinMigrator(address(weth), address(reentrantPM), permit2); - reentrantPM.setBinMigrator(migrator); - - // 1. mint some liquidity to the v3 pool - _mintV3Liquidity(address(weth), address(token0)); - assertEq(v3Nfpm.ownerOf(1), address(this)); - (,,,,,,, uint128 liquidityFromV3Before,,,,) = v3Nfpm.positions(1); - assertGt(liquidityFromV3Before, 0); - - // 2. make sure migrator can transfer user's v3 lp token - v3Nfpm.approve(address(migrator), 1); - - // 3. initialize the pool - poolManager.initialize(poolKey, ACTIVE_BIN_ID); - - IBaseMigrator.V3PoolParams memory v3PoolParams = IBaseMigrator.V3PoolParams({ - nfp: address(v3Nfpm), - tokenId: 1, - liquidity: liquidityFromV3Before, - amount0Min: 9.9 ether, - amount1Min: 9.9 ether, - collectFee: false, - deadline: block.timestamp + 100 - }); - - IBinPositionManager.BinAddLiquidityParams memory params = - _getAddParams(poolKey, getBinIds(ACTIVE_BIN_ID, 3), 10 ether, 10 ether, ACTIVE_BIN_ID, address(this)); - - IBinMigrator.InfiBinPoolParams memory infiBinPoolParams = IBinMigrator.InfiBinPoolParams({ - poolKey: params.poolKey, - amount0Max: params.amount0Max, - amount1Max: params.amount1Max, - activeIdDesired: params.activeIdDesired, - idSlippage: params.idSlippage, - deltaIds: params.deltaIds, - distributionX: params.distributionX, - distributionY: params.distributionY, - to: params.to, - deadline: block.timestamp + 1, - hookData: new bytes(0) - }); - - vm.expectRevert(ReentrancyLock.ContractLocked.selector); - migrator.migrateFromV3(v3PoolParams, infiBinPoolParams, 0, 0); - } - - function testMigrateFromV3IncludingInit() public { - // 1. mint some liquidity to the v3 pool - _mintV3Liquidity(address(weth), address(token0)); - assertEq(v3Nfpm.ownerOf(1), address(this)); - (,,,,,,, uint128 liquidityFromV3Before,,,,) = v3Nfpm.positions(1); - assertGt(liquidityFromV3Before, 0); - - // 2. make sure migrator can transfer user's v3 lp token - v3Nfpm.approve(address(migrator), 1); - - IBaseMigrator.V3PoolParams memory v3PoolParams = IBaseMigrator.V3PoolParams({ - nfp: address(v3Nfpm), - tokenId: 1, - liquidity: liquidityFromV3Before, - amount0Min: 9.9 ether, - amount1Min: 9.9 ether, - collectFee: false, - deadline: block.timestamp + 100 - }); - - IBinPositionManager.BinAddLiquidityParams memory params = - _getAddParams(poolKey, getBinIds(ACTIVE_BIN_ID, 3), 10 ether, 10 ether, ACTIVE_BIN_ID, address(this)); - - IBinMigrator.InfiBinPoolParams memory infiBinPoolParams = IBinMigrator.InfiBinPoolParams({ - poolKey: params.poolKey, - amount0Max: params.amount0Max, - amount1Max: params.amount1Max, - activeIdDesired: params.activeIdDesired, - idSlippage: params.idSlippage, - deltaIds: params.deltaIds, - distributionX: params.distributionX, - distributionY: params.distributionY, - to: params.to, - deadline: block.timestamp + 1, - hookData: new bytes(0) - }); - - // 3. multicall, combine initialize and migrateFromV3 - bytes[] memory data = new bytes[](2); - data[0] = abi.encodeWithSelector(migrator.initializePool.selector, poolKey, ACTIVE_BIN_ID, bytes("")); - data[1] = abi.encodeWithSelector(migrator.migrateFromV3.selector, v3PoolParams, infiBinPoolParams, 0, 0); - migrator.multicall(data); - vm.snapshotGasLastCall("testMigrateFromV3IncludingInit"); - - // necessary checks - // v3 liqudity should be 0 - (,,,,,,, uint128 liquidityFromV3After,,,,) = v3Nfpm.positions(1); - assertEq(liquidityFromV3After, 0); - - // make sure liuqidty is minted to the correct pooA - assertApproxEqAbs(address(vault).balance, 10 ether, 0.000001 ether); - assertApproxEqAbs(token0.balanceOf(address(vault)), 10 ether, 0.000001 ether); - - uint256 positionId0 = poolKey.toId().toTokenId(ACTIVE_BIN_ID - 1); - uint256 positionId1 = poolKey.toId().toTokenId(ACTIVE_BIN_ID); - uint256 positionId2 = poolKey.toId().toTokenId(ACTIVE_BIN_ID + 1); - uint256 positionId3 = poolKey.toId().toTokenId(ACTIVE_BIN_ID + 2); - assertGt(binPm.balanceOf(address(this), positionId0), 0); - assertGt(binPm.balanceOf(address(this), positionId1), 0); - assertGt(binPm.balanceOf(address(this), positionId2), 0); - assertEq(binPm.balanceOf(address(this), positionId3), 0); - - (PoolKey memory _poolKey, uint24 binId) = binPm.positions(positionId0); - assertEq(PoolId.unwrap(_poolKey.toId()), PoolId.unwrap(poolKey.toId())); - assertEq(Currency.unwrap(_poolKey.currency0), address(0)); - assertEq(Currency.unwrap(_poolKey.currency1), address(token0)); - assertEq(_poolKey.fee, 0); - assertEq(binId, ACTIVE_BIN_ID - 1); - - (_poolKey, binId) = binPm.positions(positionId1); - assertEq(PoolId.unwrap(_poolKey.toId()), PoolId.unwrap(poolKey.toId())); - assertEq(Currency.unwrap(_poolKey.currency0), address(0)); - assertEq(Currency.unwrap(_poolKey.currency1), address(token0)); - assertEq(_poolKey.fee, 0); - assertEq(binId, ACTIVE_BIN_ID); - - (_poolKey, binId) = binPm.positions(positionId2); - assertEq(PoolId.unwrap(_poolKey.toId()), PoolId.unwrap(poolKey.toId())); - assertEq(Currency.unwrap(_poolKey.currency0), address(0)); - assertEq(Currency.unwrap(_poolKey.currency1), address(token0)); - assertEq(_poolKey.fee, 0); - assertEq(binId, ACTIVE_BIN_ID + 1); - - vm.expectRevert(IPositionManager.InvalidTokenID.selector); - binPm.positions(positionId3); - } - - function testMigrateFromV3TokenMismatch() public { - // 1. mint some liquidity to the v3 pool - _mintV3Liquidity(address(weth), address(token0)); - assertEq(v3Nfpm.ownerOf(1), address(this)); - (,,,,,,, uint128 liquidityFromV3Before,,,,) = v3Nfpm.positions(1); - assertGt(liquidityFromV3Before, 0); - - // 2. make sure migrator can transfer user's v3 lp token - v3Nfpm.approve(address(migrator), 1); - - IBaseMigrator.V3PoolParams memory v3PoolParams = IBaseMigrator.V3PoolParams({ - nfp: address(v3Nfpm), - tokenId: 1, - liquidity: liquidityFromV3Before, - amount0Min: 9.9 ether, - amount1Min: 9.9 ether, - collectFee: false, - deadline: block.timestamp + 100 - }); - - IBinPositionManager.BinAddLiquidityParams memory params = - _getAddParams(poolKey, getBinIds(ACTIVE_BIN_ID, 3), 10 ether, 10 ether, ACTIVE_BIN_ID, address(this)); - - // v3 weth, token0 - // infinity ETH, token1 - PoolKey memory poolKeyMismatch = poolKey; - poolKeyMismatch.currency1 = Currency.wrap(address(token1)); - IBinMigrator.InfiBinPoolParams memory infiBinPoolParams = IBinMigrator.InfiBinPoolParams({ - poolKey: poolKeyMismatch, - amount0Max: params.amount0Max, - amount1Max: params.amount1Max, - activeIdDesired: params.activeIdDesired, - idSlippage: params.idSlippage, - deltaIds: params.deltaIds, - distributionX: params.distributionX, - distributionY: params.distributionY, - to: params.to, - deadline: block.timestamp + 1, - hookData: new bytes(0) - }); - - // 3. multicall, combine initialize and migrateFromV3 - bytes[] memory data = new bytes[](2); - data[0] = abi.encodeWithSelector(migrator.initializePool.selector, poolKeyMismatch, ACTIVE_BIN_ID, bytes("")); - data[1] = abi.encodeWithSelector(migrator.migrateFromV3.selector, v3PoolParams, infiBinPoolParams, 0, 0); - vm.expectRevert(); - migrator.multicall(data); - - { - // v3 weth, token0 - // infinity token0, token1 - poolKeyMismatch.currency0 = Currency.wrap(address(token0)); - poolKeyMismatch.currency1 = Currency.wrap(address(token1)); - infiBinPoolParams.poolKey = poolKeyMismatch; - data = new bytes[](2); - data[0] = - abi.encodeWithSelector(migrator.initializePool.selector, poolKeyMismatch, ACTIVE_BIN_ID, bytes("")); - data[1] = abi.encodeWithSelector(migrator.migrateFromV3.selector, v3PoolParams, infiBinPoolParams, 0, 0); - vm.expectRevert(); - migrator.multicall(data); - } - } - - function testMigrateFromV3WithoutInit() public { - // 1. mint some liquidity to the v3 pool - _mintV3Liquidity(address(weth), address(token0)); - assertEq(v3Nfpm.ownerOf(1), address(this)); - (,,,,,,, uint128 liquidityFromV3Before,,,,) = v3Nfpm.positions(1); - assertGt(liquidityFromV3Before, 0); - - // 2. make sure migrator can transfer user's v3 lp token - v3Nfpm.approve(address(migrator), 1); - - // 3. initialize the pool - migrator.initializePool(poolKey, ACTIVE_BIN_ID); - - IBaseMigrator.V3PoolParams memory v3PoolParams = IBaseMigrator.V3PoolParams({ - nfp: address(v3Nfpm), - tokenId: 1, - liquidity: liquidityFromV3Before, - amount0Min: 9.9 ether, - amount1Min: 9.9 ether, - collectFee: false, - deadline: block.timestamp + 100 - }); - - IBinPositionManager.BinAddLiquidityParams memory params = - _getAddParams(poolKey, getBinIds(ACTIVE_BIN_ID, 3), 10 ether, 10 ether, ACTIVE_BIN_ID, address(this)); - - IBinMigrator.InfiBinPoolParams memory infiBinPoolParams = IBinMigrator.InfiBinPoolParams({ - poolKey: params.poolKey, - amount0Max: params.amount0Max, - amount1Max: params.amount1Max, - activeIdDesired: params.activeIdDesired, - idSlippage: params.idSlippage, - deltaIds: params.deltaIds, - distributionX: params.distributionX, - distributionY: params.distributionY, - to: params.to, - deadline: block.timestamp + 1, - hookData: new bytes(0) - }); - - // 4. migrateFromV3 directly given pool has been initialized - migrator.migrateFromV3(v3PoolParams, infiBinPoolParams, 0, 0); - vm.snapshotGasLastCall("testMigrateFromV3WithoutInit"); - - // necessary checks - // v3 liqudity should be 0 - (,,,,,,, uint128 liquidityFromV3After,,,,) = v3Nfpm.positions(1); - assertEq(liquidityFromV3After, 0); - - // make sure liuqidty is minted to the correct pool - assertApproxEqAbs(address(vault).balance, 10 ether, 0.000001 ether); - assertApproxEqAbs(token0.balanceOf(address(vault)), 10 ether, 0.000001 ether); - - uint256 positionId0 = poolKey.toId().toTokenId(ACTIVE_BIN_ID - 1); - uint256 positionId1 = poolKey.toId().toTokenId(ACTIVE_BIN_ID); - uint256 positionId2 = poolKey.toId().toTokenId(ACTIVE_BIN_ID + 1); - uint256 positionId3 = poolKey.toId().toTokenId(ACTIVE_BIN_ID + 2); - assertGt(binPm.balanceOf(address(this), positionId0), 0); - assertGt(binPm.balanceOf(address(this), positionId1), 0); - assertGt(binPm.balanceOf(address(this), positionId2), 0); - assertEq(binPm.balanceOf(address(this), positionId3), 0); - - (PoolKey memory _poolKey, uint24 binId) = binPm.positions(positionId0); - assertEq(PoolId.unwrap(_poolKey.toId()), PoolId.unwrap(poolKey.toId())); - assertEq(Currency.unwrap(_poolKey.currency0), address(0)); - assertEq(Currency.unwrap(_poolKey.currency1), address(token0)); - assertEq(_poolKey.fee, 0); - assertEq(binId, ACTIVE_BIN_ID - 1); - - (_poolKey, binId) = binPm.positions(positionId1); - assertEq(PoolId.unwrap(_poolKey.toId()), PoolId.unwrap(poolKey.toId())); - assertEq(Currency.unwrap(_poolKey.currency0), address(0)); - assertEq(Currency.unwrap(_poolKey.currency1), address(token0)); - assertEq(_poolKey.fee, 0); - assertEq(binId, ACTIVE_BIN_ID); - - (_poolKey, binId) = binPm.positions(positionId2); - assertEq(PoolId.unwrap(_poolKey.toId()), PoolId.unwrap(poolKey.toId())); - assertEq(Currency.unwrap(_poolKey.currency0), address(0)); - assertEq(Currency.unwrap(_poolKey.currency1), address(token0)); - assertEq(_poolKey.fee, 0); - assertEq(binId, ACTIVE_BIN_ID + 1); - - vm.expectRevert(IPositionManager.InvalidTokenID.selector); - binPm.positions(positionId3); - } - - function testMigrateFromV3WithoutNativeToken() public { - // 1. mint some liquidity to the v3 pool - _mintV3Liquidity(address(token0), address(token1)); - - assertEq(v3Nfpm.ownerOf(1), address(this)); - (,,,,,,, uint128 liquidityFromV3Before,,,,) = v3Nfpm.positions(1); - assertGt(liquidityFromV3Before, 0); - - // 2. make sure migrator can transfer user's v3 lp token - v3Nfpm.approve(address(migrator), 1); - - // 3. initialize the pool - migrator.initializePool(poolKeyWithoutNativeToken, ACTIVE_BIN_ID); - - IBaseMigrator.V3PoolParams memory v3PoolParams = IBaseMigrator.V3PoolParams({ - nfp: address(v3Nfpm), - tokenId: 1, - liquidity: liquidityFromV3Before, - amount0Min: 9.9 ether, - amount1Min: 9.9 ether, - collectFee: false, - deadline: block.timestamp + 100 - }); - - IBinPositionManager.BinAddLiquidityParams memory params = _getAddParams( - poolKeyWithoutNativeToken, getBinIds(ACTIVE_BIN_ID, 3), 10 ether, 10 ether, ACTIVE_BIN_ID, address(this) - ); - - IBinMigrator.InfiBinPoolParams memory infiBinPoolParams = IBinMigrator.InfiBinPoolParams({ - poolKey: params.poolKey, - amount0Max: params.amount0Max, - amount1Max: params.amount1Max, - activeIdDesired: params.activeIdDesired, - idSlippage: params.idSlippage, - deltaIds: params.deltaIds, - distributionX: params.distributionX, - distributionY: params.distributionY, - to: params.to, - deadline: block.timestamp + 1, - hookData: new bytes(0) - }); - - // 4. migrate from v3 to infinity - migrator.migrateFromV3(v3PoolParams, infiBinPoolParams, 0, 0); - vm.snapshotGasLastCall("testMigrateFromV3WithoutNativeToken"); - - // necessary checks - // v3 liqudity should be 0 - (,,,,,,, uint128 liquidityFromV3After,,,,) = v3Nfpm.positions(1); - assertEq(liquidityFromV3After, 0); - - // make sure liuqidty is minted to the correct pool - assertApproxEqAbs(token0.balanceOf(address(vault)), 10 ether, 0.000001 ether); - assertApproxEqAbs(token1.balanceOf(address(vault)), 10 ether, 0.000001 ether); - - uint256 positionId0 = poolKeyWithoutNativeToken.toId().toTokenId(ACTIVE_BIN_ID - 1); - uint256 positionId1 = poolKeyWithoutNativeToken.toId().toTokenId(ACTIVE_BIN_ID); - uint256 positionId2 = poolKeyWithoutNativeToken.toId().toTokenId(ACTIVE_BIN_ID + 1); - uint256 positionId3 = poolKeyWithoutNativeToken.toId().toTokenId(ACTIVE_BIN_ID + 2); - assertGt(binPm.balanceOf(address(this), positionId0), 0); - assertGt(binPm.balanceOf(address(this), positionId1), 0); - assertGt(binPm.balanceOf(address(this), positionId2), 0); - assertEq(binPm.balanceOf(address(this), positionId3), 0); - - (PoolKey memory _poolKey, uint24 binId) = binPm.positions(positionId0); - assertEq(PoolId.unwrap(_poolKey.toId()), PoolId.unwrap(poolKeyWithoutNativeToken.toId())); - assertEq(Currency.unwrap(_poolKey.currency0), address(token0)); - assertEq(Currency.unwrap(_poolKey.currency1), address(token1)); - assertEq(_poolKey.fee, 0); - assertEq(binId, ACTIVE_BIN_ID - 1); - - (_poolKey, binId) = binPm.positions(positionId1); - assertEq(PoolId.unwrap(_poolKey.toId()), PoolId.unwrap(poolKeyWithoutNativeToken.toId())); - assertEq(Currency.unwrap(_poolKey.currency0), address(token0)); - assertEq(Currency.unwrap(_poolKey.currency1), address(token1)); - assertEq(_poolKey.fee, 0); - assertEq(binId, ACTIVE_BIN_ID); - - (_poolKey, binId) = binPm.positions(positionId2); - assertEq(PoolId.unwrap(_poolKey.toId()), PoolId.unwrap(poolKeyWithoutNativeToken.toId())); - assertEq(Currency.unwrap(_poolKey.currency0), address(token0)); - assertEq(Currency.unwrap(_poolKey.currency1), address(token1)); - assertEq(_poolKey.fee, 0); - assertEq(binId, ACTIVE_BIN_ID + 1); - - vm.expectRevert(IPositionManager.InvalidTokenID.selector); - binPm.positions(positionId3); - } - - function testMigrateFromV3AddExtraAmount() public { - // 1. mint some liquidity to the v3 pool - _mintV3Liquidity(address(weth), address(token0)); - assertEq(v3Nfpm.ownerOf(1), address(this)); - (,,,,,,, uint128 liquidityFromV3Before,,,,) = v3Nfpm.positions(1); - assertGt(liquidityFromV3Before, 0); - - // 2. make sure migrator can transfer user's v3 lp token - v3Nfpm.approve(address(migrator), 1); - - // 3. init the pool - migrator.initializePool(poolKey, ACTIVE_BIN_ID); - - IBaseMigrator.V3PoolParams memory v3PoolParams = IBaseMigrator.V3PoolParams({ - nfp: address(v3Nfpm), - tokenId: 1, - liquidity: liquidityFromV3Before, - amount0Min: 9.9 ether, - amount1Min: 9.9 ether, - collectFee: false, - deadline: block.timestamp + 100 - }); - - IBinPositionManager.BinAddLiquidityParams memory params = - _getAddParams(poolKey, getBinIds(ACTIVE_BIN_ID, 3), 10 ether, 10 ether, ACTIVE_BIN_ID, address(this)); - - IBinMigrator.InfiBinPoolParams memory infiBinPoolParams = IBinMigrator.InfiBinPoolParams({ - poolKey: params.poolKey, - amount0Max: params.amount0Max, - amount1Max: params.amount1Max, - activeIdDesired: params.activeIdDesired, - idSlippage: params.idSlippage, - deltaIds: params.deltaIds, - distributionX: params.distributionX, - distributionY: params.distributionY, - to: params.to, - deadline: block.timestamp + 1, - hookData: new bytes(0) - }); - - uint256 balance0Before = address(this).balance; - uint256 balance1Before = token0.balanceOf(address(this)); - - permit2ApproveWithSpecificAllowance( - address(this), permit2, address(token0), address(migrator), 20 ether, 20 ether - ); - // 4. migrate from v3 to infinity - migrator.migrateFromV3{value: 20 ether}(v3PoolParams, infiBinPoolParams, 20 ether, 20 ether); - - // necessary checks - // consumed extra 20 ether from user - assertApproxEqAbs(balance0Before - address(this).balance, 20 ether, 0.000001 ether); - assertApproxEqAbs(balance1Before - token0.balanceOf(address(this)), 20 ether, 0.000001 ether); - // WETH balance unchanged - assertEq(weth.balanceOf(address(this)), 90 ether); - - // v3 liqudity should be 0 - (,,,,,,, uint128 liquidityFromV3After,,,,) = v3Nfpm.positions(1); - assertEq(liquidityFromV3After, 0); - - // make sure liuqidty is minted to the correct pool - assertApproxEqAbs(address(vault).balance, 30 ether, 0.000001 ether); - assertApproxEqAbs(token0.balanceOf(address(vault)), 30 ether, 0.000001 ether); - - uint256 positionId0 = poolKey.toId().toTokenId(ACTIVE_BIN_ID - 1); - uint256 positionId1 = poolKey.toId().toTokenId(ACTIVE_BIN_ID); - uint256 positionId2 = poolKey.toId().toTokenId(ACTIVE_BIN_ID + 1); - uint256 positionId3 = poolKey.toId().toTokenId(ACTIVE_BIN_ID + 2); - assertGt(binPm.balanceOf(address(this), positionId0), 0); - assertGt(binPm.balanceOf(address(this), positionId1), 0); - assertGt(binPm.balanceOf(address(this), positionId2), 0); - assertEq(binPm.balanceOf(address(this), positionId3), 0); - - (PoolKey memory _poolKey, uint24 binId) = binPm.positions(positionId0); - assertEq(PoolId.unwrap(_poolKey.toId()), PoolId.unwrap(poolKey.toId())); - assertEq(Currency.unwrap(_poolKey.currency0), address(0)); - assertEq(Currency.unwrap(_poolKey.currency1), address(token0)); - assertEq(_poolKey.fee, 0); - assertEq(binId, ACTIVE_BIN_ID - 1); - - (_poolKey, binId) = binPm.positions(positionId1); - assertEq(PoolId.unwrap(_poolKey.toId()), PoolId.unwrap(poolKey.toId())); - assertEq(Currency.unwrap(_poolKey.currency0), address(0)); - assertEq(Currency.unwrap(_poolKey.currency1), address(token0)); - assertEq(_poolKey.fee, 0); - assertEq(binId, ACTIVE_BIN_ID); - - (_poolKey, binId) = binPm.positions(positionId2); - assertEq(PoolId.unwrap(_poolKey.toId()), PoolId.unwrap(poolKey.toId())); - assertEq(Currency.unwrap(_poolKey.currency0), address(0)); - assertEq(Currency.unwrap(_poolKey.currency1), address(token0)); - assertEq(_poolKey.fee, 0); - assertEq(binId, ACTIVE_BIN_ID + 1); - - vm.expectRevert(IPositionManager.InvalidTokenID.selector); - binPm.positions(positionId3); - } - - function testMigrateFromV3WhenPMHaveNativeBalance() public { - // 1. mint some liquidity to the v3 pool - _mintV3Liquidity(address(weth), address(token0)); - assertEq(v3Nfpm.ownerOf(1), address(this)); - (,,,,,,, uint128 liquidityFromV3Before,,,,) = v3Nfpm.positions(1); - assertGt(liquidityFromV3Before, 0); - - // 2. make sure migrator can transfer user's v3 lp token - v3Nfpm.approve(address(migrator), 1); - - // 3. init the pool - migrator.initializePool(poolKey, ACTIVE_BIN_ID); - - IBaseMigrator.V3PoolParams memory v3PoolParams = IBaseMigrator.V3PoolParams({ - nfp: address(v3Nfpm), - tokenId: 1, - liquidity: liquidityFromV3Before, - amount0Min: 9.9 ether, - amount1Min: 9.9 ether, - collectFee: false, - deadline: block.timestamp + 100 - }); - - IBinPositionManager.BinAddLiquidityParams memory params = - _getAddParams(poolKey, getBinIds(ACTIVE_BIN_ID, 3), 10 ether, 10 ether, ACTIVE_BIN_ID, address(this)); - - IBinMigrator.InfiBinPoolParams memory infiBinPoolParams = IBinMigrator.InfiBinPoolParams({ - poolKey: params.poolKey, - amount0Max: params.amount0Max, - amount1Max: params.amount1Max, - activeIdDesired: params.activeIdDesired, - idSlippage: params.idSlippage, - deltaIds: params.deltaIds, - distributionX: params.distributionX, - distributionY: params.distributionY, - to: params.to, - deadline: block.timestamp + 1, - hookData: new bytes(0) - }); - - uint256 nativeBlanceBefore = address(this).balance; - - permit2ApproveWithSpecificAllowance( - address(this), permit2, address(token0), address(migrator), 20 ether, 20 ether - ); - // deposit native token to the PM - vm.deal(address(binPm), 100 ether); - - // user can collect native token by removing liquidity - uint256 v3PositionNativeAmount = 9999999999999999999; - // mint bin position data - bytes32[] memory mintAmounts = new bytes32[](3); - mintAmounts[0] = 0x0000000000000000d02ab486cedbffff00000000000000000000000000000000; - mintAmounts[1] = 0x0000000000000000d02ab486cedbffff0000000000000000d02ab486cedbffff; - mintAmounts[2] = 0x000000000000000000000000000000000000000000000000d02ab486cedbffff; - uint256[] memory ids = new uint256[](3); - ids[0] = 8388607; - ids[1] = 8388608; - ids[2] = 8388609; - // check v3 position collect - vm.expectEmit(true, true, true, true); - emit IV3NonfungiblePositionManager.Collect(1, address(migrator), v3PositionNativeAmount, 9999999999999999999); - // check bin pool mint event - vm.expectEmit(false, false, false, true); - emit IBinPoolManager.Mint( - PoolId.wrap(0x0000000000000000000000000000000000000000000000000000000000000000), - address(0), - ids, - 0x0000000000000000000000000000000000000000000000000000000000000000, - mintAmounts, - 0x0000000000000000000000000000000000000000000000000000000000000000, - 0x0000000000000000000000000000000000000000000000000000000000000000 - ); - // 4. migrate from v3 to infinity - migrator.migrateFromV3{value: 20 ether}(v3PoolParams, infiBinPoolParams, 20 ether, 20 ether); - - uint256 nativeBlanceAfter = address(this).balance; - // user did not consume any native token, and also get the v3 liquidity native token as refund - assertEq(nativeBlanceAfter - nativeBlanceBefore, v3PositionNativeAmount); - - migrator.refundETH(); - - // calculate the mint consuemd native token - uint128 totalConsumedNative = mintAmounts[0].decodeX() + mintAmounts[1].decodeX() + mintAmounts[2].decodeX(); - uint256 nativeBlanceAfterRefund = address(this).balance; - assertTrue(nativeBlanceAfterRefund > nativeBlanceBefore); - assertEq( - nativeBlanceAfterRefund - nativeBlanceBefore, - 100 ether + v3PositionNativeAmount - uint256(totalConsumedNative) - ); - } - - function testMigrateFromV3AddExtraAmountThroughWETH() public { - // 1. mint some liquidity to the v3 pool - _mintV3Liquidity(address(weth), address(token0)); - assertEq(v3Nfpm.ownerOf(1), address(this)); - (,,,,,,, uint128 liquidityFromV3Before,,,,) = v3Nfpm.positions(1); - assertGt(liquidityFromV3Before, 0); - - // 2. make sure migrator can transfer user's v3 lp token - v3Nfpm.approve(address(migrator), 1); - - // 3. init the pool - migrator.initializePool(poolKey, ACTIVE_BIN_ID); - - IBaseMigrator.V3PoolParams memory v3PoolParams = IBaseMigrator.V3PoolParams({ - nfp: address(v3Nfpm), - tokenId: 1, - liquidity: liquidityFromV3Before, - amount0Min: 9.9 ether, - amount1Min: 9.9 ether, - collectFee: false, - deadline: block.timestamp + 100 - }); - - IBinPositionManager.BinAddLiquidityParams memory params = - _getAddParams(poolKey, getBinIds(ACTIVE_BIN_ID, 3), 10 ether, 10 ether, ACTIVE_BIN_ID, address(this)); - - IBinMigrator.InfiBinPoolParams memory infiBinPoolParams = IBinMigrator.InfiBinPoolParams({ - poolKey: params.poolKey, - amount0Max: params.amount0Max, - amount1Max: params.amount1Max, - activeIdDesired: params.activeIdDesired, - idSlippage: params.idSlippage, - deltaIds: params.deltaIds, - distributionX: params.distributionX, - distributionY: params.distributionY, - to: params.to, - deadline: block.timestamp + 1, - hookData: new bytes(0) - }); - - uint256 balance0Before = address(this).balance; - uint256 balance1Before = token0.balanceOf(address(this)); - - permit2ApproveWithSpecificAllowance( - address(this), permit2, address(weth), address(migrator), 20 ether, 20 ether - ); - permit2ApproveWithSpecificAllowance( - address(this), permit2, address(token0), address(migrator), 20 ether, 20 ether - ); - // 4. migrate from v3 to infinity, not sending ETH denotes pay by WETH - migrator.migrateFromV3(v3PoolParams, infiBinPoolParams, 20 ether, 20 ether); - - // necessary checks - // consumed extra 20 ether from user - // native token balance unchanged - assertApproxEqAbs(address(this).balance - balance0Before, 0 ether, 0.000001 ether); - assertApproxEqAbs(balance1Before - token0.balanceOf(address(this)), 20 ether, 0.00001 ether); - // consumed 20 ether WETH - assertEq(weth.balanceOf(address(this)), 70 ether); - - // v3 liqudity should be 0 - (,,,,,,, uint128 liquidityFromV3After,,,,) = v3Nfpm.positions(1); - assertEq(liquidityFromV3After, 0); - - // make sure liuqidty is minted to the correct pool - assertApproxEqAbs(address(vault).balance, 30 ether, 0.000001 ether); - assertApproxEqAbs(token0.balanceOf(address(vault)), 30 ether, 0.000001 ether); - - uint256 positionId0 = poolKey.toId().toTokenId(ACTIVE_BIN_ID - 1); - uint256 positionId1 = poolKey.toId().toTokenId(ACTIVE_BIN_ID); - uint256 positionId2 = poolKey.toId().toTokenId(ACTIVE_BIN_ID + 1); - uint256 positionId3 = poolKey.toId().toTokenId(ACTIVE_BIN_ID + 2); - assertGt(binPm.balanceOf(address(this), positionId0), 0); - assertGt(binPm.balanceOf(address(this), positionId1), 0); - assertGt(binPm.balanceOf(address(this), positionId2), 0); - assertEq(binPm.balanceOf(address(this), positionId3), 0); - - (PoolKey memory _poolKey, uint24 binId) = binPm.positions(positionId0); - assertEq(PoolId.unwrap(_poolKey.toId()), PoolId.unwrap(poolKey.toId())); - assertEq(Currency.unwrap(_poolKey.currency0), address(0)); - assertEq(Currency.unwrap(_poolKey.currency1), address(token0)); - assertEq(_poolKey.fee, 0); - assertEq(binId, ACTIVE_BIN_ID - 1); - - (_poolKey, binId) = binPm.positions(positionId1); - assertEq(PoolId.unwrap(_poolKey.toId()), PoolId.unwrap(poolKey.toId())); - assertEq(Currency.unwrap(_poolKey.currency0), address(0)); - assertEq(Currency.unwrap(_poolKey.currency1), address(token0)); - assertEq(_poolKey.fee, 0); - assertEq(binId, ACTIVE_BIN_ID); - - (_poolKey, binId) = binPm.positions(positionId2); - assertEq(PoolId.unwrap(_poolKey.toId()), PoolId.unwrap(poolKey.toId())); - assertEq(Currency.unwrap(_poolKey.currency0), address(0)); - assertEq(Currency.unwrap(_poolKey.currency1), address(token0)); - assertEq(_poolKey.fee, 0); - assertEq(binId, ACTIVE_BIN_ID + 1); - - vm.expectRevert(IPositionManager.InvalidTokenID.selector); - binPm.positions(positionId3); - } - - function testMigrateFromV3Refund() public { - // 1. mint some liquidity to the v3 pool - _mintV3Liquidity(address(weth), address(token0)); - assertEq(v3Nfpm.ownerOf(1), address(this)); - (,,,,,,, uint128 liquidityFromV3Before,,,,) = v3Nfpm.positions(1); - assertGt(liquidityFromV3Before, 0); - - // 2. make sure migrator can transfer user's v3 lp token - v3Nfpm.approve(address(migrator), 1); - - // 3. init the pool - migrator.initializePool(poolKey, ACTIVE_BIN_ID); - - IBaseMigrator.V3PoolParams memory v3PoolParams = IBaseMigrator.V3PoolParams({ - nfp: address(v3Nfpm), - tokenId: 1, - liquidity: liquidityFromV3Before, - amount0Min: 0, - amount1Min: 0, - collectFee: false, - deadline: block.timestamp + 100 - }); - - // adding half of the liquidity to the pool - IBinPositionManager.BinAddLiquidityParams memory params = - _getAddParams(poolKey, getBinIds(ACTIVE_BIN_ID, 3), 10 ether, 10 ether, ACTIVE_BIN_ID, address(this)); - - int256[] memory deltaIds = new int256[](2); - deltaIds[0] = params.deltaIds[0]; - deltaIds[1] = params.deltaIds[1]; - - uint256[] memory distributionX = new uint256[](2); - distributionX[0] = params.distributionX[0]; - distributionX[1] = params.distributionX[1]; - - uint256[] memory distributionY = new uint256[](2); - distributionY[0] = params.distributionY[0]; - distributionY[1] = params.distributionY[1]; - - // delete the last distribution point so that the refund is triggered - // we expect to get 50% of tokenX back - // (0, 50%) (50%, 50%) (50%, 0) => (0, 50%) (50%, 50%) - IBinMigrator.InfiBinPoolParams memory infiBinPoolParams = IBinMigrator.InfiBinPoolParams({ - poolKey: params.poolKey, - amount0Max: params.amount0Max, - amount1Max: params.amount1Max, - activeIdDesired: params.activeIdDesired, - idSlippage: params.idSlippage, - deltaIds: deltaIds, - distributionX: distributionX, - distributionY: distributionY, - to: params.to, - deadline: block.timestamp + 1, - hookData: new bytes(0) - }); - - uint256 balance0Before = address(this).balance; - uint256 balance1Before = token0.balanceOf(address(this)); - - // 4. migrate from v3 to infinity, not sending ETH denotes pay by WETH - migrator.migrateFromV3(v3PoolParams, infiBinPoolParams, 0, 0); - - // necessary checks - // refund 5 ether in the form of native token - assertApproxEqAbs(address(this).balance - balance0Before, 5.0 ether, 0.1 ether); - assertApproxEqAbs(token0.balanceOf(address(this)) - balance1Before, 0 ether, 1); - // WETH balance unchanged - assertApproxEqAbs(weth.balanceOf(address(this)), 90 ether, 0.1 ether); - - // v3 liqudity should be 0 - (,,,,,,, uint128 liquidityFromV3After,,,,) = v3Nfpm.positions(1); - assertEq(liquidityFromV3After, 0); - - // make sure liuqidty is minted to the correct pool - assertApproxEqAbs(address(vault).balance, 5 ether, 0.000001 ether); - assertApproxEqAbs(token0.balanceOf(address(vault)), 10 ether, 0.000001 ether); - - uint256 positionId0 = poolKey.toId().toTokenId(ACTIVE_BIN_ID - 1); - uint256 positionId1 = poolKey.toId().toTokenId(ACTIVE_BIN_ID); - uint256 positionId2 = poolKey.toId().toTokenId(ACTIVE_BIN_ID + 1); - uint256 positionId3 = poolKey.toId().toTokenId(ACTIVE_BIN_ID + 2); - assertGt(binPm.balanceOf(address(this), positionId0), 0); - assertGt(binPm.balanceOf(address(this), positionId1), 0); - assertEq(binPm.balanceOf(address(this), positionId2), 0); - assertEq(binPm.balanceOf(address(this), positionId3), 0); - - (PoolKey memory _poolKey, uint24 binId) = binPm.positions(positionId0); - assertEq(PoolId.unwrap(_poolKey.toId()), PoolId.unwrap(poolKey.toId())); - assertEq(Currency.unwrap(_poolKey.currency0), address(0)); - assertEq(Currency.unwrap(_poolKey.currency1), address(token0)); - assertEq(_poolKey.fee, 0); - assertEq(binId, ACTIVE_BIN_ID - 1); - - (_poolKey, binId) = binPm.positions(positionId1); - // assertEq(_poolKey.toId(), poolKey.toId()); - assertEq(PoolId.unwrap(_poolKey.toId()), PoolId.unwrap(poolKey.toId())); - assertEq(Currency.unwrap(_poolKey.currency0), address(0)); - assertEq(Currency.unwrap(_poolKey.currency1), address(token0)); - assertEq(_poolKey.fee, 0); - assertEq(binId, ACTIVE_BIN_ID); - - vm.expectRevert(IPositionManager.InvalidTokenID.selector); - binPm.positions(positionId2); - - vm.expectRevert(IPositionManager.InvalidTokenID.selector); - binPm.positions(positionId3); - } - - function testMigrateFromV3RefundNonNativeToken() public { - // 1. mint some liquidity to the v3 pool - _mintV3Liquidity(address(token0), address(token1)); - assertEq(v3Nfpm.ownerOf(1), address(this)); - (,,,,,,, uint128 liquidityFromV3Before,,,,) = v3Nfpm.positions(1); - assertGt(liquidityFromV3Before, 0); - - // 2. make sure migrator can transfer user's v3 lp token - v3Nfpm.approve(address(migrator), 1); - - // 3. init the pool - migrator.initializePool(poolKeyWithoutNativeToken, ACTIVE_BIN_ID); - - IBaseMigrator.V3PoolParams memory v3PoolParams = IBaseMigrator.V3PoolParams({ - nfp: address(v3Nfpm), - tokenId: 1, - liquidity: liquidityFromV3Before, - amount0Min: 0, - amount1Min: 0, - collectFee: false, - deadline: block.timestamp + 100 - }); - - // adding half of the liquidity to the pool - IBinPositionManager.BinAddLiquidityParams memory params = _getAddParams( - poolKeyWithoutNativeToken, getBinIds(ACTIVE_BIN_ID, 3), 10 ether, 10 ether, ACTIVE_BIN_ID, address(this) - ); - - int256[] memory deltaIds = new int256[](2); - deltaIds[0] = params.deltaIds[0]; - deltaIds[1] = params.deltaIds[1]; - - uint256[] memory distributionX = new uint256[](2); - distributionX[0] = params.distributionX[0]; - distributionX[1] = params.distributionX[1]; - - uint256[] memory distributionY = new uint256[](2); - distributionY[0] = params.distributionY[0]; - distributionY[1] = params.distributionY[1]; - - // delete the last distribution point so that the refund is triggered - // we expect to get 50% of tokenX back - // (0, 50%) (50%, 50%) (50%, 0) => (0, 50%) (50%, 50%) - IBinMigrator.InfiBinPoolParams memory infiBinPoolParams = IBinMigrator.InfiBinPoolParams({ - poolKey: params.poolKey, - amount0Max: params.amount0Max, - amount1Max: params.amount1Max, - activeIdDesired: params.activeIdDesired, - idSlippage: params.idSlippage, - deltaIds: deltaIds, - distributionX: distributionX, - distributionY: distributionY, - to: params.to, - deadline: block.timestamp + 1, - hookData: new bytes(0) - }); - - uint256 balance0Before = token0.balanceOf(address(this)); - uint256 balance1Before = token1.balanceOf(address(this)); - - // 4. migrate from v3 to infinity - migrator.migrateFromV3(v3PoolParams, infiBinPoolParams, 0, 0); - - // necessary checks - - // refund 5 ether of token0 - assertApproxEqAbs(token0.balanceOf(address(this)) - balance0Before, 5 ether, 0.1 ether); - assertApproxEqAbs(token1.balanceOf(address(this)) - balance1Before, 0 ether, 1); - // WETH balance unchanged - assertEq(weth.balanceOf(address(this)), 100 ether); - - // v3 liqudity should be 0 - (,,,,,,, uint128 liquidityFromV3After,,,,) = v3Nfpm.positions(1); - assertEq(liquidityFromV3After, 0); - - // make sure liuqidty is minted to the correct pool - assertApproxEqAbs(token0.balanceOf(address(vault)), 5 ether, 0.000001 ether); - assertApproxEqAbs(token1.balanceOf(address(vault)), 10 ether, 0.000001 ether); - - uint256 positionId0 = poolKeyWithoutNativeToken.toId().toTokenId(ACTIVE_BIN_ID - 1); - uint256 positionId1 = poolKeyWithoutNativeToken.toId().toTokenId(ACTIVE_BIN_ID); - uint256 positionId2 = poolKeyWithoutNativeToken.toId().toTokenId(ACTIVE_BIN_ID + 1); - uint256 positionId3 = poolKeyWithoutNativeToken.toId().toTokenId(ACTIVE_BIN_ID + 2); - assertGt(binPm.balanceOf(address(this), positionId0), 0); - assertGt(binPm.balanceOf(address(this), positionId1), 0); - assertEq(binPm.balanceOf(address(this), positionId2), 0); - assertEq(binPm.balanceOf(address(this), positionId3), 0); - - (PoolKey memory _poolKey, uint24 binId) = binPm.positions(positionId0); - assertEq(PoolId.unwrap(_poolKey.toId()), PoolId.unwrap(poolKeyWithoutNativeToken.toId())); - assertEq(Currency.unwrap(_poolKey.currency0), address(token0)); - assertEq(Currency.unwrap(_poolKey.currency1), address(token1)); - assertEq(_poolKey.fee, 0); - assertEq(binId, ACTIVE_BIN_ID - 1); - - (_poolKey, binId) = binPm.positions(positionId1); - assertEq(PoolId.unwrap(_poolKey.toId()), PoolId.unwrap(poolKeyWithoutNativeToken.toId())); - assertEq(Currency.unwrap(_poolKey.currency0), address(token0)); - assertEq(Currency.unwrap(_poolKey.currency1), address(token1)); - assertEq(_poolKey.fee, 0); - assertEq(binId, ACTIVE_BIN_ID); - - vm.expectRevert(IPositionManager.InvalidTokenID.selector); - binPm.positions(positionId2); - - vm.expectRevert(IPositionManager.InvalidTokenID.selector); - binPm.positions(positionId3); - } - - function testMigrateFromV3FromNonOwner() public { - // 1. mint some liquidity to the v3 pool - _mintV3Liquidity(address(weth), address(token0)); - assertEq(v3Nfpm.ownerOf(1), address(this)); - (,,,,,,, uint128 liquidityFromV3Before,,,,) = v3Nfpm.positions(1); - assertGt(liquidityFromV3Before, 0); - - // 2. make sure migrator can transfer user's v3 lp token - v3Nfpm.approve(address(migrator), 1); - - // 3. init the pool - migrator.initializePool(poolKey, ACTIVE_BIN_ID); - - IBaseMigrator.V3PoolParams memory v3PoolParams = IBaseMigrator.V3PoolParams({ - nfp: address(v3Nfpm), - tokenId: 1, - // half of the liquidity - liquidity: liquidityFromV3Before / 2, - amount0Min: 9.9 ether / 2, - amount1Min: 9.9 ether / 2, - collectFee: false, - deadline: block.timestamp + 100 - }); - - IBinPositionManager.BinAddLiquidityParams memory params = - _getAddParams(poolKey, getBinIds(ACTIVE_BIN_ID, 3), 10 ether, 10 ether, ACTIVE_BIN_ID, address(this)); - - int256[] memory deltaIds = new int256[](2); - deltaIds[0] = params.deltaIds[0]; - deltaIds[1] = params.deltaIds[1]; - - uint256[] memory distributionX = new uint256[](2); - distributionX[0] = params.distributionX[0]; - distributionX[1] = params.distributionX[1]; - - uint256[] memory distributionY = new uint256[](2); - distributionY[0] = params.distributionY[0]; - distributionY[1] = params.distributionY[1]; - - // delete the last distribution point so that the refund is triggered - // we expect to get 50% of tokenX back - // (0, 50%) (50%, 50%) (50%, 0) => (0, 50%) (50%, 50%) - IBinMigrator.InfiBinPoolParams memory infiBinPoolParams = IBinMigrator.InfiBinPoolParams({ - poolKey: params.poolKey, - amount0Max: params.amount0Max, - amount1Max: params.amount1Max, - activeIdDesired: params.activeIdDesired, - idSlippage: params.idSlippage, - deltaIds: deltaIds, - distributionX: distributionX, - distributionY: distributionY, - to: params.to, - deadline: block.timestamp + 1, - hookData: new bytes(0) - }); - - // 4. migrate half - migrator.migrateFromV3(v3PoolParams, infiBinPoolParams, 0, 0); - - // make sure there are still liquidity left in v3 position token - (,,,,,,, uint128 liquidityFromV3After,,,,) = v3Nfpm.positions(1); - assertEq(liquidityFromV3After, liquidityFromV3Before - liquidityFromV3Before / 2); - - // 5. make sure non-owner can't migrate the rest - vm.expectRevert(IBaseMigrator.NOT_TOKEN_OWNER.selector); - vm.prank(makeAddr("someone")); - migrator.migrateFromV3(v3PoolParams, infiBinPoolParams, 0, 0); - } - - function testMigrateFromV3ThroughOffchainSign() public { - // 1. mint some liquidity to the v3 pool - _mintV3Liquidity(address(weth), address(token0)); - assertEq(v3Nfpm.ownerOf(1), address(this)); - (uint96 nonce,,,,,,, uint128 liquidityFromV3Before,,,,) = v3Nfpm.positions(1); - assertGt(liquidityFromV3Before, 0); - - // 2. make sure migrator can transfer user's v3 lp token through offchain sign - // v3Nfpm.approve(address(migrator), 1); - (address userAddr, uint256 userPrivateKey) = makeAddrAndKey("user"); - - // 2.a transfer the lp token to the user - v3Nfpm.transferFrom(address(this), userAddr, 1); - - uint256 ddl = block.timestamp + 100; - // 2.b prepare the hash - bytes32 structHash = keccak256(abi.encode(v3Nfpm.PERMIT_TYPEHASH(), address(migrator), 1, nonce, ddl)); - bytes32 hash = keccak256(abi.encodePacked("\x19\x01", v3Nfpm.DOMAIN_SEPARATOR(), structHash)); - - // 2.c generate the signature - (uint8 v, bytes32 r, bytes32 s) = vm.sign(userPrivateKey, hash); - - IBaseMigrator.V3PoolParams memory v3PoolParams = IBaseMigrator.V3PoolParams({ - nfp: address(v3Nfpm), - tokenId: 1, - liquidity: liquidityFromV3Before, - amount0Min: 9.9 ether, - amount1Min: 9.9 ether, - collectFee: false, - deadline: block.timestamp + 100 - }); - - IBinPositionManager.BinAddLiquidityParams memory params = - _getAddParams(poolKey, getBinIds(ACTIVE_BIN_ID, 3), 10 ether, 10 ether, ACTIVE_BIN_ID, address(this)); - - IBinMigrator.InfiBinPoolParams memory infiBinPoolParams = IBinMigrator.InfiBinPoolParams({ - poolKey: params.poolKey, - amount0Max: params.amount0Max, - amount1Max: params.amount1Max, - activeIdDesired: params.activeIdDesired, - idSlippage: params.idSlippage, - deltaIds: params.deltaIds, - distributionX: params.distributionX, - distributionY: params.distributionY, - to: params.to, - deadline: block.timestamp + 1, - hookData: new bytes(0) - }); - - // 3. multicall, combine selfPermitERC721, initialize and migrateFromV3 - bytes[] memory data = new bytes[](3); - data[0] = abi.encodeWithSelector(migrator.selfPermitERC721.selector, v3Nfpm, 1, ddl, v, r, s); - data[1] = abi.encodeWithSelector(migrator.initializePool.selector, poolKey, ACTIVE_BIN_ID, bytes("")); - data[2] = abi.encodeWithSelector(migrator.migrateFromV3.selector, v3PoolParams, infiBinPoolParams, 0, 0); - vm.prank(userAddr); - migrator.multicall(data); - - // necessary checks - // v3 liqudity should be 0 - (,,,,,,, uint128 liquidityFromV3After,,,,) = v3Nfpm.positions(1); - assertEq(liquidityFromV3After, 0); - - // make sure liuqidty is minted to the correct pooA - assertApproxEqAbs(address(vault).balance, 10 ether, 0.000001 ether); - assertApproxEqAbs(token0.balanceOf(address(vault)), 10 ether, 0.000001 ether); - - uint256 positionId0 = poolKey.toId().toTokenId(ACTIVE_BIN_ID - 1); - uint256 positionId1 = poolKey.toId().toTokenId(ACTIVE_BIN_ID); - uint256 positionId2 = poolKey.toId().toTokenId(ACTIVE_BIN_ID + 1); - uint256 positionId3 = poolKey.toId().toTokenId(ACTIVE_BIN_ID + 2); - assertGt(binPm.balanceOf(address(this), positionId0), 0); - assertGt(binPm.balanceOf(address(this), positionId1), 0); - assertGt(binPm.balanceOf(address(this), positionId2), 0); - assertEq(binPm.balanceOf(address(this), positionId3), 0); - - (PoolKey memory _poolKey, uint24 binId) = binPm.positions(positionId0); - assertEq(PoolId.unwrap(_poolKey.toId()), PoolId.unwrap(poolKey.toId())); - assertEq(Currency.unwrap(_poolKey.currency0), address(0)); - assertEq(Currency.unwrap(_poolKey.currency1), address(token0)); - assertEq(_poolKey.fee, 0); - assertEq(binId, ACTIVE_BIN_ID - 1); - - (_poolKey, binId) = binPm.positions(positionId1); - assertEq(PoolId.unwrap(_poolKey.toId()), PoolId.unwrap(poolKey.toId())); - assertEq(Currency.unwrap(_poolKey.currency0), address(0)); - assertEq(Currency.unwrap(_poolKey.currency1), address(token0)); - assertEq(_poolKey.fee, 0); - assertEq(binId, ACTIVE_BIN_ID); - - (_poolKey, binId) = binPm.positions(positionId2); - assertEq(PoolId.unwrap(_poolKey.toId()), PoolId.unwrap(poolKey.toId())); - assertEq(Currency.unwrap(_poolKey.currency0), address(0)); - assertEq(Currency.unwrap(_poolKey.currency1), address(token0)); - assertEq(_poolKey.fee, 0); - assertEq(binId, ACTIVE_BIN_ID + 1); - - vm.expectRevert(IPositionManager.InvalidTokenID.selector); - binPm.positions(positionId3); - } - - function testMigrateFromV3ThroughOffchainSignPayWithETH() public { - // 1. mint some liquidity to the v3 pool - _mintV3Liquidity(address(weth), address(token0)); - assertEq(v3Nfpm.ownerOf(1), address(this)); - (uint96 nonce,,,,,,, uint128 liquidityFromV3Before,,,,) = v3Nfpm.positions(1); - assertGt(liquidityFromV3Before, 0); - - // 2. make sure migrator can transfer user's v3 lp token through offchain sign - // v3Nfpm.approve(address(migrator), 1); - (address userAddr, uint256 userPrivateKey) = makeAddrAndKey("user"); - - // 2.a transfer the lp token to the user - v3Nfpm.transferFrom(address(this), userAddr, 1); - - uint256 ddl = block.timestamp + 100; - // 2.b prepare the hash - bytes32 structHash = keccak256(abi.encode(v3Nfpm.PERMIT_TYPEHASH(), address(migrator), 1, nonce, ddl)); - bytes32 hash = keccak256(abi.encodePacked("\x19\x01", v3Nfpm.DOMAIN_SEPARATOR(), structHash)); - - // 2.c generate the signature - (uint8 v, bytes32 r, bytes32 s) = vm.sign(userPrivateKey, hash); - - IBaseMigrator.V3PoolParams memory v3PoolParams = IBaseMigrator.V3PoolParams({ - nfp: address(v3Nfpm), - tokenId: 1, - liquidity: liquidityFromV3Before, - amount0Min: 9.9 ether, - amount1Min: 9.9 ether, - collectFee: false, - deadline: block.timestamp + 100 - }); - - IBinPositionManager.BinAddLiquidityParams memory params = - _getAddParams(poolKey, getBinIds(ACTIVE_BIN_ID, 3), 10 ether, 10 ether, ACTIVE_BIN_ID, address(this)); - - IBinMigrator.InfiBinPoolParams memory infiBinPoolParams = IBinMigrator.InfiBinPoolParams({ - poolKey: params.poolKey, - amount0Max: params.amount0Max, - amount1Max: params.amount1Max, - activeIdDesired: params.activeIdDesired, - idSlippage: params.idSlippage, - deltaIds: params.deltaIds, - distributionX: params.distributionX, - distributionY: params.distributionY, - to: params.to, - deadline: block.timestamp + 1, - hookData: new bytes(0) - }); - - // make the guy rich - token0.transfer(userAddr, 10 ether); - deal(userAddr, 10 ether); - - permit2ApproveWithSpecificAllowance( - userAddr, permit2, address(token0), address(migrator), 10 ether, uint160(10 ether) - ); - - // 3. multicall, combine selfPermitERC721, initialize and migrateFromV3 - bytes[] memory data = new bytes[](3); - data[0] = abi.encodeWithSelector(migrator.selfPermitERC721.selector, v3Nfpm, 1, ddl, v, r, s); - data[1] = abi.encodeWithSelector(migrator.initializePool.selector, poolKey, ACTIVE_BIN_ID, bytes("")); - data[2] = - abi.encodeWithSelector(migrator.migrateFromV3.selector, v3PoolParams, infiBinPoolParams, 10 ether, 10 ether); - vm.prank(userAddr); - migrator.multicall{value: 10 ether}(data); - - // necessary checks - // v3 liqudity should be 0 - (,,,,,,, uint128 liquidityFromV3After,,,,) = v3Nfpm.positions(1); - assertEq(liquidityFromV3After, 0); - - // make sure liuqidty is minted to the correct pooA - assertApproxEqAbs(address(vault).balance, 20 ether, 0.000001 ether); - assertApproxEqAbs(token0.balanceOf(address(vault)), 20 ether, 0.000001 ether); - - uint256 positionId0 = poolKey.toId().toTokenId(ACTIVE_BIN_ID - 1); - uint256 positionId1 = poolKey.toId().toTokenId(ACTIVE_BIN_ID); - uint256 positionId2 = poolKey.toId().toTokenId(ACTIVE_BIN_ID + 1); - uint256 positionId3 = poolKey.toId().toTokenId(ACTIVE_BIN_ID + 2); - assertGt(binPm.balanceOf(address(this), positionId0), 0); - assertGt(binPm.balanceOf(address(this), positionId1), 0); - assertGt(binPm.balanceOf(address(this), positionId2), 0); - assertEq(binPm.balanceOf(address(this), positionId3), 0); - - (PoolKey memory _poolKey, uint24 binId) = binPm.positions(positionId0); - assertEq(PoolId.unwrap(_poolKey.toId()), PoolId.unwrap(poolKey.toId())); - assertEq(Currency.unwrap(_poolKey.currency0), address(0)); - assertEq(Currency.unwrap(_poolKey.currency1), address(token0)); - assertEq(_poolKey.fee, 0); - assertEq(binId, ACTIVE_BIN_ID - 1); - - (_poolKey, binId) = binPm.positions(positionId1); - assertEq(PoolId.unwrap(_poolKey.toId()), PoolId.unwrap(poolKey.toId())); - assertEq(Currency.unwrap(_poolKey.currency0), address(0)); - assertEq(Currency.unwrap(_poolKey.currency1), address(token0)); - assertEq(_poolKey.fee, 0); - assertEq(binId, ACTIVE_BIN_ID); - - (_poolKey, binId) = binPm.positions(positionId2); - assertEq(PoolId.unwrap(_poolKey.toId()), PoolId.unwrap(poolKey.toId())); - assertEq(Currency.unwrap(_poolKey.currency0), address(0)); - assertEq(Currency.unwrap(_poolKey.currency1), address(token0)); - assertEq(_poolKey.fee, 0); - assertEq(binId, ACTIVE_BIN_ID + 1); - - vm.expectRevert(IPositionManager.InvalidTokenID.selector); - binPm.positions(positionId3); - } - - function _mintV3Liquidity(address _token0, address _token1) internal { - (_token0, _token1) = _token0 < _token1 ? (_token0, _token1) : (_token1, _token0); - v3Nfpm.createAndInitializePoolIfNecessary(_token0, _token1, 500, INIT_SQRT_PRICE); - IV3NonfungiblePositionManager.MintParams memory mintParams = IV3NonfungiblePositionManager.MintParams({ - token0: _token0, - token1: _token1, - fee: 500, - tickLower: -100, - tickUpper: 100, - amount0Desired: 10 ether, - amount1Desired: 10 ether, - amount0Min: 0, - amount1Min: 0, - recipient: address(this), - deadline: block.timestamp + 100 - }); - - v3Nfpm.mint(mintParams); - } - - receive() external payable {} -} diff --git a/test/pool-bin/migrator/mocks/MockBinMigratorHook.sol b/test/pool-bin/migrator/mocks/MockBinMigratorHook.sol deleted file mode 100644 index 9b142cd..0000000 --- a/test/pool-bin/migrator/mocks/MockBinMigratorHook.sol +++ /dev/null @@ -1,42 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.24; - -import {PoolKey} from "infinity-core/src/types/PoolKey.sol"; -import {BaseBinTestHook} from "infinity-core/test/pool-bin/helpers/BaseBinTestHook.sol"; -import {IBinPoolManager} from "infinity-core/src/pool-bin/interfaces/IBinPoolManager.sol"; - -/// @notice store hookData -contract MockBinMigratorHook is BaseBinTestHook { - bytes public hookData; - - function getHooksRegistrationBitmap() external pure override returns (uint16) { - return _hooksRegistrationBitmapFrom( - Permissions({ - beforeInitialize: false, - afterInitialize: false, - beforeMint: true, - afterMint: false, - beforeBurn: false, - afterBurn: false, - beforeSwap: false, - afterSwap: false, - beforeDonate: false, - afterDonate: false, - beforeSwapReturnsDelta: false, - afterSwapReturnsDelta: false, - afterMintReturnsDelta: false, - afterBurnReturnsDelta: false - }) - ); - } - - function beforeMint(address, PoolKey calldata, IBinPoolManager.MintParams calldata, bytes calldata _hookData) - external - override - returns (bytes4, uint24) - { - hookData = _hookData; - - return (this.beforeMint.selector, 0); - } -} diff --git a/test/pool-bin/shared/BinHookModifyLiquidities.sol b/test/pool-bin/shared/BinHookModifyLiquidities.sol index 215c411..0be22f8 100644 --- a/test/pool-bin/shared/BinHookModifyLiquidities.sol +++ b/test/pool-bin/shared/BinHookModifyLiquidities.sol @@ -31,7 +31,11 @@ contract BinHookModifyLiquidities is BinHookSavesDelta { bool, /* swapForY **/ int128, /* amountSpecified **/ bytes calldata hookData - ) external override returns (bytes4, BeforeSwapDelta, uint24) { + ) + external + override + returns (bytes4, BeforeSwapDelta, uint24) + { approvePosmCurrency(key.currency0); approvePosmCurrency(key.currency1); @@ -45,7 +49,11 @@ contract BinHookModifyLiquidities is BinHookSavesDelta { PoolKey calldata, /* key **/ IBinPoolManager.MintParams calldata, /* params **/ bytes calldata hookData - ) external override returns (bytes4, uint24) { + ) + external + override + returns (bytes4, uint24) + { if (hookData.length > 0) { (bytes memory actions, bytes[] memory params) = abi.decode(hookData, (bytes, bytes[])); posm.modifyLiquiditiesWithoutLock(actions, params); @@ -58,7 +66,11 @@ contract BinHookModifyLiquidities is BinHookSavesDelta { PoolKey calldata, /* key **/ IBinPoolManager.BurnParams calldata, /* params **/ bytes calldata hookData - ) external override returns (bytes4) { + ) + external + override + returns (bytes4) + { if (hookData.length > 0) { (bytes memory actions, bytes[] memory params) = abi.decode(hookData, (bytes, bytes[])); posm.modifyLiquiditiesWithoutLock(actions, params); diff --git a/test/pool-cl/CLQuoter.t.sol b/test/pool-cl/CLQuoter.t.sol index 95eaf2f..669c424 100644 --- a/test/pool-cl/CLQuoter.t.sol +++ b/test/pool-cl/CLQuoter.t.sol @@ -89,10 +89,7 @@ contract CLQuoterTest is Test, Deployers { (uint256 _amountOut, uint256 _gasEstimate) = quoter.quoteExactInputSingle( IQuoter.QuoteExactSingleParams({ - poolKey: key02, - zeroForOne: true, - exactAmount: uint128(amountIn), - hookData: ZERO_BYTES + poolKey: key02, zeroForOne: true, exactAmount: uint128(amountIn), hookData: ZERO_BYTES }) ); @@ -117,10 +114,7 @@ contract CLQuoterTest is Test, Deployers { (uint256 _amountOut, uint256 _gasEstimate) = quoter.quoteExactInputSingle( IQuoter.QuoteExactSingleParams({ - poolKey: key02, - zeroForOne: false, - exactAmount: uint128(amountIn), - hookData: ZERO_BYTES + poolKey: key02, zeroForOne: false, exactAmount: uint128(amountIn), hookData: ZERO_BYTES }) ); @@ -290,10 +284,7 @@ contract CLQuoterTest is Test, Deployers { ); quoter.quoteExactOutputSingle( IQuoter.QuoteExactSingleParams({ - poolKey: key01, - zeroForOne: true, - exactAmount: type(uint128).max, - hookData: ZERO_BYTES + poolKey: key01, zeroForOne: true, exactAmount: type(uint128).max, hookData: ZERO_BYTES }) ); } @@ -501,8 +492,8 @@ contract CLQuoterTest is Test, Deployers { ICLPoolManager.ModifyLiquidityParams({ tickLower: MIN_TICK, tickUpper: MAX_TICK, - liquidityDelta: calculateLiquidityFromAmounts(SQRT_RATIO_1_1, MIN_TICK, MAX_TICK, 1000000, 1000000).toInt256( - ), + liquidityDelta: calculateLiquidityFromAmounts(SQRT_RATIO_1_1, MIN_TICK, MAX_TICK, 1000000, 1000000) + .toInt256(), salt: bytes32(0) }), ZERO_BYTES diff --git a/test/pool-cl/CLSwapRouter.t.sol b/test/pool-cl/CLSwapRouter.t.sol index eed6578..7207b32 100644 --- a/test/pool-cl/CLSwapRouter.t.sol +++ b/test/pool-cl/CLSwapRouter.t.sol @@ -74,10 +74,7 @@ contract CLSwapRouterTest is TokenFixture, Test { positionManager.modifyPosition( poolKey0, ICLPoolManager.ModifyLiquidityParams({ - tickLower: 46053, - tickUpper: 46055, - liquidityDelta: 1e4 ether, - salt: bytes32(0) + tickLower: 46053, tickUpper: 46055, liquidityDelta: 1e4 ether, salt: bytes32(0) }), new bytes(0) ); @@ -99,10 +96,7 @@ contract CLSwapRouterTest is TokenFixture, Test { positionManager.modifyPosition( poolKey1, ICLPoolManager.ModifyLiquidityParams({ - tickLower: -5, - tickUpper: 5, - liquidityDelta: 1e5 ether, - salt: bytes32(0) + tickLower: -5, tickUpper: 5, liquidityDelta: 1e5 ether, salt: bytes32(0) }), new bytes(0) ); @@ -126,10 +120,7 @@ contract CLSwapRouterTest is TokenFixture, Test { positionManager.modifyPosition{value: 25 ether}( poolKey2, ICLPoolManager.ModifyLiquidityParams({ - tickLower: -5, - tickUpper: 5, - liquidityDelta: 1e5 ether, - salt: bytes32(0) + tickLower: -5, tickUpper: 5, liquidityDelta: 1e5 ether, salt: bytes32(0) }), new bytes(0) ); diff --git a/test/pool-cl/TickLens.t.sol b/test/pool-cl/TickLens.t.sol index 86be081..80637ca 100644 --- a/test/pool-cl/TickLens.t.sol +++ b/test/pool-cl/TickLens.t.sol @@ -74,10 +74,7 @@ contract TickLensTest is TokenFixture, Test { positionManager.modifyPosition( poolKey0, ICLPoolManager.ModifyLiquidityParams({ - tickLower: -300, - tickUpper: 300, - liquidityDelta: 10 ether, - salt: bytes32(0) + tickLower: -300, tickUpper: 300, liquidityDelta: 10 ether, salt: bytes32(0) }), new bytes(0) ); @@ -124,10 +121,7 @@ contract TickLensTest is TokenFixture, Test { positionManager.modifyPosition( poolKey0, ICLPoolManager.ModifyLiquidityParams({ - tickLower: -260, - tickUpper: 260, - liquidityDelta: 9 ether, - salt: bytes32(0) + tickLower: -260, tickUpper: 260, liquidityDelta: 9 ether, salt: bytes32(0) }), new bytes(0) ); @@ -135,10 +129,7 @@ contract TickLensTest is TokenFixture, Test { positionManager.modifyPosition( poolKey0, ICLPoolManager.ModifyLiquidityParams({ - tickLower: -360, - tickUpper: 360, - liquidityDelta: 11 ether, - salt: bytes32(0) + tickLower: -360, tickUpper: 360, liquidityDelta: 11 ether, salt: bytes32(0) }), new bytes(0) ); @@ -173,10 +164,7 @@ contract TickLensTest is TokenFixture, Test { positionManager.modifyPosition( poolKey2, ICLPoolManager.ModifyLiquidityParams({ - tickLower: tickLower, - tickUpper: tickUpper, - liquidityDelta: 10 ether, - salt: bytes32(0) + tickLower: tickLower, tickUpper: tickUpper, liquidityDelta: 10 ether, salt: bytes32(0) }), new bytes(0) ); @@ -197,10 +185,7 @@ contract TickLensTest is TokenFixture, Test { positionManager.modifyPosition( poolKey1, ICLPoolManager.ModifyLiquidityParams({ - tickLower: tickLower, - tickUpper: tickUpper, - liquidityDelta: 1 ether, - salt: bytes32(0) + tickLower: tickLower, tickUpper: tickUpper, liquidityDelta: 1 ether, salt: bytes32(0) }), new bytes(0) ); @@ -229,10 +214,7 @@ contract TickLensTest is TokenFixture, Test { positionManager.modifyPosition( poolKey1, ICLPoolManager.ModifyLiquidityParams({ - tickLower: tickLower, - tickUpper: tickUpper, - liquidityDelta: 1 ether, - salt: bytes32(0) + tickLower: tickLower, tickUpper: tickUpper, liquidityDelta: 1 ether, salt: bytes32(0) }), new bytes(0) ); diff --git a/test/pool-cl/erc721Permit/ERC721Permit.permitForAll.t.sol b/test/pool-cl/erc721Permit/ERC721Permit.permitForAll.t.sol index c66c6fa..03697fa 100644 --- a/test/pool-cl/erc721Permit/ERC721Permit.permitForAll.t.sol +++ b/test/pool-cl/erc721Permit/ERC721Permit.permitForAll.t.sol @@ -319,9 +319,7 @@ contract ERC721PermitForAllTest is Test { } // Helpers related to permitForAll - function _permitForAll(uint256 privateKey, address owner, address operator, bool approved, uint256 nonce) - internal - { + function _permitForAll(uint256 privateKey, address owner, address operator, bool approved, uint256 nonce) internal { bytes32 digest = _getPermitForAllDigest(operator, approved, nonce, block.timestamp); (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, digest); diff --git a/test/pool-cl/libraries/CLCalldataDecoder.t.sol b/test/pool-cl/libraries/CLCalldataDecoder.t.sol index 476949e..5bed428 100644 --- a/test/pool-cl/libraries/CLCalldataDecoder.t.sol +++ b/test/pool-cl/libraries/CLCalldataDecoder.t.sol @@ -77,8 +77,9 @@ contract CLCalldataDecoderTest is Test { address _owner, bytes calldata _hookData ) public view { - bytes memory params = - abi.encode(_poolKey, _tickLower, _tickUpper, _liquidity, _amount0Max, _amount1Max, _owner, _hookData); + bytes memory params = abi.encode( + _poolKey, _tickLower, _tickUpper, _liquidity, _amount0Max, _amount1Max, _owner, _hookData + ); ( PoolKey memory poolKey, int24 tickLower, @@ -138,10 +139,7 @@ contract CLCalldataDecoderTest is Test { function test_decodeSwapExactInParams_outOfBounds() public { PathKey[] memory path = new PathKey[](0); IInfinityRouter.CLSwapExactInputParams memory _swapParams = ICLRouterBase.CLSwapExactInputParams({ - currencyIn: Currency.wrap(makeAddr("currencyIn")), - path: path, - amountIn: 1 ether, - amountOutMinimum: 1 ether + currencyIn: Currency.wrap(makeAddr("currencyIn")), path: path, amountIn: 1 ether, amountOutMinimum: 1 ether }); /// @dev params.length is 192 as abi.encode adds 32 bytes for dynamic field. However ether.js doesn't add 32 bytes @@ -172,11 +170,7 @@ contract CLCalldataDecoderTest is Test { function test_fuzz_decodeSwapExactInSingleParams_outOfBounds(PoolKey memory key) public { IInfinityRouter.CLSwapExactInputSingleParams memory _swapParams = ICLRouterBase.CLSwapExactInputSingleParams({ - poolKey: key, - zeroForOne: true, - amountIn: 1 ether, - amountOutMinimum: 1 ether, - hookData: "" + poolKey: key, zeroForOne: true, amountIn: 1 ether, amountOutMinimum: 1 ether, hookData: "" }); /// @dev params.length is 384 as abi.encode adds 32 bytes for dynamic field. However ether.js doesn't add 32 bytes @@ -257,11 +251,7 @@ contract CLCalldataDecoderTest is Test { function test_fuzz_decodeSwapExactOutSingleParams_outOfBounds(PoolKey memory key) public { IInfinityRouter.CLSwapExactOutputSingleParams memory _swapParams = ICLRouterBase.CLSwapExactOutputSingleParams({ - poolKey: key, - zeroForOne: true, - amountOut: 1 ether, - amountInMaximum: 1 ether, - hookData: "" + poolKey: key, zeroForOne: true, amountOut: 1 ether, amountInMaximum: 1 ether, hookData: "" }); /// @dev params.length is 384 as abi.encode adds 32 bytes for dynamic field. However ether.js doesn't add 32 bytes diff --git a/test/pool-cl/migrator/CLMigratorFromPancakeswapV2.t.sol b/test/pool-cl/migrator/CLMigratorFromPancakeswapV2.t.sol deleted file mode 100644 index ae77cf8..0000000 --- a/test/pool-cl/migrator/CLMigratorFromPancakeswapV2.t.sol +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.24; - -import "forge-std/Test.sol"; -import {CLMigratorFromV2} from "./CLMigratorFromV2.sol"; - -contract CLMigratorFromPancakeswapV2Test is CLMigratorFromV2 { - function _getBytecodePath() internal pure override returns (string memory) { - // Create a Pancakeswap V2 pair - // relative to the root of the project - // https://etherscan.io/address/0x1097053Fd2ea711dad45caCcc45EfF7548fCB362#code - return "./test/bin/pcsV2Factory.bytecode"; - } - - function _getContractName() internal pure override returns (string memory) { - return "CLMigratorFromPancakeswapV2Test"; - } -} diff --git a/test/pool-cl/migrator/CLMigratorFromPancakeswapV3.t.sol b/test/pool-cl/migrator/CLMigratorFromPancakeswapV3.t.sol deleted file mode 100644 index 9e2ec21..0000000 --- a/test/pool-cl/migrator/CLMigratorFromPancakeswapV3.t.sol +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.24; - -import "forge-std/Test.sol"; -import {CLMigratorFromV3} from "./CLMigratorFromV3.sol"; - -contract CLMigratorFromPancakeswapV3Test is CLMigratorFromV3 { - function _getDeployerBytecodePath() internal pure override returns (string memory) { - // https://etherscan.io/address/0x41ff9AA7e16B8B1a8a8dc4f0eFacd93D02d071c9#code - return "./test/bin/pcsV3Deployer.bytecode"; - } - - function _getFactoryBytecodePath() internal pure override returns (string memory) { - // https://etherscan.io/address/0x0BFbCF9fa4f9C56B0F40a671Ad40E0805A091865#code - return "./test/bin/pcsV3Factory.bytecode"; - } - - function _getNfpmBytecodePath() internal pure override returns (string memory) { - // https://etherscan.io/address/0x46A15B0b27311cedF172AB29E4f4766fbE7F4364#code - return "./test/bin/pcsV3Nfpm.bytecode"; - } - - function _getContractName() internal pure override returns (string memory) { - return "CLMigratorFromPancakeswapV3Test"; - } -} diff --git a/test/pool-cl/migrator/CLMigratorFromUniswapV2.t.sol b/test/pool-cl/migrator/CLMigratorFromUniswapV2.t.sol deleted file mode 100644 index c37d8e8..0000000 --- a/test/pool-cl/migrator/CLMigratorFromUniswapV2.t.sol +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.24; - -import "forge-std/Test.sol"; -import {CLMigratorFromV2} from "./CLMigratorFromV2.sol"; - -contract CLMigratorFromUniswapV2Test is CLMigratorFromV2 { - function _getBytecodePath() internal pure override returns (string memory) { - // Create a Uniswap V2 pair - // relative to the root of the project - // https://etherscan.io/address/0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f#code - return "./test/bin/uniV2Factory.bytecode"; - } - - function _getContractName() internal pure override returns (string memory) { - return "CLMigratorFromUniswapV2Test"; - } -} diff --git a/test/pool-cl/migrator/CLMigratorFromUniswapV3.t.sol b/test/pool-cl/migrator/CLMigratorFromUniswapV3.t.sol deleted file mode 100644 index ca7f278..0000000 --- a/test/pool-cl/migrator/CLMigratorFromUniswapV3.t.sol +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.24; - -import "forge-std/Test.sol"; -import {CLMigratorFromV3} from "./CLMigratorFromV3.sol"; - -contract CLMigratorFromUniswapV3Test is CLMigratorFromV3 { - function _getDeployerBytecodePath() internal pure override returns (string memory) { - return ""; - } - - function _getFactoryBytecodePath() internal pure override returns (string memory) { - // https://etherscan.io/address/0x1F98431c8aD98523631AE4a59f267346ea31F984#code - return "./test/bin/uniV3Factory.bytecode"; - } - - function _getNfpmBytecodePath() internal pure override returns (string memory) { - // https://etherscan.io/address/0xC36442b4a4522E871399CD717aBDD847Ab11FE88#code - return "./test/bin/uniV3Nfpm.bytecode"; - } - - function _getContractName() internal pure override returns (string memory) { - return "CLMigratorFromUniswapV3Test"; - } -} diff --git a/test/pool-cl/migrator/CLMigratorFromV2.sol b/test/pool-cl/migrator/CLMigratorFromV2.sol deleted file mode 100644 index 288a167..0000000 --- a/test/pool-cl/migrator/CLMigratorFromV2.sol +++ /dev/null @@ -1,930 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.24; - -import "forge-std/Test.sol"; -import {OldVersionHelper} from "../../helpers/OldVersionHelper.sol"; -import {IPancakePair} from "../../../src/interfaces/external/IPancakePair.sol"; -import {WETH} from "solmate/src/tokens/WETH.sol"; -import {MockERC20} from "solmate/src/test/utils/mocks/MockERC20.sol"; -import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import {CLMigrator} from "../../../src/pool-cl/CLMigrator.sol"; -import {ICLMigrator, IBaseMigrator} from "../../../src/pool-cl/interfaces/ICLMigrator.sol"; -import {CLPositionManager} from "../../../src/pool-cl/CLPositionManager.sol"; -import {Vault} from "infinity-core/src/Vault.sol"; -import {CLPoolManager} from "infinity-core/src/pool-cl/CLPoolManager.sol"; -import {PoolKey} from "infinity-core/src/types/PoolKey.sol"; -import {CLPoolParametersHelper} from "infinity-core/src/pool-cl/libraries/CLPoolParametersHelper.sol"; -import {Currency} from "infinity-core/src/types/Currency.sol"; -import {IPoolManager} from "infinity-core/src/interfaces/IPoolManager.sol"; -import {IHooks} from "infinity-core/src/interfaces/IHooks.sol"; -import {PoolId, PoolIdLibrary} from "infinity-core/src/types/PoolId.sol"; -import {PosmTestSetup} from "../shared/PosmTestSetup.sol"; -import {MockReentrantPositionManager} from "../../mocks/MockReentrantPositionManager.sol"; -import {ReentrancyLock} from "../../../src/base/ReentrancyLock.sol"; -import {Permit2ApproveHelper} from "../../helpers/Permit2ApproveHelper.sol"; -import {IAllowanceTransfer} from "permit2/src/interfaces/IAllowanceTransfer.sol"; -import {Permit2SignatureHelpers} from "../../shared/Permit2SignatureHelpers.sol"; -import {Permit2Forwarder} from "../../../src/base/Permit2Forwarder.sol"; -import {Pausable} from "infinity-core/src/base/Pausable.sol"; -import {MockCLMigratorHook} from "./mocks/MockCLMigratorHook.sol"; - -interface IPancakeV2LikePairFactory { - function getPair(address tokenA, address tokenB) external view returns (address pair); - function createPair(address tokenA, address tokenB) external returns (address pair); -} - -abstract contract CLMigratorFromV2 is OldVersionHelper, PosmTestSetup, Permit2ApproveHelper, Permit2SignatureHelpers { - using CLPoolParametersHelper for bytes32; - - WETH weth; - MockERC20 token0; - MockERC20 token1; - - Vault vault; - CLPoolManager poolManager; - ICLMigrator migrator; - PoolKey poolKey; - PoolKey poolKeyWithoutNativeToken; - - IPancakeV2LikePairFactory v2Factory; - IPancakePair v2Pair; - IPancakePair v2PairWithoutNativeToken; - MockCLMigratorHook clMigratorHook; - bytes32 PERMIT2_DOMAIN_SEPARATOR; - - int24 tickLower; - int24 tickUpper; - - function _getBytecodePath() internal pure virtual returns (string memory); - - function _getContractName() internal pure virtual returns (string memory); - - function setUp() public { - weth = new WETH(); - token0 = new MockERC20("Token0", "TKN0", 18); - token1 = new MockERC20("Token1", "TKN1", 18); - (token0, token1) = token0 < token1 ? (token0, token1) : (token1, token0); - (vault, poolManager) = createFreshManager(); - deployPosm(vault, poolManager); - migrator = new CLMigrator(address(weth), address(lpm), permit2); - clMigratorHook = new MockCLMigratorHook(); - - PERMIT2_DOMAIN_SEPARATOR = permit2.DOMAIN_SEPARATOR(); - - poolKey = PoolKey({ - // WETH after migration will be native token - currency0: Currency.wrap(address(0)), - currency1: Currency.wrap(address(token0)), - /// @dev hook only present in migrate from v2, so migrate from v3 test pool w/o hooks - hooks: IHooks(address(clMigratorHook)), - poolManager: poolManager, - fee: 0, - parameters: bytes32(uint256(clMigratorHook.getHooksRegistrationBitmap())).setTickSpacing(10) - }); - - poolKeyWithoutNativeToken = poolKey; - poolKeyWithoutNativeToken.currency0 = Currency.wrap(address(token0)); - poolKeyWithoutNativeToken.currency1 = Currency.wrap(address(token1)); - - // make sure the contract has enough balance - // WETH: 100 ether - // Token: 100 ether - // ETH: 90 ether - deal(address(this), 1000 ether); - weth.deposit{value: 100 ether}(); - token0.mint(address(this), 100 ether); - token1.mint(address(this), 100 ether); - - v2Factory = IPancakeV2LikePairFactory(createContractThroughBytecode(_getBytecodePath())); - v2Pair = IPancakePair(v2Factory.createPair(address(weth), address(token0))); - v2PairWithoutNativeToken = IPancakePair(v2Factory.createPair(address(token0), address(token1))); - - tickLower = -100; - tickUpper = 100; - } - - function test_Owner() public { - // casted as owner/transferOwnership not in ICLMigrator interface - CLMigrator _migrator = CLMigrator(payable(address(migrator))); - assertEq(_migrator.owner(), address(this)); - - address alice = makeAddr("alice"); - _migrator.transferOwnership(alice); - - assertEq(_migrator.owner(), alice); - } - - function testCLMigrateFromV2_WhenPaused() public { - // 1. mint some liquidity to the v2 pair - _mintV2Liquidity(v2Pair); - uint256 lpTokenBefore = v2Pair.balanceOf(address(this)); - - // 2. make sure migrator can transfer user's v2 lp token - permit2ApproveWithSpecificAllowance( - address(this), permit2, address(v2Pair), address(migrator), lpTokenBefore, uint160(lpTokenBefore) - ); - - // 3. initialize the pool - uint160 initSqrtPrice = 79228162514264337593543950336; - migrator.initializePool(poolKey, initSqrtPrice); - - IBaseMigrator.V2PoolParams memory v2PoolParams = IBaseMigrator.V2PoolParams({ - pair: address(v2Pair), - migrateAmount: lpTokenBefore, - // minor precision loss is acceptable - amount0Min: 9.999 ether, - amount1Min: 9.999 ether - }); - - ICLMigrator.InfiCLPoolParams memory infiMintParams = ICLMigrator.InfiCLPoolParams({ - poolKey: poolKey, - tickLower: -100, - tickUpper: 100, - liquidityMin: 0, - recipient: address(this), - deadline: block.timestamp + 100, - hookData: new bytes(0) - }); - - // pre-req: pause - CLMigrator _migrator = CLMigrator(payable(address(migrator))); - _migrator.pause(); - - // 4. migrate from v2 to infinity - vm.expectRevert(Pausable.EnforcedPause.selector); - migrator.migrateFromV2(v2PoolParams, infiMintParams, 0, 0); - } - - function testCLMigrateFromV2_HookData() public { - // 1. mint some liquidity to the v2 pair - _mintV2Liquidity(v2Pair); - uint256 lpTokenBefore = v2Pair.balanceOf(address(this)); - - // 2. make sure migrator can transfer user's v2 lp token - permit2ApproveWithSpecificAllowance( - address(this), permit2, address(v2Pair), address(migrator), lpTokenBefore, uint160(lpTokenBefore) - ); - - // 3. initialize the pool - uint160 initSqrtPrice = 79228162514264337593543950336; - migrator.initializePool(poolKey, initSqrtPrice); - - IBaseMigrator.V2PoolParams memory v2PoolParams = IBaseMigrator.V2PoolParams({ - pair: address(v2Pair), - migrateAmount: lpTokenBefore, - // minor precision loss is acceptable - amount0Min: 9.999 ether, - amount1Min: 9.999 ether - }); - - bytes memory hookData = abi.encode(32); - ICLMigrator.InfiCLPoolParams memory infiMintParams = ICLMigrator.InfiCLPoolParams({ - poolKey: poolKey, - tickLower: -100, - tickUpper: 100, - liquidityMin: 0, - recipient: address(this), - deadline: block.timestamp + 100, - hookData: hookData - }); - - migrator.migrateFromV2(v2PoolParams, infiMintParams, 0, 0); - - // assert hookData flown to hook - assertEq(clMigratorHook.hookData(), hookData); - } - - function testCLMigrateFromV2ReentrancyLockRevert() public { - MockReentrantPositionManager reentrantPM = new MockReentrantPositionManager(permit2); - reentrantPM.setCLPoolMnager(poolManager); - migrator = new CLMigrator(address(weth), address(reentrantPM), permit2); - reentrantPM.setCLMigrator(migrator); - reentrantPM.setRenentrantType(MockReentrantPositionManager.ReentrantType.CLMigrateFromV2); - - // 1. mint some liquidity to the v2 pair - _mintV2Liquidity(v2Pair); - uint256 lpTokenBefore = v2Pair.balanceOf(address(this)); - assertGt(lpTokenBefore, 0); - - // 2. make sure migrator can transfer user's v2 lp token - - permit2ApproveWithSpecificAllowance( - address(this), permit2, address(v2Pair), address(migrator), lpTokenBefore, uint160(lpTokenBefore) - ); - - // 3. initialize the pool - uint160 initSqrtPrice = 79228162514264337593543950336; - poolManager.initialize(poolKey, initSqrtPrice); - - IBaseMigrator.V2PoolParams memory v2PoolParams = IBaseMigrator.V2PoolParams({ - pair: address(v2Pair), - migrateAmount: lpTokenBefore, - // minor precision loss is acceptable - amount0Min: 9.999 ether, - amount1Min: 9.999 ether - }); - - ICLMigrator.InfiCLPoolParams memory infiMintParams = ICLMigrator.InfiCLPoolParams({ - poolKey: poolKey, - tickLower: -100, - tickUpper: 100, - liquidityMin: 0, - recipient: address(this), - deadline: block.timestamp + 100, - hookData: new bytes(0) - }); - - vm.expectRevert(ReentrancyLock.ContractLocked.selector); - migrator.migrateFromV2(v2PoolParams, infiMintParams, 0, 0); - } - - function testCLMigrateFromV2IncludingInit() public { - // 1. mint some liquidity to the v2 pair - _mintV2Liquidity(v2Pair); - uint256 lpTokenBefore = v2Pair.balanceOf(address(this)); - assertGt(lpTokenBefore, 0); - - // 2. make sure migrator can transfer user's v2 lp token - - permit2ApproveWithSpecificAllowance( - address(this), permit2, address(v2Pair), address(migrator), lpTokenBefore, uint160(lpTokenBefore) - ); - - IBaseMigrator.V2PoolParams memory v2PoolParams = IBaseMigrator.V2PoolParams({ - pair: address(v2Pair), - migrateAmount: lpTokenBefore, - // minor precision loss is acceptable - amount0Min: 9.999 ether, - amount1Min: 9.999 ether - }); - - ICLMigrator.InfiCLPoolParams memory infiMintParams = ICLMigrator.InfiCLPoolParams({ - poolKey: poolKey, - tickLower: -100, - tickUpper: 100, - liquidityMin: 0, - recipient: address(this), - deadline: block.timestamp + 100, - hookData: new bytes(0) - }); - - // 3. multicall, combine initialize and migrateFromV2 - uint160 initSqrtPrice = 79228162514264337593543950336; - bytes[] memory data = new bytes[](2); - data[0] = abi.encodeWithSelector(migrator.initializePool.selector, poolKey, initSqrtPrice, bytes("")); - data[1] = abi.encodeWithSelector(migrator.migrateFromV2.selector, v2PoolParams, infiMintParams, 0, 0); - migrator.multicall(data); - vm.snapshotGasLastCall("testCLMigrateFromV2IncludingInit"); - - // necessary checks - // v2 pair should be burned already - assertEq(v2Pair.balanceOf(address(this)), 0); - - // make sure liuqidty is minted to the correct pool - assertEq(lpm.ownerOf(1), address(this)); - - uint128 liquidity = lpm.getPositionLiquidity(1); - - assertEq(liquidity, 2005104164790027832367); - } - - function testCLMigrateFromV2TokenMismatch() public { - // 1. mint some liquidity to the v2 pair - _mintV2Liquidity(v2Pair); - uint256 lpTokenBefore = v2Pair.balanceOf(address(this)); - - // 2. make sure migrator can transfer user's v2 lp token - - permit2ApproveWithSpecificAllowance( - address(this), permit2, address(v2Pair), address(migrator), lpTokenBefore, uint160(lpTokenBefore) - ); - - IBaseMigrator.V2PoolParams memory v2PoolParams = IBaseMigrator.V2PoolParams({ - pair: address(v2Pair), - migrateAmount: lpTokenBefore, - // minor precision loss is acceptable - amount0Min: 9.999 ether, - amount1Min: 9.999 ether - }); - - // v2 weth, token0 - // infinity ETH, token1 - PoolKey memory poolKeyMismatch = poolKey; - poolKeyMismatch.currency1 = Currency.wrap(address(token1)); - ICLMigrator.InfiCLPoolParams memory infiMintParams = ICLMigrator.InfiCLPoolParams({ - poolKey: poolKeyMismatch, - tickLower: -100, - tickUpper: 100, - liquidityMin: 0, - recipient: address(this), - deadline: block.timestamp + 100, - hookData: new bytes(0) - }); - - // 3. multicall, combine initialize and migrateFromV2 - uint160 initSqrtPrice = 79228162514264337593543950336; - bytes[] memory data = new bytes[](2); - data[0] = abi.encodeWithSelector(migrator.initializePool.selector, poolKeyMismatch, initSqrtPrice, bytes("")); - data[1] = abi.encodeWithSelector(migrator.migrateFromV2.selector, v2PoolParams, infiMintParams, 0, 0); - vm.expectRevert(); - migrator.multicall(data); - - { - // v2 weth, token0 - // infinity token0, token1 - poolKeyMismatch.currency0 = Currency.wrap(address(token0)); - poolKeyMismatch.currency1 = Currency.wrap(address(token1)); - infiMintParams.poolKey = poolKeyMismatch; - data = new bytes[](2); - data[0] = - abi.encodeWithSelector(migrator.initializePool.selector, poolKeyMismatch, initSqrtPrice, bytes("")); - data[1] = abi.encodeWithSelector(migrator.migrateFromV2.selector, v2PoolParams, infiMintParams, 0, 0); - vm.expectRevert(); - migrator.multicall(data); - } - } - - function testCLMigrateFromV2InsufficientLiquidity() public { - // 1. mint some liquidity to the v2 pair - _mintV2Liquidity(v2Pair); - uint256 lpTokenBefore = v2Pair.balanceOf(address(this)); - assertGt(lpTokenBefore, 0); - - // 2. make sure migrator can transfer user's v2 lp token - - permit2ApproveWithSpecificAllowance( - address(this), permit2, address(v2Pair), address(migrator), lpTokenBefore, uint160(lpTokenBefore) - ); - - // 3. initialize the pool - uint160 initSqrtPrice = 79228162514264337593543950336; - migrator.initializePool(poolKey, initSqrtPrice); - - IBaseMigrator.V2PoolParams memory v2PoolParams = IBaseMigrator.V2PoolParams({ - pair: address(v2Pair), - migrateAmount: lpTokenBefore, - // minor precision loss is acceptable - amount0Min: 9.999 ether, - amount1Min: 9.999 ether - }); - - ICLMigrator.InfiCLPoolParams memory infiMintParams = ICLMigrator.InfiCLPoolParams({ - poolKey: poolKey, - tickLower: -100, - tickUpper: 100, - liquidityMin: 2005104164790027832368, // minted liquidity + 1 - recipient: address(this), - deadline: block.timestamp + 100, - hookData: new bytes(0) - }); - - vm.expectRevert(ICLMigrator.INSUFFICIENT_LIQUIDITY.selector); - migrator.migrateFromV2(v2PoolParams, infiMintParams, 0, 0); - } - - function testCLMigrateFromV2WithoutInit() public { - // 1. mint some liquidity to the v2 pair - _mintV2Liquidity(v2Pair); - uint256 lpTokenBefore = v2Pair.balanceOf(address(this)); - assertGt(lpTokenBefore, 0); - - // 2. make sure migrator can transfer user's v2 lp token - - permit2ApproveWithSpecificAllowance( - address(this), permit2, address(v2Pair), address(migrator), lpTokenBefore, uint160(lpTokenBefore) - ); - - // 3. initialize the pool - uint160 initSqrtPrice = 79228162514264337593543950336; - migrator.initializePool(poolKey, initSqrtPrice); - - IBaseMigrator.V2PoolParams memory v2PoolParams = IBaseMigrator.V2PoolParams({ - pair: address(v2Pair), - migrateAmount: lpTokenBefore, - // minor precision loss is acceptable - amount0Min: 9.999 ether, - amount1Min: 9.999 ether - }); - - ICLMigrator.InfiCLPoolParams memory infiMintParams = ICLMigrator.InfiCLPoolParams({ - poolKey: poolKey, - tickLower: -100, - tickUpper: 100, - liquidityMin: 0, - recipient: address(this), - deadline: block.timestamp + 100, - hookData: new bytes(0) - }); - - // 4. migrate from v2 to infinity - migrator.migrateFromV2(v2PoolParams, infiMintParams, 0, 0); - vm.snapshotGasLastCall("testCLMigrateFromV2WithoutInit"); - - // necessary checks - // v2 pair should be burned already - assertEq(v2Pair.balanceOf(address(this)), 0); - - // make sure liuqidty is minted to the correct pool - assertEq(lpm.ownerOf(1), address(this)); - uint128 liquidity = lpm.getPositionLiquidity(1); - - assertEq(liquidity, 2005104164790027832367); - assertApproxEqAbs(address(vault).balance, 10 ether, 0.000001 ether); - assertApproxEqAbs(token0.balanceOf(address(vault)), 10 ether, 0.000001 ether); - } - - function testCLMigrateFromV2WithoutNativeToken() public { - // 1. mint some liquidity to the v2 pair - _mintV2Liquidity(v2PairWithoutNativeToken); - uint256 lpTokenBefore = v2PairWithoutNativeToken.balanceOf(address(this)); - assertGt(lpTokenBefore, 0); - - // 2. make sure migrator can transfer user's v2 lp token - v2PairWithoutNativeToken.approve(address(migrator), lpTokenBefore); - permit2ApproveWithSpecificAllowance( - address(this), - permit2, - address(v2PairWithoutNativeToken), - address(migrator), - lpTokenBefore, - uint160(lpTokenBefore) - ); - - // 3. initialize the pool - uint160 initSqrtPrice = 79228162514264337593543950336; - migrator.initializePool(poolKeyWithoutNativeToken, initSqrtPrice); - - IBaseMigrator.V2PoolParams memory v2PoolParams = IBaseMigrator.V2PoolParams({ - pair: address(v2PairWithoutNativeToken), - migrateAmount: lpTokenBefore, - // minor precision loss is acceptable - amount0Min: 9.999 ether, - amount1Min: 9.999 ether - }); - - ICLMigrator.InfiCLPoolParams memory infiMintParams = ICLMigrator.InfiCLPoolParams({ - poolKey: poolKeyWithoutNativeToken, - tickLower: -100, - tickUpper: 100, - liquidityMin: 0, - recipient: address(this), - deadline: block.timestamp + 100, - hookData: new bytes(0) - }); - - // 4. migrate from v2 to infinity - migrator.migrateFromV2(v2PoolParams, infiMintParams, 0, 0); - vm.snapshotGasLastCall("testCLMigrateFromV2WithoutNativeToken"); - - // necessary checks - // v2 pair should be burned already - assertEq(v2PairWithoutNativeToken.balanceOf(address(this)), 0); - - // make sure liuqidty is minted to the correct pool - assertEq(lpm.ownerOf(1), address(this)); - uint128 liquidity = lpm.getPositionLiquidity(1); - - assertEq(liquidity, 2005104164790027832367); - - assertApproxEqAbs(token0.balanceOf(address(vault)), 10 ether, 0.000001 ether); - assertApproxEqAbs(token1.balanceOf(address(vault)), 10 ether, 0.000001 ether); - } - - function testCLMigrateFromV2AddExtraAmount() public { - // 1. mint some liquidity to the v2 pair - _mintV2Liquidity(v2Pair); - uint256 lpTokenBefore = v2Pair.balanceOf(address(this)); - assertGt(lpTokenBefore, 0); - - // 2. make sure migrator can transfer user's v2 lp token - - permit2ApproveWithSpecificAllowance( - address(this), permit2, address(v2Pair), address(migrator), lpTokenBefore, uint160(lpTokenBefore) - ); - - // 3. initialize the pool - uint160 initSqrtPrice = 79228162514264337593543950336; - migrator.initializePool(poolKey, initSqrtPrice); - - IBaseMigrator.V2PoolParams memory v2PoolParams = IBaseMigrator.V2PoolParams({ - pair: address(v2Pair), - migrateAmount: lpTokenBefore, - // minor precision loss is acceptable - amount0Min: 9.999 ether, - amount1Min: 9.999 ether - }); - - ICLMigrator.InfiCLPoolParams memory infiMintParams = ICLMigrator.InfiCLPoolParams({ - poolKey: poolKey, - tickLower: -100, - tickUpper: 100, - liquidityMin: 0, - recipient: address(this), - deadline: block.timestamp + 100, - hookData: new bytes(0) - }); - - uint256 balance0Before = address(this).balance; - uint256 balance1Before = token0.balanceOf(address(this)); - - permit2ApproveWithSpecificAllowance( - address(this), permit2, address(token0), address(migrator), 20 ether, 20 ether - ); - // 4. migrate from v2 to infinity - migrator.migrateFromV2{value: 20 ether}(v2PoolParams, infiMintParams, 20 ether, uint160(20 ether)); - - // necessary checks - // consumed extra 20 ether from user - assertApproxEqAbs(balance0Before - address(this).balance, 20 ether, 0.000001 ether); - assertEq(balance1Before - token0.balanceOf(address(this)), 20 ether); - // WETH balance unchanged - assertEq(weth.balanceOf(address(this)), 90 ether); - - // v2 pair should be burned already - assertEq(v2Pair.balanceOf(address(this)), 0); - - // make sure liuqidty is minted to the correct pool - assertEq(lpm.ownerOf(1), address(this)); - uint128 liquidity = lpm.getPositionLiquidity(1); - - // liquidity is 3 times of the original - assertApproxEqAbs(liquidity, 2005104164790027832367 * 3, 0.000001 ether); - - assertApproxEqAbs(address(vault).balance, 30 ether, 0.000001 ether); - assertApproxEqAbs(token0.balanceOf(address(vault)), 30 ether, 0.000001 ether); - } - - function testCLMigrateFromV2AddExtraAmountThroughWETH() public { - // 1. mint some liquidity to the v2 pair - _mintV2Liquidity(v2Pair); - uint256 lpTokenBefore = v2Pair.balanceOf(address(this)); - assertGt(lpTokenBefore, 0); - - // 2. make sure migrator can transfer user's v2 lp token - - permit2ApproveWithSpecificAllowance( - address(this), permit2, address(v2Pair), address(migrator), lpTokenBefore, uint160(lpTokenBefore) - ); - - // 3. initialize the pool - uint160 initSqrtPrice = 79228162514264337593543950336; - migrator.initializePool(poolKey, initSqrtPrice); - - IBaseMigrator.V2PoolParams memory v2PoolParams = IBaseMigrator.V2PoolParams({ - pair: address(v2Pair), - migrateAmount: lpTokenBefore, - // minor precision loss is acceptable - amount0Min: 9.999 ether, - amount1Min: 9.999 ether - }); - - ICLMigrator.InfiCLPoolParams memory infiMintParams = ICLMigrator.InfiCLPoolParams({ - poolKey: poolKey, - tickLower: -100, - tickUpper: 100, - liquidityMin: 0, - recipient: address(this), - deadline: block.timestamp + 100, - hookData: new bytes(0) - }); - - uint256 balance0Before = address(this).balance; - uint256 balance1Before = token0.balanceOf(address(this)); - - permit2ApproveWithSpecificAllowance( - address(this), permit2, address(weth), address(migrator), 20 ether, 20 ether - ); - permit2ApproveWithSpecificAllowance( - address(this), permit2, address(token0), address(migrator), 20 ether, 20 ether - ); - // 4. migrate from v2 to infinity, not sending ETH denotes pay by WETH - migrator.migrateFromV2(v2PoolParams, infiMintParams, 20 ether, 20 ether); - - // necessary checks - // consumed extra 20 ether from user - // native token balance unchanged - assertApproxEqAbs(balance0Before - address(this).balance, 0 ether, 0.000001 ether); - assertEq(balance1Before - token0.balanceOf(address(this)), 20 ether); - // consumed 20 ether WETH - assertEq(weth.balanceOf(address(this)), 70 ether); - - // v2 pair should be burned already - assertEq(v2Pair.balanceOf(address(this)), 0); - - // make sure liuqidty is minted to the correct pool - assertEq(lpm.ownerOf(1), address(this)); - uint128 liquidity = lpm.getPositionLiquidity(1); - - // liquidity is 3 times of the original - assertApproxEqAbs(liquidity, 2005104164790027832367 * 3, 0.000001 ether); - - assertApproxEqAbs(address(vault).balance, 30 ether, 0.000001 ether); - assertApproxEqAbs(token0.balanceOf(address(vault)), 30 ether, 0.000001 ether); - } - - function testFuzz_CLMigrateFromV2AddExtraAmountThroughWETH(uint256 extraAmount) public { - extraAmount = bound(extraAmount, 1 ether, 60 ether); - - // 1. mint some liquidity to the v2 pair - _mintV2Liquidity(v2Pair); - uint256 lpTokenBefore = v2Pair.balanceOf(address(this)); - assertGt(lpTokenBefore, 0); - - // 2. make sure migrator can transfer user's v2 lp token - - permit2ApproveWithSpecificAllowance( - address(this), permit2, address(v2Pair), address(migrator), lpTokenBefore, uint160(lpTokenBefore) - ); - - // 3. initialize the pool - uint160 initSqrtPrice = 79228162514264337593543950336; - migrator.initializePool(poolKey, initSqrtPrice); - - IBaseMigrator.V2PoolParams memory v2PoolParams = IBaseMigrator.V2PoolParams({ - pair: address(v2Pair), - migrateAmount: lpTokenBefore, - // minor precision loss is acceptable - amount0Min: 9.999 ether, - amount1Min: 9.999 ether - }); - - ICLMigrator.InfiCLPoolParams memory infiMintParams = ICLMigrator.InfiCLPoolParams({ - poolKey: poolKey, - tickLower: -100, - tickUpper: 100, - liquidityMin: 0, - recipient: address(this), - deadline: block.timestamp + 100, - hookData: new bytes(0) - }); - - uint256 balance0Before = address(this).balance; - uint256 balance1Before = token0.balanceOf(address(this)); - - permit2ApproveWithSpecificAllowance( - address(this), permit2, address(weth), address(migrator), extraAmount, uint160(extraAmount) - ); - permit2ApproveWithSpecificAllowance( - address(this), permit2, address(token0), address(migrator), extraAmount, uint160(extraAmount) - ); - // 4. migrate from v2 to infinity, not sending ETH denotes pay by WETH - migrator.migrateFromV2(v2PoolParams, infiMintParams, extraAmount, extraAmount); - - // clPositionManager native balance should be 0 - uint256 lPositionManagerNativeBalance = address(lpm).balance; - assertEq(lPositionManagerNativeBalance, 0); - - // necessary checks - // consumed extra extraAmount from user - // native token balance unchanged - assertApproxEqAbs(balance0Before - address(this).balance, 0 ether, 0.000001 ether); - assertEq(balance1Before - token0.balanceOf(address(this)), extraAmount); - // consumed extraAmount WETH - assertEq(weth.balanceOf(address(this)), 90 ether - extraAmount); - - // v2 pair should be burned already - assertEq(v2Pair.balanceOf(address(this)), 0); - - // make sure liuqidty is minted to the correct pool - assertEq(lpm.ownerOf(1), address(this)); - uint128 liquidity = lpm.getPositionLiquidity(1); - - // liquidity is 3 times of the original - assertApproxEqAbs(liquidity, 2005104164790027832367 * (10 ether + extraAmount) / 10 ether, 0.000001 ether); - - assertApproxEqAbs(address(vault).balance, 10 ether + extraAmount, 0.000001 ether); - assertApproxEqAbs(token0.balanceOf(address(vault)), 10 ether + extraAmount, 0.000001 ether); - } - - function testCLMigrateFromV2Refund() public { - // 1. mint some liquidity to the v2 pair - // 10 ether WETH, 5 ether token0 - // addr of weth > addr of token0, hence the order has to be reversed - bool isWETHFirst = address(weth) < address(token0); - if (isWETHFirst) { - _mintV2Liquidity(v2Pair, 10 ether, 5 ether); - } else { - _mintV2Liquidity(v2Pair, 5 ether, 10 ether); - } - uint256 lpTokenBefore = v2Pair.balanceOf(address(this)); - assertGt(lpTokenBefore, 0); - - // 2. make sure migrator can transfer user's v2 lp token - - permit2ApproveWithSpecificAllowance( - address(this), permit2, address(v2Pair), address(migrator), lpTokenBefore, uint160(lpTokenBefore) - ); - - // 3. initialize the pool - uint160 initSqrtPrice = 79228162514264337593543950336; - migrator.initializePool(poolKey, initSqrtPrice); - - IBaseMigrator.V2PoolParams memory v2PoolParams = IBaseMigrator.V2PoolParams({ - pair: address(v2Pair), - migrateAmount: lpTokenBefore, - // the order of token0 and token1 respect to the pair - // but may mismatch the order of infinity pool key when WETH is invovled - amount0Min: isWETHFirst ? 9.999 ether : 4.999 ether, - amount1Min: isWETHFirst ? 4.999 ether : 9.999 ether - }); - - ICLMigrator.InfiCLPoolParams memory infiMintParams = ICLMigrator.InfiCLPoolParams({ - poolKey: poolKey, - tickLower: -100, - tickUpper: 100, - liquidityMin: 0, - recipient: address(this), - deadline: block.timestamp + 100, - hookData: new bytes(0) - }); - - uint256 balance0Before = address(this).balance; - uint256 balance1Before = token0.balanceOf(address(this)); - - // 4. migrate from v2 to infinity, not sending ETH denotes pay by WETH - migrator.migrateFromV2(v2PoolParams, infiMintParams, 0, 0); - - // necessary checks - // refund 5 ether in the form of native token - assertApproxEqAbs(address(this).balance - balance0Before, 5 ether, 0.000001 ether); - assertEq(balance1Before - token0.balanceOf(address(this)), 0 ether); - // WETH balance unchanged - assertEq(weth.balanceOf(address(this)), 90 ether); - - // v2 pair should be burned already - assertEq(v2Pair.balanceOf(address(this)), 0); - - // make sure liuqidty is minted to the correct pool - assertEq(lpm.ownerOf(1), address(this)); - uint128 liquidity = lpm.getPositionLiquidity(1); - - // liquidity is half of the original - assertApproxEqAbs(liquidity * 2, 2005104164790027832367, 0.000001 ether); - - assertApproxEqAbs(address(vault).balance, 5 ether, 0.000001 ether); - assertApproxEqAbs(token0.balanceOf(address(vault)), 5 ether, 0.000001 ether); - } - - function testCLMigrateFromV2RefundNonNativeToken() public { - // 1. mint some liquidity to the v2 pair - _mintV2Liquidity(v2PairWithoutNativeToken, 10 ether, 5 ether); - uint256 lpTokenBefore = v2PairWithoutNativeToken.balanceOf(address(this)); - assertGt(lpTokenBefore, 0); - - // 2. make sure migrator can transfer user's v2 lp token - - permit2ApproveWithSpecificAllowance( - address(this), - permit2, - address(v2PairWithoutNativeToken), - address(migrator), - lpTokenBefore, - uint160(lpTokenBefore) - ); - - // 3. initialize the pool - uint160 initSqrtPrice = 79228162514264337593543950336; - migrator.initializePool(poolKeyWithoutNativeToken, initSqrtPrice); - - IBaseMigrator.V2PoolParams memory v2PoolParams = IBaseMigrator.V2PoolParams({ - pair: address(v2PairWithoutNativeToken), - migrateAmount: lpTokenBefore, - // the order of token0 and token1 respect to the pair - // but may mismatch the order of infinity pool key when WETH is invovled - amount0Min: 9.999 ether, - amount1Min: 4.999 ether - }); - - ICLMigrator.InfiCLPoolParams memory infiMintParams = ICLMigrator.InfiCLPoolParams({ - poolKey: poolKeyWithoutNativeToken, - tickLower: -100, - tickUpper: 100, - liquidityMin: 0, - recipient: address(this), - deadline: block.timestamp + 100, - hookData: new bytes(0) - }); - - uint256 balance0Before = token0.balanceOf(address(this)); - uint256 balance1Before = token1.balanceOf(address(this)); - - // 4. migrate from v2 to infinity - migrator.migrateFromV2(v2PoolParams, infiMintParams, 0, 0); - - // necessary checks - - // refund 5 ether of token0 - assertApproxEqAbs(token0.balanceOf(address(this)) - balance0Before, 5 ether, 0.000001 ether); - assertEq(balance1Before - token1.balanceOf(address(this)), 0 ether); - // WETH balance unchanged - assertEq(weth.balanceOf(address(this)), 100 ether); - - // v2 pair should be burned already - assertEq(v2PairWithoutNativeToken.balanceOf(address(this)), 0); - - // make sure liuqidty is minted to the correct pool - assertEq(lpm.ownerOf(1), address(this)); - uint128 liquidity = lpm.getPositionLiquidity(1); - - // liquidity is half of the original - assertApproxEqAbs(liquidity * 2, 2005104164790027832367, 0.000001 ether); - - assertApproxEqAbs(token0.balanceOf(address(vault)), 5 ether, 0.000001 ether); - assertApproxEqAbs(token1.balanceOf(address(vault)), 5 ether, 0.000001 ether); - } - - function testCLMigrateFromV2ThroughOffchainSign() public { - // 1. mint some liquidity to the v2 pair - _mintV2Liquidity(v2Pair); - uint256 lpTokenBefore = v2Pair.balanceOf(address(this)); - assertGt(lpTokenBefore, 0); - - // 2. instead of approve, we generate a offchain signature here - - (address userAddr, uint256 userPrivateKey) = makeAddrAndKey("user"); - - // 2.a transfer the lp token to the user - v2Pair.transfer(userAddr, lpTokenBefore); - - uint256 ddl = block.timestamp + 100; - - // 2.b prepare the hash - bytes32 structHash = keccak256( - abi.encode( - keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"), - userAddr, - address(permit2), - lpTokenBefore, - v2Pair.nonces(userAddr), - ddl - ) - ); - bytes32 hash = keccak256(abi.encodePacked("\x19\x01", v2Pair.DOMAIN_SEPARATOR(), structHash)); - - // 2.c generate the signature - (uint8 v, bytes32 r, bytes32 s) = vm.sign(userPrivateKey, hash); - - IAllowanceTransfer.PermitSingle memory permit = - defaultERC20PermitAllowance(address(v2Pair), uint160(lpTokenBefore), type(uint48).max, 0); - permit.spender = address(migrator); - bytes memory sig = getPermitSignature(permit, userPrivateKey, PERMIT2_DOMAIN_SEPARATOR); - - IBaseMigrator.V2PoolParams memory v2PoolParams = IBaseMigrator.V2PoolParams({ - pair: address(v2Pair), - migrateAmount: lpTokenBefore, - // minor precision loss is acceptable - amount0Min: 9.999 ether, - amount1Min: 9.999 ether - }); - - ICLMigrator.InfiCLPoolParams memory infiMintParams = ICLMigrator.InfiCLPoolParams({ - poolKey: poolKey, - tickLower: -100, - tickUpper: 100, - liquidityMin: 0, - recipient: address(this), - deadline: ddl, - hookData: new bytes(0) - }); - - // 3. multicall, combine permit2.permit, initialize and migrateFromV2 - uint160 initSqrtPrice = 79228162514264337593543950336; - bytes[] memory data = new bytes[](3); - data[0] = abi.encodeWithSelector(migrator.initializePool.selector, poolKey, initSqrtPrice, bytes("")); - data[1] = abi.encodeWithSelector(Permit2Forwarder.permit.selector, userAddr, permit, sig); - data[2] = abi.encodeWithSelector(migrator.migrateFromV2.selector, v2PoolParams, infiMintParams, 0, 0); - vm.startPrank(userAddr); - v2Pair.permit(userAddr, address(permit2), lpTokenBefore, ddl, v, r, s); - migrator.multicall(data); - vm.stopPrank(); - - // necessary checks - // v2 pair should be burned already - assertEq(v2Pair.balanceOf(address(this)), 0); - - // make sure liuqidty is minted to the correct pool - assertEq(lpm.ownerOf(1), address(this)); - uint128 liquidity = lpm.getPositionLiquidity(1); - - assertEq(liquidity, 2005104164790027832367); - - assertApproxEqAbs(address(vault).balance, 10 ether, 0.000001 ether); - assertApproxEqAbs(token0.balanceOf(address(vault)), 10 ether, 0.000001 ether); - } - - function _mintV2Liquidity(IPancakePair pair) public { - IERC20(pair.token0()).transfer(address(pair), 10 ether); - IERC20(pair.token1()).transfer(address(pair), 10 ether); - - pair.mint(address(this)); - } - - function _mintV2Liquidity(IPancakePair pair, uint256 amount0, uint256 amount1) public { - IERC20(pair.token0()).transfer(address(pair), amount0); - IERC20(pair.token1()).transfer(address(pair), amount1); - - pair.mint(address(this)); - } - - receive() external payable {} -} diff --git a/test/pool-cl/migrator/CLMigratorFromV3.sol b/test/pool-cl/migrator/CLMigratorFromV3.sol deleted file mode 100644 index bd65625..0000000 --- a/test/pool-cl/migrator/CLMigratorFromV3.sol +++ /dev/null @@ -1,1202 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.24; - -import "forge-std/Test.sol"; -import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol"; -import {OldVersionHelper} from "../../helpers/OldVersionHelper.sol"; -import {IPancakePair} from "../../../src/interfaces/external/IPancakePair.sol"; -import {WETH} from "solmate/src/tokens/WETH.sol"; -import {MockERC20} from "solmate/src/test/utils/mocks/MockERC20.sol"; -import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import {CLMigrator} from "../../../src/pool-cl/CLMigrator.sol"; -import {ICLMigrator, IBaseMigrator} from "../../../src/pool-cl/interfaces/ICLMigrator.sol"; -import {CLPositionManager} from "../../../src/pool-cl/CLPositionManager.sol"; -import {Vault} from "infinity-core/src/Vault.sol"; -import {CLPoolManager} from "infinity-core/src/pool-cl/CLPoolManager.sol"; -import {PoolKey} from "infinity-core/src/types/PoolKey.sol"; -import {CLPoolParametersHelper} from "infinity-core/src/pool-cl/libraries/CLPoolParametersHelper.sol"; -import {Currency, CurrencyLibrary} from "infinity-core/src/types/Currency.sol"; -import {IPoolManager} from "infinity-core/src/interfaces/IPoolManager.sol"; -import {IHooks} from "infinity-core/src/interfaces/IHooks.sol"; -import {PoolId, PoolIdLibrary} from "infinity-core/src/types/PoolId.sol"; -import {IV3NonfungiblePositionManager} from "../../../src/interfaces/external/IV3NonfungiblePositionManager.sol"; -import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; -import {PosmTestSetup} from "../shared/PosmTestSetup.sol"; -import {MockReentrantPositionManager} from "../../mocks/MockReentrantPositionManager.sol"; -import {ReentrancyLock} from "../../../src/base/ReentrancyLock.sol"; -import {Permit2ApproveHelper} from "../../helpers/Permit2ApproveHelper.sol"; -import {IAllowanceTransfer} from "permit2/src/interfaces/IAllowanceTransfer.sol"; -import {SqrtPriceMath} from "infinity-core/src/pool-cl/libraries/SqrtPriceMath.sol"; -import {LiquidityAmounts} from "../../../src/pool-cl/libraries/LiquidityAmounts.sol"; -import {BalanceDelta, BalanceDeltaLibrary, toBalanceDelta} from "infinity-core/src/types/BalanceDelta.sol"; -import {TickMath} from "infinity-core/src/pool-cl/libraries/TickMath.sol"; -import {Pausable} from "infinity-core/src/base/Pausable.sol"; -import {MockCLMigratorHook} from "./mocks/MockCLMigratorHook.sol"; - -interface IPancakeV3LikePairFactory { - function createPool(address tokenA, address tokenB, uint24 fee) external returns (address pool); -} - -abstract contract CLMigratorFromV3 is OldVersionHelper, PosmTestSetup, Permit2ApproveHelper { - using SafeCast for *; - using CLPoolParametersHelper for bytes32; - - error ContractSizeTooLarge(uint256 diff); - - uint160 public constant INIT_SQRT_PRICE = 79228162514264337593543950336; - - WETH weth; - MockERC20 token0; - MockERC20 token1; - - Vault vault; - CLPoolManager poolManager; - ICLMigrator migrator; - PoolKey poolKey; - PoolKey poolKeyWithoutNativeToken; - MockCLMigratorHook clMigratorHook; - - IPancakeV3LikePairFactory v3Factory; - IV3NonfungiblePositionManager v3Nfpm; - - function _getDeployerBytecodePath() internal pure virtual returns (string memory); - function _getFactoryBytecodePath() internal pure virtual returns (string memory); - function _getNfpmBytecodePath() internal pure virtual returns (string memory); - - function _getContractName() internal pure virtual returns (string memory); - - function setUp() public { - weth = new WETH(); - token0 = new MockERC20("Token0", "TKN0", 18); - token1 = new MockERC20("Token1", "TKN1", 18); - (token0, token1) = token0 < token1 ? (token0, token1) : (token1, token0); - - (vault, poolManager) = createFreshManager(); - deployPosm(vault, poolManager); - migrator = new CLMigrator(address(weth), address(lpm), permit2); - clMigratorHook = new MockCLMigratorHook(); - - // also include hook - poolKey = PoolKey({ - // WETH after migration will be native token - currency0: Currency.wrap(address(0)), - currency1: Currency.wrap(address(token0)), - hooks: IHooks(address(clMigratorHook)), - poolManager: poolManager, - fee: 0, - parameters: bytes32(uint256(clMigratorHook.getHooksRegistrationBitmap())).setTickSpacing(10) - }); - - poolKeyWithoutNativeToken = PoolKey({ - currency0: Currency.wrap(address(token0)), - currency1: Currency.wrap(address(token1)), - hooks: IHooks(address(0)), - poolManager: poolManager, - fee: 0, - parameters: bytes32(0).setTickSpacing(10) - }); - - // make sure the contract has enough balance - // WETH: 100 ether - // Token: 100 ether - // ETH: 90 ether - deal(address(this), 1000 ether); - weth.deposit{value: 100 ether}(); - token0.mint(address(this), 100 ether); - token1.mint(address(this), 100 ether); - - // pcs v3 - if (bytes(_getDeployerBytecodePath()).length != 0) { - address deployer = createContractThroughBytecode(_getDeployerBytecodePath()); - v3Factory = IPancakeV3LikePairFactory( - createContractThroughBytecode(_getFactoryBytecodePath(), toBytes32(address(deployer))) - ); - (bool success,) = deployer.call(abi.encodeWithSignature("setFactoryAddress(address)", address(v3Factory))); - require(success, "setFactoryAddress failed"); - v3Nfpm = IV3NonfungiblePositionManager( - createContractThroughBytecode( - _getNfpmBytecodePath(), - toBytes32(deployer), - toBytes32(address(v3Factory)), - toBytes32(address(weth)), - 0 - ) - ); - } else { - v3Factory = IPancakeV3LikePairFactory(createContractThroughBytecode(_getFactoryBytecodePath())); - - v3Nfpm = IV3NonfungiblePositionManager( - createContractThroughBytecode( - _getNfpmBytecodePath(), toBytes32(address(v3Factory)), toBytes32(address(weth)), 0 - ) - ); - } - - // make sure v3Nfpm has allowance - weth.approve(address(v3Nfpm), type(uint256).max); - token0.approve(address(v3Nfpm), type(uint256).max); - token1.approve(address(v3Nfpm), type(uint256).max); - } - - function test_bytecodeSize() public { - vm.snapshotValue("CLMigratorBytecode size", address(migrator).code.length); - - if (address(migrator).code.length > 24576) { - revert ContractSizeTooLarge(address(migrator).code.length - 24576); - } - } - - function testCLMigrateFromV3_WhenPaused() public { - // 1. mint some liquidity to the v3 pool - _mintV3Liquidity(address(weth), address(token0)); - assertEq(v3Nfpm.ownerOf(1), address(this)); - (,,,,,,, uint128 liquidityFromV3Before,,,,) = v3Nfpm.positions(1); - assertGt(liquidityFromV3Before, 0); - - // 2. make sure migrator can transfer user's v3 lp token - v3Nfpm.approve(address(migrator), 1); - - // 3. init the pool - lpm.initializePool(poolKey, INIT_SQRT_PRICE); - - IBaseMigrator.V3PoolParams memory v3PoolParams = IBaseMigrator.V3PoolParams({ - nfp: address(v3Nfpm), - tokenId: 1, - liquidity: liquidityFromV3Before, - amount0Min: 9.9 ether, - amount1Min: 9.9 ether, - collectFee: false, - deadline: block.timestamp + 100 - }); - - ICLMigrator.InfiCLPoolParams memory infiMintParams = ICLMigrator.InfiCLPoolParams({ - poolKey: poolKey, - tickLower: -100, - tickUpper: 100, - liquidityMin: 0, - recipient: address(this), - deadline: block.timestamp + 100, - hookData: new bytes(0) - }); - - // pre-req: pause - CLMigrator _migrator = CLMigrator(payable(address(migrator))); - _migrator.pause(); - - // 4. migrateFromV3 directly given pool has been initialized - vm.expectRevert(Pausable.EnforcedPause.selector); - migrator.migrateFromV3(v3PoolParams, infiMintParams, 0, 0); - } - - function testCLMigrateFromV3_HookData() public { - // 1. mint some liquidity to the v3 pool - _mintV3Liquidity(address(weth), address(token0)); - (,,,,,,, uint128 liquidityFromV3Before,,,,) = v3Nfpm.positions(1); - - // 2. make sure migrator can transfer user's v3 lp token - v3Nfpm.approve(address(migrator), 1); - - // 3. init the pool - lpm.initializePool(poolKey, INIT_SQRT_PRICE); - - IBaseMigrator.V3PoolParams memory v3PoolParams = IBaseMigrator.V3PoolParams({ - nfp: address(v3Nfpm), - tokenId: 1, - liquidity: liquidityFromV3Before, - amount0Min: 9.9 ether, - amount1Min: 9.9 ether, - collectFee: false, - deadline: block.timestamp + 100 - }); - - bytes memory hookData = abi.encode(32); - ICLMigrator.InfiCLPoolParams memory infiMintParams = ICLMigrator.InfiCLPoolParams({ - poolKey: poolKey, - tickLower: -100, - tickUpper: 100, - liquidityMin: 0, - recipient: address(this), - deadline: block.timestamp + 100, - hookData: hookData - }); - - // 4. migrateFromV3 directly given pool has been initialized - migrator.migrateFromV3(v3PoolParams, infiMintParams, 0, 0); - - // assert hookData flown to hook - assertEq(clMigratorHook.hookData(), hookData); - } - - function testCLMigrateFromV3ReentrancyLockRevert() public { - MockReentrantPositionManager reentrantPM = new MockReentrantPositionManager(permit2); - reentrantPM.setCLPoolMnager(poolManager); - migrator = new CLMigrator(address(weth), address(reentrantPM), permit2); - reentrantPM.setCLMigrator(migrator); - reentrantPM.setRenentrantType(MockReentrantPositionManager.ReentrantType.CLMigrateFromV3); - - // 1. mint some liquidity to the v3 pool - _mintV3Liquidity(address(weth), address(token0)); - assertEq(v3Nfpm.ownerOf(1), address(this)); - (,,,,,,, uint128 liquidityFromV3Before,,,,) = v3Nfpm.positions(1); - assertGt(liquidityFromV3Before, 0); - - // 2. make sure migrator can transfer user's v3 lp token - v3Nfpm.approve(address(migrator), 1); - - // 3. init the pool - lpm.initializePool(poolKey, INIT_SQRT_PRICE); - - IBaseMigrator.V3PoolParams memory v3PoolParams = IBaseMigrator.V3PoolParams({ - nfp: address(v3Nfpm), - tokenId: 1, - liquidity: liquidityFromV3Before, - amount0Min: 9.9 ether, - amount1Min: 9.9 ether, - collectFee: false, - deadline: block.timestamp + 100 - }); - - ICLMigrator.InfiCLPoolParams memory infiMintParams = ICLMigrator.InfiCLPoolParams({ - poolKey: poolKey, - tickLower: -100, - tickUpper: 100, - liquidityMin: 0, - recipient: address(this), - deadline: block.timestamp + 100, - hookData: new bytes(0) - }); - - vm.expectRevert(ReentrancyLock.ContractLocked.selector); - migrator.migrateFromV3(v3PoolParams, infiMintParams, 0, 0); - } - - function testCLMigrateFromV3IncludingInit() public { - // 1. mint some liquidity to the v3 pool - _mintV3Liquidity(address(weth), address(token0)); - assertEq(v3Nfpm.ownerOf(1), address(this)); - (,,,,,,, uint128 liquidityFromV3Before,,,,) = v3Nfpm.positions(1); - assertGt(liquidityFromV3Before, 0); - - // 2. make sure migrator can transfer user's v3 lp token - v3Nfpm.approve(address(migrator), 1); - - IBaseMigrator.V3PoolParams memory v3PoolParams = IBaseMigrator.V3PoolParams({ - nfp: address(v3Nfpm), - tokenId: 1, - liquidity: liquidityFromV3Before, - amount0Min: 9.9 ether, - amount1Min: 9.9 ether, - collectFee: false, - deadline: block.timestamp + 100 - }); - - ICLMigrator.InfiCLPoolParams memory infiMintParams = ICLMigrator.InfiCLPoolParams({ - poolKey: poolKey, - tickLower: -100, - tickUpper: 100, - liquidityMin: 0, - recipient: address(this), - deadline: block.timestamp + 100, - hookData: new bytes(0) - }); - - // 3. multicall, combine initialize and migrateFromV3 - bytes[] memory data = new bytes[](2); - data[0] = abi.encodeWithSelector(migrator.initializePool.selector, poolKey, INIT_SQRT_PRICE, bytes("")); - data[1] = abi.encodeWithSelector(migrator.migrateFromV3.selector, v3PoolParams, infiMintParams, 0, 0); - - migrator.multicall(data); - vm.snapshotGasLastCall("testCLMigrateFromV3IncludingInit"); - - // necessary checks - // v3 liqudity should be 0 - (,,,,,,, uint128 liquidityFromV3After,,,,) = v3Nfpm.positions(1); - assertEq(liquidityFromV3After, 0); - - // make sure liuqidty is minted to the correct pool - assertEq(lpm.ownerOf(1), address(this)); - uint128 liquidity = lpm.getPositionLiquidity(1); - - assertEq(liquidity, 2005104164790028032677); - - assertApproxEqAbs(address(vault).balance, 10 ether, 0.000001 ether); - assertApproxEqAbs(token0.balanceOf(address(vault)), 10 ether, 0.000001 ether); - } - - function testCLMigrateFromV3TokenMismatch() public { - // 1. mint some liquidity to the v3 pool - _mintV3Liquidity(address(weth), address(token0)); - assertEq(v3Nfpm.ownerOf(1), address(this)); - (,,,,,,, uint128 liquidityFromV3Before,,,,) = v3Nfpm.positions(1); - - // 2. make sure migrator can transfer user's v3 lp token - v3Nfpm.approve(address(migrator), 1); - - IBaseMigrator.V3PoolParams memory v3PoolParams = IBaseMigrator.V3PoolParams({ - nfp: address(v3Nfpm), - tokenId: 1, - liquidity: liquidityFromV3Before, - amount0Min: 9.9 ether, - amount1Min: 9.9 ether, - collectFee: false, - deadline: block.timestamp + 100 - }); - - // v3 weth, token0 - // infinity ETH, token1 - PoolKey memory poolKeyMismatch = poolKey; - poolKeyMismatch.currency1 = Currency.wrap(address(token1)); - ICLMigrator.InfiCLPoolParams memory infiMintParams = ICLMigrator.InfiCLPoolParams({ - poolKey: poolKeyMismatch, - tickLower: -100, - tickUpper: 100, - liquidityMin: 0, - recipient: address(this), - deadline: block.timestamp + 100, - hookData: new bytes(0) - }); - - // 3. multicall, combine initialize and migrateFromV3 - bytes[] memory data = new bytes[](2); - data[0] = abi.encodeWithSelector(migrator.initializePool.selector, poolKey, INIT_SQRT_PRICE, bytes("")); - data[1] = abi.encodeWithSelector(migrator.migrateFromV3.selector, v3PoolParams, infiMintParams, 0, 0); - vm.expectRevert(); - migrator.multicall(data); - - { - // v3 weth, token0 - // infinity token0, token1 - poolKeyMismatch.currency0 = Currency.wrap(address(token0)); - poolKeyMismatch.currency1 = Currency.wrap(address(token1)); - infiMintParams.poolKey = poolKeyMismatch; - data = new bytes[](2); - data[0] = abi.encodeWithSelector(migrator.initializePool.selector, poolKey, INIT_SQRT_PRICE, bytes("")); - data[1] = abi.encodeWithSelector(migrator.migrateFromV3.selector, v3PoolParams, infiMintParams, 0, 0); - vm.expectRevert(); - migrator.multicall(data); - } - } - - function testCLMigrateFromV3InsufficientLiquidity() public { - // 1. mint some liquidity to the v3 pool - _mintV3Liquidity(address(weth), address(token0)); - assertEq(v3Nfpm.ownerOf(1), address(this)); - (,,,,,,, uint128 liquidityFromV3Before,,,,) = v3Nfpm.positions(1); - assertGt(liquidityFromV3Before, 0); - - // 2. make sure migrator can transfer user's v3 lp token - v3Nfpm.approve(address(migrator), 1); - - // 3. init the pool - lpm.initializePool(poolKey, INIT_SQRT_PRICE); - - IBaseMigrator.V3PoolParams memory v3PoolParams = IBaseMigrator.V3PoolParams({ - nfp: address(v3Nfpm), - tokenId: 1, - liquidity: liquidityFromV3Before, - amount0Min: 9.9 ether, - amount1Min: 9.9 ether, - collectFee: false, - deadline: block.timestamp + 100 - }); - - ICLMigrator.InfiCLPoolParams memory infiMintParams = ICLMigrator.InfiCLPoolParams({ - poolKey: poolKey, - tickLower: -100, - tickUpper: 100, - liquidityMin: 2005104164790028032678, // minted liquidity + 1 - recipient: address(this), - deadline: block.timestamp + 100, - hookData: new bytes(0) - }); - - vm.expectRevert(ICLMigrator.INSUFFICIENT_LIQUIDITY.selector); - migrator.migrateFromV3(v3PoolParams, infiMintParams, 0, 0); - } - - function testCLMigrateFromV3WithoutInit() public { - // 1. mint some liquidity to the v3 pool - _mintV3Liquidity(address(weth), address(token0)); - assertEq(v3Nfpm.ownerOf(1), address(this)); - (,,,,,,, uint128 liquidityFromV3Before,,,,) = v3Nfpm.positions(1); - assertGt(liquidityFromV3Before, 0); - - // 2. make sure migrator can transfer user's v3 lp token - v3Nfpm.approve(address(migrator), 1); - - // 3. init the pool - lpm.initializePool(poolKey, INIT_SQRT_PRICE); - - IBaseMigrator.V3PoolParams memory v3PoolParams = IBaseMigrator.V3PoolParams({ - nfp: address(v3Nfpm), - tokenId: 1, - liquidity: liquidityFromV3Before, - amount0Min: 9.9 ether, - amount1Min: 9.9 ether, - collectFee: false, - deadline: block.timestamp + 100 - }); - - ICLMigrator.InfiCLPoolParams memory infiMintParams = ICLMigrator.InfiCLPoolParams({ - poolKey: poolKey, - tickLower: -100, - tickUpper: 100, - liquidityMin: 0, - recipient: address(this), - deadline: block.timestamp + 100, - hookData: new bytes(0) - }); - - // 4. migrateFromV3 directly given pool has been initialized - migrator.migrateFromV3(v3PoolParams, infiMintParams, 0, 0); - vm.snapshotGasLastCall("testCLMigrateFromV3WithoutInit"); - - // necessary checks - // v3 liqudity should be 0 - (,,,,,,, uint128 liquidityFromV3After,,,,) = v3Nfpm.positions(1); - assertEq(liquidityFromV3After, 0); - - // make sure liuqidty is minted to the correct pool - assertEq(lpm.ownerOf(1), address(this)); - uint128 liquidity = lpm.getPositionLiquidity(1); - - assertEq(liquidity, 2005104164790028032677); - - assertApproxEqAbs(address(vault).balance, 10 ether, 0.000001 ether); - assertApproxEqAbs(token0.balanceOf(address(vault)), 10 ether, 0.000001 ether); - } - - function testCLMigrateFromV3WithoutNativeToken() public { - // 1. mint some liquidity to the v3 pool - _mintV3Liquidity(address(token0), address(token1)); - - assertEq(v3Nfpm.ownerOf(1), address(this)); - (,,,,,,, uint128 liquidityFromV3Before,,,,) = v3Nfpm.positions(1); - assertGt(liquidityFromV3Before, 0); - - // 2. make sure migrator can transfer user's v3 lp token - v3Nfpm.approve(address(migrator), 1); - - // 3. initialize the pool - migrator.initializePool(poolKeyWithoutNativeToken, INIT_SQRT_PRICE); - - IBaseMigrator.V3PoolParams memory v3PoolParams = IBaseMigrator.V3PoolParams({ - nfp: address(v3Nfpm), - tokenId: 1, - liquidity: liquidityFromV3Before, - amount0Min: 9.9 ether, - amount1Min: 9.9 ether, - collectFee: false, - deadline: block.timestamp + 100 - }); - - ICLMigrator.InfiCLPoolParams memory infiMintParams = ICLMigrator.InfiCLPoolParams({ - poolKey: poolKeyWithoutNativeToken, - tickLower: -100, - tickUpper: 100, - liquidityMin: 0, - recipient: address(this), - deadline: block.timestamp + 100, - hookData: new bytes(0) - }); - - // 4. migrate from v3 to infinity - migrator.migrateFromV3(v3PoolParams, infiMintParams, 0, 0); - vm.snapshotGasLastCall("testCLMigrateFromV3WithoutNativeToken"); - - // necessary checks - // v3 liqudity should be 0 - (,,,,,,, uint128 liquidityFromV3After,,,,) = v3Nfpm.positions(1); - assertEq(liquidityFromV3After, 0); - - // make sure liuqidty is minted to the correct pool - assertEq(lpm.ownerOf(1), address(this)); - uint128 liquidity = lpm.getPositionLiquidity(1); - - assertEq(liquidity, 2005104164790028032677); - - assertApproxEqAbs(token0.balanceOf(address(vault)), 10 ether, 0.000001 ether); - assertApproxEqAbs(token1.balanceOf(address(vault)), 10 ether, 0.000001 ether); - } - - function testCLMigrateFromV3AddExtraAmount() public { - // 1. mint some liquidity to the v3 pool - _mintV3Liquidity(address(weth), address(token0)); - assertEq(v3Nfpm.ownerOf(1), address(this)); - (,,,,,,, uint128 liquidityFromV3Before,,,,) = v3Nfpm.positions(1); - assertGt(liquidityFromV3Before, 0); - - // 2. make sure migrator can transfer user's v3 lp token - v3Nfpm.approve(address(migrator), 1); - - // 3. init the pool - lpm.initializePool(poolKey, INIT_SQRT_PRICE); - - IBaseMigrator.V3PoolParams memory v3PoolParams = IBaseMigrator.V3PoolParams({ - nfp: address(v3Nfpm), - tokenId: 1, - liquidity: liquidityFromV3Before, - amount0Min: 9.9 ether, - amount1Min: 9.9 ether, - collectFee: false, - deadline: block.timestamp + 100 - }); - - ICLMigrator.InfiCLPoolParams memory infiMintParams = ICLMigrator.InfiCLPoolParams({ - poolKey: poolKey, - tickLower: -100, - tickUpper: 100, - liquidityMin: 0, - recipient: address(this), - deadline: block.timestamp + 100, - hookData: new bytes(0) - }); - - uint256 balance0Before = address(this).balance; - uint256 balance1Before = token0.balanceOf(address(this)); - - permit2ApproveWithSpecificAllowance( - address(this), permit2, address(token0), address(migrator), 20 ether, 20 ether - ); - // 4. migrate from v3 to infinity - migrator.migrateFromV3{value: 20 ether}(v3PoolParams, infiMintParams, 20 ether, 20 ether); - - // necessary checks - // consumed extra 20 ether from user - assertApproxEqAbs(balance0Before - address(this).balance, 20 ether, 0.000001 ether); - assertEq(balance1Before - token0.balanceOf(address(this)), 20 ether); - // WETH balance unchanged - assertEq(weth.balanceOf(address(this)), 90 ether); - - // v3 liqudity should be 0 - (,,,,,,, uint128 liquidityFromV3After,,,,) = v3Nfpm.positions(1); - assertEq(liquidityFromV3After, 0); - - // make sure liuqidty is minted to the correct pool - assertEq(lpm.ownerOf(1), address(this)); - uint128 liquidity = lpm.getPositionLiquidity(1); - - // liquidity is 3 times of the original - assertApproxEqAbs(liquidity, 2005104164790028032677 * 3, 0.000001 ether); - - assertApproxEqAbs(address(vault).balance, 30 ether, 0.000001 ether); - assertApproxEqAbs(token0.balanceOf(address(vault)), 30 ether, 0.000001 ether); - } - - function testCLMigrateFromV3AddExtraAmountThroughWETH() public { - // 1. mint some liquidity to the v3 pool - _mintV3Liquidity(address(weth), address(token0)); - assertEq(v3Nfpm.ownerOf(1), address(this)); - (,,,,,,, uint128 liquidityFromV3Before,,,,) = v3Nfpm.positions(1); - assertGt(liquidityFromV3Before, 0); - - // 2. make sure migrator can transfer user's v3 lp token - v3Nfpm.approve(address(migrator), 1); - - // 3. init the pool - lpm.initializePool(poolKey, INIT_SQRT_PRICE); - - IBaseMigrator.V3PoolParams memory v3PoolParams = IBaseMigrator.V3PoolParams({ - nfp: address(v3Nfpm), - tokenId: 1, - liquidity: liquidityFromV3Before, - amount0Min: 9.9 ether, - amount1Min: 9.9 ether, - collectFee: false, - deadline: block.timestamp + 100 - }); - - ICLMigrator.InfiCLPoolParams memory infiMintParams = ICLMigrator.InfiCLPoolParams({ - poolKey: poolKey, - tickLower: -100, - tickUpper: 100, - liquidityMin: 0, - recipient: address(this), - deadline: block.timestamp + 100, - hookData: new bytes(0) - }); - - uint256 balance0Before = address(this).balance; - uint256 balance1Before = token0.balanceOf(address(this)); - - permit2ApproveWithSpecificAllowance( - address(this), permit2, address(weth), address(migrator), 20 ether, 20 ether - ); - permit2ApproveWithSpecificAllowance( - address(this), permit2, address(token0), address(migrator), 20 ether, 20 ether - ); - // 4. migrate from v3 to infinity, not sending ETH denotes pay by WETH - migrator.migrateFromV3(v3PoolParams, infiMintParams, 20 ether, 20 ether); - - // necessary checks - // consumed extra 20 ether from user - // native token balance unchanged - assertApproxEqAbs(balance0Before - address(this).balance, 0 ether, 0.000001 ether); - assertEq(balance1Before - token0.balanceOf(address(this)), 20 ether); - // consumed 20 ether WETH - assertEq(weth.balanceOf(address(this)), 70 ether); - - // v3 liqudity should be 0 - (,,,,,,, uint128 liquidityFromV3After,,,,) = v3Nfpm.positions(1); - assertEq(liquidityFromV3After, 0); - - // make sure liuqidty is minted to the correct pool - assertEq(lpm.ownerOf(1), address(this)); - uint128 liquidity = lpm.getPositionLiquidity(1); - - // liquidity is 3 times of the original - assertApproxEqAbs(liquidity, 2005104164790028032677 * 3, 0.000001 ether); - - assertApproxEqAbs(address(vault).balance, 30 ether, 0.000001 ether); - assertApproxEqAbs(token0.balanceOf(address(vault)), 30 ether, 0.000001 ether); - } - - function testFuzz_CLMigrateFromV3AddExtraAmountThroughWETH(uint256 extraAmount) public { - extraAmount = bound(extraAmount, 1 ether, 60 ether); - // 1. mint some liquidity to the v3 pool - _mintV3Liquidity(address(weth), address(token0)); - assertEq(v3Nfpm.ownerOf(1), address(this)); - (,,,,,,, uint128 liquidityFromV3Before,,,,) = v3Nfpm.positions(1); - assertGt(liquidityFromV3Before, 0); - - // 2. make sure migrator can transfer user's v3 lp token - v3Nfpm.approve(address(migrator), 1); - - // 3. init the pool - lpm.initializePool(poolKey, INIT_SQRT_PRICE); - - IBaseMigrator.V3PoolParams memory v3PoolParams = IBaseMigrator.V3PoolParams({ - nfp: address(v3Nfpm), - tokenId: 1, - liquidity: liquidityFromV3Before, - amount0Min: 9.9 ether, - amount1Min: 9.9 ether, - collectFee: false, - deadline: block.timestamp + 100 - }); - - ICLMigrator.InfiCLPoolParams memory infiMintParams = ICLMigrator.InfiCLPoolParams({ - poolKey: poolKey, - tickLower: -100, - tickUpper: 100, - liquidityMin: 0, - recipient: address(this), - deadline: block.timestamp + 100, - hookData: new bytes(0) - }); - - uint256 balance0Before = address(this).balance; - uint256 balance1Before = token0.balanceOf(address(this)); - - permit2ApproveWithSpecificAllowance( - address(this), permit2, address(weth), address(migrator), extraAmount, uint160(extraAmount) - ); - permit2ApproveWithSpecificAllowance( - address(this), permit2, address(token0), address(migrator), extraAmount, uint160(extraAmount) - ); - // 4. migrate from v3 to infinity, not sending ETH denotes pay by WETH - migrator.migrateFromV3(v3PoolParams, infiMintParams, extraAmount, extraAmount); - - // clPositionManager native balance should be 0 - uint256 lPositionManagerNativeBalance = address(lpm).balance; - assertEq(lPositionManagerNativeBalance, 0); - - // necessary checks - // consumed extra extraAmount from user - // native token balance unchanged - assertApproxEqAbs(balance0Before - address(this).balance, 0 ether, 0.000001 ether); - assertEq(balance1Before - token0.balanceOf(address(this)), extraAmount); - // consumed extraAmount WETH - assertEq(weth.balanceOf(address(this)), 90 ether - extraAmount); - - // v3 liqudity should be 0 - (,,,,,,, uint128 liquidityFromV3After,,,,) = v3Nfpm.positions(1); - assertEq(liquidityFromV3After, 0); - - // make sure liuqidty is minted to the correct pool - assertEq(lpm.ownerOf(1), address(this)); - uint128 liquidity = lpm.getPositionLiquidity(1); - - // liquidity is 3 times of the original - assertApproxEqAbs(liquidity, 2005104164790028032677 * (10 ether + extraAmount) / 10 ether, 0.000001 ether); - - assertApproxEqAbs(address(vault).balance, 10 ether + extraAmount, 0.000001 ether); - assertApproxEqAbs(token0.balanceOf(address(vault)), 10 ether + extraAmount, 0.000001 ether); - } - - function testFuzz_InfiPositionAmountConsumedCalculationBySqrtPriceMath(uint256 extraAmount0, uint256 extraAmount1) - public - { - extraAmount0 = bound(extraAmount0, 1 ether, 60 ether); - extraAmount1 = bound(extraAmount1, 1 ether, 60 ether); - - // 1. mint some liquidity to the v3 pool - _mintV3Liquidity(address(weth), address(token0)); - assertEq(v3Nfpm.ownerOf(1), address(this)); - (,,,,,,, uint128 liquidityFromV3Before,,,,) = v3Nfpm.positions(1); - assertGt(liquidityFromV3Before, 0); - - // 2. make sure migrator can transfer user's v3 lp token - v3Nfpm.approve(address(migrator), 1); - - // 3. init the pool - lpm.initializePool(poolKey, INIT_SQRT_PRICE); - - int24 tickLower = -100; - int24 tickUpper = 100; - IBaseMigrator.V3PoolParams memory v3PoolParams = IBaseMigrator.V3PoolParams({ - nfp: address(v3Nfpm), - tokenId: 1, - liquidity: liquidityFromV3Before, - amount0Min: 9.9 ether, - amount1Min: 9.9 ether, - collectFee: false, - deadline: block.timestamp + 100 - }); - - ICLMigrator.InfiCLPoolParams memory infiMintParams = ICLMigrator.InfiCLPoolParams({ - poolKey: poolKey, - tickLower: tickLower, - tickUpper: tickUpper, - liquidityMin: 0, - recipient: address(this), - deadline: block.timestamp + 100, - hookData: new bytes(0) - }); - - permit2ApproveWithSpecificAllowance( - address(this), permit2, address(weth), address(migrator), extraAmount0, uint160(extraAmount0) - ); - permit2ApproveWithSpecificAllowance( - address(this), permit2, address(token0), address(migrator), extraAmount1, uint160(extraAmount1) - ); - - (uint160 sqrtPriceX96, int24 activeTick,,) = poolManager.getSlot0(poolKey.toId()); - uint256 vaultCurrency0BalanceBefore = poolKey.currency0.balanceOf(address(vault)); - uint256 vaultCurrency1BalanceBefore = poolKey.currency1.balanceOf(address(vault)); - vm.recordLogs(); - // 4. migrate from v3 to infinity, not sending ETH denotes pay by WETH - migrator.migrateFromV3(v3PoolParams, infiMintParams, extraAmount0, extraAmount1); - - uint256 vaultCurrency0BalanceAfter = poolKey.currency0.balanceOf(address(vault)); - uint256 vaultCurrency1BalanceAfter = poolKey.currency1.balanceOf(address(vault)); - - Vm.Log[] memory entries = vm.getRecordedLogs(); - // event ModifyLiquidity(PoolId indexed id, address indexed sender, int24 tickLower, int24 tickUpper, int256 liquidityDelta, bytes32 salt); - bytes32 infiPoolModifyLiquidityEventTopic0 = - keccak256("ModifyLiquidity(bytes32,address,int24,int24,int256,bytes32)"); - - // event Collect(uint256 indexed tokenId, address recipient, uint256 amount0, uint256 amount1); - bytes32 v3CollectEventTopic0 = keccak256("Collect(uint256,address,uint256,uint256)"); - - bytes memory modifyLiquidityEventData; - bytes memory collectEventData; - for (uint256 i; i < entries.length; i++) { - if (entries[i].topics[0] == infiPoolModifyLiquidityEventTopic0) { - modifyLiquidityEventData = entries[i].data; - } else if (entries[i].topics[0] == v3CollectEventTopic0) { - collectEventData = entries[i].data; - } - } - - // infinity position liquidity delta - (,, int256 liquidityDeltaOfModifyLiquidity,) = - abi.decode(modifyLiquidityEventData, (int24, int24, int256, bytes32)); - // v3 liquidity collect amounts - (, uint256 v3LiquidityAmount0, uint256 v3LiquidityAmount1) = - abi.decode(collectEventData, (address, uint256, uint256)); - // calculate infinity position consumed amount - uint160 sqrtRatioAX96 = TickMath.getSqrtRatioAtTick(tickLower); - uint160 sqrtRatioBX96 = TickMath.getSqrtRatioAtTick(tickUpper); - uint128 liquidity = LiquidityAmounts.getLiquidityForAmounts( - sqrtPriceX96, - sqrtRatioAX96, - sqrtRatioBX96, - extraAmount0 + v3LiquidityAmount0, - extraAmount1 + v3LiquidityAmount1 - ); - uint256 amount0Consumed; - uint256 amount1Consumed; - - // Calculate amt0/amt1 from liquidity, similar to CLPool modifyLiquidity logic - if (activeTick < tickLower) { - amount0Consumed = SqrtPriceMath.getAmount0Delta(sqrtRatioAX96, sqrtRatioBX96, liquidity, true); - } else if (activeTick < tickUpper) { - amount0Consumed = SqrtPriceMath.getAmount0Delta(sqrtPriceX96, sqrtRatioBX96, liquidity, true); - amount1Consumed = SqrtPriceMath.getAmount1Delta(sqrtRatioAX96, sqrtPriceX96, liquidity, true); - } else { - amount1Consumed = SqrtPriceMath.getAmount1Delta(sqrtRatioAX96, sqrtPriceX96, liquidity, true); - } - - assertEq(amount0Consumed, vaultCurrency0BalanceAfter - vaultCurrency0BalanceBefore); - assertEq(amount1Consumed, vaultCurrency1BalanceAfter - vaultCurrency1BalanceBefore); - - assertEq(liquidityDeltaOfModifyLiquidity, liquidity.toInt256()); - - // clPositionManager native balance should be 0 - uint256 clPositionManagerNativeBalance = address(lpm).balance; - assertEq(clPositionManagerNativeBalance, 0); - } - - function testCLMigrateFromV3Refund() public { - // 1. mint some liquidity to the v3 pool - // 10 ether WETH, 5 ether token0 - _mintV3Liquidity(address(weth), address(token0), 10 ether, 5 ether); - assertEq(v3Nfpm.ownerOf(1), address(this)); - (,,,,,,, uint128 liquidityFromV3Before,,,,) = v3Nfpm.positions(1); - assertGt(liquidityFromV3Before, 0); - - // 2. make sure migrator can transfer user's v3 lp token - v3Nfpm.approve(address(migrator), 1); - - // 3. init the pool - lpm.initializePool(poolKey, INIT_SQRT_PRICE); - - IBaseMigrator.V3PoolParams memory v3PoolParams = IBaseMigrator.V3PoolParams({ - nfp: address(v3Nfpm), - tokenId: 1, - liquidity: liquidityFromV3Before, - amount0Min: 0, - amount1Min: 0, - collectFee: false, - deadline: block.timestamp + 100 - }); - - ICLMigrator.InfiCLPoolParams memory infiMintParams = ICLMigrator.InfiCLPoolParams({ - poolKey: poolKey, - tickLower: -100, - tickUpper: 100, - liquidityMin: 0, - recipient: address(this), - deadline: block.timestamp + 100, - hookData: new bytes(0) - }); - - uint256 balance0Before = address(this).balance; - uint256 balance1Before = token0.balanceOf(address(this)); - - // 4. migrate from v3 to infinity, not sending ETH denotes pay by WETH - migrator.migrateFromV3(v3PoolParams, infiMintParams, 0, 0); - - // necessary checks - // refund 5 ether in the form of native token - assertApproxEqAbs(address(this).balance - balance0Before, 5.0 ether, 0.1 ether); - assertEq(balance1Before - token0.balanceOf(address(this)), 0 ether); - // WETH balance unchanged - assertApproxEqAbs(weth.balanceOf(address(this)), 90 ether, 0.1 ether); - - // v3 liqudity should be 0 - (,,,,,,, uint128 liquidityFromV3After,,,,) = v3Nfpm.positions(1); - assertEq(liquidityFromV3After, 0); - - // make sure liuqidty is minted to the correct pool - assertEq(lpm.ownerOf(1), address(this)); - uint128 liquidity = lpm.getPositionLiquidity(1); - - // liquidity is half of the original - assertApproxEqAbs(liquidity * 2, 2005104164790028032677, 0.1 ether); - - assertApproxEqAbs(address(vault).balance, 5 ether, 0.1 ether); - assertApproxEqAbs(token0.balanceOf(address(vault)), 5 ether, 0.1 ether); - } - - function testCLMigrateFromV3RefundNonNativeToken() public { - // 1. mint some liquidity to the v3 pool - // 10 ether token0, 5 ether token1 - _mintV3Liquidity(address(token0), address(token1), 10 ether, 5 ether); - assertEq(v3Nfpm.ownerOf(1), address(this)); - (,,,,,,, uint128 liquidityFromV3Before,,,,) = v3Nfpm.positions(1); - assertGt(liquidityFromV3Before, 0); - - // 2. make sure migrator can transfer user's v3 lp token - v3Nfpm.approve(address(migrator), 1); - - // 3. init the pool - lpm.initializePool(poolKeyWithoutNativeToken, INIT_SQRT_PRICE); - - IBaseMigrator.V3PoolParams memory v3PoolParams = IBaseMigrator.V3PoolParams({ - nfp: address(v3Nfpm), - tokenId: 1, - liquidity: liquidityFromV3Before, - amount0Min: 0, - amount1Min: 0, - collectFee: false, - deadline: block.timestamp + 100 - }); - - ICLMigrator.InfiCLPoolParams memory infiMintParams = ICLMigrator.InfiCLPoolParams({ - poolKey: poolKeyWithoutNativeToken, - tickLower: -100, - tickUpper: 100, - liquidityMin: 0, - recipient: address(this), - deadline: block.timestamp + 100, - hookData: new bytes(0) - }); - - uint256 balance0Before = token0.balanceOf(address(this)); - uint256 balance1Before = token1.balanceOf(address(this)); - - // 4. migrate from v3 to infinity - migrator.migrateFromV3(v3PoolParams, infiMintParams, 0, 0); - - // necessary checks - - // refund 5 ether of token0 - assertApproxEqAbs(token0.balanceOf(address(this)) - balance0Before, 5 ether, 0.1 ether); - assertEq(balance1Before - token1.balanceOf(address(this)), 0 ether); - // WETH balance unchanged - assertEq(weth.balanceOf(address(this)), 100 ether); - - // v3 liqudity should be 0 - (,,,,,,, uint128 liquidityFromV3After,,,,) = v3Nfpm.positions(1); - assertEq(liquidityFromV3After, 0); - - // make sure liuqidty is minted to the correct pool - assertEq(lpm.ownerOf(1), address(this)); - uint128 liquidity = lpm.getPositionLiquidity(1); - - // liquidity is half of the original - assertApproxEqAbs(liquidity * 2, 2005104164790028032677, 0.1 ether); - - assertApproxEqAbs(token0.balanceOf(address(vault)), 5 ether, 0.1 ether); - assertApproxEqAbs(token1.balanceOf(address(vault)), 5 ether, 0.1 ether); - } - - function testCLMigrateFromV3FromNonOwner() public { - // 1. mint some liquidity to the v3 pool - _mintV3Liquidity(address(weth), address(token0)); - assertEq(v3Nfpm.ownerOf(1), address(this)); - (,,,,,,, uint128 liquidityFromV3Before,,,,) = v3Nfpm.positions(1); - assertGt(liquidityFromV3Before, 0); - - // 2. make sure migrator can transfer user's v3 lp token - v3Nfpm.approve(address(migrator), 1); - - // 3. init the pool - lpm.initializePool(poolKey, INIT_SQRT_PRICE); - - IBaseMigrator.V3PoolParams memory v3PoolParams = IBaseMigrator.V3PoolParams({ - nfp: address(v3Nfpm), - tokenId: 1, - // half of the liquidity - liquidity: liquidityFromV3Before / 2, - amount0Min: 9.9 ether / 2, - amount1Min: 9.9 ether / 2, - collectFee: false, - deadline: block.timestamp + 100 - }); - - ICLMigrator.InfiCLPoolParams memory infiMintParams = ICLMigrator.InfiCLPoolParams({ - poolKey: poolKey, - tickLower: -100, - tickUpper: 100, - liquidityMin: 0, - recipient: address(this), - deadline: block.timestamp + 100, - hookData: new bytes(0) - }); - - // 4. migrate half - migrator.migrateFromV3(v3PoolParams, infiMintParams, 0, 0); - - // make sure there are still liquidity left in v3 position token - (,,,,,,, uint128 liquidityFromV3After,,,,) = v3Nfpm.positions(1); - assertEq(liquidityFromV3After, liquidityFromV3Before - liquidityFromV3Before / 2); - - // 5. make sure non-owner can't migrate the rest - vm.expectRevert(IBaseMigrator.NOT_TOKEN_OWNER.selector); - vm.prank(makeAddr("someone")); - migrator.migrateFromV3(v3PoolParams, infiMintParams, 0, 0); - } - - function testCLMigrateFromV3ThroughOffchainSign() public { - // 1. mint some liquidity to the v3 pool - _mintV3Liquidity(address(weth), address(token0)); - assertEq(v3Nfpm.ownerOf(1), address(this)); - (uint96 nonce,,,,,,, uint128 liquidityFromV3Before,,,,) = v3Nfpm.positions(1); - assertGt(liquidityFromV3Before, 0); - - // 2. make sure migrator can transfer user's v3 lp token through offchain sign - // v3Nfpm.approve(address(migrator), 1); - (address userAddr, uint256 userPrivateKey) = makeAddrAndKey("user"); - - // 2.a transfer the lp token to the user - v3Nfpm.transferFrom(address(this), userAddr, 1); - - uint256 ddl = block.timestamp + 100; - // 2.b prepare the hash - bytes32 structHash = keccak256(abi.encode(v3Nfpm.PERMIT_TYPEHASH(), address(migrator), 1, nonce, ddl)); - bytes32 hash = keccak256(abi.encodePacked("\x19\x01", v3Nfpm.DOMAIN_SEPARATOR(), structHash)); - - // 2.c generate the signature - (uint8 v, bytes32 r, bytes32 s) = vm.sign(userPrivateKey, hash); - - IBaseMigrator.V3PoolParams memory v3PoolParams = IBaseMigrator.V3PoolParams({ - nfp: address(v3Nfpm), - tokenId: 1, - liquidity: liquidityFromV3Before, - amount0Min: 9.9 ether, - amount1Min: 9.9 ether, - collectFee: false, - deadline: block.timestamp + 100 - }); - - ICLMigrator.InfiCLPoolParams memory infiMintParams = ICLMigrator.InfiCLPoolParams({ - poolKey: poolKey, - tickLower: -100, - tickUpper: 100, - liquidityMin: 0, - recipient: address(this), - deadline: block.timestamp + 100, - hookData: new bytes(0) - }); - - // 3. multicall, combine selfPermitERC721, initialize and migrateFromV3 - bytes[] memory data = new bytes[](3); - data[0] = abi.encodeWithSelector(migrator.selfPermitERC721.selector, v3Nfpm, 1, ddl, v, r, s); - data[1] = abi.encodeWithSelector(migrator.initializePool.selector, poolKey, INIT_SQRT_PRICE, bytes("")); - data[2] = abi.encodeWithSelector(migrator.migrateFromV3.selector, v3PoolParams, infiMintParams, 0, 0); - vm.prank(userAddr); - migrator.multicall(data); - - // necessary checks - // v3 liqudity should be 0 - (,,,,,,, uint128 liquidityFromV3After,,,,) = v3Nfpm.positions(1); - assertEq(liquidityFromV3After, 0); - - // make sure liuqidty is minted to the correct pool - assertEq(lpm.ownerOf(1), address(this)); - uint128 liquidity = lpm.getPositionLiquidity(1); - - assertEq(liquidity, 2005104164790028032677); - - assertApproxEqAbs(address(vault).balance, 10 ether, 0.000001 ether); - assertApproxEqAbs(token0.balanceOf(address(vault)), 10 ether, 0.000001 ether); - } - - function testCLMigrateFromV3ThroughOffchainSignPayWithETH() public { - // 1. mint some liquidity to the v3 pool - _mintV3Liquidity(address(weth), address(token0)); - assertEq(v3Nfpm.ownerOf(1), address(this)); - (uint96 nonce,,,,,,, uint128 liquidityFromV3Before,,,,) = v3Nfpm.positions(1); - assertGt(liquidityFromV3Before, 0); - - // 2. make sure migrator can transfer user's v3 lp token through offchain sign - // v3Nfpm.approve(address(migrator), 1); - (address userAddr, uint256 userPrivateKey) = makeAddrAndKey("user"); - - // 2.a transfer the lp token to the user - v3Nfpm.transferFrom(address(this), userAddr, 1); - - uint256 ddl = block.timestamp + 100; - // 2.b prepare the hash - bytes32 structHash = keccak256(abi.encode(v3Nfpm.PERMIT_TYPEHASH(), address(migrator), 1, nonce, ddl)); - bytes32 hash = keccak256(abi.encodePacked("\x19\x01", v3Nfpm.DOMAIN_SEPARATOR(), structHash)); - - // 2.c generate the signature - (uint8 v, bytes32 r, bytes32 s) = vm.sign(userPrivateKey, hash); - - IBaseMigrator.V3PoolParams memory v3PoolParams = IBaseMigrator.V3PoolParams({ - nfp: address(v3Nfpm), - tokenId: 1, - liquidity: liquidityFromV3Before, - amount0Min: 9.9 ether, - amount1Min: 9.9 ether, - collectFee: false, - deadline: block.timestamp + 100 - }); - - ICLMigrator.InfiCLPoolParams memory infiMintParams = ICLMigrator.InfiCLPoolParams({ - poolKey: poolKey, - tickLower: -100, - tickUpper: 100, - liquidityMin: 0, - recipient: address(this), - deadline: block.timestamp + 100, - hookData: new bytes(0) - }); - - // make the guy rich - token0.transfer(userAddr, 10 ether); - deal(userAddr, 10 ether); - - permit2ApproveWithSpecificAllowance(userAddr, permit2, address(token0), address(migrator), 10 ether, 10 ether); - - // 3. multicall, combine selfPermitERC721, initialize and migrateFromV3 - bytes[] memory data = new bytes[](3); - data[0] = abi.encodeWithSelector(migrator.selfPermitERC721.selector, v3Nfpm, 1, ddl, v, r, s); - data[1] = abi.encodeWithSelector(migrator.initializePool.selector, poolKey, INIT_SQRT_PRICE, bytes("")); - data[2] = - abi.encodeWithSelector(migrator.migrateFromV3.selector, v3PoolParams, infiMintParams, 10 ether, 10 ether); - vm.prank(userAddr); - migrator.multicall{value: 10 ether}(data); - - // necessary checks - // v3 liqudity should be 0 - (,,,,,,, uint128 liquidityFromV3After,,,,) = v3Nfpm.positions(1); - assertEq(liquidityFromV3After, 0); - - // make sure liuqidty is minted to the correct pool - assertEq(lpm.ownerOf(1), address(this)); - uint128 liquidity = lpm.getPositionLiquidity(1); - - assertEq(liquidity, 4010208329580056065555); - - assertApproxEqAbs(address(vault).balance, 20 ether, 0.000001 ether); - assertApproxEqAbs(token0.balanceOf(address(vault)), 20 ether, 0.000001 ether); - } - - function _mintV3Liquidity(address _token0, address _token1) internal { - (_token0, _token1) = _token0 < _token1 ? (_token0, _token1) : (_token1, _token0); - v3Nfpm.createAndInitializePoolIfNecessary(_token0, _token1, 500, INIT_SQRT_PRICE); - IV3NonfungiblePositionManager.MintParams memory mintParams = IV3NonfungiblePositionManager.MintParams({ - token0: _token0, - token1: _token1, - fee: 500, - tickLower: -100, - tickUpper: 100, - amount0Desired: 10 ether, - amount1Desired: 10 ether, - amount0Min: 0, - amount1Min: 0, - recipient: address(this), - deadline: block.timestamp + 100 - }); - - v3Nfpm.mint(mintParams); - } - - function _mintV3Liquidity(address _token0, address _token1, uint256 amount0, uint256 amount1) internal { - int24 tickLower; - int24 tickUpper; - if (_token0 < _token1) { - tickLower = -100; - tickUpper = 200; - } else { - (_token0, _token1) = (_token1, _token0); - (amount0, amount1) = (amount1, amount0); - tickLower = -200; - tickUpper = 100; - } - v3Nfpm.createAndInitializePoolIfNecessary(_token0, _token1, 500, INIT_SQRT_PRICE); - - IV3NonfungiblePositionManager.MintParams memory mintParams = IV3NonfungiblePositionManager.MintParams({ - token0: _token0, - token1: _token1, - fee: 500, - tickLower: tickLower, - tickUpper: tickUpper, - amount0Desired: amount0, - amount1Desired: amount1, - amount0Min: amount0 - 0.1 ether, - amount1Min: amount1 - 0.1 ether, - recipient: address(this), - deadline: block.timestamp + 100 - }); - - v3Nfpm.mint(mintParams); - } - - receive() external payable {} -} diff --git a/test/pool-cl/migrator/mocks/MockCLMigratorHook.sol b/test/pool-cl/migrator/mocks/MockCLMigratorHook.sol deleted file mode 100644 index 707cf5a..0000000 --- a/test/pool-cl/migrator/mocks/MockCLMigratorHook.sol +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.24; - -import {PoolKey} from "infinity-core/src/types/PoolKey.sol"; -import {BaseCLTestHook} from "infinity-core/test/pool-cl/helpers/BaseCLTestHook.sol"; -import {ICLPoolManager} from "infinity-core/src/pool-cl/interfaces/ICLPoolManager.sol"; - -/// @notice store hookData to verify hookData is passed to the hook -contract MockCLMigratorHook is BaseCLTestHook { - bytes public hookData; - - function getHooksRegistrationBitmap() external pure override returns (uint16) { - return _hooksRegistrationBitmapFrom( - Permissions({ - beforeInitialize: false, - afterInitialize: false, - beforeAddLiquidity: true, - afterAddLiquidity: false, - beforeRemoveLiquidity: false, - afterRemoveLiquidity: false, - beforeSwap: false, - afterSwap: false, - beforeDonate: false, - afterDonate: false, - befreSwapReturnsDelta: false, - afterSwapReturnsDelta: false, - afterAddLiquidityReturnsDelta: false, - afterRemoveLiquidityReturnsDelta: false - }) - ); - } - - function beforeAddLiquidity( - address, - PoolKey calldata, - ICLPoolManager.ModifyLiquidityParams calldata, - bytes calldata _hookData - ) external override returns (bytes4) { - hookData = _hookData; - - return this.beforeAddLiquidity.selector; - } -} diff --git a/test/pool-cl/mocks/ReentrantToken.sol b/test/pool-cl/mocks/ReentrantToken.sol index b68e4d4..8d11754 100644 --- a/test/pool-cl/mocks/ReentrantToken.sol +++ b/test/pool-cl/mocks/ReentrantToken.sol @@ -11,7 +11,17 @@ contract ReentrantToken is MockERC20 { posm = _posm; } - function transferFrom(address, /*from*/ address, /*to*/ uint256 /*amount*/ ) public override returns (bool) { + function transferFrom( + address, + /*from*/ + address, + /*to*/ + uint256 /*amount*/ + ) + public + override + returns (bool) + { // we dont need data because itll revert before it does anything posm.modifyLiquidities("", type(uint256).max); return true; diff --git a/test/pool-cl/position-managers/CLPositionManager.gas.t.sol b/test/pool-cl/position-managers/CLPositionManager.gas.t.sol index 3f19ff0..fad1bc5 100644 --- a/test/pool-cl/position-managers/CLPositionManager.gas.t.sol +++ b/test/pool-cl/position-managers/CLPositionManager.gas.t.sol @@ -87,31 +87,40 @@ contract CLPositionManagerGasTest is Test, PosmTestSetup { } function test_gas_mint_withClose() public { - Plan memory planner = Planner.init().add( - Actions.CL_MINT_POSITION, - abi.encode( - key, - -300, - 300, - 10_000 ether, - MAX_SLIPPAGE_INCREASE, - MAX_SLIPPAGE_INCREASE, - ActionConstants.MSG_SENDER, - ZERO_BYTES - ) - ); + Plan memory planner = Planner.init() + .add( + Actions.CL_MINT_POSITION, + abi.encode( + key, + -300, + 300, + 10_000 ether, + MAX_SLIPPAGE_INCREASE, + MAX_SLIPPAGE_INCREASE, + ActionConstants.MSG_SENDER, + ZERO_BYTES + ) + ); bytes memory calls = planner.finalizeModifyLiquidityWithClose(key); lpm.modifyLiquidities(calls, _deadline); vm.snapshotGasLastCall("test_gas_mint_withClose"); } function test_gas_mint_withSettlePair() public { - Plan memory planner = Planner.init().add( - Actions.CL_MINT_POSITION, - abi.encode( - key, -300, 300, 10_000 ether, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, address(this), ZERO_BYTES - ) - ); + Plan memory planner = Planner.init() + .add( + Actions.CL_MINT_POSITION, + abi.encode( + key, + -300, + 300, + 10_000 ether, + MAX_SLIPPAGE_INCREASE, + MAX_SLIPPAGE_INCREASE, + address(this), + ZERO_BYTES + ) + ); bytes memory calls = planner.finalizeModifyLiquidityWithSettlePair(key); lpm.modifyLiquidities(calls, _deadline); vm.snapshotGasLastCall("test_gas_mint_withSettlePair"); @@ -123,19 +132,20 @@ contract CLPositionManagerGasTest is Test, PosmTestSetup { mint(key, 0, 60, 10_000 ether, address(bob), ZERO_BYTES); vm.stopPrank(); // Mint to a diff config, diff user. - Plan memory planner = Planner.init().add( - Actions.CL_MINT_POSITION, - abi.encode( - key, - -300, - 300, - 10_000 ether, - MAX_SLIPPAGE_INCREASE, - MAX_SLIPPAGE_INCREASE, - ActionConstants.MSG_SENDER, - ZERO_BYTES - ) - ); + Plan memory planner = Planner.init() + .add( + Actions.CL_MINT_POSITION, + abi.encode( + key, + -300, + 300, + 10_000 ether, + MAX_SLIPPAGE_INCREASE, + MAX_SLIPPAGE_INCREASE, + ActionConstants.MSG_SENDER, + ZERO_BYTES + ) + ); bytes memory calls = planner.finalizeModifyLiquidityWithClose(key); vm.prank(alice); lpm.modifyLiquidities(calls, _deadline); @@ -148,19 +158,20 @@ contract CLPositionManagerGasTest is Test, PosmTestSetup { mint(key, -300, -60, 10_000 ether, address(bob), ZERO_BYTES); vm.stopPrank(); // Mint to a diff config, diff user. - Plan memory planner = Planner.init().add( - Actions.CL_MINT_POSITION, - abi.encode( - key, - -300, - 300, - 10_000 ether, - MAX_SLIPPAGE_INCREASE, - MAX_SLIPPAGE_INCREASE, - ActionConstants.MSG_SENDER, - ZERO_BYTES - ) - ); + Plan memory planner = Planner.init() + .add( + Actions.CL_MINT_POSITION, + abi.encode( + key, + -300, + 300, + 10_000 ether, + MAX_SLIPPAGE_INCREASE, + MAX_SLIPPAGE_INCREASE, + ActionConstants.MSG_SENDER, + ZERO_BYTES + ) + ); bytes memory calls = planner.finalizeModifyLiquidityWithClose(key); vm.prank(alice); lpm.modifyLiquidities(calls, _deadline); @@ -173,19 +184,20 @@ contract CLPositionManagerGasTest is Test, PosmTestSetup { mint(key, 60, 300, 10_000 ether, address(bob), ZERO_BYTES); vm.stopPrank(); // Mint to a diff config, diff user. - Plan memory planner = Planner.init().add( - Actions.CL_MINT_POSITION, - abi.encode( - key, - -300, - 300, - 10_000 ether, - MAX_SLIPPAGE_INCREASE, - MAX_SLIPPAGE_INCREASE, - ActionConstants.MSG_SENDER, - ZERO_BYTES - ) - ); + Plan memory planner = Planner.init() + .add( + Actions.CL_MINT_POSITION, + abi.encode( + key, + -300, + 300, + 10_000 ether, + MAX_SLIPPAGE_INCREASE, + MAX_SLIPPAGE_INCREASE, + ActionConstants.MSG_SENDER, + ZERO_BYTES + ) + ); bytes memory calls = planner.finalizeModifyLiquidityWithClose(key); vm.prank(alice); lpm.modifyLiquidities(calls, _deadline); @@ -196,10 +208,11 @@ contract CLPositionManagerGasTest is Test, PosmTestSetup { uint256 tokenId = lpm.nextTokenId(); mint(key, -300, 300, 10_000 ether, ActionConstants.MSG_SENDER, ZERO_BYTES); - Plan memory planner = Planner.init().add( - Actions.CL_INCREASE_LIQUIDITY, - abi.encode(tokenId, 10_000 ether, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, ZERO_BYTES) - ); + Plan memory planner = Planner.init() + .add( + Actions.CL_INCREASE_LIQUIDITY, + abi.encode(tokenId, 10_000 ether, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, ZERO_BYTES) + ); bytes memory calls = planner.finalizeModifyLiquidityWithClose(key); lpm.modifyLiquidities(calls, _deadline); @@ -210,10 +223,11 @@ contract CLPositionManagerGasTest is Test, PosmTestSetup { uint256 tokenId = lpm.nextTokenId(); mint(key, -300, 300, 10_000 ether, address(this), ZERO_BYTES); - Plan memory planner = Planner.init().add( - Actions.CL_INCREASE_LIQUIDITY, - abi.encode(tokenId, 10_000 ether, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, ZERO_BYTES) - ); + Plan memory planner = Planner.init() + .add( + Actions.CL_INCREASE_LIQUIDITY, + abi.encode(tokenId, 10_000 ether, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, ZERO_BYTES) + ); bytes memory calls = planner.finalizeModifyLiquidityWithSettlePair(key); lpm.modifyLiquidities(calls, _deadline); @@ -254,10 +268,11 @@ contract CLPositionManagerGasTest is Test, PosmTestSetup { tokensOwedAlice ); - Plan memory planner = Planner.init().add( - Actions.CL_INCREASE_LIQUIDITY, - abi.encode(tokenIdAlice, liquidityDelta, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, ZERO_BYTES) - ); + Plan memory planner = Planner.init() + .add( + Actions.CL_INCREASE_LIQUIDITY, + abi.encode(tokenIdAlice, liquidityDelta, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, ZERO_BYTES) + ); // because its a perfect autocompound, the delta is exactly 0 and we dont need to "close" deltas bytes memory calls = planner.encode(); @@ -349,10 +364,11 @@ contract CLPositionManagerGasTest is Test, PosmTestSetup { halfTokensOwedAlice ); - Plan memory planner = Planner.init().add( - Actions.CL_INCREASE_LIQUIDITY, - abi.encode(tokenIdAlice, liquidityDelta, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, ZERO_BYTES) - ); + Plan memory planner = Planner.init() + .add( + Actions.CL_INCREASE_LIQUIDITY, + abi.encode(tokenIdAlice, liquidityDelta, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, ZERO_BYTES) + ); bytes memory calls = planner.finalizeModifyLiquidityWithClose(key); @@ -365,10 +381,11 @@ contract CLPositionManagerGasTest is Test, PosmTestSetup { uint256 tokenId = lpm.nextTokenId(); mint(key, -300, 300, 10_000 ether, ActionConstants.MSG_SENDER, ZERO_BYTES); - Plan memory planner = Planner.init().add( - Actions.CL_DECREASE_LIQUIDITY, - abi.encode(tokenId, 10_000 ether, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) - ); + Plan memory planner = Planner.init() + .add( + Actions.CL_DECREASE_LIQUIDITY, + abi.encode(tokenId, 10_000 ether, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) + ); bytes memory calls = planner.finalizeModifyLiquidityWithClose(key); lpm.modifyLiquidities(calls, _deadline); @@ -379,10 +396,11 @@ contract CLPositionManagerGasTest is Test, PosmTestSetup { uint256 tokenId = lpm.nextTokenId(); mint(key, -300, 300, 10_000 ether, ActionConstants.MSG_SENDER, ZERO_BYTES); - Plan memory planner = Planner.init().add( - Actions.CL_DECREASE_LIQUIDITY, - abi.encode(tokenId, 10_000 ether, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) - ); + Plan memory planner = Planner.init() + .add( + Actions.CL_DECREASE_LIQUIDITY, + abi.encode(tokenId, 10_000 ether, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) + ); bytes memory calls = planner.finalizeModifyLiquidityWithTakePair(key, address(this)); lpm.modifyLiquidities(calls, _deadline); @@ -433,10 +451,11 @@ contract CLPositionManagerGasTest is Test, PosmTestSetup { router.donate(key, 0.2e18, 0.2e18, ZERO_BYTES); // Collect by calling decrease with 0. - Plan memory planner = Planner.init().add( - Actions.CL_DECREASE_LIQUIDITY, - abi.encode(tokenId, 0, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) - ); + Plan memory planner = Planner.init() + .add( + Actions.CL_DECREASE_LIQUIDITY, + abi.encode(tokenId, 0, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) + ); bytes memory calls = planner.finalizeModifyLiquidityWithClose(key); lpm.modifyLiquidities(calls, _deadline); @@ -451,10 +470,11 @@ contract CLPositionManagerGasTest is Test, PosmTestSetup { router.donate(key, 0.2e18, 0.2e18, ZERO_BYTES); // Collect by calling decrease with 0. - Plan memory planner = Planner.init().add( - Actions.CL_DECREASE_LIQUIDITY, - abi.encode(tokenId, 0, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) - ); + Plan memory planner = Planner.init() + .add( + Actions.CL_DECREASE_LIQUIDITY, + abi.encode(tokenId, 0, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) + ); bytes memory calls = planner.finalizeModifyLiquidityWithTakePair(key, address(this)); lpm.modifyLiquidities(calls, _deadline); @@ -465,19 +485,20 @@ contract CLPositionManagerGasTest is Test, PosmTestSetup { function test_gas_sameRange_mint() public { mint(key, -300, 300, 10_000 ether, ActionConstants.MSG_SENDER, ZERO_BYTES); - Plan memory planner = Planner.init().add( - Actions.CL_MINT_POSITION, - abi.encode( - key, - -300, - 300, - 10_001 ether, - MAX_SLIPPAGE_INCREASE, - MAX_SLIPPAGE_INCREASE, - ActionConstants.MSG_SENDER, - ZERO_BYTES - ) - ); + Plan memory planner = Planner.init() + .add( + Actions.CL_MINT_POSITION, + abi.encode( + key, + -300, + 300, + 10_001 ether, + MAX_SLIPPAGE_INCREASE, + MAX_SLIPPAGE_INCREASE, + ActionConstants.MSG_SENDER, + ZERO_BYTES + ) + ); bytes memory calls = planner.finalizeModifyLiquidityWithClose(key); vm.prank(alice); lpm.modifyLiquidities(calls, _deadline); @@ -493,10 +514,11 @@ contract CLPositionManagerGasTest is Test, PosmTestSetup { uint256 tokenId = lpm.nextTokenId(); mint(key, -300, 300, 10_000 ether, ActionConstants.MSG_SENDER, ZERO_BYTES); - Plan memory planner = Planner.init().add( - Actions.CL_DECREASE_LIQUIDITY, - abi.encode(tokenId, 10_000 ether, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) - ); + Plan memory planner = Planner.init() + .add( + Actions.CL_DECREASE_LIQUIDITY, + abi.encode(tokenId, 10_000 ether, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) + ); bytes memory calls = planner.finalizeModifyLiquidityWithClose(key); lpm.modifyLiquidities(calls, _deadline); @@ -515,10 +537,11 @@ contract CLPositionManagerGasTest is Test, PosmTestSetup { // donate to create fee revenue router.donate(key, 0.2e18, 0.2e18, ZERO_BYTES); - Plan memory planner = Planner.init().add( - Actions.CL_DECREASE_LIQUIDITY, - abi.encode(tokenId, 0, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) - ); + Plan memory planner = Planner.init() + .add( + Actions.CL_DECREASE_LIQUIDITY, + abi.encode(tokenId, 0, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) + ); bytes memory calls = planner.finalizeModifyLiquidityWithClose(key); lpm.modifyLiquidities(calls, _deadline); @@ -529,9 +552,10 @@ contract CLPositionManagerGasTest is Test, PosmTestSetup { uint256 tokenId = lpm.nextTokenId(); mint(key, -300, 300, 10_000 ether, ActionConstants.MSG_SENDER, ZERO_BYTES); - Plan memory planner = Planner.init().add( - Actions.CL_BURN_POSITION, abi.encode(tokenId, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) - ); + Plan memory planner = Planner.init() + .add( + Actions.CL_BURN_POSITION, abi.encode(tokenId, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) + ); bytes memory calls = planner.finalizeModifyLiquidityWithClose(key); lpm.modifyLiquidities(calls, _deadline); @@ -542,9 +566,10 @@ contract CLPositionManagerGasTest is Test, PosmTestSetup { uint256 tokenId = lpm.nextTokenId(); mint(key, -300, 300, 10_000 ether, ActionConstants.MSG_SENDER, ZERO_BYTES); - Plan memory planner = Planner.init().add( - Actions.CL_BURN_POSITION, abi.encode(tokenId, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) - ); + Plan memory planner = Planner.init() + .add( + Actions.CL_BURN_POSITION, abi.encode(tokenId, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) + ); bytes memory calls = planner.finalizeModifyLiquidityWithTakePair(key, address(this)); lpm.modifyLiquidities(calls, _deadline); @@ -556,9 +581,10 @@ contract CLPositionManagerGasTest is Test, PosmTestSetup { mint(key, -300, 300, 10_000 ether, ActionConstants.MSG_SENDER, ZERO_BYTES); decreaseLiquidity(tokenId, 10_000 ether, ZERO_BYTES); - Plan memory planner = Planner.init().add( - Actions.CL_BURN_POSITION, abi.encode(tokenId, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) - ); + Plan memory planner = Planner.init() + .add( + Actions.CL_BURN_POSITION, abi.encode(tokenId, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) + ); // There is no need to include CLOSE commands. bytes memory calls = planner.encode(); @@ -572,10 +598,11 @@ contract CLPositionManagerGasTest is Test, PosmTestSetup { uint256 tokenId = lpm.nextTokenId(); mint(key, -300, 300, 10_000 ether, ActionConstants.MSG_SENDER, ZERO_BYTES); - Plan memory planner = Planner.init().add( - Actions.CL_DECREASE_LIQUIDITY, - abi.encode(tokenId, 10_000 ether, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) - ); + Plan memory planner = Planner.init() + .add( + Actions.CL_DECREASE_LIQUIDITY, + abi.encode(tokenId, 10_000 ether, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) + ); planner.add( Actions.CL_BURN_POSITION, abi.encode(tokenId, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) ); @@ -701,9 +728,10 @@ contract CLPositionManagerGasTest is Test, PosmTestSetup { uint256 tokenId = lpm.nextTokenId(); mintWithNative(SQRT_RATIO_1_1, nativeKey, -300, 300, 10_000 ether, ActionConstants.MSG_SENDER, ZERO_BYTES); - Plan memory planner = Planner.init().add( - Actions.CL_BURN_POSITION, abi.encode(tokenId, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) - ); + Plan memory planner = Planner.init() + .add( + Actions.CL_BURN_POSITION, abi.encode(tokenId, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) + ); bytes memory calls = planner.finalizeModifyLiquidityWithClose(nativeKey); lpm.modifyLiquidities(calls, _deadline); @@ -714,9 +742,10 @@ contract CLPositionManagerGasTest is Test, PosmTestSetup { uint256 tokenId = lpm.nextTokenId(); mintWithNative(SQRT_RATIO_1_1, nativeKey, -300, 300, 10_000 ether, ActionConstants.MSG_SENDER, ZERO_BYTES); - Plan memory planner = Planner.init().add( - Actions.CL_BURN_POSITION, abi.encode(tokenId, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) - ); + Plan memory planner = Planner.init() + .add( + Actions.CL_BURN_POSITION, abi.encode(tokenId, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) + ); bytes memory calls = planner.finalizeModifyLiquidityWithTakePair(nativeKey, address(this)); lpm.modifyLiquidities(calls, _deadline); @@ -728,9 +757,10 @@ contract CLPositionManagerGasTest is Test, PosmTestSetup { mintWithNative(SQRT_RATIO_1_1, nativeKey, -300, 300, 10_000 ether, ActionConstants.MSG_SENDER, ZERO_BYTES); decreaseLiquidity(tokenId, 10_000 ether, ZERO_BYTES); - Plan memory planner = Planner.init().add( - Actions.CL_BURN_POSITION, abi.encode(tokenId, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) - ); + Plan memory planner = Planner.init() + .add( + Actions.CL_BURN_POSITION, abi.encode(tokenId, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) + ); // There is no need to include CLOSE commands. bytes memory calls = planner.encode(); @@ -744,10 +774,11 @@ contract CLPositionManagerGasTest is Test, PosmTestSetup { uint256 tokenId = lpm.nextTokenId(); mintWithNative(SQRT_RATIO_1_1, nativeKey, -300, 300, 10_000 ether, ActionConstants.MSG_SENDER, ZERO_BYTES); - Plan memory planner = Planner.init().add( - Actions.CL_DECREASE_LIQUIDITY, - abi.encode(tokenId, 10_000 ether, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) - ); + Plan memory planner = Planner.init() + .add( + Actions.CL_DECREASE_LIQUIDITY, + abi.encode(tokenId, 10_000 ether, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) + ); planner.add(Actions.CL_BURN_POSITION, abi.encode(tokenId, 0 wei, 0 wei, ZERO_BYTES)); // We must include CLOSE commands. diff --git a/test/pool-cl/position-managers/CLPositionManager.modifyLiquidity.t.sol b/test/pool-cl/position-managers/CLPositionManager.modifyLiquidity.t.sol index 8cc62c5..79ed494 100644 --- a/test/pool-cl/position-managers/CLPositionManager.modifyLiquidity.t.sol +++ b/test/pool-cl/position-managers/CLPositionManager.modifyLiquidity.t.sol @@ -129,9 +129,7 @@ contract CLPositionManagerModifyLiquiditiesTest is Test, PosmTestSetup, Liquidit // seedWeth(address(this)); wethConfig = PositionConfig({ - poolKey: wethKey, - tickLower: TickMath.minUsableTick(60), - tickUpper: TickMath.maxUsableTick(60) + poolKey: wethKey, tickLower: TickMath.minUsableTick(60), tickUpper: TickMath.maxUsableTick(60) }); nativeConfig = PositionConfig({poolKey: nativeKey, tickLower: -120, tickUpper: 120}); fotConfig = PositionConfig({poolKey: fotKey, tickLower: -120, tickUpper: 120}); diff --git a/test/pool-cl/shared/FeeMath.sol b/test/pool-cl/shared/FeeMath.sol index 0e85624..5e4cada 100644 --- a/test/pool-cl/shared/FeeMath.sol +++ b/test/pool-cl/shared/FeeMath.sol @@ -58,8 +58,8 @@ library FeeMath { pure returns (uint128 tokenOwed) { - tokenOwed = - (FullMath.mulDiv(feeGrowthInsideX128 - feeGrowthInsideLastX128, liquidity, FixedPoint128.Q128)).toUint128(); + tokenOwed = (FullMath.mulDiv(feeGrowthInsideX128 - feeGrowthInsideLastX128, liquidity, FixedPoint128.Q128)) + .toUint128(); } // TODO: should we consider migrating this into core repo ? diff --git a/test/pool-cl/shared/HookModifyLiquidities.sol b/test/pool-cl/shared/HookModifyLiquidities.sol index ae2057a..ece102d 100644 --- a/test/pool-cl/shared/HookModifyLiquidities.sol +++ b/test/pool-cl/shared/HookModifyLiquidities.sol @@ -30,7 +30,11 @@ contract HookModifyLiquidities is HookSavesDelta { PoolKey calldata key, /* key **/ ICLPoolManager.SwapParams calldata, /* params **/ bytes calldata hookData - ) external override returns (bytes4, BeforeSwapDelta, uint24) { + ) + external + override + returns (bytes4, BeforeSwapDelta, uint24) + { approvePosmCurrency(key.currency0); approvePosmCurrency(key.currency1); diff --git a/test/pool-cl/shared/fuzz/LiquidityFuzzers.sol b/test/pool-cl/shared/fuzz/LiquidityFuzzers.sol index 8959ca5..2d02975 100644 --- a/test/pool-cl/shared/fuzz/LiquidityFuzzers.sol +++ b/test/pool-cl/shared/fuzz/LiquidityFuzzers.sol @@ -27,19 +27,20 @@ contract LiquidityFuzzers is Fuzzers { params = Fuzzers.createFuzzyLiquidityParams(key, params, sqrtPriceX96); uint128 MAX_SLIPPAGE_INCREASE = type(uint128).max; - Plan memory planner = Planner.init().add( - Actions.CL_MINT_POSITION, - abi.encode( - key, - params.tickLower, - params.tickUpper, - uint256(params.liquidityDelta), - MAX_SLIPPAGE_INCREASE, - MAX_SLIPPAGE_INCREASE, - recipient, - hookData - ) - ); + Plan memory planner = Planner.init() + .add( + Actions.CL_MINT_POSITION, + abi.encode( + key, + params.tickLower, + params.tickUpper, + uint256(params.liquidityDelta), + MAX_SLIPPAGE_INCREASE, + MAX_SLIPPAGE_INCREASE, + recipient, + hookData + ) + ); uint256 tokenId = lpm.nextTokenId(); bytes memory calls = planner.finalizeModifyLiquidityWithClose(key); @@ -81,19 +82,20 @@ contract LiquidityFuzzers is Fuzzers { params = createFuzzyTwoSidedLiquidityParams(key, params, sqrtPriceX96); uint128 MAX_SLIPPAGE_INCREASE = type(uint128).max; - Plan memory planner = Planner.init().add( - Actions.CL_MINT_POSITION, - abi.encode( - key, - params.tickLower, - params.tickUpper, - uint256(params.liquidityDelta), - MAX_SLIPPAGE_INCREASE, - MAX_SLIPPAGE_INCREASE, - recipient, - hookData - ) - ); + Plan memory planner = Planner.init() + .add( + Actions.CL_MINT_POSITION, + abi.encode( + key, + params.tickLower, + params.tickUpper, + uint256(params.liquidityDelta), + MAX_SLIPPAGE_INCREASE, + MAX_SLIPPAGE_INCREASE, + recipient, + hookData + ) + ); uint256 tokenId = lpm.nextTokenId(); bytes memory calls = planner.finalizeModifyLiquidityWithClose(key); diff --git a/test/shared/Permit2SignatureHelpers.sol b/test/shared/Permit2SignatureHelpers.sol index 1f5d075..a96bb26 100644 --- a/test/shared/Permit2SignatureHelpers.sol +++ b/test/shared/Permit2SignatureHelpers.sol @@ -105,11 +105,10 @@ contract Permit2SignatureHelpers { { IAllowanceTransfer.PermitDetails memory details = IAllowanceTransfer.PermitDetails({token: token0, amount: amount, expiration: expiration, nonce: nonce}); - return IAllowanceTransfer.PermitSingle({ - details: details, - spender: address(this), - sigDeadline: block.timestamp + 100 - }); + return + IAllowanceTransfer.PermitSingle({ + details: details, spender: address(this), sigDeadline: block.timestamp + 100 + }); } function defaultERC20PermitBatchAllowance(address[] memory tokens, uint160 amount, uint48 expiration, uint48 nonce) @@ -121,17 +120,13 @@ contract Permit2SignatureHelpers { for (uint256 i = 0; i < tokens.length; ++i) { details[i] = IAllowanceTransfer.PermitDetails({ - token: tokens[i], - amount: amount, - expiration: expiration, - nonce: nonce + token: tokens[i], amount: amount, expiration: expiration, nonce: nonce }); } - return IAllowanceTransfer.PermitBatch({ - details: details, - spender: address(this), - sigDeadline: block.timestamp + 100 - }); + return + IAllowanceTransfer.PermitBatch({ + details: details, spender: address(this), sigDeadline: block.timestamp + 100 + }); } }