Malicious user can claim any amount of ``lpeth`` and stake that amount without depositing equivalent amount of ETH/WETH or other LRT's in a specific case.
Audit: 2024-05-loop-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-05-loop/blob/40167e469edde09969643b6808c57e25d1b9c203/src/PrelaunchPoints.sol#L240


# Vulnerability details

## Impact
After you lock some amount of ETH/WETH or LRTs for certain ``LoopActivation`` period(max 120days), 7 days TIMELOCK and up to ``startClaimDate``(can be months, years), you are finally allowed to ``claim`` equivalent amount of ``lpEth`` using the ``_claim()`` function.

```solidity
    function _claim(address _token, address _receiver, uint8 _percentage, Exchange _exchange, bytes calldata _data)
        internal
        returns (uint256 claimedAmount)
    {
        uint256 userStake = balances[msg.sender][_token];
        if (userStake == 0) {
            revert NothingToClaim();
        }
        if (_token == ETH) {
            claimedAmount = userStake.mulDiv(totalLpETH, totalSupply);
            balances[msg.sender][_token] = 0;
            lpETH.safeTransfer(_receiver, claimedAmount);
        } else {
            uint256 userClaim = userStake * _percentage / 100;
            _validateData(_token, userClaim, _exchange, _data);
            balances[msg.sender][_token] = userStake - userClaim;

            // At this point there should not be any ETH in the contract
            // Swap token to ETH
            _fillQuote(IERC20(_token), userClaim, _data);

            // Convert swapped ETH to lpETH (1 to 1 conversion)
            claimedAmount = address(this).balance;
            lpETH.deposit{value: claimedAmount}(_receiver);
        }
        emit Claimed(msg.sender, _token, claimedAmount);
    }
```
Now, if you stake ``ETH/WETH``, you are directly transferred ``lpETH`` in 1 to 1 conversion ratio. But for LRTs, first the LRT token needs to be swapped into ``ETH``. It is done with ``_fillQuote()`` function. Before ``_fillQuote()`` function is executed inside ``_claim()`` function, It is assumed that there should not be any ETH in the contract. It is because the amount of ``lpETH`` minted for receiver is equal to amount of ``ETH`` this contract receives after ``_fillQuote()`` function is executed.
```solidity
            // At this point there should not be any ETH in the contract
            // Swap token to ETH
            _fillQuote(IERC20(_token), userClaim, _data);

            // Convert swapped ETH to lpETH (1 to 1 conversion)
            claimedAmount = address(this).balance;
            lpETH.deposit{value: claimedAmount}(_receiver);
```

Now, Consider this scenario:
1. Alice is a malicious user who locks 0.001(any minimum) amount of any LRT token.
2. Now, After some time ``startClaimDate`` is reached and ``_claim()`` function can now be called.
3. Alice then deposits any amount of ETH(lets assume 100ETH) into the contract and calls the ``_claim()`` function.
4. Inside the ``_claim()`` function, ``_fillQuote()`` function executes and ``0.001`` LRT locked is converted to ``0.001`` ETH(lets assume).
5. Now, instead of Alice getting ``0.001`` lpETH, she will get ``100.00