xanoutas/solidity-security-findings huggingface dataset:
In the fallback function, the success value of the `transferFromTo` function is not checked, which could result in the loss of funds.
Audit: 2023-03-zksync-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/MsgValueSimulator.sol#L36-L38
https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/MsgValueSimulator.sol#L35-L46


# Vulnerability details

## Impact
The flaw enables an attacker to deprive loss of funds for the contract caller.

## Description
The success value of a called function is not checked at two places in the fallback function. When sending money to the destination address in the first case, the success value of the `transferFromTo` function is not checked.
In the second case, the fallback method does not deal with the error and may result in the caller losing money if the `mimicCall` function returns an error.

## Proof of Concept

### Affected Code
The vulnerable code is in the fallback function of the `MsgValueSimulator` contract, specifically the failure to check the success value of the `transferFromTo` function and the lack of handling of errors from the `mimicCall` function.
https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/MsgValueSimulator.sol#L35-L46

In the fallback function, the success value of the `transferFromTo` function is not checked, which could result in the loss of funds. If the transfer fails, the code should handle the error and revert the transaction to prevent the loss of funds.

The `MsgValueSimulator` contract is responsible for simulating transactions with `msg.value` inside `zkEVM`. However, the contract is vulnerable to the failure of checking the return value of the `transferFromTo` function in the fallback function and the `mimicCall` function.

The following proof of concept will demonstrate how an attacker can exploit this vulnerability to steal funds from the contract.

1. Attacker Deploy a malicious contract that contains a fallback function which reverts the transaction.
```solidity
contract MaliciousContract {
    fallback() external payable {
        revert();
    }
}
```

```
[vm]from: 0x5B3...eddC4to: MaliciousContract.(constructor)value: 0 weidata: 0x608...20033logs: 0hash: 0xfbc...3d0fa
```

2. Send some ETH to the `MsgValueSimulator` contract using the following code:
```solidity
// Simulate a call to the MsgValueSimulator contract
// with value 1 ETH and a non-system call to the MaliciousContract
// The first ABI param contains the value in the [0..127] bits. The 128th contains
// the flag whether or not the call should be a system one.
// The second ABI params contains the callee.
bytes memory abiParams = abi.encodePacked(bytes16(1 ether), uint160(address(new MaliciousContract())), uint256(0));

// Send the ABI params to the MsgValueSimulator contract
(bool success, ) = address(msgValueSimulatorAddress).call{value: 1 ether}(abiParams);
```

3. The `transferFromTo` function in the `MsgValueSimulator` contract will fail because the `MaliciousContract` reverts the transaction. Howev
Overflow/Underflow of Integers vulnerability in the `msg.value` simulation of the zkEVM.
Audit: 2023-03-zksync-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/MsgValueSimulator.sol#L29


# Vulnerability details

## Impact
The vulnerability enables an attacker to simulate transactions with `msg.value` inside the "zkEVM" and produce an integer overflow or underflow, and this will lead to a loss of funds or a contract attack and possible DoS attack on the contract.

## Proof of Concept
The first "extraAbi" parameter's "[0..127]" bits contain a value that the `MsgValueSimulator` contract will accept, a "value" variable with the type "uint256" has been given this value. If the value is not zero later, it is passed to the "callee" and the function keeps running, the function performs a raw revert if the value exceeds the maximum message value `((MAX MSG VALUE))`. If the number is too large, an integer underflow might occur if it is allocated to a "uint160" variable that will be used as the callee address.

By sending an integer number that exceeds the maximum message value or by passing a value that is too high and will result in an integer underflow, a malicious user could take advantage of this vulnerability.

An attacker could exploit the flaw by passing a value larger than the maximum message `value of 2^129` the first "extraAbi" parameter, causing the function to revert with a raw reversion.

Alternately, if the attacker passes a value of `2^256` as the first "extraAbi" parameter, an "uint160" variable will experience an integer underflow and an integer overflow.

## Tools Used

Manual Audit.

## Recommended Mitigation Steps
a) Replace `uint160(addressAsUint)` with `address(uint160(addressAsUint))` to prevent integer underflow.

b) Add a check to ensure that the value transferred is not greater than `MAX_MSG_VALUE`.
_encodeLength() might have an undeteced silent overflow error. 
Audit: 2023-03-zksync-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/libraries/RLPEncoder.sol#L55-L74


# Vulnerability details

## Impact
Detailed description of the impact of this finding.
``_encodeLength()`` might have an undeteced silent overflow error. As a result, the returned encoded length is wrong and might cause serious system error in the protocol. 

## Proof of Concept
Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.

``_encodeLength()`` encodes the size of list items in RLP format as follows:

