xanoutas/solidity-security-findings huggingface dataset:
``isContractConstructing()`` might return wrong result
Audit: 2023-03-zksync-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
Detailed description of the impact of this finding.
``isContractConstructing()`` might return wrong result


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

``isContractConstructing()`` is testing the equality of a whole byte not testing just the constructor bit. As a result, it might return the wrong result. In particular, if the constructor bit is set, and some other bits are also set, then ``isContractConstructing()`` will return a false negative. 

Wrong implementation: 
```javascript
 /// @notice Denotes whether bytecode hash corresponds to a contract that is on constructor or has already been constructed
    function isContractConstructing(bytes32 _bytecodeHash) internal pure returns (bool) {
        return _bytecodeHash[1] == 0x01;
    }
```

For example, suppose ``_bytecodeHash[1] == 0x11``, ``isContractConstructing()`` is supposed to return true, but it will return false instead due to its testing of equality of the whole byte not just the bit.

## Tools Used
VScode

## Recommended Mitigation Steps
Correction:
```javascript
 /// @notice Denotes whether bytecode hash corresponds to a contract that is on constructor or has already been constructed
    function isContractConstructing(bytes32 _bytecodeHash) internal pure returns (bool) {
        return _bytecodeHash[1] & 0x01;
    }
```
DefaultAccount#fallback lack payable
Audit: 2023-03-zksync-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/DefaultAccount.sol#L223-L228


# Vulnerability details

## Impact
fallback lack `payable`,will lead to differences from the mainnet, and many existing protocols may not work

## Proof of Concept
DefaultAccount Defined as follows:
```
DefaultAccount

The implementation of the default account abstraction. This is the code that is used by default for all addresses that are not in kernel space and have no contract deployed on them. This address:

Contains the minimal implementation of our account abstraction protocol. Note that it supports the built-in paymaster flows.
When anyone (except bootloader) calls/delegate calls it, it behaves in the same way as a call to an EOA, i.e. it always returns success = 1, returndatasize = 0 for calls from anyone except for the bootloader.
```
If there is no code for the address, the DefaultAccount #fallback method will be executed, which is compatible with the behavior of the mainnet

But At present, `fallback` is not `payable`
The code is as follows
```solidity
contract DefaultAccount is IAccount {
    using TransactionHelper for *;
..
    fallback() external { //<--------without payable
        // fallback of default account shouldn't be called by bootloader under no circumstances
        assert(msg.sender != BOOTLOADER_FORMAL_ADDRESS);

        // If the contract is called directly, behave like an EOA
    }

    receive() external payable {
        // If the contract is called directly, behave like an EOA
    }

```
which will lead to differences from the mainnet.

For example, the mainnet execution of the method with value will return true, and the corresponding value will be transfer
but DefaultAccount.sol will return false


It is quite common for `call ()` to with `value`. If it is not compatible, many existing protocols may not work

Mainnet code example, executing 0x0 call with value can be successful:
```solidity
  function test() external {
    vm.deal(address(this),1000);
    console.log("before value:",address(0x0).balance);
    (bool result,bytes memory datas) = address(0x0).call{value:10}("abc");
    console.log("call result:",result);
    console.log("after value:",address(0x0).balance);
  }
```
```console
$ forge test -vvv

[PASS] test() (gas: 42361)
Logs:
  before value: 0
  call result: true
  after value: 10
```

Simulate DefaultAccount #fallback without payable, it will fail:

```solidity

    contract DefaultAccount {
        fallback() external {     
        }
        receive() external payable {      
        }
    }

    function test() external {
        DefaultAccount defaultAccount = new DefaultAccount();
        vm.deal(address(this),1000);
        console.log("before value:",address(defaultAccount).balance);
        (bool result,bytes memory datas) = address(defaultAccount).call{value:10}("abc");
        console.log("call result:",result);
        console.log("after va
forceDeployOnAddress() may be missing markAccountCodeHashAsConstructed()
Audit: 2023-03-zksync-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/ContractDeployer.sol#L232-L247


# Vulnerability details

## Impact
may be missing markAccountCodeHashAsConstructed(), this causes the code corresponding to the address to be invalid

## Proof of Concept
forceDeployOnAddress() can be used to forcefully deploy a contract.
The code is as follows:
```solidity
    function forceDeployOnAddress(ForceDeployment calldata _deployment, address _sender) external payable onlySelf {
        _ensureBytecodeIsKnown(_deployment.bytecodeHash);
        _storeConstructingByteCodeHashOnAddress(_deployment.newAddress, _deployment.bytecodeHash); //<-----set codehash to Constructing

        AccountInfo memory newAccountInfo;
        newAccountInfo.supportedAAVersion = AccountAbstractionVersion.None;
        // Accounts have sequential nonces by default.
        newAccountInfo.nonceOrdering = AccountNonceOrdering.Sequential;
        _storeAccountInfo(_deployment.newAddress, newAccountInfo);

        if (_deployment.callConstructor) {
            _constructContract(_sender, _deployment.newAddress, _deployment.input, false); //<----set codehash to Constructed in _constructContract()
        }

        //<-------but if don't need callConstructor, codehash will still  Constructing

        emit ContractDeployed(_sender, _deployment.bytecodeHash, _deployment.newAddress);
    }
