zkSync v2 contest - Rolezn's results

Rely on math, not validators.

General Information

Platform: Code4rena

Start Date: 28/10/2022

Pot Size: $165,500 USDC

Total HM: 2

Participants: 24

Period: 12 days

Judge: GalloDaSballo

Total Solo HM: 1

Id: 177

League: ETH

zkSync

Findings Distribution

Researcher Performance

Rank: 12/24

Findings: 2

Award: $480.53

QA:
grade-b
Gas:
grade-b

🌟 Selected for report: 0

🚀 Solo Findings: 0

Findings Information

🌟 Selected for report: HE1M

Also found by: 0xSmartContract, Rolezn, Tomo, brgltd, cccz, chaduke, ctf_sec, datapunk, jayjonah8, ladboy233, pashov, rbserver

Labels

bug
QA (Quality Assurance)
grade-b
Q-04

Awards

250.7706 USDC - $250.77

External Links

Summary<a name="Summary">

Low Risk Issues

IssueContexts
LOW‑1Missing Checks for Address(0x0)5
LOW‑2Missing Contract-existence Checks Before Low-level Calls1
LOW‑3Contracts are not using their OZ Upgradeable counterparts8
LOW‑4Missing parameter validation in constructor3

Total: 17 contexts over 4 issues

Non-critical Issues

IssueContexts
NC‑1Critical Changes Should Use Two-step Procedure10
NC‑2Use a more recent version of SolidityAll in-scope contracts
NC‑3Redundant Cast1
NC‑4Event Is Missing Indexed Fields5
NC‑5require() / revert() Statements Should Have Descriptive Reason Strings12
NC‑6Implementation contract may not be initialized8
NC‑7Avoid Floating Pragmas: The Version Should Be LockedAll in-scope contracts
NC‑8Non-usage of specific imports77
NC‑9Expressions for constant values such as a call to keccak256(), should use immutable rather than constant2
NC‑10Lines are too long8
NC‑11Open TODOs5

Total: 206 contexts over 11 issues

Low Risk Issues

<a href="#Summary">[LOW‑1]</a><a name="LOW&#x2011;1"> Missing Checks for Address(0x0)

Lack of zero-address validation on address parameters may lead to transaction reverts, waste gas, require resubmission of transactions and may even force contract redeployments in certain cases within the protocol.

<ins>Proof Of Concept</ins>
73: function initialize: address _l2TokenFactory

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/bridge/L1ERC20Bridge.sol#L73

113: function deposit: address _l1Token

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/bridge/L1ERC20Bridge.sol#LL113

180: function claimFailedDeposit: address _depositSender

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/bridge/L1ERC20Bridge.sol#L180

133: function claimFailedDeposit: address _depositSender

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/bridge/L1EthBridge.sol#L133

102: function bridgeMint: address _to

https://github.com/code-423n4/2022-10-zksync/tree/main/zksync/contracts/bridge/L2StandardERC20.sol#L102

<ins>Recommended Mitigation Steps</ins>

Consider adding explicit zero-address validation on input parameters of address type.

<a href="#Summary">[LOW‑2]</a><a name="LOW&#x2011;2"> Missing Contract-existence Checks Before Low-level Calls

Low-level calls return success if there is no code present at the specified address.

<ins>Proof Of Concept</ins>
282: (bool success, bytes memory data) = _init.delegatecall(_calldata);

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/libraries/Diamond.sol#L282

<ins>Recommended Mitigation Steps</ins>

Add a check to verify that _init.code.length > 0

<a href="#Summary">[LOW‑3]</a><a name="LOW&#x2011;3"> Contracts are not using their OZ Upgradeable counterparts

The non-upgradeable standard version of OpenZeppelin’s library are inherited / used by the contracts. It would be safer to use the upgradeable versions of the library contracts to avoid unexpected behaviour.

<ins>Proof of Concept</ins>
5: import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; 6: import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/bridge/L1ERC20Bridge.sol#L5-L6

5: import "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol"; 6: import "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol";

https://github.com/code-423n4/2022-10-zksync/tree/main/zksync/contracts/bridge/L2ERC20Bridge.sol#L5-L6

<ins>Recommended Mitigation Steps</ins>

Where applicable, use the contracts from @openzeppelin/contracts-upgradeable instead of @openzeppelin/contracts.

<a href="#Summary">[LOW‑4]</a><a name="LOW&#x2011;4"> Missing parameter validation in constructor

Some parameters of constructors are not checked for invalid values.

<ins>Proof Of Concept</ins>
32: address _l1Bridge 34: address _governor

https://github.com/code-423n4/2022-10-zksync/tree/main/zksync/contracts/bridge/L2ERC20Bridge.sol#L32-L34

30: address _l1Bridge

https://github.com/code-423n4/2022-10-zksync/tree/main/zksync/contracts/bridge/L2ETHBridge.sol#L30

<ins>Recommended Mitigation Steps</ins>

Validate the parameters.

Non Critical Issues

<a href="#Summary">[NC‑1]</a><a name="NC&#x2011;1"> Critical Changes Should Use Two-step Procedure

The critical procedures should be two step process.

See similar findings in previous Code4rena contests for reference: https://code4rena.com/reports/2022-06-illuminate/#2-critical-changes-should-use-two-step-procedure