[https://github.co](https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/libraries/RLPEncoder.sol#L55-L74)
m/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/libraries/RLPEncoder.sol#L55-L74

There are two cases: 1) ``_len < 56``, the result will be returned in one byte; 2) ``_len >= 56``, the result will be returned as bytes array. The silence overflow might happen for both cases, we will discuss case 2 first since this is more common and serious.

The silent overflow will occur in the following line: 
```javascript
 encoded[0] = bytes1(uint8(_offset + hbs + 56));
```

For example, given ``len = 512``, ``offset = 199``, ``encoded[0]`` will be equal to ZERO (Remix run shows encoded =   0x000200). It is important to catch this overflow error to avoid the break of the protocol.


For the first case, the silent overflow will occur in the following line: 

```javascript 
encoded[0] = bytes1(uint8(_len + _offset));
```
For example, when ``len =  55``, and ``_offset = 201`` (this case can be considered as an input error, but it is not captured by the function either and overflow happens), the function has an overflow and returns 
zero. 

In both cases, the silent overflow will go undetected. 


## Tools Used
Remix

## Recommended Mitigation Steps
Use safecast to make sure uint256 is safely casted to uint8 without overflow error.
Risks of Calling transferFromTo function with amount value 0 ,Sender may lose gas fee
Audit: 2023-03-zksync-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/L2EthToken.sol#L40-L58


# Vulnerability details

## Impact
The transferFromTo function with a transfer amount of 0, the function will still execute and consume gas, even though no tokens are actually transferred to the recipient's address. This can result in unnecessary gas consumption and transaction fees for the sender.

## Proof of Concept

function transferFromTo(address _from, address _to, uint256 _amount) external override {
        require(
            msg.sender == MSG_VALUE_SYSTEM_CONTRACT ||
                msg.sender == address(DEPLOYER_SYSTEM_CONTRACT) ||
                msg.sender == BOOTLOADER_FORMAL_ADDRESS,
            "Only system contracts with special access can call this method"
        );

        uint256 fromBalance = balance[_from];
        require(fromBalance >= _amount, "Transfer amount exceeds balance");
        unchecked {
            balance[_from] = fromBalance - _amount;
            // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
            // decrementing then incrementing.
            balance[_to] += _amount;
        }

        emit Transfer(_from, _to, _amount);
    }