```
This method first sets `codeHash` to `Constructing`
If `callConstructor` is required, `_constructContract ()` will be called, and `codehash` will be set to `Constructed` in the `_constructContract()`
But do nothing if don't need `callConstructor`, so `codehash` will still `Constructing`
This will cause the code corresponding to the address to remain invalid.


## Tools Used

## Recommended Mitigation Steps

```solidity
    function forceDeployOnAddress(ForceDeployment calldata _deployment, address _sender) external payable onlySelf {
        _ensureBytecodeIsKnown(_deployment.bytecodeHash);
        _storeConstructingByteCodeHashOnAddress(_deployment.newAddress, _deployment.bytecodeHash);

        AccountInfo memory newAccountInfo;
        newAccountInfo.supportedAAVersion = AccountAbstractionVersion.None;
        // Accounts have sequential nonces by default.
        newAccountInfo.nonceOrdering = AccountNonceOrdering.Sequential;
        _storeAccountInfo(_deployment.newAddress, newAccountInfo);

        if (_deployment.callConstructor) {
            _constructContract(_sender, _deployment.newAddress, _deployment.input, false);
-       }            
+       }else{
+           ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT.markAccountCodeHashAsConstructed(_newAddress);
+       }

        emit ContractDeployed(_sender, _deployment.bytecodeHash, _deployment.newAddress);
    }
```
Bytecode hash of `AccountCodeStorage` is not stored in its mapping
Audit: 2023-03-zksync-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact

The bytecode hash of `AccountCodeStorage` is not stored in its mapping for the code hashes of the 32-byte account addresses. So, 

## Proof of Concept

`AccountCodeStorage` is responsible of storing the code hashes of account addresses. Moreover, it mimics the opcodes `extcodehash` and `extcodesize` as EVM. 

The problem is that this contract bytecode hash is not stored in its storage:
 - If invoking `extcodehash(the address of AccountCodeStorage)`, it returns 0. This is not correct, it should return the correct bytecode hash of `AccountCodeStorage`.
 - If invoking `extcodesize(the address of AccountCodeStorage)`, it returns 0. This is not correct, it should return the correct bytecode length of `AccountCodeStorage`.

## Tools Used

## Recommended Mitigation Steps
It is recommended to set the bytecode hash of `AccountCodeStorage` at the time of deploying this contract (constructor) in its storage.
`DefaultAccount` does not accept transfers with calldata
Audit: 2023-03-zksync-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-03-zksync/blob/main/contracts/DefaultAccount.sol#L223


# Vulnerability details

## Impact

The fallback function in `DefaultAccount` is defined as `fallback() external {...} `, but it must be `fallback() external payable {...} `. This bug causes transfers with calldata to default accounts always to be reverted. On the other hand, they are not be reverted in Ethereum. Since `DefaultAccount` is required to behave like EOA, this fact is inconsistent with the specification.

In addition, in the `ignoreNonBootloader` and `ignoreInDelegateCall` modifiers, there is the following statement, but as mentioned above, `DefaultAccount` implements an empty fallback instead of an empty payable fallback, so it can be distinguished. 

```
If all functions will use this modifier AND the contract will implement an empty payable fallback() then the contract will be indistinguishable from the EOA when called.
```

This bug makes it impossible to transfer Ether to any default account from any contract that implements transfers with calldata. In particular, if Ether deposited in a contract can only be withdrawn via a transfer with calldata, the assets will be permanently frozen.

For example, calling the `returnEther` function of the following contract has no risk in Ethereum. The Ether is simply transferred to `Foo` and returned. However, in zkSync Era, the above bug causes the Ether to not be returned, and it remains in `Foo` and is lost.

```solidity
contract Foo {
    function returnEther() public payable {
        payable(msg.sender).call{value: msg.value}("00");
    }
}
```

Transfers with calldata are general, as seen in [2023-03-zksync/blob/main/contracts/openzeppelin/utils/Address.sol#L159](https://github.com/code-423n4/2023-03-zksync/blob/main/contracts/openzeppelin/utils/Address.sol#L159) (this `functionCallWithValue` checks for `success`, so the asset is not lost and only reverted). Therefore, the possibility of asset loss in various protocols due to this vulnerability is very high.

## Proof of Concept

The code causing this vulnerability: https://github.com/code-423n4/2023-03-zksync/blob/main/contracts/DefaultAccount.sol#L223

The test below is a transfer with calldata to a default account address (`0x100000`); for Ethereum, this succeeds, but for zkSync Era, it fails. This is dangerous.

```solidity
//! { "cases": [ {
//!     "name": "test",
//!     "inputs": [
//!         {
//!             "method": "test",
//!             "calldata": [
//!             ],
//!             "value": "1 ETH"
//!         }
//!     ],
//!     "expected": [
//!     ]
//! } ] }