<ins>Proof Of Concept</ins>
57: function setPublicAccess(address _target, bool _enable) external onlyOwner {

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/common/AllowList.sol#L57

65: function setBatchPublicAccess(address[] calldata _targets, bool[] calldata _enables) external onlyOwner {

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/common/AllowList.sol#L65

89: function setBatchPermissionToCall(

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/common/AllowList.sol#L89

113: function setPermissionToCall(

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/common/AllowList.sol#L113

45: function setValidator(address _validator, bool _active) external onlyGovernor {

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/facets/Governance.sol#L45

54: function setL2BootloaderBytecodeHash(bytes32 _l2BootloaderBytecodeHash) external onlyGovernor {

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/facets/Governance.sol#L54

69: function setL2DefaultAccountBytecodeHash(bytes32 _l2DefaultAccountBytecodeHash) external onlyGovernor {

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/facets/Governance.sol#L69

84: function setPorterAvailability(bool _zkPorterIsAvailable) external onlyGovernor {

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/facets/Governance.sol#L84

94: function setVerifier(Verifier _newVerifier) external onlyGovernor {

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/facets/Governance.sol#L94

104: function setVerifierParams(VerifierParams calldata _newVerifierParams) external onlyGovernor {

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/facets/Governance.sol#L104

<ins>Recommended Mitigation Steps</ins>

Lack of two-step procedure for critical operations leaves them error-prone. Consider adding two step procedure on the critical functions.

<a href="#Summary">[NC‑2]</a><a name="NC&#x2011;2"> Use a more recent version of Solidity

Use a solidity version of at least 0.8.4 to get bytes.concat() instead of abi.encodePacked(<bytes>,<bytes>) Use a solidity version of at least 0.8.12 to get string.concat() instead of abi.encodePacked(<str>,<str>) Use a solidity version of at least 0.8.13 to get the ability to use using for with a list of free functions

<ins>Proof Of Concept</ins>

Relevant to all in-scope contracts.

<ins>Recommended Mitigation Steps</ins>

Consider updating to a more recent solidity version.

<a href="#Summary">[NC‑3]</a><a name="NC&#x2011;3"> Redundant Cast

The type of the variable is the same as the type to which the variable is being cast

<ins>Proof Of Concept</ins>
206: uint256(_txId)

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/facets/Mailbox.sol#L206

<a href="#Summary">[NC‑4]</a><a name="NC&#x2011;4"> Event Is Missing Indexed Fields

Index event fields make the field more quickly accessible to off-chain tools that parse events. However, note that each index field costs extra gas during emission, so it's not necessarily best to index the maximum allowed per event (three fields).

Each event should use three indexed fields if there are three or more fields, and gas usage is not particularly of concern for the events in question. If there are fewer than three fields, all of the fields should be indexed.

<ins>Proof Of Concept</ins>
event DiamondCutProposal(Diamond.FacetCut[] _facetCuts, address _initAddress);

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/interfaces/IDiamondCut.sol#L20

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

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/interfaces/IExecutor.sol#L85

event NewVerifierParams(VerifierParams oldVerifierParams, VerifierParams newVerifierParams);

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/interfaces/IGovernance.sol#L48

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

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/interfaces/IMailbox.sol#L95

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

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/libraries/Diamond.sol#L16

<a href="#Summary">[NC‑5]</a><a name="NC&#x2011;5"> require() / revert() Statements Should Have Descriptive Reason Strings

<ins>Proof Of Concept</ins>
145: require(amount != 0);

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/bridge/L1EthBridge.sol#L145

221: require(_message.length == 56);

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/bridge/L1EthBridge.sol#L221

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

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/bridge/L1EthBridge.sol#L224

238: require(callSuccess);

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/bridge/L1EthBridge.sol#L238

43: require(expectedNumberOfLayer1Txs == _newBlock.numberOfLayer1Txs);

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/facets/Executor.sol#L43

45: require(l2BlockTimestamp == _newBlock.timestamp);

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/facets/Executor.sol#L45

297: require(_recurisiveAggregationInput.length == 4);

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/facets/Executor.sol#L297

50: require(_l1Token == CONVENTIONAL_ETH_ADDRESS);

https://github.com/code-423n4/2022-10-zksync/tree/main/zksync/contracts/bridge/L2ETHBridge.sol#L50

96: require(msg.sender == l2Bridge);

https://github.com/code-423n4/2022-10-zksync/tree/main/zksync/contracts/bridge/L2StandardERC20.sol#L96

116: revert();

https://github.com/code-423n4/2022-10-zksync/tree/main/zksync/contracts/bridge/L2StandardERC20.sol#L116

122: revert();

https://github.com/code-423n4/2022-10-zksync/tree/main/zksync/contracts/bridge/L2StandardERC20.sol#L122

128: revert();

https://github.com/code-423n4/2022-10-zksync/tree/main/zksync/contracts/bridge/L2StandardERC20.sol#L128

<a href="#Summary">[NC‑6]</a><a name="NC&#x2011;6"> Implementation contract may not be initialized

OpenZeppelin recommends that the initializer modifier be applied to constructors. Per OZs Post implementation contract should be initialized to avoid potential griefs or exploits. https://forum.openzeppelin.com/t/uupsupgradeable-vulnerability-post-mortem/15680/5

<ins>Proof Of Concept</ins>
57: constructor(IMailbox _mailbox, IAllowList _allowList) reentrancyGuardInitializer { 58: zkSyncMailbox = _mailbox; 59: allowList = _allowList; 60: }

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/bridge/L1ERC20Bridge.sol#L57-L60

48: constructor(IMailbox _mailbox, IAllowList _allowList) reentrancyGuardInitializer { 49: zkSyncMailbox = _mailbox; 50: allowList = _allowList; 51: }

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/bridge/L1EthBridge.sol#L48-L51

32: constructor(address _owner) { 33: require(_owner != address(0), "kq"); 34: owner = _owner; 35: }

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/common/AllowList.sol#L32-L35

10: constructor(uint256 _chainId, Diamond.DiamondCutData memory _diamondCut) { 13: require(_chainId == block.chainid, "pr"); 14: Diamond.diamondCut(_diamondCut); 15: }

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/DiamondProxy.sol#L10-L15

13: constructor() { 16: assert(SECURITY_COUNCIL_APPROVALS_FOR_EMERGENCY_UPGRADE > 0); 17: }

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/facets/DiamondCut.sol#L13-L17

constructor( address _l1Bridge, bytes32 _l2TokenProxyBytecodeHash, address _governor ) { l1Bridge = _l1Bridge; l2TokenProxyBytecodeHash = _l2TokenProxyBytecodeHash; address l2StandardToken = address(new L2StandardERC20{salt: bytes32(0)}()); l2TokenFactory = new UpgradeableBeacon{salt: bytes32(0)}(l2StandardToken); l2TokenFactory.transferOwnership(_governor); }

https://github.com/code-423n4/2022-10-zksync/tree/main/zksync/contracts/bridge/L2ERC20Bridge.sol#L31-L42

constructor(address _l1Bridge) { l1Bridge = _l1Bridge; IL2EthInitializable(address(ETH_TOKEN_SYSTEM_CONTRACT_ADDRESS)).initialization(address(this)); }

https://github.com/code-423n4/2022-10-zksync/tree/main/zksync/contracts/bridge/L2ETHBridge.sol#L30-L34

<a href="#Summary">[NC‑7]</a><a name="NC&#x2011;7"> Avoid Floating Pragmas: The Version Should Be Locked

Avoid floating pragmas for non-library contracts.

While floating pragmas make sense for libraries to allow them to be included with multiple different versions of applications, it may be a security risk for application implementations.

A known vulnerable compiler version may accidentally be selected or security tools might fall-back to an older compiler version ending up checking a different EVM compilation that is ultimately deployed on the blockchain.

It is recommended to pin to a concrete compiler version.

<ins>Proof Of Concept</ins>

Found usage of floating pragmas ^0.8.0 of Solidity in all in-scope contracts.

<a href="#Summary">[NC‑8]</a><a name="NC&#x2011;8"> Non-usage of specific imports

The current form of relative path import is not recommended for use because it can unpredictably pollute the namespace. Instead, the Solidity docs recommend specifying imported symbols explicitly. https://docs.soliditylang.org/en/v0.8.15/layout-of-source-files.html#importing-other-source-files

<ins>Proof Of Concept</ins>
8: import "./interfaces/IL1Bridge.sol"; 9: import "./interfaces/IL2Bridge.sol"; 11: import "../common/interfaces/IAllowList.sol"; 12: import "../common/AllowListed.sol"; 13: import "../common/libraries/UnsafeBytes.sol"; 14: import "../common/ReentrancyGuard.sol"; 15: import "../common/L2ContractHelper.sol";

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/bridge/L1ERC20Bridge.sol#L8-L15

5: import "./interfaces/IL1Bridge.sol"; 6: import "./interfaces/IL2Bridge.sol"; 8: import "../common/interfaces/IAllowList.sol"; 9: import "../common/AllowListed.sol"; 10: import "../common/libraries/UnsafeBytes.sol"; 11: import "../common/L2ContractHelper.sol"; 12: import "../common/ReentrancyGuard.sol";

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/bridge/L1EthBridge.sol#L5-L12

5: import "./interfaces/IAllowList.sol"; 6: import "./libraries/UncheckedMath.sol";

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/common/AllowList.sol#L5-L6

5: import "./interfaces/IAllowList.sol";

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/common/AllowListed.sol#L5

5: import "../common/interfaces/IAllowList.sol"; 6: import "./interfaces/IExecutor.sol"; 7: import "./libraries/Diamond.sol"; 8: import "./facets/Base.sol"; 9: import "./Config.sol";

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/DiamondInit.sol#L5-L9

5: import "./libraries/Diamond.sol";

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/DiamondProxy.sol#L5

5: import "./Verifier.sol"; 6: import "../common/interfaces/IAllowList.sol"; 7: import "./libraries/PriorityQueue.sol";

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/Storage.sol#L5-L7

5: import "../Storage.sol"; 6: import "../../common/ReentrancyGuard.sol"; 7: import "../../common/AllowListed.sol";

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/facets/Base.sol#L5-L7

5: import "../interfaces/IDiamondCut.sol"; 6: import "../libraries/Diamond.sol"; 7: import "../Config.sol"; 8: import "./Base.sol";

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/facets/DiamondCut.sol#L5-L8

5: import "./Base.sol"; 6: import "../Config.sol"; 7: import "../interfaces/IExecutor.sol"; 8: import "../libraries/PairingsBn254.sol"; 9: import "../libraries/PriorityQueue.sol"; 10: import "../../common/libraries/UncheckedMath.sol"; 11: import "../../common/libraries/UnsafeBytes.sol"; 12: import "../../common/L2ContractHelper.sol";

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/facets/Executor.sol#L5-L12

5: import "./Base.sol"; 6: import "../libraries/Diamond.sol"; 7: import "../libraries/PriorityQueue.sol"; 8: import "../../common/libraries/UncheckedMath.sol"; 9: import "../interfaces/IGetters.sol";

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/facets/Getters.sol#L5-L9

5: import "../interfaces/IGovernance.sol"; 6: import "../../common/L2ContractHelper.sol"; 7: import "./Base.sol";

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/facets/Governance.sol#L5-L7

5: import "../interfaces/IMailbox.sol"; 6: import "../libraries/Merkle.sol"; 7: import "../libraries/PriorityQueue.sol"; 8: import "../Storage.sol"; 9: import "../Config.sol"; 10: import "../../common/libraries/UncheckedMath.sol"; 11: import "../../common/L2ContractHelper.sol"; 12: import "./Base.sol";

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/facets/Mailbox.sol#L5-L12

5: import "../libraries/Diamond.sol";

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/interfaces/IDiamondCut.sol#L5

5: import "../libraries/PriorityQueue.sol";

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/interfaces/IGetters.sol#L5

5: import "../Verifier.sol"; 6: import "../Storage.sol";

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/interfaces/IGovernance.sol#L5-L6

5: import "./IMailbox.sol"; 6: import "./IGovernance.sol"; 7: import "./IExecutor.sol"; 8: import "./IDiamondCut.sol"; 9: import "./IGetters.sol";

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/interfaces/IZkSync.sol#L5-L9

5: import "../../common/libraries/UncheckedMath.sol";

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/libraries/Merkle.sol#L5

8: import "./interfaces/IL1Bridge.sol"; 9: import "./interfaces/IL2Bridge.sol"; 10: import "./interfaces/IL2StandardToken.sol"; 12: import "./L2StandardERC20.sol";

https://github.com/code-423n4/2022-10-zksync/tree/main/zksync/contracts/bridge/L2ERC20Bridge.sol#L8-L12

7: import "./interfaces/IL1Bridge.sol"; 8: import "./interfaces/IL2Bridge.sol"; 9: import "./interfaces/IL2EthInitializable.sol"; 10: import "./interfaces/IL2StandardToken.sol";

https://github.com/code-423n4/2022-10-zksync/tree/main/zksync/contracts/bridge/L2ETHBridge.sol#L7-L10

6: import "./interfaces/IL2StandardToken.sol"; 7: import "../ExternalDecoder.sol";

https://github.com/code-423n4/2022-10-zksync/tree/main/zksync/contracts/bridge/L2StandardERC20.sol#L6-L7

<ins>Recommended Mitigation Steps</ins>

Use specific imports syntax per solidity docs recommendation.

<a href="#Summary">[NC‑9]</a><a name="NC&#x2011;9"> Expressions for constant values such as a call to keccak256(), should use immutable rather than constant

While it doesn't save any gas because the compiler knows that developers often make this mistake, it's still best to use the right tool for the task at hand. There is a difference between constant variables and immutable variables, and they should each be used in their appropriate contexts. constants should be used for literal values written into the code, and immutable variables should be used for expressions, or values calculated in, or passed into the constructor.

<ins>Proof Of Concept</ins>
41: bytes32 constant CREATE2_PREFIX = keccak256("zksyncCreate2");

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/common/L2ContractHelper.sol#L41

24: bytes32 constant CREATE2_PREFIX = keccak256("zksyncCreate2");

https://github.com/code-423n4/2022-10-zksync/tree/main/zksync/contracts/L2ContractHelper.sol#L24

<a href="#Summary">[NC‑10]</a><a name="NC&#x2011;10"> Lines are too long

Usually lines in source code are limited to 80 characters. Today's screens are much larger so it's reasonable to stretch this in some cases. Since the files will most likely reside in GitHub, and GitHub starts using a scroll bar in all cases when the length is over 164 characters, the lines below should be split when they reach that length Reference: https://docs.soliditylang.org/en/v0.8.10/style-guide.html#maximum-line-length

<ins>Proof Of Concept</ins>
59: /// @dev NOTE: we must reserve for users enough time to send full exit operation, wait maximum time for processing this operation and withdraw funds from it.

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/Config.sol#L59

104: /// @param _diamondCutHash The hash of the diamond cut that security council members want to approve. Needed to prevent unintentional approvals, including reorg attacks

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/facets/DiamondCut.sol#L104

9: /// @param indexRepeatedStorageChanges The serial number of the shortcut index that's used as a unique identifier for storage keys that were used twice or more

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/interfaces/IExecutor.sol#L9

29: /// @param indexRepeatedStorageChanges The serial number of the shortcut index that's used as a unique identifier for storage keys that were used twice or more

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/interfaces/IExecutor.sol#L29

14: /// @param ergsPerPubdataByteLimit Maximum number of ergs that will cost one byte of pubdata (every piece of data that will be stored on L1 as calldata)

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/interfaces/IMailbox.sol#L14

15: /// @param maxFeePerErg The absolute maximum sender willing to pay per unit of ergs to get the transaction included in a block. Analog to the EIP-1559 `maxFeePerGas` on an L1 transactions

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/interfaces/IMailbox.sol#L15

16: /// @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

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/interfaces/IMailbox.sol#L16

17: /// @param paymaster The address of the EIP-4337 paymaster, that will pay fees for the transaction. `uint256` type for possible address format changes and maintaining backward compatibility

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/interfaces/IMailbox.sol#L17

<a href="#Summary">[NC‑11]</a><a name="NC&#x2011;11"> Open TODOs

An open TODO is present. It is recommended to avoid open TODOs as they may indicate programming errors that still need to be fixed.

<ins>Proof Of Concept</ins>
28: // TODO: change constant to the real root hash of empty Merkle tree (SMA-184)

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/Config.sol#L28

94: // TODO: estimate gas for L1 execute

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/facets/Mailbox.sol#L94

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

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/facets/Mailbox.sol#L127

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

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/facets/Mailbox.sol#L169

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

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/interfaces/IExecutor.sol#L56

#0 - miladpiri

2022-11-23T14:32:01Z

IMO, should be considered as high quality report!

#1 - GalloDaSballo

2022-11-24T01:55:44Z

LOW‑1 | Missing Checks for Address(0x0) | 5 L

LOW‑2 | Missing Contract-existence Checks Before Low-level Calls | 1 Incorrect, return value is checked, meaning the contract must exist

LOW‑3 | Contracts are not using their OZ Upgradeable counterparts | 8 R

LOW‑4 | Missing parameter validation in constructor | 3 Already awarded

NC‑1 | Critical Changes Should Use Two-step Procedure | 10 NC

NC‑2 | Use a more recent version of Solidity | All in-scope contracts NC

NC‑3 | Redundant Cast | 1 NC

NC‑4 | Event Is Missing Indexed Fields | 5 Disputed in lack of why

NC‑5 | require() / revert() Statements Should Have Descriptive Reason Strings | 12 NC

NC‑6 | Implementation contract may not be initialized | 8 Invalid, they have the initializer

NC‑7 | Avoid Floating Pragmas: The Version Should Be Locked | All in-scope contracts NC

NC‑8 | Non-usage of specific imports | 77 NC

NC‑9 | Expressions for constant values such as a call to keccak256(), should use immutable rather than constant | 2 invalid

NC‑10 | Lines are too long | 8 NC

NC‑11 | Open TODOs NC

#2 - GalloDaSballo

2022-12-03T00:41:27Z

1L 1R 8NC

#3 - c4-judge

2022-12-03T19:09:13Z

GalloDaSballo marked the issue as grade-b

Findings Information

🌟 Selected for report: 0x1f8b

Also found by: Aymen0909, HardlyCodeMan, IllIllI, ReyAdmirado, Rolezn, TomJ, c3phas, gogo, mcwildy

Labels

bug
G (Gas Optimization)
edited-by-warden
grade-b
G-03

Awards

229.7602 USDC - $229.76

External Links

Summary<a name="Summary">

Gas Optimizations

IssueContexts
GAS‑1++i/i++ Should Be unchecked{++i}/unchecked{i++} When It Is Not Possible For Them To Overflow, As Is The Case When Used In For- And While-loops4
GAS‑2Splitting require() Statements That Use && Saves Gas2
GAS‑3abi.encode() is less efficient than abi.encodepacked()17
GAS‑4Use calldata instead of memory for function parameters3
GAS‑5Public Functions To External2
GAS‑6Usage of uints/ints smaller than 32 bytes (256 bits) incurs overhead11
GAS‑7Optimize names to save gasAll in-scope contracts
GAS‑8Use uint256(1)/uint256(2) instead for true and false boolean states16
GAS‑9Structs can be packed into fewer storage slots2
GAS‑10Superfluous event fields2
GAS‑11Do not calculate constants3

Total: 80 contexts over 11 issues

Gas Optimizations

<a href="#Summary">[GAS‑1]</a><a name="GAS&#x2011;1"> ++i/i++ Should Be unchecked{++i}/unchecked{i++} When It Is Not Possible For Them To Overflow, As Is The Case When Used In For- And While-loops

The unchecked keyword is new in solidity version 0.8.0, so this only applies to that version or higher, which these instances are. This saves 30-40 gas PER LOOP

<ins>Proof Of Concept</ins>
94: for (uint256 i = 0; i < facetCutsLength; ++i) {

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/libraries/Diamond.sol#L94

132: for (uint256 i = 0; i < selectorsLength; ++i) {

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/libraries/Diamond.sol#L132

153: for (uint256 i = 0; i < selectorsLength; ++i) {

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/libraries/Diamond.sol#L153

173: for (uint256 i = 0; i < selectorsLength; ++i) {

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/libraries/Diamond.sol#L173

<a href="#Summary">[GAS‑2]</a><a name="GAS&#x2011;2"> Splitting require() statements that use && saves gas

Instead of using operator && on a single require. Using a two require can save more gas.

i.e. for require(version == 1 && _bytecodeHash[1] == bytes1(0), "zf"); use:

require(version == 1); require(_bytecodeHash[1] == bytes1(0));
<ins>Proof Of Concept</ins>
65: require(version == 1 && _bytecodeHash[1] == bytes1(0), "zf");

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/common/L2ContractHelper.sol#L65

65: require(version == 1 && _bytecodeHash[1] == bytes1(0), "zf");

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/common/L2ContractHelper.sol#L65

<a href="#Summary">[GAS‑3]</a><a name="GAS&#x2011;3"> abi.encode() is less efficient than abi.encodepacked()

See for more information: https://github.com/ConnorBlockchain/Solidity-Encode-Gas-Comparison

<ins>Proof Of Concept</ins>
82: bytes memory create2Input = abi.encode(address(this), l2ProxyTokenBytecodeHash, _governor);

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/bridge/L1ERC20Bridge.sol#L82

168: data = abi.encode(data1, data2, data3);

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/bridge/L1ERC20Bridge.sol#L168

283: bytes32 constructorInputHash = keccak256(abi.encode(address(l2TokenFactory), ""));

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/bridge/L1ERC20Bridge.sol#L283

58: bytes memory create2Input = abi.encode(address(this));

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/bridge/L1EthBridge.sol#L58

54: s.storedBlockHashes[0] = keccak256(abi.encode(storedBlockZero));

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/DiamondInit.sol#L54

27: s.diamondCutStorage.proposedDiamondCutHash = keccak256(abi.encode(_facetCuts, _initAddress));

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/facets/DiamondCut.sol#L27

61: keccak256(abi.encode(_diamondCut.facetCuts, _diamondCut.initAddress)),

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/facets/DiamondCut.sol#L61

85: return keccak256(abi.encode(_previousBlock.blockHash, _newBlock.newStateRoot));

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/facets/Executor.sol#L85

122: chainedPriorityTxsHash = keccak256(abi.encode(chainedPriorityTxsHash, canonicalTxHash));

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/facets/Executor.sol#L122

182: concatHash = keccak256(abi.encode(concatHash, priorityOp.canonicalTxHash));

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/facets/Executor.sol#L182

359: return keccak256(abi.encode(passThroughDataHash, metadataHash, auxiliaryOutputHash));

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/facets/Executor.sol#L359

381: return abi.encode(_block.l2LogsTreeRoot, l2ToL1LogsHash, initialStorageChangesHash, repeatedStorageChangesHash);

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/facets/Executor.sol#L381

386: return keccak256(abi.encode(_storedBlockInfo));

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/facets/Executor.sol#L386

163: canonicalTxHash = keccak256(abi.encode(transaction));

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/facets/Mailbox.sol#L163

30: currentHash = keccak256(abi.encode(currentHash, _path[i]));

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/libraries/Merkle.sol#L30

32: currentHash = keccak256(abi.encode(_path[i], currentHash));

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/libraries/Merkle.sol#L32

114: bytes32 constructorInputHash = keccak256(abi.encode(address(l2TokenFactory), ""));

https://github.com/code-423n4/2022-10-zksync/tree/main/zksync/contracts/bridge/L2ERC20Bridge.sol#L114

<a href="#Summary">[GAS‑4]</a><a name="GAS&#x2011;4"> Use calldata instead of memory for function parameters

In some cases, having function arguments in calldata instead of memory is more optimal.

Consider the following generic example:

contract C { function add(uint[] memory arr) external returns (uint sum) { uint length = arr.length; for (uint i = 0; i < arr.length; i++) { sum += arr[i]; } } }

In the above example, the dynamic array arr has the storage location memory. When the function gets called externally, the array values are kept in calldata and copied to memory during ABI decoding (using the opcode calldataload and mstore). And during the for loop, arr[i] accesses the value in memory using a mload. However, for the above example this is inefficient. Consider the following snippet instead:

contract C { function add(uint[] calldata arr) external returns (uint sum) { uint length = arr.length; for (uint i = 0; i < arr.length; i++) { sum += arr[i]; } } }

In the above snippet, instead of going via memory, the value is directly read from calldata using calldataload. That is, there are no intermediate memory operations that carries this value.

Gas savings: In the former example, the ABI decoding begins with copying value from calldata to memory in a for loop. Each iteration would cost at least 60 gas. In the latter example, this can be completely avoided. This will also reduce the number of instructions and therefore reduces the deploy time cost of the contract.

In short, use calldata instead of memory if the function argument is only read.

Note that in older Solidity versions, changing some function arguments from memory to calldata may cause "unimplemented feature error". This can be avoided by using a newer (0.8.*) Solidity compiler.

Examples Note: The following pattern is prevalent in the codebase:

function f(bytes memory data) external { (...) = abi.decode(data, (..., types, ...)); }

Here, changing to bytes calldata will decrease the gas. The total savings for this change across all such uses would be quite significant.

<ins>Proof Of Concept</ins>
function _addFunctions( address _facet, bytes4[] memory _selectors, bool _isFacetFreezable ) private {

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/libraries/Diamond.sol#L119

function _replaceFunctions( address _facet, bytes4[] memory _selectors, bool _isFacetFreezable ) private {

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/libraries/Diamond.sol#L143

function _removeFunctions(address _facet, bytes4[] memory _selectors) private {

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/libraries/Diamond.sol#L167

<a href="#Summary">[GAS‑5]</a><a name="GAS&#x2011;5"> Public Functions To External

The following functions could be set external to save gas and improve code quality. External call cost is less expensive than of public functions.

<ins>Proof Of Concept</ins>
function l2TransactionBaseCost( uint256, uint256, uint32 ) public pure returns (uint256) {

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/facets/Mailbox.sol#L89

function l2TokenAddress(address _l1Token) public view override returns (address) {

https://github.com/code-423n4/2022-10-zksync/tree/main/zksync/contracts/bridge/L2ERC20Bridge.sol#L113

<a href="#Summary">[GAS‑6]</a><a name="GAS&#x2011;6"> Usage of uints/ints smaller than 32 bytes (256 bits) incurs overhead

When using elements that are smaller than 32 bytes, your contract's gas usage may be higher. This is because the EVM operates on 32 bytes at a time. Therefore, if the element is smaller than that, the EVM must use more operations in order to reduce the size of the element from 32 bytes to the desired size.

https://docs.soliditylang.org/en/v0.8.11/internals/layout_in_storage.html Each operation involving a uint8 costs an extra 22-28 gas (depending on whether the other operand is also a variable of type uint8) as compared to ones involving uint256, due to the compiler having to clear the higher bits of the memory word before operating on the uint8, as well as the associated stack operations of doing so. Use a larger size then downcast where needed

<ins>Proof Of Concept</ins>
26: uint160 constant SYSTEM_CONTRACTS_OFFSET = 0x8000;

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/common/L2ContractHelper.sol#L26

64: uint8 version = uint8(_bytecodeHash[0]);

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/common/L2ContractHelper.sol#L64

40: uint8 l2ShardId;

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/Storage.sol#L40

54: uint16 txNumberInBlock;

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/Storage.sol#L54

125: uint64 expirationBlock = uint64(block.number + PRIORITY_EXPIRATION);

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/facets/Mailbox.sol#L125

41: uint64 blockNumber; 42: uint64 timestamp; 43: uint64 indexRepeatedStorageChanges;

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/interfaces/IExecutor.sol#L41-L43

24: uint16 selectorPosition;

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/libraries/Diamond.sol#L24

33: uint16 facetPosition;

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/libraries/Diamond.sol#L33

207: uint16 selectorPosition = uint16(ds.facetToSelectors[_facet].selectors.length);

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/libraries/Diamond.sol#L207

12: uint64 expirationBlock; 13: uint192 layer2Tip;

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/libraries/PriorityQueue.sol#L12-L13

17: uint160 constant SYSTEM_CONTRACTS_OFFSET = 0x8000;

https://github.com/code-423n4/2022-10-zksync/tree/main/zksync/contracts/L2ContractHelper.sol#L17

29: uint8 private decimals_;

https://github.com/code-423n4/2022-10-zksync/tree/main/zksync/contracts/bridge/L2StandardERC20.sol#L29

<a href="#Summary">[GAS‑7]</a><a name="GAS&#x2011;7"> Optimize names to save gas

public/external function names and public member variable names can be optimized to save gas. See this link for an example of how it works. Method IDs that have two leading zero bytes can save 128 gas each during deployment, and renaming functions to have lower method IDs will save 22 gas per call, per sorted position shifted

<ins>Proof Of Concept</ins>

Relevant to all in-scope contracts.

<a href="#Summary">[GAS‑8]</a><a name="GAS&#x2011;8"> Use uint256(1)/uint256(2) instead for true and false boolean states

If you don't use boolean for storage you will avoid Gwarmaccess 100 gas. In addition, state changes of boolean from true to false can cost up to ~20000 gas rather than uint256(2) to uint256(1) that would cost significantly less.

<ins>Proof Of Concept</ins>
40: mapping(uint256 => mapping(uint256 => bool)) public isWithdrawalFinalized;

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/bridge/L1ERC20Bridge.sol#L40

252: isWithdrawalFinalized[_l2BlockNumber][_l2MessageIndex] = true;

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/bridge/L1ERC20Bridge.sol#L252

37: mapping(uint256 => mapping(uint256 => bool)) public isWithdrawalFinalized;

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/bridge/L1EthBridge.sol#L37

207: isWithdrawalFinalized[_l2BlockNumber][_l2MessageIndex] = true;

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/bridge/L1EthBridge.sol#L207

26: mapping(address => bool) public isAccessPublic;

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/common/AllowList.sol#L26

30: mapping(address => mapping(address => mapping(bytes4 => bool))) public hasSpecialAccessToCall;

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/common/AllowList.sol#L30

40: s.validators[_validator] = true;

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/DiamondInit.sol#L40

22: mapping(address => bool) securityCouncilMembers;

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/Storage.sol#L22

77: mapping(address => bool) validators;

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/Storage.sol#L77

68: diamondStorage.isFrozen = false;

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/facets/DiamondCut.sol#L68

98: diamondStorage.isFrozen = false;

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/facets/DiamondCut.sol#L98

83: diamondStorage.isFrozen = true;

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/facets/DiamondCut.sol#L83

133: isSystemContextLogProcessed = true;

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/facets/Executor.sol#L133

72: getters.ignoreName = true;

https://github.com/code-423n4/2022-10-zksync/tree/main/zksync/contracts/bridge/L2StandardERC20.sol#L72

78: getters.ignoreSymbol = true;

https://github.com/code-423n4/2022-10-zksync/tree/main/zksync/contracts/bridge/L2StandardERC20.sol#L78

88: getters.ignoreDecimals = true;

https://github.com/code-423n4/2022-10-zksync/tree/main/zksync/contracts/bridge/L2StandardERC20.sol#L88

<a href="#Summary">[GAS‑9]</a><a name="GAS&#x2011;9"> Structs can be packed into fewer storage slots

Each slot saved can avoid an extra Gsset (20000 gas) for the first setting of the struct. Subsequent reads as well as writes have smaller gas savings

<ins>Proof Of Concept</ins>
struct AppStorage { DiamondCutStorage diamondCutStorage; address governor; address pendingGovernor; mapping(address => bool) validators; Verifier verifier; uint256 totalBlocksExecuted; uint256 totalBlocksVerified; uint256 totalBlocksCommitted; mapping(uint256 => bytes32) storedBlockHashes; mapping(uint256 => bytes32) l2LogsRootHashes; PriorityQueue.Queue priorityQueue; IAllowList allowList; VerifierParams verifierParams; bytes32 l2BootloaderBytecodeHash; bytes32 l2DefaultAccountBytecodeHash; bool zkPorterIsAvailable; }

Saving 1 slot by changing to:

struct AppStorage { DiamondCutStorage diamondCutStorage; bool zkPorterIsAvailable; //@audit 8 bits address governor; //@audit 160 bits address pendingGovernor; mapping(address => bool) validators; Verifier verifier; uint256 totalBlocksExecuted; uint256 totalBlocksVerified; uint256 totalBlocksCommitted; mapping(uint256 => bytes32) storedBlockHashes; mapping(uint256 => bytes32) l2LogsRootHashes; PriorityQueue.Queue priorityQueue; IAllowList allowList; VerifierParams verifierParams; bytes32 l2BootloaderBytecodeHash; bytes32 l2DefaultAccountBytecodeHash; }

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/Storage.sol#L69-L105

struct StoredBlockInfo { uint64 blockNumber; bytes32 blockHash; uint64 indexRepeatedStorageChanges; uint256 numberOfLayer1Txs; bytes32 priorityOperationsHash; bytes32 l2LogsTreeRoot; uint256 timestamp; bytes32 commitment; }

Saving 1 slot by changing to:

struct StoredBlockInfo { uint64 blockNumber; //@audit 64 bits uint64 indexRepeatedStorageChanges; //@audit 64 bits bytes32 blockHash; uint256 numberOfLayer1Txs; bytes32 priorityOperationsHash; bytes32 l2LogsTreeRoot; uint256 timestamp; bytes32 commitment; }

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/interfaces/IExecutor#L15-L24

<a href="#Summary">[GAS‑10]</a><a name="GAS&#x2011;10"> Superfluous event fields

block.number and block.timestamp are added to the event information by default, so adding them manually will waste additional gas.

<ins>Proof Of Concept</ins>
76: event BlockCommit(uint256 indexed blockNumber, bytes32 indexed blockHash, bytes32 indexed commitment);

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/interfaces/IExecutor#L76

82: event BlockExecution(uint256 indexed blockNumber, bytes32 indexed blockHash, bytes32 indexed commitment);

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/interfaces/IExecutor#L82

<a href="#Summary">[GAS‑11]</a><a name="GAS&#x2011;11"> Do not calculate constants

Due to how constant variables are implemented (replacements at compile-time), an expression assigned to a constant variable is recomputed each time that the variable is used, which wastes some gas.

<ins>Proof Of Concept</ins>
12: uint256 constant L2_TO_L1_LOGS_COMMITMENT_BYTES = 4 + L2_TO_L1_LOG_SERIALIZE_SIZE * 512;

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/Config.sol#L12

23: uint256 constant INITIAL_STORAGE_CHANGES_COMMITMENT_BYTES = 4 + 64 * 4896;

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/Config.sol#L23

26: uint256 constant REPEATED_STORAGE_CHANGES_COMMITMENT_BYTES = 4 + 40 * 7787;

https://github.com/code-423n4/2022-10-zksync/tree/main/ethereum/contracts/zksync/Config.sol#L26

#0 - miladpiri

2022-11-23T14:55:14Z

It can be as high quality report!

#1 - GalloDaSballo

2022-11-25T01:35:57Z

GAS‑1 | ++i/i++ Should Be unchecked{++i}/unchecked{i++} When It Is Not Possible For Them To Overflow, As Is The Case When Used In For- And While-loops | 4 200

GAS‑2 | Splitting require() Statements That Use && Saves Gas | 2 6

GAS‑3 | abi.encode() is less efficient than abi.encodepacked() | 17

GAS‑4 | Use calldata instead of memory for function parameters | 3 Code doesn't compile with those changes

GAS‑5 | Public Functions To External | 2 Will not save gas as no calldata / memory is changed

GAS‑6 | Usage of uints/ints smaller than 32 bytes (256 bits) incurs overhead | 11 GAS‑7 | Optimize names to save gas | All in-scope contracts

GAS‑8 | Use uint256(1)/uint256(2) instead for true and false boolean states | 16 Will not change as much as in reentrancy guard

GAS‑9 | Structs can be packed into fewer storage slots | 2 4k

GAS‑10 | Superfluous event fields | 2 Roughly 100 gas

GAS‑11 | Do not calculate constants Dispute for months now and has been fixed years ago

#2 - GalloDaSballo

2022-11-25T01:36:06Z

4306

#3 - c4-judge

2022-12-03T19:02:43Z

GalloDaSballo marked the issue as grade-b

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter