Skip to content

Latest commit

 

History

History
633 lines (494 loc) · 39.5 KB

delfin454000-Q.md

File metadata and controls

633 lines (494 loc) · 39.5 KB

QA Report - low risk

Missing checks for address(0x0) when assigning values to address state variables


L1ERC20Bridge.sol: L79

        l2TokenFactory = _l2TokenFactory;

L2ERC20Bridge.sol: L36

        l1Bridge = _l1Bridge;

L2ETHBridge.sol: L31

        l1Bridge = _l1Bridge;


Outdated Open Zeppelin versions are used

Outdated Open Zeppelin version are used. See ethereum/package.json and zksync/package.json

The versions used have known vulnerabilties, including three with high severity. See Security Advisories.



Duplicate contract names

The contract name L2ContractHelper.sol is used twice in the contracts in scope, as shown below.

The existence of two contracts with the same name could cause confusion, as well as compilation problems under some circumstances.

zksync/contracts/L2ContractHelper.sol

ethereum/contracts/common/L2ContractHelper.sol

Similarly for IL1Bridge.sol and IL2Bridge.sol



Solidity pragma version should be upgraded to latest available version

The current solidity version in contracts is ^0.8.0, compared to the latest version of ^0.8.17. Only the latest version receives security fixes.

Also, five contracts use pragma solidity ^0.8;, which I assume are typos and have included under Typos



QA Report: non-critical

Unclear comments

While a comment may contain abbreviations, shorthand for well-known or previously defined terms, inconsistent or nonstandard punctuation, and use of minor grammatical errors, it should be readable. In other words, it should communicate clearly, immediately and without ambiguity. The comments below should be improved:


DiamondCut.sol: L45

    /// `initCalldata` can be arbitrarily.

Diamond.sol: L285-286

            // Check that called contract returns magic value to make sure that contract logic
            // supposed to be used as diamond cut initializer.

L1EthBridge.sol: L242

    /// @dev Ignore function input and always return zero address as a only one token that the bridge process


Long single-line comments

In theory, comments over 80 characters should wrap using multi-line syntax.

Even if somewhat longer lines (up to 120 characters) are acceptable and a scroll bar is provided, there are cases where long comments interfere with readability. Below are instances of extra-long lines whose readability could be improved by wrapping.

I've divided them into three types. First are NatSpec statements of at least 120 characters.

Such a statement can be wrapped using a small indentation in the continuation line (to indicate that the line has wrapped), along with a period at its close (to indicate the end of the line), as shown below:

IMailbox.sol: L16

    /// @param maxPriorityFeePerErg The additional fee that is paid directly to the validator to incentivize them to include the transaction in a block. Analog to the EIP-1559 `maxPriorityFeePerGas` on an L1 transactions

Suggestion:

    /// @param maxPriorityFeePerErg The additional fee that is paid directly to the validator to incentivize them
    ///  to include the transaction in a block. Analog to the EIP-1559 `maxPriorityFeePerGas` on an L1 transactions.

Similarly for the following 33 statements:

DiamondInit: L24

Config.sol: L8

Config.sol: L59

Storage.sol: L9

Storage.sol: L11

Storage.sol: L12

Storage.sol: L15

Storage.sol: L28

Storage.sol: L80

DiamondCut.sol: L104

Getters.sol: L51

Mailbox.sol: L23

Mailbox.sol: L94

Mailbox.sol: L100

Mailbox.sol: L105

Mailbox.sol: L178

Mailbox.sol: L179

Diamond: L9

IExecutor.sol: L9

IExecutor.sol: L29

IExecutor.sol: L38

IMailbox.sol: L9

IMailbox.sol: L11

IMailbox.sol: L12

IMailbox.sol: L14

IMailbox.sol: L15

IMailbox.sol: L17

AllowList.sol: L64

AllowList.sol: L88

UnsafeBytes.sol: L8

L2ERC20Bridge.sol: L47

L2ETHBridge: L39

L2StandardERC20.sol: L17


The second type are comment lines over 120 characters in length. Like the first type, such statements can be wrapped using a small indentation in the second line (after the //) along with a closing period, as shown below:

L1ERC20Bridge.sol: L196

        // - l2ShardId = 0 (means that L1 -> L2 transaction was processed in a rollup shard, other shards are not available yet anyway)

Suggestion:

        // - l2ShardId = 0 (means that L1 -> L2 transaction was processed in a rollup shard, 
        //   other shards are not available yet anyway).

Similarly for the following eight cases:

DiamondProxy.sol: L12

Storage.sol: L37

DiamondCut.sol: L25

DiamondCut.sol: L84

Executor.sol: L103

Merkle.sol: L12

L1EthBridge.sol: L155

L1EthBridge.sol: L220


The third type of extra-long line includes both code and comments. If a line containing a comment is longer than 120 characters, it makes sense to put the comment in the line above, as shown:

DiamondCut.sol: L106

        require(s.diamondCutStorage.securityCouncilMembers[msg.sender], "a9"); // not a security council member

Suggestion:

        // not a security council member
        require(s.diamondCutStorage.securityCouncilMembers[msg.sender], "a9"); 

Similarly for the following seven cases:

DiamondCut.sol: L108

DiamondCut.sol: L112

Executor.sol: L159

Executor.sol: L199

Executor.sol: L216

Governance.sol: L30

Diamond.sol: L156



Named return variable is not used


Executor.sol: L23-28

    function _commitOneBlock(StoredBlockInfo memory _previousBlock, CommitBlockInfo calldata _newBlock)
        internal
        view
        returns (StoredBlockInfo memory storedNewBlock)
    {
        require(_newBlock.blockNumber == _previousBlock.blockNumber + 1, "f"); // only commit next block

The named return variable storedNewBlock is never used



TODOs and other open items

Comments that refer to open items should be addressed and more fully explained, or else modified or removed. Below are several instances:


Config.sol: L28

// TODO: change constant to the real root hash of empty Merkle tree (SMA-184)

Mailbox.sol: L94

        // TODO: estimate gas for L1 execute

Mailbox.sol: L127

        // TODO: Restore after stable priority op fee modeling. (SMA-1230)

Mailbox.sol: L169

                layer2Tip: uint192(0) // TODO: Restore after fee modeling will be stable. (SMA-1230)

IExecutor.sol: L56

    /// TODO: The verifier integration is not finished yet, change the structure for compatibility later

IMailbox.sol: L46-47

        // Reserved dynamic type for the future use-case. Using it should be avoided,
        // But it is still here, just in case we want to enable some additional functionality.

L1EthBridge.sol: L24-30

    /// @dev Ergs limit for requesting L2 deposit finalization transaction
    /// NOTE: constant is not calculated accurately, because ergs count in L2 is not stable yet
    uint256 constant DEPOSIT_ERGS_LIMIT = 2097152;


    /// @dev Ergs limit for requesting L1 -> L2 transaction of deploying L2 bridge instance
    /// NOTE: constant is not calculated accurately, because ergs count in L2 is not stable yet
    uint256 constant DEPLOY_L2_BRIDGE_COUNTERPART_ERGS_LIMIT = 2097152;

UnsafeBytes.sol: L10-16

 * @dev WARNING!
 * 1) Functions don't check the length of the bytes array, so it can go out of bounds.
 * The user of the library must check for bytes length before using any functions from the library!
 *
 * 2) Read variables are not cleaned up - https://docs.soliditylang.org/en/v0.8.16/internals/variable_cleanup.html.
 * Using data in inline assembly can lead to unexpected behavior!
 */


Missing NatSpec for functions, events, structs and constructors

NatSpec statements are partially or wholly missing for some functions, events, structs and constructors. Below are some representative examples.

Note that I am not including the many functions marked private or internal, for which NatSpec is not required.