// SPDX-License-Identifier: UNLICENSED 

pragma solidity >=0.4.16;

contract Test {
    function test() external payable {
        address defaultAccountAddress = address(0x100000);
        (bool success, ) = payable(defaultAccountAddress).call{value: msg.value}("00");
        require(success); // failed
    }
}

```

The above vulnerability can cause
Force deployment of a contract keeps the flag of `isConstructor` to true
Audit: 2023-03-zksync-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
Force deployment of a contract keep the flag of `isConstructor` true, while in normal deployment, it is set to false at the end of the deployment.

## Proof of Concept

When deploying a contract on zkSync, the flow is:
createAccount/create2Account >> _nonSystemDeployOnAddress >> _performDeployOnAddress >> 
_storeConstructingByteCodeHashOnAddress
_constructContract
https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/ContractDeployer.sol#L281
https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/ContractDeployer.sol#L289

In the `_storeConstructingByteCodeHashOnAddress`, the flag of of `isConstructor` will be set to true as well as the `bytecodeHash` will be stored in `AccountCodeStorage`. 
https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/ContractDeployer.sol#L304
https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/AccountCodeStorage.sol#L40

In the `_constructContract`, the `msg.value` will be transferred to the newly deployed contract, the constructor will be called, and finally the flag of `isConstructor` will be set to false, indicating that the contract is not in constructing mode anymore (it is already constructed).
https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/ContractDeployer.sol#L326
https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/AccountCodeStorage.sol#L52

The inconsistency is that if a contract is deployed through force deployment, the flow is:
forceDeployOnAddresses >> forceDeployOnAddress >> 
_storeConstructingByteCodeHashOnAddress
_constructContract (it may not be called)
https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/ContractDeployer.sol#L214
https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/ContractDeployer.sol#L223

It is similar to normal contract deployment until storing bytecode hash on the address, but if `_deployment.callConstructor` is false, the `_constructContract ` will not be invoked.
https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/ContractDeployer.sol#L222

It means that forcing deployment of a contract if `_deployment.callConstructor` is false, will not mark the deployed contract as constructed (setting the flag `isConstructor` to false). So, this contract will always be considered as in constructing mode.
In other words, unlike the normal deployment of contracts that at the end of deployment the flag `isConstructor` was set to false, the force deployment will keep the flag as true.

Thi
`L2EthToken` contract does not provide a function to transfer tokens in the contract
Audit: 2023-03-zksync-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Summary

```solidity
// https://github.com/code-423n4/2023-03-zksync//blob/main/contracts/L2EthToken.sol#L17
```

The `L2EthToken` contract does not provide a function to transfer tokens in the contract.

## Impact

```solidity
    /// @notice Initiate the ETH withdrawal, funds will be available to claim on L1 `finalizeEthWithdrawal` method.
    /// @param _l1Receiver The address on L1 to receive the funds.
    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);
    }
