The function `L2EthToken.withdraw` should allow users to specify the withdrawal amount as a parameter, rather than relying solely on the default `msg.value`.
Audit: 2023-10-zksync-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-03-zksync/blob/main/contracts/L2EthToken.sol#L80-L94


# Vulnerability details

## Impact
The `L2EthToken` contract's `withdraw` function currently relies on the `msg.value` sent by the account initiating the withdrawal to process ETH transfers to L1. It subsequently handles burning the corresponding amount of ETH from the contract's balance and total supply. The function can be improved by allowing users to specify the withdrawal amount as a parameter, making it more transparent and efficient. This revised version of the function will check if the sender has a sufficient balance, and if so, it will deduct the requested amount from the sender's balance without the need for intermediate transfers to the contract. This not only improves clarity but also eliminates the unnecessary ETH transfer to the contract before burning.

In essence, this change simplifies the function and avoids a redundant ETH transfer step, enhancing the overall efficiency and readability of the code.

## Proof of Concept
The existing `withdraw` function in the `L2EthToken` contract relies on the `msg.value` sent by the user initiating the withdrawal to transfer ETH to L1. When this function is called, it activates the MsgValueSimulator, which facilitates the transfer of ETH to the `L2EthToken` contract. Once the ETH is received by the contract, it proceeds to burn an equivalent amount of ETH from the contract's balance and subtracts it from the total supply of ETH tokens.

```
       function withdraw(address _l1Receiver) external payable override {
        uint256 amount = msg.value;

        // Silent burning of the ether
        unchecked {
            balance[address(this)] -= amount;
            totalSupply -= amount;
        }

        // Send the L2 log, a user could use it as proof of the withdrawal
        bytes memory message = _getL1WithdrawMessage(_l1Receiver, amount);
        L1_MESSENGER_CONTRACT.sendToL1(message);

        emit Withdrawal(msg.sender, _l1Receiver, amount);
    }
```
This process essentially ensures that users who wish to withdraw ETH to L1 must send the required amount using `msg.value`, and the contract then handles the internal mechanics of processing and burning the ETH accordingly.
```
 function withdraw(address _l1Receiver) external payable override {
        uint256 amount = msg.value;

        // Silent burning of the ether
        unchecked {
            balance[address(this)] -= amount;
            totalSupply -= amount;
        }
        ... // irrelevant code snipped
    }
```

## Tools Used

## Recommended Mitigation Steps
The function can be improved by allowing users to specify the withdrawal amount as a parameter, making it more transparent and efficient. This revised version of the function will check if the sender has a sufficient balance, and if so, it will deduct the requested amount from the sender's balance without the need for intermediate transfers to the contract. This