DiamondCut.sol: L43-46

    /// @notice Executes a proposed governor upgrade. Only the current governor can execute the upgrade.
    /// NOTE: Governor can execute diamond cut ONLY with proposed `facetCuts` and `initAddress`.
    /// `initCalldata` can be arbitrarily.
    function executeDiamondCutProposal(Diamond.DiamondCutData calldata _diamondCut) external onlyGovernor {

Missing: @param _diamondCut


Mailbox.sol: L35-45

    /// @notice Prove that a specific L2 log was sent in a specific L2 block
    /// @param _blockNumber The executed L2 block number in which the log appeared
    /// @param _index The position of the l2log in the L2 logs Merkle tree
    /// @param _log Information about the sent log
    /// @param _proof Merkle proof for inclusion of the L2 log
    function proveL2LogInclusion(
        uint256 _blockNumber,
        uint256 _index,
        L2Log memory _log,
        bytes32[] calldata _proof
    ) external view returns (bool) {

Missing: @return


NatSpec statements are also partially or wholly missing for the following:

AllowList.sol

L32-35

DiamondProxy.sol

L10-15

Executor.sol

L148-153, L205-208, L219-225, L332-336

ExternalDecoder.sol

L9-10, L14-15

Getters.sol

L21-22, L26-27, L31-32, L36-37, L41-42, L46-47

L51-52, L56-59, L63-64, L68-69, L73-74, L78-79

L83-86, L90-91, L95-96, L100-101, L105-106

L110-111, L115-116, L120-122, L126-127, L132-133

L145-146, L156-157, L171-172, L177-178, L183-184

L1ERC20Bridge.sol

L55-60

L1EthBridge.sol

L46-51

L2ContractHelper.sol

zksync/contracts/L2ContractHelper.sol

L6, L9-14

L2ContractHelper.sol

ethereum/contracts/common/L2ContractHelper.sol

L10-15, L17, L19-23

L2ERC20Bridge.sol

L31-35, L112-113, L30-34, L36-47

L57-63, L78-79, L83-84

L2StandardERC20.sol

L41-43, L100-102, L107-109, L114, L120, L126

Mailbox.sol

L87-93



Event is missing indexed fields

Each event should use three indexed fields if there are three or more fields


Diamond.sol: L16

    event DiamondCut(FacetCut[] facetCuts, address initAddress, bytes initCalldata);

IDiamondCut.sol: L30-35

    event EmergencyDiamondCutApproved(
        address indexed _address,
        uint256 currentProposalId,
        uint256 securityCouncilEmergencyApprovals,
        bytes32 indexed proposedDiamondCutHash
    );

IExecutor.sol: L85

    event BlocksRevert(uint256 totalBlocksCommitted, uint256 totalBlocksVerified, uint256 totalBlocksExecuted);

IMailbox.sol: L95-101

    event NewPriorityRequest(
        uint256 txId,
        bytes32 txHash,
        uint64 expirationBlock,
        L2CanonicalTransaction transaction,
        bytes[] factoryDeps
    );

IL1Bridge.sol: L11

    event WithdrawalFinalized(address indexed to, address indexed l1Token, uint256 amount);

IL1Bridge.sol: L13

    event ClaimedFailedDeposit(address indexed to, address indexed l1Token, uint256 amount);

L2StandardERC20.sol: L12

    event BridgeInitialization(address indexed l1Token, string name, string symbol, uint8 decimals);


Typos


DiamondProxy.sol: L7

/// @title Diamond Proxy Cotract (EIP-2535)

Change Cotract to Contract


Storage.sol: L36

/// @dev The sender is an `address` type, although we are using `uint256` for addreses in `L2CanonicalTransaction`.

Change addreses to addresses


Executor.sol: L216

        require(s.totalBlocksExecuted <= s.totalBlocksVerified, "n"); // Can't execute blocks more then committed and proven currently.

Change then to than


Executor.sol: L337

        require(s.totalBlocksCommitted > _newLastBlock, "v1"); // the last committed block is less new last block

Change less new to less than new


Mailbox.sol: L173

        // Data that needed for operator to simulate priority queue offchain

Change that needed to that is needed or needed


Diamond.sol: L63

    /// @param initAddress The address that's dellegate called after setting up new facet changes

Change dellegate to delegate


Diamond.sol: L64

    /// @param initCalldata Calldata for the delegete call to `initAddress`

Change delegete to delegate


Diamond.sol: L255

    /// NOTE: It is expected but NOT enforced that there are no selectors associated wih `_facet`

Change wih to with


IDiamondCut.sol: L3

pragma solidity ^0.8;

Change ^0.8; to ^0.8.0;`

The same typo appears in the following additional contracts:

IExecutor.sol: L3

IGovernance.sol: L3

IMailbox.sol: L3

IZkSync.sol: L3


L1ERC20Bridge.sol: L19

/// @dev It is standard implementation of ERC20 Bridge that can be used as a refference

Change refference to reference


L1ERC20Bridge.sol: L76

        // We are expecting to see the exect two bytecodes that are needed to initiailize the bridge

Change exect to exact


L2ETHBridge.sol: L58

    /// NOTE: In order to get funds on L1, receiver should finalise deposit on L1 counterpart

Change finalise to finalize. Note that the remaining instances of this word in the contract are spelled using American English ("finalize").


L2ETHBridge.sol: L78

    /// @notice Address of the L2 token by its L1 couterpart

Change couterpart to counterpart.

The same typo appears in the following line:

L2ETHBridge.sol: L83


L2StandardERC20.sol: L42

    /// @dev Stores the L1 address of the bridge and set `name`/`symbol`/`decimls` getters that L1 token has.

Change decimls to decimals



Update sensitive terms in both comments and code

Terms incorporating "black," "white," "slave" or "master" are potentially problematic. Substituting more neutral terminology is becoming common practice. I see that the sensitive term 'whitelist' has been avoided by using 'allowlist'. Two of the contracts still use variations of master, which could be converted to 'primary', as shown below.


Mailbox.sol: L205

                paymaster: uint256(0),

Suggestion: Change paymaster to primarypayer

Similarly for the following instances of paymaster:

IMailbox.sol: L17

IMailbox.sol: L22

IMailbox.sol: L32


Mailbox.sol: L210

                paymasterInput: new bytes(0),

Suggestion: Change paymasterInput to primarypayerInput

Similarly for the following instances of 'paymasterInput`:

IMailbox.sol: L22

IMailbox.sol: L45



Missing require error message

A require message should be included to enable users to understand the reason for failure

Executor.sol: L43

        require(expectedNumberOfLayer1Txs == _newBlock.numberOfLayer1Txs);

Executor.sol: L45

        require(l2BlockTimestamp == _newBlock.timestamp);

Executor.sol: L297

        require(_recurisiveAggregationInput.length == 4);

L1EthBridge.sol: L145

        require(amount != 0);

L1EthBridge.sol: L221

        require(_message.length == 56);

L1EthBridge.sol: L224

        require(bytes4(functionSignature) == this.finalizeWithdrawal.selector);

L1EthBridge.sol: L238

        require(callSuccess);

L2StandardERC20.sol: L96

        require(msg.sender == l2Bridge);