```

The `withdraw` function of this contract is a `payable` function that will accept tokens from the caller, and these tokens may be permanently stored in this contract.

## Proof of Concept
## Tools Used

Manual

## Recommended Mitigation Steps

It is recommended to provide a function to extract the token in the contract.
`L2EthToken` contract withdrawal proof may be forged
Audit: 2023-03-zksync-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Summary

```solidity
// https://github.com/code-423n4/2023-03-zksync//blob/main/contracts/L2EthToken.sol#L78-L94
    /// @notice Initiate the ETH withdrawal, funds will be available to claim on L1 `finalizeEthWithdrawal` method.
    /// @param _l1Receiver The address on L1 to receive the funds.
    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);
    }
```

When the user calls `withdraw` on L2, the `L2EthToken` contract will send a message to L1, and the user can use this message as a proof of withdrawal.

```solidity
contract L1Messenger is IL1Messenger {
    function sendToL1(bytes calldata _message) external override returns (bytes32 hash) {
```

However, the `sendToL1` function can be called by anyone, and the attacker may actively call the `sendToL1` function and send a forged withdrawal message.

## Impact

Proof of the withdrawal may be forged.

## Proof of Concept
## Tools Used

Manual

## Recommended Mitigation Steps

It is recommended to verify the sender of the message.
`L2EthToken` contract `withdraw` function may overflow
Audit: 2023-03-zksync-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Summary

```solidity
// https://github.com/code-423n4/2023-03-zksync//blob/main/contracts/L2EthToken.sol#L78-L94
    /// @notice Initiate the ETH withdrawal, funds will be available to claim on L1 `finalizeEthWithdrawal` method.
    /// @param _l1Receiver The address on L1 to receive the funds.
    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);
    }
```

`msg.value` is externally controllable and depends on the amount paid by the caller. When `msg.value` is greater than `balance[address(this)]`, integer overflow will occur. Similarly, `totalSupply` may also be affected.

## Impact

As a result, `balance[address(this)]` and `totalSupply` become super large, which may be combined with other vulnerabilities.

## Proof of Concept
## Tools Used

Manual

## Recommended Mitigation Steps

Do not use the unchecked modifier.
The value mapped under a nonce can be overwritten
Audit: 2023-03-zksync-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/NonceHolder.sol#L77-L96
https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/NonceHolder.sol#L101-L104
https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/NonceHolder.sol#L151-L166
https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/bootloader/bootloader.yul#L2498-L2523


# Vulnerability details

## Impact

*Specific inconsistent state caused by setters by sidestepping the checks in the codebase.*

Once a nonce has been used, the value mapped under it shouldn't be changeable.

## Proof of Concept

1) Some input validations exist in [NonceHolder.setValueUnderNonce()](https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/NonceHolder.sol#L77-L96). One prevents the nonce's value from being 0, the other hints at an attempt to have a sequentially used nonce.

2) However, it can be seen that there are no checks preventing a previously filled `key` to be used again and overwrite the `_value` that was used for it. Therefore, calling `setValueUnderNonce()` twice in a row, with the same valid `_key` but two different `_value`, is valid, effectively overwriting the `value` under `nonce`.

3) Given the fact that the method [getValueUnderNonce()](https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/NonceHolder.sol#L101-L104) exists, it should then be expected that it returns the right value under the nonce, and not one that has been mistakenly overwritten at some point.

4) The impact here would be limited to a dirtied state as there's a [validateNonceUsage()](https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/NonceHolder.sol#L151-L166) function called in [the bootloader](https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/bootloader/bootloader.yul#L2498-L2523). 

## Coded POC

1) Use the following to setup a Foundry POC:

[Setting up a hybrid project](https://hardhat.org/hardhat-runner/docs/advanced/hardhat-and-foundry#setting-up-a-hybrid-project)

```bash
yarn add --dev hardhat @nomicfoundation/hardhat-foundry
```

```js
File: hardhat.config.ts
5: import "@nomicfoundation/hardhat-foundry";
```

```bash
npx hardhat init-foundry
forge remappings &> remappings.txt 
```

Set the following:

```diff
File: foundry.toml
+ via_ir = true
```

And, unfortunately, for the project / current scope to compile with Foundry so that we can test what we want: we need to delete the `.yul` files under `contracts` (`precompiles/` and `EventWriter.yul`), otherwise we'll get an error telling us that the compiler only supports 1 `yul` file in the project.

2) Replace the following so that the code can compile with Foundry. We'll be forcing a Sequential