(https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/L2EthToken.sol#L40-L58)

if we call transferFromTo() function with zero amount still the transferFromTo successful and no tokens were transferred to receiver(_to). This emit the dummy event. There is no check functions when function called with 0 amount.

Even the wrong function call the sender needs to pay gas and transaction cost. So the sender lose his tokens

## Tools Used

Manual Audit

## Recommended Mitigation Steps

Add the require check for amount 

require(_amount> 0, "Zero amount");
Bootloader can refund less gas than user paid for L2 tx
Audit: 2023-03-zksync-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-03-zksync/blob/main/bootloader/bootloader.yul#L1065-L1107
https://github.com/code-423n4/2023-03-zksync/blob/main/bootloader/bootloader.yul#L1116-L1129
https://github.com/code-423n4/2023-03-zksync/blob/main/bootloader/bootloader.yul#L711
https://github.com/code-423n4/2023-03-zksync/blob/main/bootloader/bootloader.yul#L1259-L1278


# Vulnerability details

## Impact
The user specifies a gas limit when they propagate their transaction. When the transaction is validated, the bootloader pulls `gasLimit * gasPrice` ETH from the user. After the transaction was executed it checks how much gas was actually used and refunds the remaining amount. But, the bootloader doesn't necessarily grant the transaction its specified gas limit. If the user is untrusted, the operator might grant it a lower amount. When funds are pulled before execution the user's own gas limit is used while for the refund the bootloader's value is used. Since that can be smaller than the user-specified one, they might receive less funds than they should

## Proof of Concept
Here's the function to process L2 txs. I will step through the functions to explain the issue and omit anything that's not relevant:
```yul
            function processL2Tx(
                txDataOffset,
                resultPtr,
                transactionIndex,
                gasPerPubdata
            ) {
                let innerTxDataOffset := add(txDataOffset, 32)

                // Firsly, we publish all the bytecodes needed. This is needed to be done separately, since
                // bytecodes usually form the bulk of the L2 gas prices.
                
                let gasLimitForTx := getGasLimitForTx(innerTxDataOffset, transactionIndex, gasPerPubdata, L2_TX_INTRINSIC_GAS(), L2_TX_INTRINSIC_PUBDATA())
                let gasPrice := getGasPrice(getMaxFeePerGas(innerTxDataOffset), getMaxPriorityFeePerGas(innerTxDataOffset))

                debugLog("gasLimitForTx", gasLimitForTx)

                let gasLeft := l2TxValidation(
                    txDataOffset,
                    gasLimitForTx,
                    gasPrice
                )

                let gasSpentOnExecute := 0
                let success := 0
                success, gasSpentOnExecute := l2TxExecution(txDataOffset, gasLeft)

                let refund := 0
                let gasToRefund := sub(gasLeft, gasSpentOnExecute)
                if lt(gasLeft, gasSpentOnExecute){
                    gasToRefund := 0
                }

                refund := refundCurrentL2Transaction(
                    txDataOffset,
                    transactionIndex,
                    success,
                    gasToRefund,
                    gasPrice
                )

                notifyAboutRefund(refund)
                mstore(resultPtr, success)
            }
```
First, its gas limit is determined using `getGasLimitForTx()`. While the user defines their own gas limit in their tx,
Sending Tokens with Insufficient Balance to Cover Gas Fees
Audit: 2023-03-zksync-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/L2EthToken.sol#L49


# Vulnerability details

## Impact
The transaction could have failed due to insufficient gas fees or receiver(_to) lose some amount from confirmed amount if transaction successful 

## Proof of Concept

  49: require(fromBalance >= _amount, "Transfer amount exceeds balance");

(https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/L2EthToken.sol#L49)

The condition "fromBalance >= _amount" used in the code snippet may not be sufficient to ensure the security of a token transfer to another address.
When transferring tokens, it's generally advisable to keep a small buffer above the transfer amount in the account balance to cover potential gas fees. This helps to ensure that there is enough balance to cover any gas fees required for the transaction to be processed by the network

Whenever tokens are transferred, the responsibility of paying the gas fee for the transaction lies solely with the sender. Therefore, the sender must include both the transfer amount and the required gas fee in every transaction. Specifically, the sender must send an amount equal to (_amount + gas fee) to ensure that the transaction is processed by the network and the tokens are successfully transferred to the intended recipient

It requires a certain amount of gas to be paid by the transaction sender to compensate the miners for executing the transaction. The gas price is determined by the user and represents the amount of Ether that the user is willing to pay per unit of gas.

## Tools Used

Manual Audit

## Recommended Mitigation Steps

  require(fromBalance > _amount, "Transfer amount exceeds balance");

So the sender(from) account balance has a small buffer above the transfer amount to account for any potential gas fees
The transaction will fail when we try to send the the amount exactly equal the balance 
Audit: 2023-03-zksync-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/L2EthToken.sol#L49


# Vulnerability details

## Impact
Detailed description of the impact of this finding.

## Proof of Concept

 it's important to note that if there are any gas fees associated with 
        the transaction, such as the gas cost of executing the "transfer" function or the gas 
        cost of calling a smart contract function that triggers the transfer, the account 
        balance may be reduced by a small amount due to the gas fees. Therefore, it's generally a 
        good practice to ensure that the account balance has a small buffer above the transfer 
        amount to account for any potential gas fees

When a transaction is executed in Ethereum, it requires a certain amount of gas to be paid by the transaction sender to compensate the miners for executing the transaction. The gas price is determined by the user and represents the amount of Ether that the user is willing to pay per unit of gas. The total gas cost of the transaction is the gas price multiplied by the gas used during the execution of the transaction.

If the user's account balance is exactly equal to the transfer amount, then the transaction will still be processed successfully as long as the account has sufficient balance to pay for the gas cost of the transaction. If the account does not have sufficient balance to cover the gas cost, then the transaction will fail and the user will need to send a new transaction with a higher gas price or a lower gas limit to ensure that it can be processed successfully.

It's generally a good practice to ensure that the account has a small buffer of Ether above the transfer amount to account for any potential gas fees, as this can help to ensure that the transaction is processed successfully without any issues

Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.

## Tools Used

## Recommended Mitigation Steps
no checking if `balance[address(this)] < amount`
Audit: 2023-03-zksync-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/L2EthToken.sol#L81


# Vulnerability details

## Impact
underflow of `balance[address(this)]` and/or `totalSupply`

## Proof of Concept
```
        unchecked {
            balance[address(this)] -= amount;
            totalSupply -= amount;
        }
```
The subtractions happen inside of unchecked block, so when underflow happens, there is no revert.

## Tools Used

## Recommended Mitigation Steps
Check if `balance[address(this)] >= amount` or remove `unchecked`
`publishCompressedBytecode` function inside `BytecodeCompressor.sol` not working properly
Audit: 2023-03-zksync-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/BytecodeCompressor.sol#L81


# Vulnerability details

# Vulnerability details

## Impact
`publishCompressedBytecode` function inside `BytecodeCompressor.sol` not working properly

## Proof of Concept
I think you made a mistake by deploying bug in the function `publishCompressedBytecode` inside `BytecodeCompressor.sol`
dictionary = _rawCompressedData[2:2 + dictionaryLen ***8**];
```sodlitiy
    function _decodeRawBytecode(
        bytes calldata _rawCompressedData
    ) internal pure returns (bytes calldata dictionary, bytes calldata encodedData) {
        unchecked {
            // The dictionary length can't be more than 2^16, so it fits into 2 bytes.
            uint256 dictionaryLen = uint256(_rawCompressedData.readUint16(0));
            dictionary = _rawCompressedData[2:2 + dictionaryLen * 8];
            encodedData = _rawCompressedData[2 + dictionaryLen * 8:];
        }
    }
```
[BytecodeCompressor.sol#L75](https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/BytecodeCompressor.sol#L75)
In the other code that you deployed there is no `* 8`
```sodlitiy
    function _decodeRawBytecode(bytes calldata _rawCompressedData) internal pure returns(bytes calldata dictionary, bytes calldata encodedData) {
        unchecked {
            // The dictionary length can't be more than 2^16, so it fits into 2 bytes.
            uint256 dictionaryLen = uint256(_rawCompressedData.readUint16(0));
            dictionary = _rawCompressedData[2:2 + dictionaryLen];
            encodedData = _rawCompressedData[2 + dictionaryLen:];
        }
    }
```
[https://github.com/matter-labs/era-system-contracts/contracts/BytecodeCompressor.sol#L63](https://github.com/matter-labs/era-system-contracts/blob/ab2cba30200aa1619e1380be5288b6a90757aa29/contracts/BytecodeCompressor.sol#L63)

Whole function `publishCompressedBytecode` will never works correctly because `dictionary`, `encodedData` will never be initialized correctly 
```solidity
    function publishCompressedBytecode(
        bytes calldata _bytecode,
        bytes calldata _rawCompressedData
    ) external payable returns (bytes32 bytecodeHash) {
        unchecked {
            (bytes calldata dictionary, bytes calldata encodedData) = _decodeRawBytecode(_rawCompressedData);

```
[contracts/BytecodeCompressor.sol#L35](https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/BytecodeCompressor.sol#L35)


## Tools Used
Manual review
## Recommended Mitigation Steps

```diff
/2023-03-zksync/contracts/BytecodeCompressor.sol
function _decodeRawBytecode(
    bytes calldata _rawCompressedData
) internal pure returns (bytes calldata dictionary, bytes calldata encodedData) {
    unchecked {
        // The dictionary length can't be more than 2^16, so it fits into 2 bytes.
        uint256 dictionaryLen = uint256(_rawComp
Malicious user can drain L1 contract using withdraw() method in L2
Audit: 2023-03-zksync-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-03-zksync/blob/main/contracts/L2EthToken.sol#L80
https://github.com/code-423n4/2023-03-zksync/blob/main/contracts/L1Messenger.sol#L22
https://github.com/code-423n4/2023-03-zksync/blob/main/contracts/libraries/SystemContractHelper.sol#L48
https://github.com/matter-labs/era-contracts/blob/main/ethereum/contracts/zksync/facets/Mailbox.sol#L176


# Vulnerability details

## Impact
Due to lack of checks a malicious user can drain entire balance on L1 using withdraw method in L2. 

## Proof of Concept

Step 1.

User calls withdraw() functions in L2EthToken contract to initiate withdraw from zkSync to L1. He has to send the amount that he'd like get and the function create a message to L1 using encodePacked.

Step 2.

Later withdraw() makes a call with that message to sendToL1() in L1Messendger.sol as a calldata. There the function calculates gas cost, burn gas and forward the hashed message to toL1() in SystemContractHelper.sol.

Step 3.

In toL1() we get an address of L1 contract, check that call goes from _isServise and do a low-level call to L1 contract.

Step4. 

In Mailbox.sol we call finalizeEthWithdrawal() to get out ETH. It checks the blockNumber, gets a receiver and amount, verifies the amount limit, proves L2 message inclusion and calls _withdrawFunds(), where user gets his ETH. 

The point is that we can generate our own calldata message and pass it to sendToL1() in L1Messager, using  encodePacked(IMailbox.finalizeEthWithdrawal.selector, _to, _amount) with malicious receiver and amount. 

No other functions checks if user has that amount on L1. 

Creating different calldata messages a malicious user can drain the L1 contract bypassing necessary checks.


## Tools Used

Manual review

## Recommended Mitigation Steps

Provide addition checks if user has enough balance on L1 level to finish up the transaction. Or add modifier at sendToL1() to let only authorized contracts / users call it.