HIGH severity: Team Finance — In `migrate` function, the validation on `MigrateParams` is the function's input value.. https://twitter.com/TeamFinance_/status/1585770918873542656 https://twitter.com/peckshield/status/1585587858978623491 https://twitter.com/solid_group_1/status/1585643249305518083 https://twitter.com/BeosinAlert/status/1585578499125178369

Vulnerable code:
/**
     * migrate liquidity from v2 to v3
     */
    function migrate(
        uint256 _id,
        IV3Migrator.MigrateParams calldata params,
        bool noLiquidity,
        uint160 sqrtPriceX96,
        bool _mintNFT
    )
    external
    payable
    whenNotPaused
    nonReentrant
    {
        require(address(nonfungiblePositionManager) != address(0), "NFT manager not set");
        require(address(v3Migrator) != address(0), "v3 migrator not set");
        Items memory lockedERC20 = lockedToken[_id];
        require(block.timestamp < lockedERC20.unlockTime, "Unlock time already reached");
        require(_msgSender() == lockedERC20.withdrawalAddress, "Unauthorised sender");
        require(!lockedERC20.withdrawn, "Already withdrawn");

        uint256 totalSupplyBeforeMigrate = nonfungiblePositionManager.totalSupply();
        
        // scope for solving stack too deep error
        {
            uint256 ethBalanceBefore = address(this).balance;
            uint256 token0BalanceBefore = IERC20(params.token0).balanceOf(address(this));
            uint256 token1BalanceBefore = IERC20(params.token1).balanceOf(address(this));
            
            // initialize the pool if not yet initialized
            if(noLiquidity) {
                v3Migrator.createAndInitializePoolIfNecessary(params.token0, params.token1, params.fee, sqrtPriceX96);
            }

            IERC20(params.pair).approve(address(v3Migrator), params.liquidityToMigrate);

            v3Migrator.migrate(params);

            // refund eth or tokens
            uint256 refundEth = address(this).balance - ethBalanceBefore;
            (bool refundSuccess,) = _msgSender().call{value: refundEth}(""");
            require(refundSuccess, 'Refund ETH failed');

            uint256 token0BalanceAfter = IERC20(params.token0).balanceOf(address(this));
            uint256 refundToken0 = token0BalanceAfter - token0BalanceBefore;
            if(refundToken0 > 0) {
                require(IERC20(params.token0).transfer(_msgSender(), refundToken0));
            }

            uint256 token1BalanceAfter = IERC20(params.token1).balanceOf(address(this));
            uint256 refundToken1 = token1BalanceAfter - token1BalanceBefore;
            if(refundToken1 > 0) {
                require(IERC20(params.token1).transfer(_msgSender(), refundToken1));
            }
        }

        // remove old locked token details
        _removeERC20Deposit(_id);

        // Get the token id of newly generated nft for v3
        uint256 totalSupplyAfterMigrate = nonfungiblePositionManager.totalSupply();
        require(totalSupplyAfterMigrate == totalSupplyBeforeMigrate.add(1));
        uint256 tokenIndex = totalSupplyAfterMigrate.sub(1);
        uint256 tokenId = nonfungiblePositionManager.tokenByIndex(tokenIndex);

        // add new locked nft details (received after migrating liquidity)
        uint256 newDepositId = ++depositId;
        _addNFTDeposit(_id, newDepositId, tokenId);

        listMigratedDepositIds[newDepositId] = _id;

        if (_mintNFT) {
            require(NFT != address(0), 'NFT: Uninitialized');
            nftMinted[newDepositId] = true;
        }

        if (nftMinted[_id]) {
            nftMinted[_id] = false;
            IERC721Extended(NFT).burn(_id);
        }
        if (_mintNFT) {
            IERC721Extended(NFT).mintLiquidityLockNFT(_msgSender(), newDepositId);
        }
        
        emit LiquidityMigrated(_msgSender(), _id, newDepositId, tokenId);
    }

Vulnerability type: Insufficient validation