Platform: Code4rena
Start Date: 07/03/2024
Pot Size: $250,000 USDC
Total HM: 5
Participants: 24
Period: 21 days
Judge: 0xsomeone
Total Solo HM: 3
Id: 347
League: ETH
Rank: 12/24
Findings: 1
Award: $619.88
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: lsaudit
Also found by: Bauchibred, ChaseTheLight, DadeKuma, K42, Pechenite, Sathish9098, aua_oo7, hihen, oualidpro, rjs, slvDev
619.8781 USDC - $619.88
Id | Title | Instances | Gas Saved |
---|---|---|---|
[G-01] | Use Array.unsafeAccess to avoid repeated array length checks | 50 | 105,000 |
[G-02] | State variables can be packed into fewer storage slots | 2 | 40,000 |
[G-03] | State variables can be modified to fit in fewer storage slots | 1 | 20,000 |
[G-04] | Structs can be packed into fewer storage slots | 3 | 60,000 |
[G-05] | Structs can be modified to fit in fewer storage slots | 2 | 40,000 |
[G-06] | Multiple mapping s that share an ID can be combined into a single mapping of ID / struct | 3 | 60,000 |
[G-07] | Use of memory instead of storage for struct/array state variables | 5 | 10,500 |
[G-08] | Unused non-constant state variables waste gas | 1 | - |
[G-09] | State variables only set in their definitions should be declared constant | 3 | 66,291 |
[G-10] | Cache state variables with stack variables | 6 | 1,700 |
[G-11] | Low level call can be optimized with assembly | 1 | 248 |
[G-12] | Use of memory instead of calldata for immutable arguments | 40 | 11,528 |
[G-13] | Avoid updating storage when the value hasn't changed | 21 | 14,700 |
[G-14] | Use of emit inside a loop | 3 | 3,078 |
[G-15] | Use uint256(1)/uint256(2) instead of true/false to save gas for changes | 6 | 102,000 |
[G-16] | Shortcircuit rules can be be used to optimize some gas usage | 2 | 8,400 |
[G-17] | Cache multiple accesses of a mapping/array | 8 | 546 |
[G-18] | Using private for constants saves gas | 5 | 42,030 |
[G-19] | require() or revert() statements that check input arguments should be at the top of the function | 7 | - |
[G-20] | Custom error s cost less than require /assert | 333 | 9,657 |
[G-21] | Consider activating via-ir for deploying | - | - |
[G-22] | Function calls should be cached instead of re-calling the function | 3 | - |
[G-23] | Functions that revert when called by normal users can be payable | 88 | 1,848 |
[G-24] | Cache keccak256 with static arguments | 2 | 10 |
[G-25] | Array length is not cached | 11 | 33 |
[G-26] | Caching global variables is more expensive than using the actual variable | 1 | 12 |
[G-27] | Add unchecked blocks for subtractions where the operands cannot underflow | 4 | 340 |
[G-28] | Add unchecked blocks for divisions where the operands cannot overflow | 10 | 1,590 |
[G-29] | Usage of uints /ints smaller than 32 bytes (256 bits) incurs overhead | 204 | 1,224 |
[G-30] | Stack variable cost less while used in emitting event | 8 | 72 |
[G-31] | Using pre instead of post increments/decrements | 5 | 10 |
[G-32] | >= /<= costs less gas than > /< | 97 | 291 |
[G-33] | internal functions only called once can be inlined to save gas | 80 | 1,600 |
[G-34] | Inline modifiers that are only used once, to save gas | 7 | - |
[G-35] | private functions only called once can be inlined to save gas | 15 | 300 |
[G-36] | abi.encode() is less efficient than abi.encodepacked() for non-address arguments | 32 | 160 |
[G-37] | Boolean comparison with boolean literals is unnecessary | 1 | 9 |
[G-38] | Unused named return variables without optimizer waste gas | 3 | 9 |
[G-39] | Using this.<fn>() wastes gas | 4 | 400 |
[G-40] | Consider pre-calculating the address of address(this) to save gas | 21 | - |
[G-41] | Consider using Solady's FixedPointMathLib | 3 | - |
[G-42] | Reduce deployment costs by tweaking contracts' metadata | 104 | - |
[G-43] | Emitting constants wastes gas | 1 | 8 |
[G-44] | Update OpenZeppelin dependency to save gas | 22 | - |
[G-45] | Function names can be optimized | 80 | 1,760 |
[G-46] | Using delete instead of setting mapping/struct to 0 saves gas | 3 | 15 |
[G-47] | Expression "" is cheaper than new bytes(0) | 8 | 1,904 |
[G-48] | Using bool for storage incurs overhead | 6 | 600 |
[G-49] | Multiplication/division by two should use bit shifting | 4 | 80 |
[G-50] | x += y is more expensive than x = x + y for state variables | 3 | 60 |
[G-51] | Use of += is cheaper for mappings | 2 | 80 |
[G-52] | Mappings are cheaper to use than storage arrays | 1 | 2,100 |
[G-53] | Bytes constants are more efficient than string constants | 5 | 1,890 |
[G-54] | Constructors can be marked payable | 10 | 210 |
[G-55] | Long revert strings | 104 | 2,196 |
[G-56] | Splitting require statements that use && saves gas | 3 | 9 |
[G-57] | Nesting if statements that use && saves gas | 12 | 360 |
[G-58] | Counting down when iterating, saves gas | 18 | 108 |
[G-59] | do-while is cheaper than for -loops when the initial check can be skipped | 35 | - |
[G-60] | Lack of unchecked in loops | 12 | 720 |
[G-61] | Pre-compute hashes | 2 | 10 |
[G-62] | uint comparison with zero can be cheaper | 24 | 96 |
[G-63] | Use assembly to check for address(0) | 30 | 180 |
[G-64] | Use scratch space for building calldata with assembly | 275 | 60,500 |
[G-65] | Use assembly to write hashes | 56 | 6,720 |
[G-66] | Use assembly to validate msg.sender | 42 | 504 |
[G-67] | Use assembly to write address storage values | 16 | 1,184 |
[G-68] | Use assembly to emit an event | 77 | 2,926 |
Total: 2056 instances over 68 issues with an estimate of 687,806 gas saved.
Array.unsafeAccess
to avoid repeated array length checksWhen using storage arrays, solidity adds an internal lookup of the array's length (a Gcoldsload 2100 gas) to ensure it doesn't read past the array's end.
It's possible to avoid this lookup by using Array.unsafeAccess
in cases where the length has already been checked.
There are 50 instances of this issue.
<details> <summary>Expand findings</summary>File: code/system-contracts/contracts/Compressor.sol 143: uint8 metadata = uint8(bytes1(_compressedStateDiffs[stateDiffPtr])); 174: uint8 metadata = uint8(bytes1(_compressedStateDiffs[stateDiffPtr]));
File: code/system-contracts/contracts/ContractDeployer.sol 249: sumOfValues += _deployments[i].value; 254: this.forceDeployOnAddress{value: _deployments[i].value}(_deployments[i], msg.sender);
File: code/system-contracts/contracts/ImmutableSimulator.sol 39: uint256 index = _immutables[i].index; 40: bytes32 value = _immutables[i].value; 41: immutableDataStorage[uint256(uint160(_dest))][index] = value;
File: code/system-contracts/contracts/KnownCodesStorage.sol 31: _markBytecodeAsPublished(_hashes[i], _shouldSendToL1);
[31]
File: code/system-contracts/contracts/L1Messenger.sol 211: l2ToL1LogsTreeArray[i] = hashedLog; 219: l2ToL1LogsTreeArray[i] = L2_L1_LOGS_TREE_DEFAULT_LEAF_HASH; 225: l2ToL1LogsTreeArray[i] = keccak256( 226: abi.encode(l2ToL1LogsTreeArray[2 * i], l2ToL1LogsTreeArray[2 * i + 1])
File: code/system-contracts/contracts/PubdataChunkPublisher.sol 52: blobHashes[i] = blobHash;
[52]
File: code/contracts/ethereum/contracts/governance/Governance.sol 226: (bool success, bytes memory returnData) = _calls[i].target.call{value: _calls[i].value}(_calls[i].data);
[226]
File: code/contracts/ethereum/contracts/state-transition/ValidatorTimelock.sol 117: committedBatchTimestamp[ERA_CHAIN_ID].set(_newBatchesData[i].batchNumber, timestamp); 136: committedBatchTimestamp[_chainId].set(_newBatchesData[i].batchNumber, timestamp); 186: uint256 commitBatchTimestamp = committedBatchTimestamp[ERA_CHAIN_ID].get( 187: _newBatchesData[i].batchNumber 210: uint256 commitBatchTimestamp = committedBatchTimestamp[_chainId].get(_newBatchesData[i].batchNumber);
File: code/contracts/ethereum/contracts/upgrades/BaseZkSyncUpgrade.sol 228: L2ContractHelper.hashL2Bytecode(_factoryDeps[i]) == bytes32(_expectedHashes[i]),
[228]
File: code/contracts/ethereum/contracts/state-transition/libraries/Diamond.sol 102: Action action = facetCuts[i].action; 103: address facet = facetCuts[i].facet; 104: bool isFacetFreezable = facetCuts[i].isFreezable; 105: bytes4[] memory selectors = facetCuts[i].selectors; 139: bytes4 selector = _selectors[i]; 140: SelectorToFacet memory oldFacet = ds.selectorToFacet[selector]; 159: bytes4 selector = _selectors[i]; 160: SelectorToFacet memory oldFacet = ds.selectorToFacet[selector]; 179: bytes4 selector = _selectors[i]; 180: SelectorToFacet memory oldFacet = ds.selectorToFacet[selector];
[102, 103, 104, 105, 139, 140, 159, 160, 179, 180]
File: code/contracts/ethereum/contracts/state-transition/libraries/Merkle.sol 31: ? _efficientHash(currentHash, _path[i]) 32: : _efficientHash(_path[i], currentHash);
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Executor.sol 258: _lastCommittedBatchData = _commitOneBatch(_lastCommittedBatchData, _newBatchesData[i], bytes32(0)); 260: s.storedBatchHashes[_lastCommittedBatchData.batchNumber] = _hashStoredBatchInfo(_lastCommittedBatchData); 294: _newBatchesData[i], 298: s.storedBatchHashes[_lastCommittedBatchData.batchNumber] = _hashStoredBatchInfo(_lastCommittedBatchData); 352: _executeOneBatch(_batchesData[i], i); 353: emit BlockExecution(_batchesData[i].batchNumber, _batchesData[i].batchHash, _batchesData[i].commitment); 408: _hashStoredBatchInfo(_committedBatches[i]) == s.storedBatchHashes[currentTotalBatchesVerified], 412: bytes32 currentBatchCommitment = _committedBatches[i].commitment; 413: proofPublicInput[i] = _getBatchProofPublicInput( 581: blobAuxOutputWords[i * 2] = _blobHashes[i]; 582: blobAuxOutputWords[i * 2 + 1] = _blobCommitments[i]; 654: blobCommitments[versionedHashIndex] = keccak256( 669: (_blobHashes[i] == bytes32(0) && blobCommitments[i] == bytes32(0)) || 670: (_blobHashes[i] != bytes32(0) && blobCommitments[i] != bytes32(0)),
[258, 260, 294, 298, 352, 353, 408, 412, 413, 581, 582, 654, 669, 670]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Getters.sol 209: address facetAddr = ds.facets[i]; 210: Diamond.FacetToSelectors memory facetToSelectors = ds.facetToSelectors[facetAddr]; 212: result[i] = Facet(facetAddr, facetToSelectors.selectors);
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Mailbox.sol 357: bytes32 hashedBytecode = L2ContractHelper.hashL2Bytecode(_factoryDeps[i]);
[357]
</details>Each slot saved can avoid an extra Gsset (20000 gas). Reads and writes (if two variables that occupy the same slot are written by the same function) will have a cheaper gas consumption.
There are 2 instances of this issue.
File: code/system-contracts/contracts/SystemContext.sol // @audit Can save 1 storage slot (from 17 to 16) // @audit Consider using the following order: /* * uint256 chainId (32) * uint256 gasPrice (32) * uint256 blockGasLimit (32) * uint256 difficulty (32) * uint256 baseFee (32) * BlockInfo currentBatchInfo (32) * mapping(uint256 => bytes32) batchHashes (32) * BlockInfo currentL2BlockInfo (32) * bytes32 currentL2BlockTxsRollingHash (32) * bytes32[] l2BlockHash (32) * BlockInfo currentVirtualL2BlockInfo (32) * VirtualBlockUpgradeInfo virtualBlockUpgradeInfo (32) * uint256 gasPerPubdataByte (32) * uint256 basePubdataSpent (32) * address origin (20) * uint16 txNumberInBlock (2) * address coinbase (20) */ 17: contract SystemContext is ISystemContext, ISystemContextDeprecated, ISystemContract {
[17]
File: code/contracts/ethereum/contracts/state-transition/ValidatorTimelock.sol // @audit Can save 1 storage slot (from 4 to 3) // @audit Consider using the following order: /* * mapping(uint256 => LibMap.Uint32Map) committedBatchTimestamp (32) * mapping(uint256 => mapping(address => bool)) validators (32) * IStateTransitionManager stateTransitionManager (20) * uint32 executionDelay (4) */ 22: contract ValidatorTimelock is IExecutor, Ownable2Step {
[22]
Some state variables can be safely modified, and as result, the contract will use 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.
There is 1 instance of this issue.
File: code/contracts/ethereum/contracts/governance/Governance.sol // @audit Supposing an already optimal order, this can save 1 storage slot (from 3 to 2) by modifying the following variables: // @audit uint256 minDelay -> uint32 minDelay // @audit Consider using the following order: /* * mapping(bytes32 => uint256) timestamps (32) * address securityCouncil (20) * uint32 minDelay (4) */ 20: contract Governance is IGovernance, Ownable2Step {
[20]
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.
There are 3 instances of this issue.
<details> <summary>Expand findings</summary>File: code/contracts/ethereum/contracts/state-transition/IStateTransitionManager.sol // @audit Can save 1 storage slot (from 8 to 7) // @audit Consider using the following order: /* * bytes32 genesisBatchHash (32) * bytes32 genesisBatchCommitment (32) * uint256 protocolVersion (32) * address governor (20) * uint64 genesisIndexRepeatedStorageChanges (8) * address validatorTimelock (20) * address genesisUpgrade (20) * Diamond.DiamondCutData diamondCut (20) */ 15: struct StateTransitionManagerInitializeData { 16: address governor; 17: address validatorTimelock; 18: address genesisUpgrade; 19: bytes32 genesisBatchHash; 20: uint64 genesisIndexRepeatedStorageChanges; 21: bytes32 genesisBatchCommitment; 22: Diamond.DiamondCutData diamondCut; 23: uint256 protocolVersion; 24: }
[15-24]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/ZkSyncStateTransitionStorage.sol // @audit Can save 1 storage slot (from 35 to 34) // @audit Consider using the following order: /* * uint256[] __DEPRECATED_diamondCutStorage (32) * mapping(address => bool) validators (32) * uint256 totalBatchesExecuted (32) * uint256 totalBatchesVerified (32) * uint256 totalBatchesCommitted (32) * mapping(uint256 => bytes32) storedBatchHashes (32) * mapping(uint256 => bytes32) l2LogsRootHashes (32) * VerifierParams verifierParams (32) * bytes32 l2BootloaderBytecodeHash (32) * bytes32 l2DefaultAccountBytecodeHash (32) * uint256 priorityTxMaxGasLimit (32) * UpgradeStorage __DEPRECATED_upgrades (32) * mapping(uint256 => mapping(uint256 => bool)) isEthWithdrawalFinalized (32) * uint256 __DEPRECATED_lastWithdrawalLimitReset (32) * uint256 __DEPRECATED_withdrawnAmountInWindow (32) * mapping(address => uint256) __DEPRECATED_totalDepositedAmountPerUser (32) * uint256 protocolVersion (32) * bytes32 l2SystemContractsUpgradeTxHash (32) * uint256 l2SystemContractsUpgradeBatchNumber (32) * FeeParams feeParams (32) * uint256 chainId (32) * uint128 baseTokenGasPriceMultiplierNominator (16) * uint128 baseTokenGasPriceMultiplierDenominator (16) * address __DEPRECATED_governor (20) * bool zkPorterIsAvailable (1) * address __DEPRECATED_pendingGovernor (20) * IVerifier verifier (20) * PriorityQueue.Queue priorityQueue (20) * address __DEPRECATED_allowList (20) * address admin (20) * address pendingAdmin (20) * address blobVersionedHashRetriever (20) * address bridgehub (20) * address stateTransitionManager (20) * address baseToken (20) * address baseTokenBridge (20) */ 66: struct ZkSyncStateTransitionStorage { 67: /// @dev Storage of variables needed for deprecated diamond cut facet 68: uint256[7] __DEPRECATED_diamondCutStorage; 69: /// @notice Address which will exercise critical changes to the Diamond Proxy (upgrades, freezing & unfreezing). Replaced by STM 70: address __DEPRECATED_governor; 71: /// @notice Address that the governor proposed as one that will replace it 72: address __DEPRECATED_pendingGovernor; 73: /// @notice List of permitted validators 74: mapping(address validatorAddress => bool isValidator) validators; 75: /// @dev Verifier contract. Used to verify aggregated proof for batches 76: IVerifier verifier; 77: /// @notice Total number of executed batches i.e. batches[totalBatchesExecuted] points at the latest executed batch 78: /// (batch 0 is genesis) 79: uint256 totalBatchesExecuted; 80: /// @notice Total number of proved batches i.e. batches[totalBatchesProved] points at the latest proved batch 81: uint256 totalBatchesVerified; 82: /// @notice Total number of committed batches i.e. batches[totalBatchesCommitted] points at the latest committed 83: /// batch 84: uint256 totalBatchesCommitted; 85: /// @dev Stored hashed StoredBatch for batch number 86: mapping(uint256 batchNumber => bytes32 batchHash) storedBatchHashes; 87: /// @dev Stored root hashes of L2 -> L1 logs 88: mapping(uint256 batchNumber => bytes32 l2LogsRootHash) l2LogsRootHashes; 89: /// @dev Container that stores transactions requested from L1 90: PriorityQueue.Queue priorityQueue; 91: /// @dev The smart contract that manages the list with permission to call contract functions 92: address __DEPRECATED_allowList; 93: /// @notice Part of the configuration parameters of ZKP circuits. Used as an input for the verifier smart contract 94: VerifierParams verifierParams; 95: /// @notice Bytecode hash of bootloader program. 96: /// @dev Used as an input to zkp-circuit. 97: bytes32 l2BootloaderBytecodeHash; 98: /// @notice Bytecode hash of default account (bytecode for EOA). 99: /// @dev Used as an input to zkp-circuit. 100: bytes32 l2DefaultAccountBytecodeHash; 101: /// @dev Indicates that the porter may be touched on L2 transactions. 102: /// @dev Used as an input to zkp-circuit. 103: bool zkPorterIsAvailable; 104: /// @dev The maximum number of the L2 gas that a user can request for L1 -> L2 transactions 105: /// @dev This is the maximum number of L2 gas that is available for the "body" of the transaction, i.e. 106: /// without overhead for proving the batch. 107: uint256 priorityTxMaxGasLimit; 108: /// @dev Storage of variables needed for upgrade facet 109: UpgradeStorage __DEPRECATED_upgrades; 110: /// @dev A mapping L2 batch number => message number => flag. 111: /// @dev The L2 -> L1 log is sent for every withdrawal, so this mapping is serving as 112: /// a flag to indicate that the message was already processed. 113: /// @dev Used to indicate that eth withdrawal was already processed 114: mapping(uint256 l2BatchNumber => mapping(uint256 l2ToL1MessageNumber => bool isFinalized)) isEthWithdrawalFinalized; 115: /// @dev The most recent withdrawal time and amount reset 116: uint256 __DEPRECATED_lastWithdrawalLimitReset; 117: /// @dev The accumulated withdrawn amount during the withdrawal limit window 118: uint256 __DEPRECATED_withdrawnAmountInWindow; 119: /// @dev A mapping user address => the total deposited amount by the user 120: mapping(address => uint256) __DEPRECATED_totalDepositedAmountPerUser; 121: /// @dev Stores the protocol version. Note, that the protocol version may not only encompass changes to the 122: /// smart contracts, but also to the node behavior. 123: uint256 protocolVersion; 124: /// @dev Hash of the system contract upgrade transaction. If 0, then no upgrade transaction needs to be done. 125: bytes32 l2SystemContractsUpgradeTxHash; 126: /// @dev Batch number where the upgrade transaction has happened. If 0, then no upgrade transaction has happened 127: /// yet. 128: uint256 l2SystemContractsUpgradeBatchNumber; 129: /// @dev Address which will exercise non-critical changes to the Diamond Proxy (changing validator set & unfreezing) 130: address admin; 131: /// @notice Address that the admin proposed as one that will replace admin role 132: address pendingAdmin; 133: /// @dev Fee params used to derive gasPrice for the L1->L2 transactions. For L2 transactions, 134: /// the bootloader gives enough freedom to the operator. 135: FeeParams feeParams; 136: /// @dev Address of the blob versioned hash getter smart contract used for EIP-4844 versioned hashes. 137: address blobVersionedHashRetriever; 138: /// new fields 139: /// @dev The chainId of the chain 140: uint256 chainId; 141: /// @dev The address of the bridgehub 142: address bridgehub; 143: /// @dev The address of the StateTransitionManager 144: address stateTransitionManager; 145: /// @dev The address of the baseToken contract. Eth is address(1) 146: address baseToken; 147: /// @dev The address of the baseTokenbridge. Eth also uses the shared bridge 148: address baseTokenBridge; 149: /// @notice gasPriceMultiplier for each baseToken, so that each L1->L2 transaction pays for its transaction on the destination 150: /// we multiply by the nominator, and divide by the denominator 151: uint128 baseTokenGasPriceMultiplierNominator; 152: uint128 baseTokenGasPriceMultiplierDenominator; 153: }
[66-153]
File: code/contracts/ethereum/contracts/state-transition/chain-interfaces/IExecutor.sol // @audit Can save 1 storage slot (from 8 to 7) // @audit Consider using the following order: /* * bytes32 batchHash (32) * uint256 numberOfLayer1Txs (32) * bytes32 priorityOperationsHash (32) * bytes32 l2LogsTreeRoot (32) * uint256 timestamp (32) * bytes32 commitment (32) * uint64 batchNumber (8) * uint64 indexRepeatedStorageChanges (8) */ 83: struct StoredBatchInfo { 84: uint64 batchNumber; 85: bytes32 batchHash; 86: uint64 indexRepeatedStorageChanges; 87: uint256 numberOfLayer1Txs; 88: bytes32 priorityOperationsHash; 89: bytes32 l2LogsTreeRoot; 90: uint256 timestamp; 91: bytes32 commitment; 92: }
[83-92]
</details>Some member types can be safely modified, and as result, these struct
will fit in 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.
There are 2 instances of this issue.
<details> <summary>Expand findings</summary>File: code/contracts/ethereum/contracts/upgrades/BaseZkSyncUpgrade.sol // @audit Supposing an already optimal order, this can save 1 storage slot (from 10 to 9) by modifying the following variables: // @audit uint256 upgradeTimestamp -> uint32 upgradeTimestamp // @audit Consider using the following order: /* * L2CanonicalTransaction l2ProtocolUpgradeTx (32) * bytes[] factoryDeps (32) * bytes32 bootloaderHash (32) * bytes32 defaultAccountHash (32) * VerifierParams verifierParams (32) * bytes l1ContractsUpgradeCalldata (32) * bytes postUpgradeCalldata (32) * uint256 newProtocolVersion (32) * address verifier (20) * uint32 upgradeTimestamp (4) */ 28: struct ProposedUpgrade { 29: L2CanonicalTransaction l2ProtocolUpgradeTx; 30: bytes[] factoryDeps; 31: bytes32 bootloaderHash; 32: bytes32 defaultAccountHash; 33: address verifier; 34: VerifierParams verifierParams; 35: bytes l1ContractsUpgradeCalldata; 36: bytes postUpgradeCalldata; 37: uint256 upgradeTimestamp; 38: uint256 newProtocolVersion; 39: }
[28-39]
File: code/contracts/ethereum/contracts/state-transition/chain-interfaces/IExecutor.sol // @audit Supposing an already optimal order, this can save 1 storage slot (from 7 to 6) by modifying the following variables: // @audit uint256 packedBatchAndL2BlockTimestamp -> uint32 packedBatchAndL2BlockTimestamp // @audit Consider using the following order: /* * bytes32 batchHash (32) * uint256 numberOfLayer1Txs (32) * bytes32 priorityOperationsHash (32) * bytes32 l2LogsTreeRoot (32) * bytes32 commitment (32) * uint64 batchNumber (8) * uint64 indexRepeatedStorageChanges (8) * uint32 timestamp (4) */ 83: struct StoredBatchInfo { 84: uint64 batchNumber; 85: bytes32 batchHash; 86: uint64 indexRepeatedStorageChanges; 87: uint256 numberOfLayer1Txs; 88: bytes32 priorityOperationsHash; 89: bytes32 l2LogsTreeRoot; 90: uint256 timestamp; 91: bytes32 commitment; 92: }
[83-92]
</details>mapping
s that share an ID can be combined into a single mapping
of ID / struct
This can avoid a Gsset (20000 Gas) per mapping combined. Reads and writes will also be cheaper when a function requires both values as they both can fit in the same storage slot.
Finally, if both fields are accessed in the same function, this can save ~42 gas per access due to not having to recalculate the key's keccak256
hash (Gkeccak256 - 30 Gas) and that calculation's associated stack operations.
There are 3 instances of this issue.
File: code/system-contracts/contracts/NonceHolder.sol // @audit consider merging rawNonces, nonceValues 36: mapping(uint256 account => uint256 packedMinAndDeploymentNonce) internal rawNonces; 41: mapping(uint256 account => mapping(uint256 nonceKey => uint256 value)) internal nonceValues;
[36]
File: code/contracts/ethereum/contracts/bridge/L1ERC20Bridge.sol // @audit consider merging depositAmount, __DEPRECATED_totalDepositedAmountPerUser 32: mapping(address account => mapping(address l1Token => mapping(bytes32 depositL2TxHash => uint256 amount))) 33: public depositAmount; 51: mapping(address => mapping(address => uint256)) private __DEPRECATED_totalDepositedAmountPerUser; // @audit consider merging __DEPRECATED_lastWithdrawalLimitReset, __DEPRECATED_withdrawnAmountInWindow 45: mapping(address => uint256) private __DEPRECATED_lastWithdrawalLimitReset; 48: mapping(address => uint256) private __DEPRECATED_withdrawnAmountInWindow;
memory
instead of storage
for struct/array state variablesWhen fetching data from storage
, using the memory
keyword to assign it to a variable reads all fields of the struct/array and incurs a Gcoldsload (2100 gas) for each field.
This can be avoided by declaring the variable with the storage
keyword and caching the necessary fields in stack variables.
The memory
keyword should only be used if the entire struct/array is being returned or passed to a function that requires memory
, or if it is being read from another memory
array/struct.
There are 5 instances of this issue.
File: code/system-contracts/contracts/ContractDeployer.sol 41: AccountInfo memory info = accountInfo[_address];
[41]
File: code/system-contracts/contracts/SystemContext.sol 135: VirtualBlockUpgradeInfo memory currentVirtualBlockUpgradeInfo = virtualBlockUpgradeInfo; 173: BlockInfo memory batchInfo = currentBatchInfo; 181: BlockInfo memory blockInfo = currentL2BlockInfo; 275: BlockInfo memory virtualBlockInfo = currentVirtualL2BlockInfo;
If the variable is assigned a non-zero value, saves Gsset (20000 gas). If it's assigned a zero value, saves Gsreset (2900 gas).
If the variable remains unassigned, there is no gas savings unless the variable is public, in which case the compiler-generated non-payable getter deployment cost is saved.
If the state variable is overriding an interface's public function, mark the variable as constant or immutable so that it does not use a storage slot.
There is 1 instance of this issue.
File: code/system-contracts/contracts/SystemContext.sol 37: uint256 public blockGasLimit = type(uint32).max;
[37]
constant
Avoids a Gsset (20000 gas) at deployment, and replaces the first access in each transaction (Gcoldsload - 2100 gas) and each access thereafter (Gwarmacces - 100 gas) with a PUSH32
(3 gas).
There are 3 instances of this issue.
File: code/system-contracts/contracts/SystemContext.sol 37: uint256 public blockGasLimit = type(uint32).max; 41: address public coinbase = BOOTLOADER_FORMAL_ADDRESS; 44: uint256 public difficulty = 2.5e15;
Caching of a state variable replaces each Gwarmaccess (100 gas) with a cheaper stack read. Other less obvious fixes/optimizations include having local memory caches of state variable structs, or having local caches of state variable contracts/addresses.
There are 6 instances of this issue.
File: code/system-contracts/contracts/L1Messenger.sol // @audit numberOfLogsToProcess on line 109 108: logIdInMerkleTree = numberOfLogsToProcess;
[108]
File: code/system-contracts/contracts/SystemContext.sol // @audit virtualBlockUpgradeInfo on lines 268, 285 303: virtualBlockUpgradeInfo.virtualBlockFinishL2Block = _l2BlockNumber;
[303]
File: code/contracts/ethereum/contracts/bridgehub/Bridgehub.sol // @audit pendingAdmin on line 69 66: delete pendingAdmin; // @audit sharedBridge on line 130 140: address(sharedBridge),
File: code/contracts/ethereum/contracts/state-transition/StateTransitionManager.sol // @audit pendingAdmin on line 128 125: delete pendingAdmin; // @audit protocolVersion on lines 192, 216 227: emit SetChainIdUpgrade(_chainContract, l2ProtocolUpgradeTx, protocolVersion);
call
can be optimized with assemblyreturnData
is copied to memory even if the variable is not utilized: the proper way to handle this is through a low level assembly call.
// before (bool success,) = payable(receiver).call{gas: gas, value: value}(""); //after bool success; assembly { success := call(gas, receiver, value, 0, 0, 0, 0) }
There is 1 instance of this issue.
File: code/system-contracts/contracts/MsgValueSimulator.sol 63: (bool success, ) = address(REAL_BASE_TOKEN_SYSTEM_CONTRACT).call( 64: abi.encodeCall(REAL_BASE_TOKEN_SYSTEM_CONTRACT.transferFromTo, (msg.sender, to, value)) 65: );
[63-65]
memory
instead of calldata
for immutable argumentsConsider refactoring the function arguments from memory
to calldata
when they are immutable, as calldata
is cheaper.
There are 40 instances of this issue.
<details> <summary>Expand findings</summary>File: code/system-contracts/contracts/ContractDeployer.sol // @audit _newInfo 58: function _storeAccountInfo(address _address, AccountInfo memory _newInfo) internal {
[58]
File: code/system-contracts/contracts/DefaultAccount.sol // @audit _signature 159: function _isValidSignature(bytes32 _hash, bytes memory _signature) internal view returns (bool) {
[159]
File: code/system-contracts/contracts/L2BaseToken.sol // @audit _additionalData 85: function withdrawWithMessage(address _l1Receiver, bytes memory _additionalData) external payable override { // @audit _additionalData 117: function _getExtendedWithdrawMessage( 118: address _to, 119: uint256 _amount, 120: address _sender, 121: bytes memory _additionalData
File: code/contracts/zksync/contracts/L2ContractHelper.sol // @audit _message 90: function sendMessageToL1(bytes memory _message) internal returns (bytes32) {
[90]
File: code/contracts/zksync/contracts/SystemContractsCaller.sol // @audit data 37: function systemCall(uint32 gasLimit, address to, uint256 value, bytes memory data) internal returns (bool success) { // @audit data 76: function systemCallWithReturndata( 77: uint32 gasLimit, 78: address to, 79: uint128 value, 80: bytes memory data
File: code/system-contracts/contracts/libraries/SystemContractsCaller.sol // @audit data 76: function systemCall(uint32 gasLimit, address to, uint256 value, bytes memory data) internal returns (bool success) { // @audit data 123: function systemCallWithReturndata( 124: uint32 gasLimit, 125: address to, 126: uint128 value, 127: bytes memory data // @audit data 150: function systemCallWithPropagatedRevert( 151: uint32 gasLimit, 152: address to, 153: uint128 value, 154: bytes memory data
File: code/contracts/ethereum/contracts/bridge/L1SharedBridge.sol // @audit _messageParams 458: function _checkWithdrawal( 459: uint256 _chainId, 460: MessageParams memory _messageParams, 461: bytes calldata _message, 462: bytes32[] calldata _merkleProof // @audit _l2ToL1message 487: function _parseL2WithdrawalMessage( 488: uint256 _chainId, 489: bytes memory _l2ToL1message
File: code/contracts/zksync/contracts/bridge/L2StandardERC20.sol // @audit _data 50: function bridgeInitialize(address _l1Address, bytes memory _data) external initializer { // @audit _newName, _newSymbol 111: function reinitializeToken( 112: ERC20Getters calldata _availableGetters, 113: string memory _newName, 114: string memory _newSymbol, 115: uint8 _version // @audit _input 178: function decodeString(bytes memory _input) external pure returns (string memory result) { // @audit _input 183: function decodeUint8(bytes memory _input) external pure returns (uint8 result) {
File: code/contracts/ethereum/contracts/common/libraries/L2ContractHelper.sol // @audit _bytecode 21: function hashL2Bytecode(bytes memory _bytecode) internal pure returns (bytes32 hashedBytecode) {
[21]
File: code/contracts/ethereum/contracts/common/libraries/UnsafeBytes.sol // @audit _bytes 19: function readUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32 result, uint256 offset) { // @audit _bytes 26: function readAddress(bytes memory _bytes, uint256 _start) internal pure returns (address result, uint256 offset) { // @audit _bytes 33: function readUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256 result, uint256 offset) { // @audit _bytes 40: function readBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32 result, uint256 offset) {
File: code/contracts/ethereum/contracts/state-transition/libraries/Diamond.sol // @audit _diamondCut 96: function diamondCut(DiamondCutData memory _diamondCut) internal { // @audit _selectors 126: function _addFunctions(address _facet, bytes4[] memory _selectors, bool _isFacetFreezable) private { // @audit _selectors 149: function _replaceFunctions(address _facet, bytes4[] memory _selectors, bool _isFacetFreezable) private { // @audit _selectors 172: function _removeFunctions(address _facet, bytes4[] memory _selectors) private { // @audit _calldata 278: function _initializeDiamondCut(address _init, bytes memory _calldata) private {
File: code/contracts/ethereum/contracts/state-transition/libraries/PriorityQueue.sol // @audit _operation 55: function pushBack(Queue storage _queue, PriorityOperation memory _operation) internal {
[55]
File: code/contracts/ethereum/contracts/state-transition/libraries/TransactionValidator.sol // @audit _transaction, _encoded 19: function validateL1ToL2Transaction( 20: L2CanonicalTransaction memory _transaction, 21: bytes memory _encoded, 22: uint256 _priorityTxMaxGasLimit, 23: uint256 _priorityTxMaxPubdata // @audit _transaction 46: function validateUpgradeTransaction(L2CanonicalTransaction memory _transaction) internal pure {
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Executor.sol // @audit _previousBatch 32: function _commitOneBatch( 33: StoredBatchInfo memory _previousBatch, 34: CommitBatchInfo calldata _newBatch, 35: bytes32 _expectedSystemContractUpgradeTxHash // @audit _verifierParams 453: function _getBatchProofPublicInput( 454: bytes32 _prevBatchCommitment, 455: bytes32 _currentBatchCommitment, 456: VerifierParams memory _verifierParams // @audit _blobCommitments, _blobHashes 561: function _encodeBlobAuxilaryOutput( 562: bytes32[] memory _blobCommitments, 563: bytes32[] memory _blobHashes // @audit _blobHashes 625: function _verifyBlobInformation( 626: bytes calldata _pubdataCommitments, 627: bytes32[] memory _blobHashes
[32-35, 453-456, 561-563, 625-627]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Mailbox.sol // @audit _log 104: function _proveL2LogInclusion( 105: uint256 _batchNumber, 106: uint256 _index, 107: L2Log memory _log, 108: bytes32[] calldata _proof // @audit _message 130: function _L2MessageToLog(L2Message memory _message) internal pure returns (L2Log memory) { // @audit _request 228: function _requestL2TransactionSender( 229: BridgehubL2TransactionRequest memory _request // @audit _calldata 258: function _requestL2Transaction( 259: uint256 _mintValue, 260: WritePriorityOpParams memory _params, 261: bytes memory _calldata, 262: bytes[] memory _factoryDeps // @audit _priorityOpParams, _calldata 289: function _serializeL2Transaction( 290: WritePriorityOpParams memory _priorityOpParams, 291: bytes memory _calldata, 292: bytes[] memory _factoryDeps // @audit _calldata 316: function _writePriorityOp( 317: WritePriorityOpParams memory _priorityOpParams, 318: bytes memory _calldata, 319: bytes[] memory _factoryDeps // @audit _factoryDeps 353: function _hashFactoryDeps(bytes[] memory _factoryDeps) internal pure returns (uint256[] memory hashedFactoryDeps) {
[104-108, 130, 228-229, 258-262, 289-292, 316-319, 353]
</details>If the old value is equal to the new value, not re-storing the value will avoid a Gsreset (2900 gas), potentially at the expense of a Gcoldsload (2100 gas) or a Gwarmaccess (100 gas)
There are 21 instances of this issue.
<details> <summary>Expand findings</summary>File: code/system-contracts/contracts/ImmutableSimulator.sol // @audit immutableDataStorage 34: function setImmutables(address _dest, ImmutableData[] calldata _immutables) external override {
[34]
File: code/system-contracts/contracts/NonceHolder.sol // @audit nonceValues 82: function setValueUnderNonce(uint256 _key, uint256 _value) public onlySystemCall {
[82]
File: code/system-contracts/contracts/SystemContext.sol // @audit chainId 84: function setChainId(uint256 _newChainId) external onlyCallFromForceDeployer { // @audit origin 99: function setTxOrigin(address _newOrigin) external onlyCallFromBootloader { // @audit gasPrice 105: function setGasPrice(uint256 _gasPrice) external onlyCallFromBootloader { // @audit basePubdataSpent, gasPerPubdataByte 112: function setPubdataInfo(uint256 _gasPerPubdataByte, uint256 _basePubdataSpent) external onlyCallFromBootloader { // @audit l2BlockHash 212: function _setL2BlockHash(uint256 _block, bytes32 _hash) internal { // @audit currentVirtualL2BlockInfo, currentVirtualL2BlockInfo 263: function _setVirtualBlock( // @audit currentL2BlockInfo, currentL2BlockTxsRollingHash 314: function _setNewL2BlockData(uint128 _l2BlockNumber, uint128 _l2BlockTimestamp, bytes32 _prevL2BlockHash) internal { // @audit batchHashes, currentBatchInfo, baseFee 444: function setNewBatch(
[84, 99, 105, 112, 212, 263, 314, 444]
File: code/contracts/ethereum/contracts/bridgehub/Bridgehub.sol // @audit pendingAdmin 51: function setPendingAdmin(address _newPendingAdmin) external onlyOwnerOrAdmin { // @audit sharedBridge 108: function setSharedBridge(address _sharedBridge) external onlyOwner {
File: code/contracts/ethereum/contracts/governance/Governance.sol // @audit minDelay 249: function updateDelay(uint256 _newDelay) external onlySelf { // @audit securityCouncil 256: function updateSecurityCouncil(address _newSecurityCouncil) external onlySelf {
File: code/contracts/ethereum/contracts/state-transition/StateTransitionManager.sol // @audit pendingAdmin 110: function setPendingAdmin(address _newPendingAdmin) external onlyOwnerOrAdmin { // @audit validatorTimelock 132: function setValidatorTimelock(address _validatorTimelock) external onlyOwnerOrAdmin { // @audit initialCutHash 137: function setInitialCutHash(Diamond.DiamondCutData calldata _diamondCut) external onlyOwner { // @audit upgradeCutHash, protocolVersion 142: function setNewVersionUpgrade( // @audit upgradeCutHash 152: function setUpgradeDiamondCut(
</details>File: code/contracts/ethereum/contracts/state-transition/ValidatorTimelock.sol // @audit stateTransitionManager 73: function setStateTransitionManager(IStateTransitionManager _stateTransitionManager) external onlyOwner { // @audit executionDelay 96: function setExecutionDelay(uint32 _executionDelay) external onlyOwner {
emit
inside a loopEmitting an event inside a loop performs a LOG
op N times, where N is the loop length. Consider refactoring the code to emit the event only once at the end of loop. Gas savings should be multiplied by the average loop length.
There are 3 instances of this issue.
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Executor.sol 261: emit BlockCommit( 262: _lastCommittedBatchData.batchNumber, 263: _lastCommittedBatchData.batchHash, 264: _lastCommittedBatchData.commitment 265: ); 299: emit BlockCommit( 300: _lastCommittedBatchData.batchNumber, 301: _lastCommittedBatchData.batchHash, 302: _lastCommittedBatchData.commitment 303: ); 353: emit BlockExecution(_batchesData[i].batchNumber, _batchesData[i].batchHash, _batchesData[i].commitment);
uint256(1)/uint256(2)
instead of true/false
to save gas for changesUse uint256(1)
and uint256(2)
for true
/false
to avoid a Gsset (20000 gas) when changing from false
to true
, after having been true
in the past. See source.
There are 6 instances of this issue.
File: code/contracts/ethereum/contracts/bridge/L1ERC20Bridge.sol 27: mapping(uint256 l2BatchNumber => mapping(uint256 l2ToL1MessageNumber => bool isFinalized)) 28: public isWithdrawalFinalized;
[27-28]
File: code/contracts/ethereum/contracts/bridge/L1SharedBridge.sol 57: mapping(uint256 chainId => mapping(uint256 l2BatchNumber => mapping(uint256 l2ToL1MessageNumber => bool isFinalized))) 58: public isWithdrawalFinalized; 61: mapping(uint256 chainId => bool enabled) internal hyperbridgingEnabled;
File: code/contracts/ethereum/contracts/bridgehub/Bridgehub.sol 21: mapping(address _stateTransitionManager => bool) public stateTransitionManagerIsRegistered; 23: mapping(address _token => bool) public tokenIsRegistered;
File: code/contracts/ethereum/contracts/state-transition/ValidatorTimelock.sol 50: mapping(uint256 _chainId => mapping(address _validator => bool)) public validators;
[50]
Some conditions may be reordered to save an SLOAD
(2100 gas), as we avoid reading state variables when the first part of the condition fails (with &&
), or succeeds (with ||
).
There are 2 instances of this issue.
File: code/system-contracts/contracts/SystemContext.sol // @audit switch with this condition // virtualBlockInfo.timestamp == 0 && currentVirtualL2BlockInfo.number == 0 277: if (currentVirtualL2BlockInfo.number == 0 && virtualBlockInfo.timestamp == 0) {
[277]
File: code/contracts/ethereum/contracts/bridge/L1SharedBridge.sol // @audit switch with this condition // (_chainId == ERA_CHAIN_ID && msg.sender == ERA_DIAMOND_PROXY) || msg.sender == address(bridgehub) 76: msg.sender == address(bridgehub) || (_chainId == ERA_CHAIN_ID && msg.sender == ERA_DIAMOND_PROXY),
[76]
Consider using a local storage
or calldata
variable when accessing a mapping/array value multiple times.
This can be useful to avoid recalculating the mapping hash and/or the array offsets.
There are 8 instances of this issue.
File: code/contracts/ethereum/contracts/bridge/L1SharedBridge.sol // @audit chainBalance on lines 122, 123, 133 133: chainBalance[_targetChainId][_token] = chainBalance[_targetChainId][_token] + amount; // @audit depositHappened on line 237 238: depositHappened[_chainId][_txHash] = _txDataHash; // @audit depositHappened[_chainId] on line 340 343: delete depositHappened[_chainId][_l2TxHash]; // @audit chainBalance on line 349 350: chainBalance[_chainId][_l1Token] -= _amount; // @audit isWithdrawalFinalized[_chainId] on line 417 418: isWithdrawalFinalized[_chainId][_l2BatchNumber][_l2MessageIndex] = true; // @audit chainBalance on line 439 440: chainBalance[_chainId][l1Token] -= amount;
[133, 238, 343, 350, 418, 440]
File: code/contracts/ethereum/contracts/state-transition/ValidatorTimelock.sol // @audit validators on line 79 82: validators[_chainId][_newValidator] = true; // @audit validators on line 88 91: validators[_chainId][_validator] = false;
private
for constants saves gasSaves deployment gas due to the compiler not having to create non-payable getter functions for deployment calldata, not having to store the bytes of the value outside of where it's used, and not adding another entry to the method ID table.
There are 5 instances of this issue.
File: code/contracts/ethereum/contracts/state-transition/ValidatorTimelock.sol 26: string public constant override getName = "ValidatorTimelock";
[26]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Admin.sol 20: string public constant override getName = "AdminFacet";
[20]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Executor.sol 27: string public constant override getName = "ExecutorFacet";
[27]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Getters.sol 25: string public constant override getName = "GettersFacet";
[25]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Mailbox.sol 35: string public constant override getName = "MailboxFacet";
[35]
Checks that can be performed earlier should come before checks that involve state variables, function calls, and calculations. By doing these checks first, the function is able to revert before wasting a Gcoldsload (2100 gas) in a function that may ultimately revert.
There are 7 instances of this issue.
File: code/system-contracts/contracts/NonceHolder.sol // @audit expensive op on line 83 85: require(_value != 0, "Nonce value cannot be set to 0");
[85]
File: code/system-contracts/contracts/SystemContext.sol // @audit expensive op on line 268, 271, 275, 277, 278, 285 287: require(_maxVirtualBlocksToCreate > 0, "Can't initialize the first virtual block"); // @audit expensive op on line 350 355: require(_maxVirtualBlocksToCreate > 0, "There must be a virtual block created at the start of the batch"); // @audit expensive op on line 358, 363, 365, 370, 350 373: require(_maxVirtualBlocksToCreate == 0, "Can not create virtual blocks in the middle of the miniblock");
File: code/contracts/ethereum/contracts/bridge/L1SharedBridge.sol // @audit expensive op on line 316 327: require(_amount > 0, "y1");
[327]
File: code/contracts/zksync/contracts/bridge/L2SharedBridge.sol // @audit expensive op on line 64, 65, 66, 60, 61 68: require(_l1LegecyBridge != address(0), "bf2");
[68]
File: code/contracts/ethereum/contracts/state-transition/libraries/Diamond.sol // @audit expensive op on line 173 175: require(_facet == address(0), "a1"); // facet address must be zero
[175]
error
s cost less than require
/assert
Consider the use of a custom error
, as it leads to a cheaper deploy cost and run time cost. The run time cost is only relevant when the revert condition is met.
There are 333 instances of this issue.
<details> <summary>Expand findings</summary>File: code/system-contracts/contracts/AccountCodeStorage.sol 26: require(msg.sender == address(DEPLOYER_SYSTEM_CONTRACT), "Callable only by the deployer system contract"); 37: require(Utils.isContractConstructing(_hash), "Code hash is not for a contract on constructor"); 48: require(Utils.isContractConstructed(_hash), "Code hash is not for a constructed contract"); 57: require(Utils.isContractConstructing(codeHash), "Code hash is not for a contract on constructor");
File: code/system-contracts/contracts/BootloaderUtilities.sol 92: require(vInt == 27 || vInt == 28, "Invalid v value"); 191: require(vInt == 27 || vInt == 28, "Invalid v value"); 286: require(vInt == 27 || vInt == 28, "Invalid v value");
File: code/system-contracts/contracts/ComplexUpgrader.sol 22: require(msg.sender == FORCE_DEPLOYER, "Can only be called by FORCE_DEPLOYER"); 24: require(_delegateTo.code.length > 0, "Delegatee is an EOA");
File: code/system-contracts/contracts/Compressor.sol 51: require( 52: encodedData.length * 4 == _bytecode.length, 53: "Encoded data length should be 4 times shorter than the original bytecode" 54: ); 56: require( 57: dictionary.length / 8 <= encodedData.length / 2, 58: "Dictionary should have at most the same number of entries as the encoded data" 59: ); 63: require(indexOfEncodedChunk < dictionary.length, "Encoded chunk index is out of bounds"); 68: require(encodedChunk == realChunk, "Encoded chunk does not match the original bytecode"); 119: require(_enumerationIndexSize <= MAX_ENUMERATION_INDEX_SIZE, "enumeration index size is too large"); 140: require(derivedKey == _compressedStateDiffs.readBytes32(stateDiffPtr), "iw: initial key mismatch"); 156: require(numInitialWritesProcessed == numberOfInitialWrites, "Incorrect number of initial storage diffs"); 171: require(enumIndex == compressedEnumIndex, "rw: enum key mismatch"); 187: require(stateDiffPtr == _compressedStateDiffs.length, "Extra data in _compressedStateDiffs"); 230: require(convertedValue == _finalValue, "transform or no compression: compressed and final mismatch"); 232: require( 233: _initialValue + convertedValue == _finalValue, 234: "add: initial plus converted not equal to final" 235: ); 237: require( 238: _initialValue - convertedValue == _finalValue, 239: "sub: initial minus converted not equal to final" 240: );
[51-54, 56-59, 63, 68, 119, 140, 156, 171, 187, 230, 232-235, 237-240]
File: code/system-contracts/contracts/ContractDeployer.sol 29: require(msg.sender == address(this), "Callable only by self"); 77: require( 78: _nonceOrdering == AccountNonceOrdering.Arbitrary && 79: currentInfo.nonceOrdering == AccountNonceOrdering.Sequential, 80: "It is only possible to change from sequential to arbitrary ordering" 81: ); 239: require( 240: msg.sender == FORCE_DEPLOYER || msg.sender == address(COMPLEX_UPGRADER_CONTRACT), 241: "Can only be called by FORCE_DEPLOYER or COMPLEX_UPGRADER_CONTRACT" 242: ); 251: require(msg.value == sumOfValues, "`value` provided is not equal to the combined `value`s of deployments"); 264: require(_bytecodeHash != bytes32(0x0), "BytecodeHash cannot be zero"); 265: require(uint160(_newAddress) > MAX_SYSTEM_CONTRACT_ADDRESS, "Can not deploy contracts in kernel space"); 268: require( 269: ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT.getCodeHash(uint256(uint160(_newAddress))) == 0x0, 270: "Code hash is non-zero" 271: ); 273: require(NONCE_HOLDER_SYSTEM_CONTRACT.getRawNonce(_newAddress) == 0x00, "Account is occupied"); 303: require(knownCodeMarker > 0, "The code hash is not known"); 350: require(value == 0, "The value must be zero if we do not call the constructor");
[29, 77-81, 239-242, 251, 264, 265, 268-271, 273, 303, 350]
File: code/system-contracts/contracts/DefaultAccount.sol 100: require(totalRequiredBalance <= address(this).balance, "Not enough balance for fee + value"); 160: require(_signature.length == 65, "Signature length is incorrect"); 173: require(v == 27 || v == 28, "v is neither 27 nor 28"); 184: require(uint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, "Invalid s"); 202: require(success, "Failed to pay the fee to the operator"); 222: assert(msg.sender != BOOTLOADER_FORMAL_ADDRESS);
[100, 160, 173, 184, 202, 222]
File: code/system-contracts/contracts/GasBoundCaller.sol 46: require(_maxTotalGas >= gasleft(), "Gas limit is too low"); 76: require(pubdataAllowance + gasleft() >= pubdataCost + CALL_RETURN_OVERHEAD, "Not enough gas for pubdata");
File: code/system-contracts/contracts/ImmutableSimulator.sol 35: require(msg.sender == address(DEPLOYER_SYSTEM_CONTRACT), "Callable only by the deployer system contract");
[35]
File: code/system-contracts/contracts/KnownCodesStorage.sol 20: require(msg.sender == address(COMPRESSOR_CONTRACT), "Callable only by the compressor"); 76: require(version == 1 && _bytecodeHash[1] == bytes1(0), "Incorrectly formatted bytecodeHash"); 78: require(Utils.bytecodeLenInWords(_bytecodeHash) % 2 == 1, "Code length in words must be odd");
File: code/system-contracts/contracts/L1Messenger.sol 201: require(numberOfL2ToL1Logs <= L2_TO_L1_LOGS_MERKLE_TREE_LEAVES, "Too many L2->L1 logs"); 214: require( 215: reconstructedChainedLogsHash == chainedLogsHash, 216: "reconstructedChainedLogsHash is not equal to chainedLogsHash" 217: ); 245: require( 246: reconstructedChainedMessagesHash == chainedMessagesHash, 247: "reconstructedChainedMessagesHash is not equal to chainedMessagesHash" 248: ); 269: require( 270: reconstructedChainedL1BytecodesRevealDataHash == chainedL1BytecodesRevealDataHash, 271: "reconstructedChainedL1BytecodesRevealDataHash is not equal to chainedL1BytecodesRevealDataHash" 272: ); 279: require( 280: uint256(uint8(bytes1(_totalL2ToL1PubdataAndStateDiffs[calldataPtr]))) == 281: STATE_DIFF_COMPRESSION_VERSION_NUMBER, 282: "state diff compression version mismatch" 283: ); 298: require(calldataPtr <= MAX_ALLOWED_PUBDATA_PER_BATCH, "L1 Messenger pubdata is too long"); 315: require(calldataPtr == _totalL2ToL1PubdataAndStateDiffs.length, "Extra data in the totalL2ToL1Pubdata array");
[201, 214-217, 245-248, 269-272, 279-283, 298, 315]
File: code/system-contracts/contracts/L2BaseToken.sol 33: require( 34: msg.sender == MSG_VALUE_SYSTEM_CONTRACT || 35: msg.sender == address(DEPLOYER_SYSTEM_CONTRACT) || 36: msg.sender == BOOTLOADER_FORMAL_ADDRESS, 37: "Only system contracts with special access can call this method" 38: ); 41: require(fromBalance >= _amount, "Transfer amount exceeds balance");
File: code/system-contracts/contracts/MsgValueSimulator.sol 60: require(to != address(this), "MsgValueSimulator calls itself");
[60]
File: code/system-contracts/contracts/NonceHolder.sol 66: require(_value <= MAXIMAL_MIN_NONCE_INCREMENT, "The value for incrementing the nonce is too high"); 85: require(_value != 0, "Nonce value cannot be set to 0"); 89: require(isNonceUsed(msg.sender, _key - 1), "Previous nonce has not been used"); 115: require(oldMinNonce == _expectedNonce, "Incorrect nonce"); 136: require( 137: msg.sender == address(DEPLOYER_SYSTEM_CONTRACT), 138: "Only the contract deployer can increment the deployment nonce" 139: );
File: code/system-contracts/contracts/PubdataChunkPublisher.sol 22: require(_pubdata.length <= BLOB_SIZE_BYTES * MAX_NUMBER_OF_BLOBS, "pubdata should fit in 2 blobs");
[22]
File: code/system-contracts/contracts/SystemContext.sol 242: require(_isFirstInBatch, "Upgrade transaction must be first"); 245: require(_l2BlockNumber > 0, "L2 block number is never expected to be zero"); 249: require(correctPrevBlockHash == _expectedPrevL2BlockHash, "The previous L2 block hash is incorrect"); 287: require(_maxVirtualBlocksToCreate > 0, "Can't initialize the first virtual block"); 351: require( 352: _l2BlockTimestamp >= currentBatchTimestamp, 353: "The timestamp of the L2 block must be greater than or equal to the timestamp of the current batch" 354: ); 355: require(_maxVirtualBlocksToCreate > 0, "There must be a virtual block created at the start of the batch"); 367: require(!_isFirstInBatch, "Can not reuse L2 block number from the previous batch"); 368: require(currentL2BlockTimestamp == _l2BlockTimestamp, "The timestamp of the same L2 block must be same"); 369: require( 370: _expectedPrevL2BlockHash == _getLatest257L2blockHash(_l2BlockNumber - 1), 371: "The previous hash of the same L2 block must be same" 372: ); 373: require(_maxVirtualBlocksToCreate == 0, "Can not create virtual blocks in the middle of the miniblock"); 385: require(_expectedPrevL2BlockHash == pendingL2BlockHash, "The current L2 block hash is incorrect"); 386: require( 387: _l2BlockTimestamp > currentL2BlockTimestamp, 388: "The timestamp of the new L2 block must be greater than the timestamp of the previous L2 block" 389: ); 413: require(currentBatchNumber > 0, "The current batch number must be greater than 0"); 429: require( 430: _newTimestamp > currentBlockTimestamp, 431: "The timestamp of the batch must be greater than the timestamp of the previous block" 432: ); 451: require(_newTimestamp > previousBatchTimestamp, "Timestamps should be incremental"); 452: require(previousBatchNumber + 1 == _expectedNewNumber, "The provided block number is not correct");
[242, 245, 249, 287, 351-354, 355, 367, 368, 369-372, 373, 385, 386-389, 413, 429-432, 451, 452]
File: code/contracts/zksync/contracts/SystemContractsCaller.sol 28: require(_x <= type(uint32).max, "Overflow");
[28]
File: code/system-contracts/contracts/interfaces/ISystemContract.sol 21: require( 22: SystemContractHelper.isSystemCall() || SystemContractHelper.isSystemContract(msg.sender), 23: "This method require system call flag" 24: ); 31: require( 32: SystemContractHelper.isSystemContract(msg.sender), 33: "This method require the caller to be system contract" 34: ); 41: require(msg.sender == caller, "Inappropriate caller"); 48: require(msg.sender == BOOTLOADER_FORMAL_ADDRESS, "Callable only by the bootloader"); 55: require(msg.sender == FORCE_DEPLOYER);
File: code/system-contracts/contracts/libraries/EfficientCall.sol 38: require(returnData.length == 32, "keccak256 returned invalid data"); 47: require(returnData.length == 32, "sha returned invalid data");
File: code/system-contracts/contracts/libraries/RLPEncoder.sol 50: assert(_len != 1);
[50]
File: code/system-contracts/contracts/libraries/SystemContractHelper.sol 319: require(index < 10, "There are only 10 accessible registers"); 350: require(precompileCallSuccess, "Failed to charge gas");
File: code/system-contracts/contracts/libraries/TransactionHelper.sol 363: require(_transaction.paymasterInput.length >= 4, "The standard paymaster input must be at least 4 bytes long"); 367: require( 368: _transaction.paymasterInput.length >= 68, 369: "The approvalBased paymaster input must be at least 68 bytes long" 370: );
File: code/system-contracts/contracts/libraries/Utils.sol 21: require(_x <= type(uint128).max, "Overflow"); 27: require(_x <= type(uint32).max, "Overflow"); 33: require(_x <= type(uint24).max, "Overflow"); 84: require(_bytecode.length % 32 == 0, "po"); 87: require(lengthInWords < 2 ** 16, "pp"); // bytecode length must be less than 2^16 words 88: require(lengthInWords % 2 == 1, "pr"); // bytecode length in words must be odd
File: code/contracts/ethereum/contracts/bridge/L1ERC20Bridge.sol 64: require(msg.sender == address(sharedBridge), "Not shared bridge"); 66: require(amount == _amount, "Incorrect amount"); 141: require(_amount != 0, "0T"); // empty deposit 143: require(amount == _amount, "3T"); // The token has non-standard transfer logic 186: require(amount != 0, "2T"); // empty deposit 215: require(!isWithdrawalFinalized[_l2BatchNumber][_l2MessageIndex], "pw");
File: code/contracts/ethereum/contracts/bridge/L1SharedBridge.sol 69: require(msg.sender == address(bridgehub), "ShB not BH"); 75: require( 76: msg.sender == address(bridgehub) || (_chainId == ERA_CHAIN_ID && msg.sender == ERA_DIAMOND_PROXY), 77: "L1SharedBridge: not bridgehub or era chain" 78: ); 84: require(msg.sender == address(legacyBridge), "ShB not legacy bridge"); 108: require(_owner != address(0), "ShB owner 0"); 121: require(balanceAfter > balanceBefore, "ShB: 0 eth transferred"); 129: require(amount > 0, "ShB: 0 amount to transfer"); 132: require(balanceAfter - balanceBefore == amount, "ShB: wrong amount transferred"); 138: require(bridgehub.getStateTransition(_chainId) == msg.sender, "receiveEth not state transition"); 155: require(msg.value == _amount, "L1SharedBridge: msg.value not equal to amount"); 158: require(msg.value == 0, "ShB m.v > 0 b d.it"); 161: require(amount == _amount, "3T"); // The token has non-standard transfer logic 188: require(l2BridgeAddress[_chainId] != address(0), "ShB l2 bridge not deployed"); 194: require(_l1Token != l1WethAddress, "ShB: WETH deposit not supported"); 195: require(bridgehub.baseToken(_chainId) != _l1Token, "ShB: baseToken deposit not supported"); 200: require(_depositAmount == 0, "ShB wrong withdraw amount"); 202: require(msg.value == 0, "ShB m.v > 0 for BH d.it 2"); 206: require(withdrawAmount == _depositAmount, "5T"); // The token has non-standard transfer logic 208: require(amount != 0, "6T"); // empty deposit amount 237: require(depositHappened[_chainId][_txHash] == 0x00, "ShB tx hap"); 325: require(proofValid, "yn"); 327: require(_amount > 0, "y1"); 342: require(dataHash == txDataHash, "ShB: d.it not hap"); 349: require(chainBalance[_chainId][_l1Token] >= _amount, "ShB n funds"); 360: require(callSuccess, "ShB: claimFailedDeposit failed"); 396: require(!legacyBridge.isWithdrawalFinalized(_l2BatchNumber, _l2MessageIndex), "ShB: legacy withdrawal"); 417: require(!isWithdrawalFinalized[_chainId][_l2BatchNumber][_l2MessageIndex], "Withdrawal is already finalized"); 427: require(!alreadyFinalized, "Withdrawal is already finalized 2"); 439: require(chainBalance[_chainId][l1Token] >= amount, "ShB not enough funds 2"); // not enought funds 449: require(callSuccess, "ShB: withdraw failed"); 484: require(success, "ShB withd w proof"); // withdrawal wrong proof 501: require(_l2ToL1message.length >= 56, "ShB wrong msg len"); // wrong messsage length 517: require(_l2ToL1message.length == 76, "ShB wrong msg len 2"); 563: require(l2BridgeAddress[ERA_CHAIN_ID] != address(0), "ShB b. n dep"); 564: require(_l1Token != l1WethAddress, "ShB: WETH deposit not supported 2");
[69, 75-78, 84, 108, 121, 129, 132, 138, 155, 158, 161, 188, 194, 195, 200, 202, 206, 208, 237, 325, 327, 342, 349, 360, 396, 417, 427, 439, 449, 484, 501, 517, 563, 564]
File: code/contracts/ethereum/contracts/bridgehub/Bridgehub.sol 46: require(msg.sender == admin || msg.sender == owner(), "Bridgehub: not owner or admin"); 62: require(msg.sender == currentPendingAdmin, "n42"); // Only proposed by current admin address can claim the admin rights 83: require( 84: !stateTransitionManagerIsRegistered[_stateTransitionManager], 85: "Bridgehub: state transition already registered" 86: ); 93: require( 94: stateTransitionManagerIsRegistered[_stateTransitionManager], 95: "Bridgehub: state transition not registered yet" 96: ); 102: require(!tokenIsRegistered[_token], "Bridgehub: token already registered"); 122: require(_chainId != 0, "Bridgehub: chainId cannot be 0"); 123: require(_chainId <= type(uint48).max, "Bridgehub: chainId too large"); 125: require( 126: stateTransitionManagerIsRegistered[_stateTransitionManager], 127: "Bridgehub: state transition not registered" 128: ); 129: require(tokenIsRegistered[_baseToken], "Bridgehub: token not registered"); 130: require(address(sharedBridge) != address(0), "Bridgehub: weth bridge not set"); 132: require(stateTransitionManager[_chainId] == address(0), "Bridgehub: chainId already registered"); 223: require(msg.value == _request.mintValue, "Bridgehub: msg.value mismatch 1"); 225: require(msg.value == 0, "Bridgehub: non-eth bridge with msg.value"); 269: require( 270: msg.value == _request.mintValue + _request.secondBridgeValue, 271: "Bridgehub: msg.value mismatch 2" 272: ); 275: require(msg.value == _request.secondBridgeValue, "Bridgehub: msg.value mismatch 3"); 296: require(outputRequest.magicValue == TWO_BRIDGES_MAGIC_VALUE, "Bridgehub: magic value mismatch"); 300: require( 301: _request.secondBridgeAddress > BRIDGEHUB_MIN_SECOND_BRIDGE_ADDRESS, 302: "Bridgehub: second bridge address too low" 303: ); // to avoid calls to precompiles
[46, 62, 83-86, 93-96, 102, 122, 123, 125-128, 129, 130, 132, 223, 225, 269-272, 275, 296, 300-303]
File: code/contracts/ethereum/contracts/common/ReentrancyGuard.sol 58: require(lockSlotOldValue == 0, "1B"); 75: require(_status == _NOT_ENTERED, "r1");
File: code/contracts/ethereum/contracts/governance/Governance.sol 42: require(_admin != address(0), "Admin should be non zero address"); 59: require(msg.sender == address(this), "Only governance contract itself is allowed to call this function"); 65: require(msg.sender == securityCouncil, "Only security council is allowed to call this function"); 71: require( 72: msg.sender == owner() || msg.sender == securityCouncil, 73: "Only the owner and security council are allowed to call this function" 74: ); 155: require(isOperationPending(_id), "Operation must be pending"); 172: require(isOperationReady(id), "Operation must be ready before execution"); 177: require(isOperationReady(id), "Operation must be ready after execution"); 191: require(isOperationPending(id), "Operation must be pending before execution"); 196: require(isOperationPending(id), "Operation must be pending after execution"); 216: require(!isOperation(_id), "Operation with this proposal id already exists"); 217: require(_delay >= minDelay, "Proposed delay is less than minimum delay"); 240: require(_predecessorId == bytes32(0) || isOperationDone(_predecessorId), "Predecessor operation not completed");
[42, 59, 65, 71-74, 155, 172, 177, 191, 196, 216, 217, 240]
File: code/contracts/ethereum/contracts/state-transition/StateTransitionManager.sol 64: require(msg.sender == bridgehub, "StateTransition: only bridgehub"); 70: require(msg.sender == admin || msg.sender == owner(), "Bridgehub: not owner or admin"); 82: require(_initializeData.governor != address(0), "StateTransition: governor zero"); 106: assert(L2_TO_L1_LOG_SERIALIZE_SIZE != 2 * 32); 121: require(msg.sender == currentPendingAdmin, "n42"); // Only proposed by current admin address can claim the admin rights 256: require(cutHashInput == initialCutHash, "StateTransition: initial cutHash mismatch");
File: code/contracts/ethereum/contracts/state-transition/ValidatorTimelock.sol 62: require(msg.sender == stateTransitionManager.getChainAdmin(_chainId), "ValidatorTimelock: only chain admin"); 68: require(validators[_chainId][msg.sender] == true, "ValidatorTimelock: only validator"); 195: require(block.timestamp >= commitBatchTimestamp + delay, "5c"); // The delay is not passed 217: require(block.timestamp >= commitBatchTimestamp + delay, "5c"); // The delay is not passed
File: code/contracts/ethereum/contracts/upgrades/BaseZkSyncUpgrade.sol 72: require(block.timestamp >= _proposedUpgrade.upgradeTimestamp, "Upgrade is not ready yet"); 190: require(_l2ProtocolUpgradeTx.txType == SYSTEM_UPGRADE_L2_TX_TYPE, "L2 system upgrade tx type is wrong"); 205: require( 206: _l2ProtocolUpgradeTx.nonce == _newProtocolVersion, 207: "The new protocol version should be included in the L2 system upgrade tx" 208: ); 223: require(_factoryDeps.length == _expectedHashes.length, "Wrong number of factory deps"); 224: require(_factoryDeps.length <= MAX_NEW_FACTORY_DEPS, "Factory deps can be at most 32"); 227: require( 228: L2ContractHelper.hashL2Bytecode(_factoryDeps[i]) == bytes32(_expectedHashes[i]), 229: "Wrong factory dep hash" 230: ); 238: require( 239: _newProtocolVersion > previousProtocolVersion, 240: "New protocol version is not greater than the current one" 241: ); 242: require( 243: _newProtocolVersion - previousProtocolVersion <= MAX_ALLOWED_PROTOCOL_VERSION_DELTA, 244: "Too big protocol version difference" 245: ); 249: require(s.l2SystemContractsUpgradeTxHash == bytes32(0), "Previous upgrade has not been finalized"); 250: require( 251: s.l2SystemContractsUpgradeBatchNumber == 0, 252: "The batch number of the previous upgrade has not been cleaned" 253: );
[72, 190, 205-208, 223, 224, 227-230, 238-241, 242-245, 249, 250-253]
File: code/contracts/ethereum/contracts/upgrades/BaseZkSyncUpgradeGenesis.sol 22: require( 23: // Genesis Upgrade difference: Note this is the only thing change > to >= 24: _newProtocolVersion >= previousProtocolVersion, 25: "New protocol version is not greater than the current one" 26: ); 27: require( 28: _newProtocolVersion - previousProtocolVersion <= MAX_ALLOWED_PROTOCOL_VERSION_DELTA, 29: "Too big protocol version difference" 30: ); 33: require(s.l2SystemContractsUpgradeTxHash == bytes32(0), "Previous upgrade has not been finalized"); 34: require( 35: s.l2SystemContractsUpgradeBatchNumber == 0, 36: "The batch number of the previous upgrade has not been cleaned" 37: ); 52: require(block.timestamp >= _proposedUpgrade.upgradeTimestamp, "Upgrade is not ready yet");
File: code/contracts/zksync/contracts/bridge/L2SharedBridge.sol 55: require(_l1Bridge != address(0), "bf"); 56: require(_l2TokenProxyBytecodeHash != bytes32(0), "df"); 57: require(_aliasedOwner != address(0), "sf"); 58: require(_l2TokenProxyBytecodeHash != bytes32(0), "df"); 68: require(_l1LegecyBridge != address(0), "bf2"); 87: require( 88: AddressAliasHelper.undoL1ToL2Alias(msg.sender) == l1Bridge || 89: AddressAliasHelper.undoL1ToL2Alias(msg.sender) == l1LegacyBridge, 90: "mq" 91: ); 92: require(msg.value == 0, "Value should be 0 for ERC20 bridge"); 98: require(deployedToken == expectedL2Token, "mt"); 101: require(currentL1Token == _l1Token, "gg"); // Double check that the expected value equal to real one 124: require(_amount > 0, "Amount cannot be zero"); 129: require(l1Token != address(0), "yh"); 176: require(success, "mk");
[55, 56, 57, 58, 68, 87-91, 92, 98, 101, 124, 129, 176]
File: code/contracts/zksync/contracts/bridge/L2StandardERC20.sol 51: require(_l1Address != address(0), "in6"); // Should be non-zero address 120: require(msg.sender == UpgradeableBeacon(beaconAddress).owner(), "tt"); 130: require(msg.sender == l2Bridge, "xnt"); // Only L2 bridge can call this method 137: require(_version == _getInitializedVersion() + 1, "v");
File: code/contracts/ethereum/contracts/common/libraries/L2ContractHelper.sol 23: require(_bytecode.length % 32 == 0, "pq"); 26: require(bytecodeLenInWords < 2 ** 16, "pp"); // bytecode length must be less than 2^16 words 27: require(bytecodeLenInWords % 2 == 1, "ps"); // bytecode length in words must be odd 41: require(version == 1 && _bytecodeHash[1] == bytes1(0), "zf"); // Incorrectly formatted bytecodeHash 43: require(bytecodeLen(_bytecodeHash) % 2 == 1, "uy"); // Code length in words must be odd
File: code/contracts/ethereum/contracts/state-transition/chain-deps/DiamondInit.sol 25: require(address(_initializeData.verifier) != address(0), "vt"); 26: require(_initializeData.admin != address(0), "vy"); 27: require(_initializeData.validatorTimelock != address(0), "hc"); 28: require(_initializeData.priorityTxMaxGasLimit <= MAX_GAS_PER_TRANSACTION, "vu"); 51: assert(L2_TO_L1_LOG_SERIALIZE_SIZE != 2 * 32);
File: code/contracts/ethereum/contracts/state-transition/chain-deps/DiamondProxy.sol 14: require(_chainId == block.chainid, "pr"); 25: require(msg.data.length >= 4 || msg.data.length == 0, "Ut"); 30: require(facetAddress != address(0), "F"); // Proxy has no facet for this selector 31: require(!diamondStorage.isFrozen || !facet.isFreezable, "q1"); // Facet is frozen
File: code/contracts/ethereum/contracts/state-transition/libraries/Diamond.sol 107: require(selectors.length > 0, "B"); // no functions for diamond cut 132: require(_facet.code.length > 0, "G"); 141: require(oldFacet.facetAddress == address(0), "J"); // facet for this selector already exists 155: require(_facet.code.length > 0, "K"); 161: require(oldFacet.facetAddress != address(0), "L"); // it is impossible to replace the facet with zero address 175: require(_facet == address(0), "a1"); // facet address must be zero 181: require(oldFacet.facetAddress != address(0), "a2"); // Can't delete a non-existent facet 215: require(_isSelectorFreezable == ds.selectorToFacet[selector0].isFreezable, "J1"); 280: require(_calldata.length == 0, "H"); // Non-empty calldata for zero address 297: require(data.length == 32, "lp"); 298: require(abi.decode(data, (bytes32)) == DIAMOND_INIT_SUCCESS_RETURN_VALUE, "lp1");
[107, 132, 141, 155, 161, 175, 181, 215, 280, 297, 298]
File: code/contracts/ethereum/contracts/state-transition/libraries/Merkle.sol 24: require(pathLength > 0, "xc"); 25: require(pathLength < 256, "bt"); 26: require(_index < (1 << pathLength), "px");
File: code/contracts/ethereum/contracts/state-transition/libraries/PriorityQueue.sol 65: require(!_queue.isEmpty(), "D"); // priority queue is empty 73: require(!_queue.isEmpty(), "s"); // priority queue is empty
File: code/contracts/ethereum/contracts/state-transition/libraries/TransactionValidator.sol 28: require(l2GasForTxBody <= _priorityTxMaxGasLimit, "ui"); 30: require(l2GasForTxBody / _transaction.gasPerPubdataByteLimit <= _priorityTxMaxPubdata, "uk"); 34: require( 35: getMinimalPriorityTransactionGasLimit( 36: _encoded.length, 37: _transaction.factoryDeps.length, 38: _transaction.gasPerPubdataByteLimit 39: ) <= l2GasForTxBody, 40: "up" 41: ); 48: require(_transaction.from <= type(uint16).max, "ua"); 49: require(_transaction.to <= type(uint160).max, "ub"); 50: require(_transaction.paymaster == 0, "uc"); 51: require(_transaction.value == 0, "ud"); 52: require(_transaction.maxFeePerGas == 0, "uq"); 53: require(_transaction.maxPriorityFeePerGas == 0, "ux"); 54: require(_transaction.reserved[0] == 0, "ue"); 55: require(_transaction.reserved[1] <= type(uint160).max, "uf"); 56: require(_transaction.reserved[2] == 0, "ug"); 57: require(_transaction.reserved[3] == 0, "uo"); 58: require(_transaction.signature.length == 0, "uh"); 59: require(_transaction.paymasterInput.length == 0, "ul1"); 60: require(_transaction.reservedDynamic.length == 0, "um"); 115: require(_totalGasLimit >= overhead, "my"); // provided gas limit doesn't cover transaction overhead
[28, 30, 34-41, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 115]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Admin.sol 34: require(msg.sender == pendingAdmin, "n4"); // Only proposed by current admin address can claim the admin rights 59: require(_newPriorityTxMaxGasLimit <= MAX_GAS_PER_TRANSACTION, "n5"); 70: require(_newFeeParams.maxPubdataPerBatch >= _newFeeParams.priorityTxMaxPubdata, "n6"); 90: require(s.totalBatchesCommitted == 0, "AdminFacet: set validium only after genesis"); // Validium mode can be set only before the first batch is committed 105: require( 106: cutHashInput == IStateTransitionManager(s.stateTransitionManager).upgradeCutHash(_oldProtocolVersion), 107: "StateTransition: cutHash mismatch" 108: ); 110: require( 111: s.protocolVersion == _oldProtocolVersion, 112: "StateTransition: protocolVersion mismatch in STC when upgrading" 113: ); 116: require( 117: s.protocolVersion > _oldProtocolVersion, 118: "StateTransition: protocolVersion mismatch in STC after upgrading" 119: ); 136: require(!diamondStorage.isFrozen, "a9"); // diamond proxy is frozen already 146: require(diamondStorage.isFrozen, "a7"); // diamond proxy is not frozen
[34, 59, 70, 90, 105-108, 110-113, 116-119, 136, 146]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Executor.sol 37: require(_newBatch.batchNumber == _previousBatch.batchNumber + 1, "f"); // only commit next batch 40: require(pubdataSource == uint8(PubdataSource.Calldata) || pubdataSource == uint8(PubdataSource.Blob), "us"); 50: require(logOutput.pubdataHash == 0x00, "v0h"); 51: require(_newBatch.pubdataCommitments.length == 1); 61: require( 62: logOutput.pubdataHash == 63: keccak256(_newBatch.pubdataCommitments[1:_newBatch.pubdataCommitments.length - 32]), 64: "wp" 65: ); 74: require(_previousBatch.batchHash == logOutput.previousBatchHash, "l"); 76: require(logOutput.chainedPriorityTxsHash == _newBatch.priorityOperationsHash, "t"); 78: require(logOutput.numberOfLayer1Txs == _newBatch.numberOfLayer1Txs, "ta"); 110: require(batchTimestamp == _expectedBatchTimestamp, "tb"); 114: require(_previousBatchTimestamp < batchTimestamp, "h3"); 122: require(block.timestamp - COMMIT_TIMESTAMP_NOT_OLDER <= batchTimestamp, "h1"); // New batch timestamp is too small 123: require(lastL2BlockTimestamp <= block.timestamp + COMMIT_TIMESTAMP_APPROXIMATION_DELTA, "h2"); // The last L2 block timestamp is too big 149: require(!_checkBit(processedLogs, uint8(logKey)), "kp"); 154: require(logSender == L2_TO_L1_MESSENGER_SYSTEM_CONTRACT_ADDR, "lm"); 157: require(logSender == L2_TO_L1_MESSENGER_SYSTEM_CONTRACT_ADDR, "ln"); 160: require(logSender == L2_TO_L1_MESSENGER_SYSTEM_CONTRACT_ADDR, "lb"); 163: require(logSender == L2_SYSTEM_CONTEXT_SYSTEM_CONTRACT_ADDR, "sc"); 166: require(logSender == L2_SYSTEM_CONTEXT_SYSTEM_CONTRACT_ADDR, "sv"); 169: require(logSender == L2_BOOTLOADER_ADDRESS, "bl"); 172: require(logSender == L2_BOOTLOADER_ADDRESS, "bk"); 175: require(logSender == L2_PUBDATA_CHUNK_PUBLISHER_ADDR, "pc"); 178: require(logSender == L2_PUBDATA_CHUNK_PUBLISHER_ADDR, "pd"); 181: require(logSender == L2_BOOTLOADER_ADDRESS, "bu"); 182: require(_expectedSystemContractUpgradeTxHash == logValue, "ut"); 192: require(processedLogs == 511, "b7"); 194: require(processedLogs == 1023, "b8"); 226: require( 227: IStateTransitionManager(s.stateTransitionManager).protocolVersion() == s.protocolVersion, 228: "Executor facet: wrong protocol version" 229: ); 231: require(_newBatchesData.length == 1, "e4"); 233: require(s.storedBatchHashes[s.totalBatchesCommitted] == _hashStoredBatchInfo(_lastCommittedBatchData), "i"); // incorrect previous batch data 284: require(s.l2SystemContractsUpgradeBatchNumber == 0, "ik"); 323: require(currentBatchNumber == s.totalBatchesExecuted + _executedBatchIdx + 1, "k"); // Execute batches in order 324: require( 325: _hashStoredBatchInfo(_storedBatch) == s.storedBatchHashes[currentBatchNumber], 326: "exe10" // executing batch should be committed 327: ); 330: require(priorityOperationsHash == _storedBatch.priorityOperationsHash, "x"); // priority operations hash does not match to expected 358: require(newTotalBatchesExecuted <= s.totalBatchesVerified, "n"); // Can't execute batches more than committed and proven currently. 402: require(_hashStoredBatchInfo(_prevBatch) == s.storedBatchHashes[currentTotalBatchesVerified], "t1"); 407: require( 408: _hashStoredBatchInfo(_committedBatches[i]) == s.storedBatchHashes[currentTotalBatchesVerified], 409: "o1" 410: ); 421: require(currentTotalBatchesVerified <= s.totalBatchesCommitted, "q"); 426: assert(block.chainid != 1); 442: require(proofPublicInput.length == 1, "t4"); 449: require(successVerifyProof, "p"); // Proof verification fail 482: require(s.totalBatchesCommitted > _newLastBatch, "v1"); // The last committed batch is less than new last batch 483: require(_newLastBatch >= s.totalBatchesExecuted, "v2"); // Already executed batches cannot be reverted 543: require(_batch.systemLogs.length <= MAX_L2_TO_L1_LOGS_COMMITMENT_BYTES, "pu"); 567: require(_blobCommitments.length == MAX_NUMBER_OF_BLOBS, "b10"); 568: require(_blobHashes.length == MAX_NUMBER_OF_BLOBS, "b11"); 616: require(success, "failed to call point evaluation precompile"); 618: require(result == BLS_MODULUS, "precompile unexpected output"); 631: require(_pubdataCommitments.length > 0, "pl"); 632: require(_pubdataCommitments.length <= PUBDATA_COMMITMENT_SIZE * MAX_NUMBER_OF_BLOBS, "bd"); 633: require(_pubdataCommitments.length % PUBDATA_COMMITMENT_SIZE == 0, "bs"); 639: require(blobVersionedHash != bytes32(0), "vh"); 663: require(versionedHash == bytes32(0), "lh"); 668: require( 669: (_blobHashes[i] == bytes32(0) && blobCommitments[i] == bytes32(0)) || 670: (_blobHashes[i] != bytes32(0) && blobCommitments[i] != bytes32(0)), 671: "bh" 672: ); 681: require(success, "vc");
[37, 40, 50, 51, 61-65, 74, 76, 78, 110, 114, 122, 123, 149, 154, 157, 160, 163, 166, 169, 172, 175, 178, 181, 182, 192, 194, 226-229, 231, 233, 284, 323, 324-327, 330, 358, 402, 407-410, 421, 426, 442, 449, 482, 483, 543, 567, 568, 616, 618, 631, 632, 633, 639, 663, 668-672, 681]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Getters.sol 183: require(ds.selectorToFacet[_selector].facetAddress != address(0), "g2");
[183]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Mailbox.sol 39: require(s.chainId == ERA_CHAIN_ID, "transferEthToSharedBridge only available for Era on mailbox"); 110: require(_batchNumber <= s.totalBatchesExecuted, "xx"); 117: require(hashedLog != L2_L1_LOGS_TREE_DEFAULT_LEAF_HASH, "tw"); 158: require(s.baseTokenGasPriceMultiplierDenominator > 0, "Mailbox: baseTokenGasPriceDenominator not set"); 185: require(s.chainId == ERA_CHAIN_ID, "finalizeEthWithdrawal only available for Era on mailbox"); 206: require(s.chainId == ERA_CHAIN_ID, "legacy interface only available for era token"); 243: require(_request.l2GasPerPubdataByteLimit == REQUIRED_L2_GAS_PRICE_PER_PUBDATA, "qp"); 264: require(_factoryDeps.length <= MAX_NEW_FACTORY_DEPS, "uj"); 272: require(_mintValue >= baseCost + _params.l2Value, "mv"); // The `msg.value` doesn't cover the transaction cost
[39, 110, 117, 158, 185, 206, 243, 264, 272]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/ZkSyncStateTransitionBase.sol 16: require(msg.sender == s.admin, "StateTransition Chain: not admin"); 22: require(s.validators[msg.sender], "StateTransition Chain: not validator"); 27: require(msg.sender == s.stateTransitionManager, "StateTransition Chain: not state transition manager"); 32: require(msg.sender == s.bridgehub, "StateTransition Chain: not bridgehub"); 37: require( 38: msg.sender == s.admin || msg.sender == s.stateTransitionManager, 39: "StateTransition Chain: Only by admin or state transition manager" 40: ); 45: require( 46: s.validators[msg.sender] || msg.sender == s.stateTransitionManager, 47: "StateTransition Chain: Only by validator or state transition manager" 48: ); 53: require(msg.sender == s.baseTokenBridge, "Only shared bridge can call this function");
[16, 22, 27, 32, 37-40, 45-48, 53]
</details>via-ir
for deployingThe IR-based code generator was developed to make code generation more performant by enabling optimization passes that can be applied across functions.
It is possible to activate the IR-based code generator through the command line by using the flag --via-ir
or by including the option {"viaIR": true}
.
Keep in mind that compiling with this option may take longer. However, you can simply test it before deploying your code. If you find that it provides better performance, you can add the --via-ir
flag to your deploy command.
Consider caching the result instead of re-calling the function when possible. Note: this also includes casts, which cost between 42-46 gas, depending on the type.
There are 3 instances of this issue.
File: code/system-contracts/contracts/Compressor.sol // @audit stateDiff.readUint64(84) is duplicated on line 161 129: uint64 enumIndex = stateDiff.readUint64(84); // @audit stateDiff.readUint256(92) is duplicated on line 166 138: uint256 initValue = stateDiff.readUint256(92); // @audit stateDiff.readUint256(124) is duplicated on line 167 139: uint256 finalValue = stateDiff.readUint256(124);
payable
If a function modifier such as onlyOwner
is used, the function will revert if a normal user tries to pay the function.
Marking the function as payable
will lower the gas for legitimate callers, as the compiler will not include checks for whether a payment was provided.
The extra opcodes avoided are:
CALLVALUE(2), DUP1(3), ISZERO(3), PUSH2(3), JUMPI(10), PUSH1(3), DUP1(3), REVERT(0), JUMPDEST(1), POP(2)
which cost an average of about 21 gas per call to the function, in addition to the extra deployment cost.
There are 88 instances of this issue.
<details> <summary>Expand findings</summary>File: code/system-contracts/contracts/AccountCodeStorage.sol 35: function storeAccountConstructingCodeHash(address _address, bytes32 _hash) external override onlyDeployer { 46: function storeAccountConstructedCodeHash(address _address, bytes32 _hash) external override onlyDeployer { 54: function markAccountCodeHashAsConstructed(address _address) external override onlyDeployer {
File: code/system-contracts/contracts/ContractDeployer.sol 65: function updateAccountVersion(AccountAbstractionVersion _version) external onlySystemCall { 74: function updateNonceOrdering(AccountNonceOrdering _nonceOrdering) external onlySystemCall {
File: code/system-contracts/contracts/KnownCodesStorage.sol 27: function markFactoryDeps(bool _shouldSendToL1, bytes32[] calldata _hashes) external onlyCallFromBootloader { 39: function markBytecodeAsPublished(bytes32 _bytecodeHash) external onlyCompressor {
File: code/system-contracts/contracts/L1Messenger.sol 70: function sendL2ToL1Log( 71: bool _isService, 72: bytes32 _key, 73: bytes32 _value 74: ) external onlyCallFromSystemContract returns (uint256 logIdInMerkleTree) { 161: function requestBytecodeL1Publication( 162: bytes32 _bytecodeHash 163: ) external override onlyCallFrom(address(KNOWN_CODE_STORAGE_CONTRACT)) { 194: function publishPubdataAndClearState( 195: bytes calldata _totalL2ToL1PubdataAndStateDiffs 196: ) external onlyCallFromBootloader {
File: code/system-contracts/contracts/L2BaseToken.sol 64: function mint(address _account, uint256 _amount) external override onlyCallFromBootloader {
[64]
File: code/system-contracts/contracts/MsgValueSimulator.sol 47: fallback(bytes calldata _data) external onlySystemCall returns (bytes memory) {
[47]
File: code/system-contracts/contracts/NonceHolder.sol 65: function increaseMinNonce(uint256 _value) public onlySystemCall returns (uint256 oldMinNonce) { 82: function setValueUnderNonce(uint256 _key, uint256 _value) public onlySystemCall { 110: function incrementMinNonceIfEquals(uint256 _expectedNonce) external onlySystemCall {
File: code/system-contracts/contracts/PubdataChunkPublisher.sol 21: function chunkAndPublishPubdata(bytes calldata _pubdata) external onlyCallFrom(address(L1_MESSENGER_CONTRACT)) {
[21]
File: code/system-contracts/contracts/SystemContext.sol 84: function setChainId(uint256 _newChainId) external onlyCallFromForceDeployer { 99: function setTxOrigin(address _newOrigin) external onlyCallFromBootloader { 105: function setGasPrice(uint256 _gasPrice) external onlyCallFromBootloader { 112: function setPubdataInfo(uint256 _gasPerPubdataByte, uint256 _basePubdataSpent) external onlyCallFromBootloader { 341: function setL2Block( 342: uint128 _l2BlockNumber, 343: uint128 _l2BlockTimestamp, 344: bytes32 _expectedPrevL2BlockHash, 345: bool _isFirstInBatch, 346: uint128 _maxVirtualBlocksToCreate 347: ) external onlyCallFromBootloader { 402: function appendTransactionToCurrentL2Block(bytes32 _txHash) external onlyCallFromBootloader { 408: function publishTimestampDataToL1() external onlyCallFromBootloader { 444: function setNewBatch( 445: bytes32 _prevBatchHash, 446: uint128 _newTimestamp, 447: uint128 _expectedNewNumber, 448: uint256 _baseFee 449: ) external onlyCallFromBootloader { 471: function unsafeOverrideBatch( 472: uint256 _newTimestamp, 473: uint256 _number, 474: uint256 _baseFee 475: ) external onlyCallFromBootloader { 482: function incrementTxNumberInBatch() external onlyCallFromBootloader { 486: function resetTxNumberInBatch() external onlyCallFromBootloader {
[84, 99, 105, 112, 341-347, 402, 408, 444-449, 471-475, 482, 486]
File: code/contracts/ethereum/contracts/bridge/L1SharedBridge.sol 116: function transferFundsFromLegacy(address _token, address _target, uint256 _targetChainId) external onlyOwner { 142: function initializeChainGovernance(uint256 _chainId, address _l2BridgeAddress) external onlyOwner { 232: function bridgehubConfirmL2Transaction( 233: uint256 _chainId, 234: bytes32 _txDataHash, 235: bytes32 _txHash 236: ) external override onlyBridgehub { 615: function finalizeWithdrawalLegacyErc20Bridge( 616: uint256 _l2BatchNumber, 617: uint256 _l2MessageIndex, 618: uint16 _l2TxNumberInBatch, 619: bytes calldata _message, 620: bytes32[] calldata _merkleProof 621: ) external override onlyLegacyBridge returns (address l1Receiver, address l1Token, uint256 amount) { 644: function claimFailedDepositLegacyErc20Bridge( 645: address _depositSender, 646: address _l1Token, 647: uint256 _amount, 648: bytes32 _l2TxHash, 649: uint256 _l2BatchNumber, 650: uint256 _l2MessageIndex, 651: uint16 _l2TxNumberInBatch, 652: bytes32[] calldata _merkleProof 653: ) external override onlyLegacyBridge {
[116, 142, 232-236, 615-621, 644-653]
File: code/contracts/ethereum/contracts/bridgehub/Bridgehub.sol 51: function setPendingAdmin(address _newPendingAdmin) external onlyOwnerOrAdmin { 82: function addStateTransitionManager(address _stateTransitionManager) external onlyOwner { 92: function removeStateTransitionManager(address _stateTransitionManager) external onlyOwner { 101: function addToken(address _token) external onlyOwner { 108: function setSharedBridge(address _sharedBridge) external onlyOwner { 114: function createNewChain( 115: uint256 _chainId, 116: address _stateTransitionManager, 117: address _baseToken, 118: uint256, //_salt 119: address _admin, 120: bytes calldata _initData 121: ) external onlyOwnerOrAdmin nonReentrant returns (uint256 chainId) {
[51, 82, 92, 101, 108, 114-121]
File: code/contracts/ethereum/contracts/governance/Governance.sol 129: function scheduleTransparent(Operation calldata _operation, uint256 _delay) external onlyOwner { 142: function scheduleShadow(bytes32 _id, uint256 _delay) external onlyOwner { 154: function cancel(bytes32 _id) external onlyOwner { 249: function updateDelay(uint256 _newDelay) external onlySelf { 256: function updateSecurityCouncil(address _newSecurityCouncil) external onlySelf {
File: code/contracts/ethereum/contracts/state-transition/StateTransitionManager.sol 110: function setPendingAdmin(address _newPendingAdmin) external onlyOwnerOrAdmin { 132: function setValidatorTimelock(address _validatorTimelock) external onlyOwnerOrAdmin { 137: function setInitialCutHash(Diamond.DiamondCutData calldata _diamondCut) external onlyOwner { 142: function setNewVersionUpgrade( 143: Diamond.DiamondCutData calldata _cutData, 144: uint256 _oldProtocolVersion, 145: uint256 _newProtocolVersion 146: ) external onlyOwner { 152: function setUpgradeDiamondCut( 153: Diamond.DiamondCutData calldata _cutData, 154: uint256 _oldProtocolVersion 155: ) external onlyOwner { 160: function freezeChain(uint256 _chainId) external onlyOwner { 165: function unfreezeChain(uint256 _chainId) external onlyOwner { 170: function revertBatches(uint256 _chainId, uint256 _newLastBatch) external onlyOwnerOrAdmin { 230: function registerAlreadyDeployedStateTransition( 231: uint256 _chainId, 232: address _stateTransitionContract 233: ) external onlyOwner { 239: function createNewChain( 240: uint256 _chainId, 241: address _baseToken, 242: address _sharedBridge, 243: address _admin, 244: bytes calldata _diamondCut 245: ) external onlyBridgehub {
[110, 132, 137, 142-146, 152-155, 160, 165, 170, 230-233, 239-245]
File: code/contracts/ethereum/contracts/state-transition/ValidatorTimelock.sol 73: function setStateTransitionManager(IStateTransitionManager _stateTransitionManager) external onlyOwner { 78: function addValidator(uint256 _chainId, address _newValidator) external onlyChainAdmin(_chainId) { 87: function removeValidator(uint256 _chainId, address _validator) external onlyChainAdmin(_chainId) { 96: function setExecutionDelay(uint32 _executionDelay) external onlyOwner { 108: function commitBatches( 109: StoredBatchInfo calldata, 110: CommitBatchInfo[] calldata _newBatchesData 111: ) external onlyValidator(ERA_CHAIN_ID) { 126: function commitBatchesSharedBridge( 127: uint256 _chainId, 128: StoredBatchInfo calldata, 129: CommitBatchInfo[] calldata _newBatchesData 130: ) external onlyValidator(_chainId) { 146: function revertBatches(uint256) external onlyValidator(ERA_CHAIN_ID) { 153: function revertBatchesSharedBridge(uint256 _chainId, uint256) external onlyValidator(_chainId) { 160: function proveBatches( 161: StoredBatchInfo calldata, 162: StoredBatchInfo[] calldata, 163: ProofInput calldata 164: ) external onlyValidator(ERA_CHAIN_ID) { 171: function proveBatchesSharedBridge( 172: uint256 _chainId, 173: StoredBatchInfo calldata, 174: StoredBatchInfo[] calldata, 175: ProofInput calldata 176: ) external onlyValidator(_chainId) { 182: function executeBatches(StoredBatchInfo[] calldata _newBatchesData) external onlyValidator(ERA_CHAIN_ID) { 203: function executeBatchesSharedBridge( 204: uint256 _chainId, 205: StoredBatchInfo[] calldata _newBatchesData 206: ) external onlyValidator(_chainId) {
[73, 78, 87, 96, 108-111, 126-130, 146, 153, 160-164, 171-176, 182, 203-206]
File: code/contracts/zksync/contracts/bridge/L2StandardERC20.sol 111: function reinitializeToken( 112: ERC20Getters calldata _availableGetters, 113: string memory _newName, 114: string memory _newSymbol, 115: uint8 _version 116: ) external onlyNextVersion(_version) reinitializer(_version) { 145: function bridgeMint(address _to, uint256 _amount) external override onlyBridge { 154: function bridgeBurn(address _from, uint256 _amount) external override onlyBridge {
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Admin.sol 23: function setPendingAdmin(address _newPendingAdmin) external onlyAdmin { 45: function setValidator(address _validator, bool _active) external onlyStateTransitionManager { 51: function setPorterAvailability(bool _zkPorterIsAvailable) external onlyStateTransitionManager { 58: function setPriorityTxMaxGasLimit(uint256 _newPriorityTxMaxGasLimit) external onlyStateTransitionManager { 67: function changeFeeParams(FeeParams calldata _newFeeParams) external onlyAdminOrStateTransitionManager { 79: function setTokenMultiplier(uint128 _nominator, uint128 _denominator) external onlyAdminOrStateTransitionManager { 89: function setValidiumMode(PubdataPricingMode _validiumMode) external onlyAdmin { 100: function upgradeChainFromVersion( 101: uint256 _oldProtocolVersion, 102: Diamond.DiamondCutData calldata _diamondCut 103: ) external onlyAdminOrStateTransitionManager { 123: function executeUpgrade(Diamond.DiamondCutData calldata _diamondCut) external onlyStateTransitionManager { 133: function freezeDiamond() external onlyAdminOrStateTransitionManager { 143: function unfreezeDiamond() external onlyAdminOrStateTransitionManager {
[23, 45, 51, 58, 67, 79, 89, 100-103, 123, 133, 143]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Executor.sol 199: function commitBatches( 200: StoredBatchInfo memory _lastCommittedBatchData, 201: CommitBatchInfo[] calldata _newBatchesData 202: ) external nonReentrant onlyValidator { 207: function commitBatchesSharedBridge( 208: uint256, // _chainId 209: StoredBatchInfo memory _lastCommittedBatchData, 210: CommitBatchInfo[] calldata _newBatchesData 211: ) external nonReentrant onlyValidator { 337: function executeBatchesSharedBridge( 338: uint256, 339: StoredBatchInfo[] calldata _batchesData 340: ) external nonReentrant onlyValidator { 345: function executeBatches(StoredBatchInfo[] calldata _batchesData) external nonReentrant onlyValidator { 368: function proveBatches( 369: StoredBatchInfo calldata _prevBatch, 370: StoredBatchInfo[] calldata _committedBatches, 371: ProofInput calldata _proof 372: ) external nonReentrant onlyValidator { 377: function proveBatchesSharedBridge( 378: uint256, // _chainId 379: StoredBatchInfo calldata _prevBatch, 380: StoredBatchInfo[] calldata _committedBatches, 381: ProofInput calldata _proof 382: ) external nonReentrant onlyValidator { 472: function revertBatches(uint256 _newLastBatch) external nonReentrant onlyValidatorOrStateTransitionManager { 477: function revertBatchesSharedBridge(uint256, uint256 _newLastBatch) external nonReentrant onlyValidator {
[199-202, 207-211, 337-340, 345, 368-372, 377-382, 472, 477]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Mailbox.sol 38: function transferEthToSharedBridge() external onlyBaseTokenBridge {
[38]
</details>keccak256
with static argumentsWhen the arguments are static, the keccak256
should be calculated only once and stored in an immutable
state variable.
There are 2 instances of this issue.
File: code/system-contracts/contracts/libraries/TransactionHelper.sol // @audit keccak256("zkSync") 139: abi.encode(EIP712_DOMAIN_TYPEHASH, keccak256("zkSync"), keccak256("2"), block.chainid) // @audit keccak256("2") 139: abi.encode(EIP712_DOMAIN_TYPEHASH, keccak256("zkSync"), keccak256("2"), block.chainid)
length
is not cachedSolidity compiler reads array length every iteration if not cached. Storage array requires an extra sload operation (100 gas), memory array requires an extra mload operation (3 gas).
There are 11 instances of this issue.
File: code/system-contracts/contracts/Compressor.sol 61: for (uint256 encodedDataPointer = 0; encodedDataPointer < encodedData.length; encodedDataPointer += 2) {
[61]
File: code/contracts/ethereum/contracts/governance/Governance.sol 225: for (uint256 i = 0; i < _calls.length; ++i) {
[225]
File: code/contracts/ethereum/contracts/state-transition/ValidatorTimelock.sol 116: for (uint256 i = 0; i < _newBatchesData.length; ++i) { 135: for (uint256 i = 0; i < _newBatchesData.length; ++i) { 185: for (uint256 i = 0; i < _newBatchesData.length; ++i) { 209: for (uint256 i = 0; i < _newBatchesData.length; ++i) {
File: code/contracts/ethereum/contracts/upgrades/BaseZkSyncUpgrade.sol 226: for (uint256 i = 0; i < _factoryDeps.length; ++i) {
[226]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Executor.sol 142: for (uint256 i = 0; i < emittedL2Logs.length; i = i.uncheckedAdd(L2_TO_L1_LOG_SERIALIZE_SIZE)) { 257: for (uint256 i = 0; i < _newBatchesData.length; i = i.uncheckedInc()) { 289: for (uint256 i = 0; i < _newBatchesData.length; i = i.uncheckedInc()) { 636: for (uint256 i = 0; i < _pubdataCommitments.length; i += PUBDATA_COMMITMENT_SIZE) {
It's better to not cache global variables, as their direct usage is cheaper (e.g. msg.sender
).
There is 1 instance of this issue.
File: code/system-contracts/contracts/ContractDeployer.sol 329: uint256 value = msg.value;
[329]
unchecked
blocks for subtractions where the operands cannot underflowThere are some checks to avoid an underflow, so it's safe to use unchecked
to have some gas savings.
There are 4 instances of this issue.
File: code/system-contracts/contracts/SystemContext.sol // @audit check on line 144 144: if (blockNumber <= _block || blockNumber - _block > 256) {
[144]
File: code/contracts/ethereum/contracts/bridge/L1SharedBridge.sol // @audit check on line 121 132: require(balanceAfter - balanceBefore == amount, "ShB: wrong amount transferred");
[132]
File: code/contracts/ethereum/contracts/upgrades/BaseZkSyncUpgrade.sol // @audit check on line 238 243: _newProtocolVersion - previousProtocolVersion <= MAX_ALLOWED_PROTOCOL_VERSION_DELTA,
[243]
File: code/contracts/ethereum/contracts/upgrades/BaseZkSyncUpgradeGenesis.sol // @audit check on line 22 28: _newProtocolVersion - previousProtocolVersion <= MAX_ALLOWED_PROTOCOL_VERSION_DELTA,
[28]
unchecked
blocks for divisions where the operands cannot overflowuint
divisions can't overflow, while int
divisions can overflow only in one specific case.
Consider adding an unchecked
block to have some gas savings.
There are 10 instances of this issue.
<details> <summary>Expand findings</summary>File: code/system-contracts/contracts/L1Messenger.sol 51: return KECCAK_ROUND_GAS_COST * (_length / KECCAK_ROUND_NUMBER_OF_BYTES + 1); 62: return SHA256_ROUND_GAS_COST * ((_length + 8) / SHA256_ROUND_NUMBER_OF_BYTES + 1);
File: code/system-contracts/contracts/NonceHolder.sol 180: deploymentNonce = _rawMinNonce / DEPLOY_NONCE_MULTIPLIER;
[180]
File: code/system-contracts/contracts/libraries/Utils.sol 86: uint256 lengthInWords = _bytecode.length / 32;
[86]
File: code/contracts/ethereum/contracts/common/libraries/L2ContractHelper.sol 25: uint256 bytecodeLenInWords = _bytecode.length / 32;
[25]
File: code/contracts/ethereum/contracts/state-transition/libraries/TransactionValidator.sol 30: require(l2GasForTxBody / _transaction.gasPerPubdataByteLimit <= _priorityTxMaxPubdata, "uk");
[30]
</details>File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Mailbox.sol 159: uint256 l1GasPriceConverted = (_l1GasPrice * s.baseTokenGasPriceMultiplierNominator) / 160: s.baseTokenGasPriceMultiplierDenominator; 168: batchOverheadBaseToken / 169: uint256(feeParams.maxPubdataPerBatch); 171: uint256 l2GasPrice = feeParams.minimalL2GasPrice + batchOverheadBaseToken / uint256(feeParams.maxL2GasPerBatch); 172: uint256 minL2GasPriceBaseToken = (fullPubdataPriceBaseToken + _gasPerPubdata - 1) / _gasPerPubdata;
uints
/ints
smaller than 32 bytes (256 bits) incurs overheadCiting the documentation:
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.
For example, 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 involvinguint256
, due to the compiler having to clear the higher bits of the memory word before operating on theuint8
, as well as the associated stack operations of doing so.
Note that it might be beneficial to use reduced-size types when dealing with storage values because the compiler will pack multiple elements into one storage slot, but if not, it will have the opposite effect.
There are 204 instances of this issue.
<details> <summary>Expand findings</summary>File: code/system-contracts/contracts/BootloaderUtilities.sol 68: uint64 txDataLen = uint64(_transaction.data.length); 164: uint64 txDataLen = uint64(_transaction.data.length); 259: uint64 txDataLen = uint64(_transaction.data.length);
File: code/system-contracts/contracts/Compressor.sol 65: uint64 encodedChunk = dictionary.readUint64(indexOfEncodedChunk); 66: uint64 realChunk = _bytecode.readUint64(encodedDataPointer * 4); 129: uint64 enumIndex = stateDiff.readUint64(84); 143: uint8 metadata = uint8(bytes1(_compressedStateDiffs[stateDiffPtr])); 145: uint8 operation = metadata & OPERATION_BITMASK; 146: uint8 len = operation == 0 ? 32 : metadata >> LENGTH_BITS_OFFSET; 161: uint64 enumIndex = stateDiff.readUint64(84); 174: uint8 metadata = uint8(bytes1(_compressedStateDiffs[stateDiffPtr])); 176: uint8 operation = metadata & OPERATION_BITMASK; 177: uint8 len = operation == 0 ? 32 : metadata >> LENGTH_BITS_OFFSET;
[65, 66, 129, 143, 145, 146, 161, 174, 176, 177]
File: code/system-contracts/contracts/DefaultAccount.sol 133: uint128 value = Utils.safeCastToU128(_transaction.value); 135: uint32 gas = Utils.safeCastToU32(gasleft()); 161: uint8 v;
File: code/system-contracts/contracts/KnownCodesStorage.sol 75: uint8 version = uint8(_bytecodeHash[0]);
[75]
File: code/system-contracts/contracts/L1Messenger.sol 200: uint32 numberOfL2ToL1Logs = uint32(bytes4(_totalL2ToL1PubdataAndStateDiffs[calldataPtr:calldataPtr + 4])); 233: uint32 numberOfMessages = uint32(bytes4(_totalL2ToL1PubdataAndStateDiffs[calldataPtr:calldataPtr + 4])); 237: uint32 currentMessageLength = uint32(bytes4(_totalL2ToL1PubdataAndStateDiffs[calldataPtr:calldataPtr + 4])); 251: uint32 numberOfBytecodes = uint32(bytes4(_totalL2ToL1PubdataAndStateDiffs[calldataPtr:calldataPtr + 4])); 255: uint32 currentBytecodeLength = uint32( 286: uint24 compressedStateDiffSize = uint24(bytes3(_totalL2ToL1PubdataAndStateDiffs[calldataPtr:calldataPtr + 3])); 289: uint8 enumerationIndexSize = uint8(bytes1(_totalL2ToL1PubdataAndStateDiffs[calldataPtr])); 300: uint32 numberOfStateDiffs = uint32(bytes4(_totalL2ToL1PubdataAndStateDiffs[calldataPtr:calldataPtr + 4]));
[200, 233, 237, 251, 255, 286, 289, 300]
File: code/system-contracts/contracts/L2BaseToken.sol 140: function decimals() external pure override returns (uint8) {
[140]
File: code/system-contracts/contracts/SystemContext.sol 89: uint16 public txNumberInBlock; 133: uint128 blockNumber = currentVirtualL2BlockInfo.number; 172: function getBatchNumberAndTimestamp() public view returns (uint128 batchNumber, uint128 batchTimestamp) { 172: function getBatchNumberAndTimestamp() public view returns (uint128 batchNumber, uint128 batchTimestamp) { 180: function getL2BlockNumberAndTimestamp() public view returns (uint128 blockNumber, uint128 blockTimestamp) { 180: function getL2BlockNumberAndTimestamp() public view returns (uint128 blockNumber, uint128 blockTimestamp) { 190: function getBlockNumber() public view returns (uint128) { 198: function getBlockTimestamp() public view returns (uint128) { 222: uint128 _blockNumber, 223: uint128 _blockTimestamp, 233: function _calculateLegacyL2BlockHash(uint128 _blockNumber) internal pure returns (bytes32) { 241: function _upgradeL2Blocks(uint128 _l2BlockNumber, bytes32 _expectedPrevL2BlockHash, bool _isFirstInBatch) internal { 264: uint128 _l2BlockNumber, 265: uint128 _maxVirtualBlocksToCreate, 266: uint128 _newTimestamp 278: uint128 currentBatchNumber = currentBatchInfo.number; 314: function _setNewL2BlockData(uint128 _l2BlockNumber, uint128 _l2BlockTimestamp, bytes32 _prevL2BlockHash) internal { 314: function _setNewL2BlockData(uint128 _l2BlockNumber, uint128 _l2BlockTimestamp, bytes32 _prevL2BlockHash) internal { 342: uint128 _l2BlockNumber, 343: uint128 _l2BlockTimestamp, 346: uint128 _maxVirtualBlocksToCreate 350: uint128 currentBatchTimestamp = currentBatchInfo.timestamp; 358: (uint128 currentL2BlockNumber, uint128 currentL2BlockTimestamp) = getL2BlockNumberAndTimestamp(); 358: (uint128 currentL2BlockNumber, uint128 currentL2BlockTimestamp) = getL2BlockNumberAndTimestamp(); 409: (uint128 currentBatchNumber, uint128 currentBatchTimestamp) = getBatchNumberAndTimestamp(); 409: (uint128 currentBatchNumber, uint128 currentBatchTimestamp) = getBatchNumberAndTimestamp(); 410: (, uint128 currentL2BlockTimestamp) = getL2BlockNumberAndTimestamp(); 427: function _ensureBatchConsistentWithL2Block(uint128 _newTimestamp) internal view { 428: uint128 currentBlockTimestamp = currentL2BlockInfo.timestamp; 446: uint128 _newTimestamp, 447: uint128 _expectedNewNumber, 450: (uint128 previousBatchNumber, uint128 previousBatchTimestamp) = getBatchNumberAndTimestamp(); 450: (uint128 previousBatchNumber, uint128 previousBatchTimestamp) = getBatchNumberAndTimestamp(); 497: (uint128 blockNumber, uint128 blockTimestamp) = getBatchNumberAndTimestamp(); 497: (uint128 blockNumber, uint128 blockTimestamp) = getBatchNumberAndTimestamp();
[89, 133, 172, 172, 180, 180, 190, 198, 222, 223, 233, 241, 264, 265, 266, 278, 314, 314, 342, 343, 346, 350, 358, 358, 409, 409, 410, 427, 428, 446, 447, 450, 450, 497, 497]
File: code/contracts/zksync/contracts/SystemContractsCaller.sol 27: function safeCastToU32(uint256 _x) internal pure returns (uint32) { 37: function systemCall(uint32 gasLimit, address to, uint256 value, bytes memory data) internal returns (bool success) { 40: uint32 dataStart; 44: uint32 dataLength = uint32(Utils.safeCastToU32(data.length)); 77: uint32 gasLimit, 79: uint128 value, 96: uint32 dataOffset, 97: uint32 memoryPage, 98: uint32 dataStart, 99: uint32 dataLength, 100: uint32 gasPassed, 101: uint8 shardId, 122: uint32 gasPassed, 123: uint8 shardId,
[27, 37, 40, 44, 77, 79, 96, 97, 98, 99, 100, 101, 122, 123]
File: code/system-contracts/contracts/interfaces/IBaseToken.sol 16: function decimals() external pure returns (uint8);
[16]
File: code/system-contracts/contracts/interfaces/IMailbox.sol 9: uint16 _l2TxNumberInBlock,
[9]
File: code/system-contracts/contracts/interfaces/ISystemContext.sol 13: uint128 timestamp; 14: uint128 number; 23: uint128 virtualBlockStartBatch; 27: uint128 virtualBlockFinishL2Block; 44: function txNumberInBlock() external view returns (uint16); 50: function getBlockNumber() external view returns (uint128); 52: function getBlockTimestamp() external view returns (uint128); 54: function getBatchNumberAndTimestamp() external view returns (uint128 blockNumber, uint128 blockTimestamp); 54: function getBatchNumberAndTimestamp() external view returns (uint128 blockNumber, uint128 blockTimestamp); 56: function getL2BlockNumberAndTimestamp() external view returns (uint128 blockNumber, uint128 blockTimestamp); 56: function getL2BlockNumberAndTimestamp() external view returns (uint128 blockNumber, uint128 blockTimestamp);
[13, 14, 23, 27, 44, 50, 52, 54, 54, 56, 56]
File: code/system-contracts/contracts/libraries/EfficientCall.sol 261: uint32 shrinkTo = uint32(msg.data.length - (_data.length + dataOffset)); 264: uint32 gas = Utils.safeCastToU32(_gas);
File: code/system-contracts/contracts/libraries/RLPEncoder.sol 49: function encodeNonSingleBytesLen(uint64 _len) internal pure returns (bytes memory) { 56: function encodeListLen(uint64 _len) internal pure returns (bytes memory) { 60: function _encodeLength(uint64 _len, uint256 _offset) private pure returns (bytes memory encoded) {
File: code/system-contracts/contracts/libraries/SystemContractHelper.sol 94: function ptrAddIntoActive(uint32 _value) internal view { 106: function ptrShrinkIntoActive(uint32 _shrink) internal view { 125: uint32 _inputMemoryOffset, 126: uint32 _inputMemoryLength, 127: uint32 _outputMemoryOffset, 128: uint32 _outputMemoryLength, 129: uint64 _perPrecompileInterpreted 150: uint32 _gasToBurn, 151: uint32 _pubdataToSpend 168: function setValueForNextFarCall(uint128 _value) internal returns (bool success) { 227: function getPubdataPublishedFromMeta(uint256 meta) internal pure returns (uint32 pubdataPublished) { 237: function getHeapSizeFromMeta(uint256 meta) internal pure returns (uint32 heapSize) { 246: function getAuxHeapSizeFromMeta(uint256 meta) internal pure returns (uint32 auxHeapSize) { 254: function getShardIdFromMeta(uint256 meta) internal pure returns (uint8 shardId) { 263: function getCallerShardIdFromMeta(uint256 meta) internal pure returns (uint8 callerShardId) { 272: function getCodeShardIdFromMeta(uint256 meta) internal pure returns (uint8 codeShardId) { 344: function burnGas(uint32 _gasToPay, uint32 _pubdataToSpend) internal view { 344: function burnGas(uint32 _gasToPay, uint32 _pubdataToSpend) internal view {
[94, 106, 125, 126, 127, 128, 129, 150, 151, 168, 227, 237, 246, 254, 263, 272, 344, 344]
File: code/system-contracts/contracts/libraries/SystemContractsCaller.sol 76: function systemCall(uint32 gasLimit, address to, uint256 value, bytes memory data) internal returns (bool success) { 79: uint32 dataStart; 83: uint32 dataLength = uint32(Utils.safeCastToU32(data.length)); 124: uint32 gasLimit, 126: uint128 value, 151: uint32 gasLimit, 153: uint128 value, 215: uint32 dataOffset, 216: uint32 memoryPage, 217: uint32 dataStart, 218: uint32 dataLength, 219: uint32 gasPassed, 220: uint8 shardId, 251: uint32 gasPassed, 252: uint8 shardId,
[76, 79, 83, 124, 126, 151, 153, 215, 216, 217, 218, 219, 220, 251, 252]
File: code/system-contracts/contracts/libraries/TransactionHelper.sol 171: uint64 txDataLen = uint64(_transaction.data.length); 249: uint64 txDataLen = uint64(_transaction.data.length); 321: uint64 txDataLen = uint64(_transaction.data.length);
File: code/system-contracts/contracts/libraries/UnsafeBytesCalldata.sol 19: function readUint16(bytes calldata _bytes, uint256 _start) internal pure returns (uint16 result) { 26: function readUint32(bytes calldata _bytes, uint256 _start) internal pure returns (uint32 result) { 33: function readUint64(bytes calldata _bytes, uint256 _start) internal pure returns (uint64 result) {
File: code/system-contracts/contracts/libraries/Utils.sol 20: function safeCastToU128(uint256 _x) internal pure returns (uint128) { 26: function safeCastToU32(uint256 _x) internal pure returns (uint32) { 32: function safeCastToU24(uint256 _x) internal pure returns (uint24) {
File: code/contracts/ethereum/contracts/bridge/L1ERC20Bridge.sol 182: uint16 _l2TxNumberInBatch, 211: uint16 _l2TxNumberInBatch,
File: code/contracts/ethereum/contracts/bridge/L1SharedBridge.sol 285: uint16 _l2TxNumberInBatch, 312: uint16 _l2TxNumberInBatch, 389: uint16 _l2TxNumberInBatch, 404: uint16 l2TxNumberInBatch; 413: uint16 _l2TxNumberInBatch, 503: (uint32 functionSignature, uint256 offset) = UnsafeBytes.readUint32(_l2ToL1message, 0); 618: uint16 _l2TxNumberInBatch, 651: uint16 _l2TxNumberInBatch,
[285, 312, 389, 404, 413, 503, 618, 651]
File: code/contracts/ethereum/contracts/bridgehub/Bridgehub.sol 181: uint16 _l2TxNumberInBatch,
[181]
File: code/contracts/ethereum/contracts/bridgehub/IBridgehub.sol 94: uint16 _l2TxNumberInBatch,
[94]
File: code/contracts/ethereum/contracts/state-transition/ValidatorTimelock.sol 53: uint32 public executionDelay; 55: constructor(address _initialOwner, uint32 _executionDelay) { 96: function setExecutionDelay(uint32 _executionDelay) external onlyOwner { 115: uint32 timestamp = uint32(block.timestamp); 134: uint32 timestamp = uint32(block.timestamp);
File: code/contracts/ethereum/contracts/vendor/AddressAliasHelper.sol 22: uint160 constant offset = uint160(0x1111000000000000000000000000000000001111);
[22]
File: code/contracts/zksync/contracts/bridge/L2StandardERC20.sol 31: uint8 private decimals_; 93: try this.decodeUint8(decimalsBytes) returns (uint8 decimalsUint8) { 115: uint8 _version 134: modifier onlyNextVersion(uint8 _version) { 171: function decimals() public view override returns (uint8) { 183: function decodeUint8(bytes memory _input) external pure returns (uint8 result) {
File: code/contracts/zksync/contracts/vendor/AddressAliasHelper.sol 22: uint160 constant offset = uint160(0x1111000000000000000000000000000000001111);
[22]
File: code/contracts/ethereum/contracts/bridge/interfaces/IL1ERC20Bridge.sol 49: uint16 _l2TxNumberInBatch, 56: uint16 _l2TxNumberInBatch,
File: code/contracts/ethereum/contracts/bridge/interfaces/IL1SharedBridge.sol 81: uint16 _l2TxNumberInBatch, 93: uint16 _l2TxNumberInBatch, 100: uint16 _l2TxNumberInBatch, 109: uint16 _l2TxNumberInBatch,
File: code/contracts/ethereum/contracts/common/libraries/L2ContractHelper.sol 40: uint8 version = uint8(_bytecodeHash[0]);
[40]
File: code/contracts/ethereum/contracts/common/libraries/UnsafeBytes.sol 19: function readUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32 result, uint256 offset) {
[19]
File: code/contracts/ethereum/contracts/state-transition/chain-interfaces/IAdmin.sol 40: function setTokenMultiplier(uint128 _nominator, uint128 _denominator) external; 40: function setTokenMultiplier(uint128 _nominator, uint128 _denominator) external; 84: uint128 oldNominator, 85: uint128 oldDenominator, 86: uint128 newNominator, 87: uint128 newDenominator
File: code/contracts/ethereum/contracts/state-transition/chain-interfaces/IExecutor.sol 84: uint64 batchNumber; 86: uint64 indexRepeatedStorageChanges; 113: uint64 batchNumber; 114: uint64 timestamp; 115: uint64 indexRepeatedStorageChanges;
File: code/contracts/ethereum/contracts/state-transition/chain-interfaces/IGetters.sol 112: function baseTokenGasPriceMultiplierNominator() external view returns (uint128); 115: function baseTokenGasPriceMultiplierDenominator() external view returns (uint128);
File: code/contracts/ethereum/contracts/state-transition/chain-interfaces/IMailbox.sol 51: uint16 _l2TxNumberInBatch, 65: uint16 _l2TxNumberInBatch, 126: uint64 expirationTimestamp,
File: code/contracts/ethereum/contracts/state-transition/libraries/Diamond.sol 32: uint16 selectorPosition; 41: uint16 facetPosition; 208: uint16 selectorPosition = (ds.facetToSelectors[_facet].selectors.length).toUint16();
File: code/contracts/ethereum/contracts/state-transition/libraries/LibMap.sol 18: function get(Uint32Map storage _map, uint256 _index) internal view returns (uint32 result) { 38: function set(Uint32Map storage _map, uint256 _index, uint32 _value) internal { 54: uint32 oldValue = uint32(mapValue >> bitOffset);
File: code/contracts/zksync/contracts/bridge/interfaces/IL1ERC20Bridge.sol 12: uint16 _l2TxNumberInBatch,
[12]
File: code/contracts/zksync/contracts/bridge/interfaces/IL1SharedBridge.sol 13: uint16 _l2TxNumberInBatch,
[13]
File: code/contracts/zksync/contracts/bridge/interfaces/IL2StandardToken.sol 6: event BridgeInitialize(address indexed l1Token, string name, string symbol, uint8 decimals);
[6]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Admin.sol 79: function setTokenMultiplier(uint128 _nominator, uint128 _denominator) external onlyAdminOrStateTransitionManager { 79: function setTokenMultiplier(uint128 _nominator, uint128 _denominator) external onlyAdminOrStateTransitionManager { 80: uint128 oldNominator = s.baseTokenGasPriceMultiplierNominator; 81: uint128 oldDenominator = s.baseTokenGasPriceMultiplierDenominator;
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Executor.sol 39: uint8 pubdataSource = uint8(bytes1(_newBatch.pubdataCommitments[0])); 592: function _checkBit(uint256 _bitMap, uint8 _index) internal pure returns (bool) { 597: function _setBit(uint256 _bitMap, uint8 _index) internal pure returns (uint256) {
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Getters.sol 67: function baseTokenGasPriceMultiplierNominator() external view returns (uint128) { 72: function baseTokenGasPriceMultiplierDenominator() external view returns (uint128) {
</details>File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Mailbox.sol 78: uint16 _l2TxNumberInBatch, 181: uint16 _l2TxNumberInBatch,
Using a stack variable instead of a state variable is cheaper when emitting an event.
There are 8 instances of this issue.
File: code/contracts/ethereum/contracts/bridgehub/Bridgehub.sol // @audit pendingAdmin 69: emit NewAdmin(previousAdmin, pendingAdmin);
[69]
File: code/contracts/ethereum/contracts/governance/Governance.sol // @audit minDelay 250: emit ChangeMinDelay(minDelay, _newDelay); // @audit securityCouncil 257: emit ChangeSecurityCouncil(securityCouncil, _newSecurityCouncil);
File: code/contracts/ethereum/contracts/state-transition/StateTransitionManager.sol // @audit pendingAdmin 128: emit NewAdmin(previousAdmin, pendingAdmin); // @audit protocolVersion 227: emit SetChainIdUpgrade(_chainContract, l2ProtocolUpgradeTx, protocolVersion);
File: code/contracts/zksync/contracts/bridge/L2StandardERC20.sol // @audit decimals_ 101: emit BridgeInitialize(_l1Address, decodedName, decodedSymbol, decimals_); // @audit l1Address 126: emit BridgeInitialize(l1Address, _newName, _newSymbol, decimals_); // @audit decimals_ 126: emit BridgeInitialize(l1Address, _newName, _newSymbol, decimals_);
Pre increments/decrements (++i/--i
) are cheaper than post increments/decrements (i++/i--
): it saves 6 gas per expression.
There are 5 instances of this issue.
File: code/system-contracts/contracts/Compressor.sol 135: numInitialWritesProcessed++; 144: stateDiffPtr++;
File: code/system-contracts/contracts/PubdataChunkPublisher.sol 36: for (uint256 i = 0; i < MAX_NUMBER_OF_BLOBS; i++) {
[36]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Executor.sol 580: for (uint i = 0; i < MAX_NUMBER_OF_BLOBS; i++) { 667: for (uint256 i = 0; i < MAX_NUMBER_OF_BLOBS; i++) {
>=
/<=
costs less gas than >
/<
The compiler uses opcodes GT
and ISZERO
for code that uses >
, but only requires LT
for >=
. A similar behaviour applies for >
, which uses opcodes LT
and ISZERO
, but only requires GT
for <=
.
There are 97 instances of this issue.
<details> <summary>Expand findings</summary>File: code/system-contracts/contracts/AccountCodeStorage.sol 102: if (codeHash == 0x00 && NONCE_HOLDER_SYSTEM_CONTRACT.getRawNonce(account) > 0) { 132: uint160(account) > CURRENT_MAX_PRECOMPILE_ADDRESS &&
File: code/system-contracts/contracts/ComplexUpgrader.sol 24: require(_delegateTo.code.length > 0, "Delegatee is an EOA");
[24]
File: code/system-contracts/contracts/Compressor.sol 61: for (uint256 encodedDataPointer = 0; encodedDataPointer < encodedData.length; encodedDataPointer += 2) { 63: require(indexOfEncodedChunk < dictionary.length, "Encoded chunk index is out of bounds"); 127: for (uint256 i = 0; i < _numberOfStateDiffs * STATE_DIFF_ENTRY_SIZE; i += STATE_DIFF_ENTRY_SIZE) { 159: for (uint256 i = 0; i < _numberOfStateDiffs * STATE_DIFF_ENTRY_SIZE; i += STATE_DIFF_ENTRY_SIZE) {
File: code/system-contracts/contracts/ContractDeployer.sol 48: _address > address(MAX_SYSTEM_CONTRACT_ADDRESS) && 248: for (uint256 i = 0; i < deploymentsLength; ++i) { 253: for (uint256 i = 0; i < deploymentsLength; ++i) { 265: require(uint160(_newAddress) > MAX_SYSTEM_CONTRACT_ADDRESS, "Can not deploy contracts in kernel space"); 303: require(knownCodeMarker > 0, "The code hash is not known"); 332: if (value > 0) { 339: if (value > 0) {
[48, 248, 253, 265, 303, 332, 339]
File: code/system-contracts/contracts/GasBoundCaller.sol 49: uint256 pubdataAllowance = _maxTotalGas > expectedForCompute ? _maxTotalGas - expectedForCompute : 0; 63: uint256 pubdataSpent = pubdataPublishedAfter > pubdataPublishedBefore
File: code/system-contracts/contracts/ImmutableSimulator.sol 38: for (uint256 i = 0; i < immutablesLength; ++i) {
[38]
File: code/system-contracts/contracts/KnownCodesStorage.sol 30: for (uint256 i = 0; i < hashesLen; ++i) {
[30]
File: code/system-contracts/contracts/L1Messenger.sol 206: for (uint256 i = 0; i < numberOfL2ToL1Logs; ++i) { 218: for (uint256 i = numberOfL2ToL1Logs; i < L2_TO_L1_LOGS_MERKLE_TREE_LEAVES; ++i) { 222: while (nodesOnCurrentLevel > 1) { 224: for (uint256 i = 0; i < nodesOnCurrentLevel; ++i) { 236: for (uint256 i = 0; i < numberOfMessages; ++i) { 254: for (uint256 i = 0; i < numberOfBytecodes; ++i) {
[206, 218, 222, 224, 236, 254]
File: code/system-contracts/contracts/MsgValueSimulator.sol 53: uint256 userGas = gasInContext > MSG_VALUE_SIMULATOR_STIPEND_GAS
[53]
File: code/system-contracts/contracts/NonceHolder.sol 156: return (_nonce < getMinNonce(_address) || nonceValues[addressAsKey][_nonce] > 0); 156: return (_nonce < getMinNonce(_address) || nonceValues[addressAsKey][_nonce] > 0);
File: code/system-contracts/contracts/PubdataChunkPublisher.sol 36: for (uint256 i = 0; i < MAX_NUMBER_OF_BLOBS; i++) {
[36]
File: code/system-contracts/contracts/SystemContext.sol 119: return pubdataPublished > basePubdataSpent ? pubdataPublished - basePubdataSpent : 0; 144: if (blockNumber <= _block || blockNumber - _block > 256) { 146: } else if (_block < currentVirtualBlockUpgradeInfo.virtualBlockStartBatch) { 152: currentVirtualBlockUpgradeInfo.virtualBlockFinishL2Block > 0 245: require(_l2BlockNumber > 0, "L2 block number is never expected to be zero"); 287: require(_maxVirtualBlocksToCreate > 0, "Can't initialize the first virtual block"); 355: require(_maxVirtualBlocksToCreate > 0, "There must be a virtual block created at the start of the batch"); 387: _l2BlockTimestamp > currentL2BlockTimestamp, 413: require(currentBatchNumber > 0, "The current batch number must be greater than 0"); 430: _newTimestamp > currentBlockTimestamp, 451: require(_newTimestamp > previousBatchTimestamp, "Timestamps should be incremental");
[119, 144, 146, 152, 245, 287, 355, 387, 413, 430, 451]
File: code/system-contracts/contracts/libraries/RLPEncoder.sol 26: if (_val < 128) { 62: if (_len < 56) { 86: if (_number > type(uint128).max) { 90: if (_number > type(uint64).max) { 94: if (_number > type(uint32).max) { 98: if (_number > type(uint16).max) { 102: if (_number > type(uint8).max) {
File: code/system-contracts/contracts/libraries/SystemContractHelper.sol 319: require(index < 10, "There are only 10 accessible registers");
[319]
File: code/system-contracts/contracts/libraries/TransactionHelper.sol 378: if (currentAllowance < minAllowance) {
[378]
File: code/system-contracts/contracts/libraries/Utils.sol 87: require(lengthInWords < 2 ** 16, "pp"); // bytecode length must be less than 2^16 words
[87]
File: code/contracts/ethereum/contracts/bridge/L1SharedBridge.sol 121: require(balanceAfter > balanceBefore, "ShB: 0 eth transferred"); 129: require(amount > 0, "ShB: 0 amount to transfer"); 327: require(_amount > 0, "y1"); 375: return (_chainId == ERA_CHAIN_ID) && (_l2BatchNumber < eraFirstPostUpgradeBatch);
File: code/contracts/ethereum/contracts/bridgehub/Bridgehub.sol 301: _request.secondBridgeAddress > BRIDGEHUB_MIN_SECOND_BRIDGE_ADDRESS, 329: } else if (_refundRecipient.code.length > 0) {
File: code/contracts/ethereum/contracts/governance/Governance.sol 111: } else if (timestamp > block.timestamp) { 225: for (uint256 i = 0; i < _calls.length; ++i) {
File: code/contracts/ethereum/contracts/state-transition/ValidatorTimelock.sol 116: for (uint256 i = 0; i < _newBatchesData.length; ++i) { 135: for (uint256 i = 0; i < _newBatchesData.length; ++i) { 185: for (uint256 i = 0; i < _newBatchesData.length; ++i) { 209: for (uint256 i = 0; i < _newBatchesData.length; ++i) {
File: code/contracts/ethereum/contracts/upgrades/BaseZkSyncUpgrade.sol 226: for (uint256 i = 0; i < _factoryDeps.length; ++i) { 239: _newProtocolVersion > previousProtocolVersion,
File: code/contracts/zksync/contracts/bridge/L2SharedBridge.sol 124: require(_amount > 0, "Amount cannot be zero");
[124]
File: code/contracts/ethereum/contracts/common/libraries/L2ContractHelper.sol 26: require(bytecodeLenInWords < 2 ** 16, "pp"); // bytecode length must be less than 2^16 words
[26]
File: code/contracts/ethereum/contracts/state-transition/libraries/Diamond.sol 101: for (uint256 i = 0; i < facetCutsLength; i = i.uncheckedInc()) { 107: require(selectors.length > 0, "B"); // no functions for diamond cut 132: require(_facet.code.length > 0, "G"); 138: for (uint256 i = 0; i < selectorsLength; i = i.uncheckedInc()) { 155: require(_facet.code.length > 0, "K"); 158: for (uint256 i = 0; i < selectorsLength; i = i.uncheckedInc()) { 178: for (uint256 i = 0; i < selectorsLength; i = i.uncheckedInc()) {
[101, 107, 132, 138, 155, 158, 178]
File: code/contracts/ethereum/contracts/state-transition/libraries/Merkle.sol 24: require(pathLength > 0, "xc"); 25: require(pathLength < 256, "bt"); 26: require(_index < (1 << pathLength), "px"); 29: for (uint256 i; i < pathLength; i = i.uncheckedInc()) {
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Admin.sol 117: s.protocolVersion > _oldProtocolVersion,
[117]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Executor.sol 114: require(_previousBatchTimestamp < batchTimestamp, "h3"); 142: for (uint256 i = 0; i < emittedL2Logs.length; i = i.uncheckedAdd(L2_TO_L1_LOG_SERIALIZE_SIZE)) { 257: for (uint256 i = 0; i < _newBatchesData.length; i = i.uncheckedInc()) { 289: for (uint256 i = 0; i < _newBatchesData.length; i = i.uncheckedInc()) { 311: for (uint256 i = 0; i < _nPriorityOps; i = i.uncheckedInc()) { 351: for (uint256 i = 0; i < nBatches; i = i.uncheckedInc()) { 405: for (uint256 i = 0; i < committedBatchesLength; i = i.uncheckedInc()) { 429: if (_proof.serializedProof.length > 0) { 482: require(s.totalBatchesCommitted > _newLastBatch, "v1"); // The last committed batch is less than new last batch 485: if (_newLastBatch < s.totalBatchesVerified) { 492: if (s.l2SystemContractsUpgradeBatchNumber > _newLastBatch) { 580: for (uint i = 0; i < MAX_NUMBER_OF_BLOBS; i++) { 593: return (_bitMap & (1 << _index)) > 0; 631: require(_pubdataCommitments.length > 0, "pl"); 636: for (uint256 i = 0; i < _pubdataCommitments.length; i += PUBDATA_COMMITMENT_SIZE) { 667: for (uint256 i = 0; i < MAX_NUMBER_OF_BLOBS; i++) {
[114, 142, 257, 289, 311, 351, 405, 429, 482, 485, 492, 580, 593, 631, 636, 667]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Getters.sol 208: for (uint256 i = 0; i < facetsLen; i = i.uncheckedInc()) {
[208]
</details>File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Mailbox.sol 158: require(s.baseTokenGasPriceMultiplierDenominator > 0, "Mailbox: baseTokenGasPriceDenominator not set"); 277: if (refundRecipient.code.length > 0) { 356: for (uint256 i = 0; i < factoryDepsLen; i = i.uncheckedInc()) {
internal
functions only called once can be inlined to save gasConsider removing the following internal functions, and put the logic directly where they are called, as they are called only once.
There are 80 instances of this issue.
<details> <summary>Expand findings</summary>File: code/system-contracts/contracts/BootloaderUtilities.sol 44: function encodeLegacyTransactionHash(Transaction calldata _transaction) internal view returns (bytes32 txHash) { 139: function encodeEIP2930TransactionHash(Transaction calldata _transaction) internal view returns (bytes32) { 229: function encodeEIP1559TransactionHash(Transaction calldata _transaction) internal view returns (bytes32) {
File: code/system-contracts/contracts/Compressor.sol 197: function _decodeRawBytecode(
[197]
File: code/system-contracts/contracts/ContractDeployer.sol 283: function _performDeployOnAddress( 309: function _storeConstructingByteCodeHashOnAddress(address _newAddress, bytes32 _bytecodeHash) internal {
File: code/system-contracts/contracts/DefaultAccount.sol 78: function _validateTransaction( 131: function _execute(Transaction calldata _transaction) internal { 159: function _isValidSignature(bytes32 _hash, bytes memory _signature) internal view returns (bool) {
File: code/system-contracts/contracts/KnownCodesStorage.sol 74: function _validateBytecode(bytes32 _bytecodeHash) internal pure {
[74]
File: code/system-contracts/contracts/L1Messenger.sol 61: function sha256GasCost(uint256 _length) internal pure returns (uint256) {
[61]
File: code/system-contracts/contracts/L2BaseToken.sol 112: function _getL1WithdrawMessage(address _to, uint256 _amount) internal pure returns (bytes memory) { 117: function _getExtendedWithdrawMessage(
File: code/system-contracts/contracts/MsgValueSimulator.sol 25: function _getAbiParams() internal view returns (uint256 value, bool isSystemCall, address to) {
[25]
File: code/system-contracts/contracts/SystemContext.sol 221: function _calculateL2BlockHash( 233: function _calculateLegacyL2BlockHash(uint128 _blockNumber) internal pure returns (bytes32) { 241: function _upgradeL2Blocks(uint128 _l2BlockNumber, bytes32 _expectedPrevL2BlockHash, bool _isFirstInBatch) internal { 263: function _setVirtualBlock( 427: function _ensureBatchConsistentWithL2Block(uint128 _newTimestamp) internal view {
File: code/contracts/zksync/contracts/SystemContractsCaller.sol 37: function systemCall(uint32 gasLimit, address to, uint256 value, bytes memory data) internal returns (bool success) { 95: function getFarCallABI( 121: function getFarCallABIWithEmptyFatPointer(
File: code/system-contracts/contracts/libraries/EfficientCall.sol 124: function rawCall( 159: function rawStaticCall(uint256 _gas, address _address, bytes calldata _data) internal view returns (bool success) { 173: function rawDelegateCall(uint256 _gas, address _address, bytes calldata _data) internal returns (bool success) { 191: function rawMimicCall( 232: function propagateRevert() internal pure {
File: code/system-contracts/contracts/libraries/SystemContractHelper.sol 148: function unsafePrecompileCall( 203: function getZkSyncMetaBytes() internal view returns (uint256 meta) { 227: function getPubdataPublishedFromMeta(uint256 meta) internal pure returns (uint32 pubdataPublished) { 237: function getHeapSizeFromMeta(uint256 meta) internal pure returns (uint32 heapSize) { 246: function getAuxHeapSizeFromMeta(uint256 meta) internal pure returns (uint32 auxHeapSize) { 254: function getShardIdFromMeta(uint256 meta) internal pure returns (uint8 shardId) { 263: function getCallerShardIdFromMeta(uint256 meta) internal pure returns (uint8 callerShardId) { 272: function getCodeShardIdFromMeta(uint256 meta) internal pure returns (uint8 codeShardId) { 296: function getCallFlags() internal view returns (uint256 callFlags) {
[148, 203, 227, 237, 246, 254, 263, 272, 296]
File: code/system-contracts/contracts/libraries/SystemContractsCaller.sol 76: function systemCall(uint32 gasLimit, address to, uint256 value, bytes memory data) internal returns (bool success) { 123: function systemCallWithReturndata( 250: function getFarCallABIWithEmptyFatPointer(
File: code/system-contracts/contracts/libraries/Utils.sol 44: function bytecodeLenInWords(bytes32 _bytecodeHash) internal pure returns (uint256 codeLengthInWords) { 71: function constructedBytecodeHash(bytes32 _bytecodeHash) internal pure returns (bytes32) {
File: code/contracts/ethereum/contracts/bridge/L1ERC20Bridge.sol 160: function _depositFundsToSharedBridge(address _from, IERC20 _token, uint256 _amount) internal returns (uint256) {
[160]
File: code/contracts/ethereum/contracts/bridge/L1SharedBridge.sol 254: function _getERC20Getters(address _token) internal view returns (bytes memory) { 458: function _checkWithdrawal( 487: function _parseL2WithdrawalMessage(
File: code/contracts/ethereum/contracts/state-transition/StateTransitionManager.sol 177: function _setChainIdUpgrade(uint256 _chainId, address _chainContract) internal {
[177]
File: code/contracts/ethereum/contracts/upgrades/BaseZkSyncUpgrade.sol 163: function _upgradeVerifier(address _newVerifier, VerifierParams calldata _verifierParams) internal { 171: function _setBaseSystemContracts(bytes32 _bootloaderHash, bytes32 _defaultAccountHash) internal { 180: function _setL2SystemContractUpgrade( 236: function _setNewProtocolVersion(uint256 _newProtocolVersion) internal virtual { 263: function _upgradeL1Contract(bytes calldata _customCallDataForUpgrade) internal virtual {} 269: function _postUpgrade(bytes calldata _customCallDataForUpgrade) internal virtual {}
[163, 171, 180, 236, 263, 269]
File: code/contracts/zksync/contracts/bridge/L2SharedBridge.sol 109: function _deployL2Token(address _l1Token, bytes calldata _data) internal returns (address) { 138: function _getL1WithdrawMessage( 164: function _deployBeaconProxy(bytes32 salt) internal returns (BeaconProxy proxy) {
File: code/contracts/ethereum/contracts/common/libraries/L2ContractHelper.sol 49: function bytecodeLen(bytes32 _bytecodeHash) internal pure returns (uint256 codeLengthInWords) {
[49]
File: code/contracts/ethereum/contracts/state-transition/libraries/TransactionValidator.sol 69: function getMinimalPriorityTransactionGasLimit( 109: function getTransactionBodyGasLimit( 128: function getOverheadForTransaction(
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Executor.sol 103: function _verifyBatchTimestamp( 130: function _processL2Logs( 253: function _commitBatchesWithoutSystemContractsUpgrade( 273: function _commitBatchesWithSystemContractsUpgrade( 308: function _collectOperationsFromPriorityQueue(uint256 _nPriorityOps) internal returns (bytes32 concatHash) { 321: function _executeOneBatch(StoredBatchInfo memory _storedBatch, uint256 _executedBatchIdx) internal { 453: function _getBatchProofPublicInput( 500: function _createBatchCommitment( 515: function _batchPassThroughData(CommitBatchInfo calldata _batch) internal pure returns (bytes memory) { 525: function _batchMetaParameters() internal view returns (bytes memory) { 537: function _batchAuxiliaryOutput( 561: function _encodeBlobAuxilaryOutput( 592: function _checkBit(uint256 _bitMap, uint8 _index) internal pure returns (bool) { 597: function _setBit(uint256 _bitMap, uint8 _index) internal pure returns (uint256) { 605: function _pointEvaluationPrecompile( 625: function _verifyBlobInformation(
[103, 130, 253, 273, 308, 321, 453, 500, 515, 525, 537, 561, 592, 597, 605, 625]
</details>File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Mailbox.sol 130: function _L2MessageToLog(L2Message memory _message) internal pure returns (L2Log memory) { 258: function _requestL2Transaction( 289: function _serializeL2Transaction( 316: function _writePriorityOp( 353: function _hashFactoryDeps(bytes[] memory _factoryDeps) internal pure returns (uint256[] memory hashedFactoryDeps) {
modifiers
that are only used once, to save gasConsider removing the following modifiers, and put the logic directly in the function where they are used, as they are used only once.
There are 7 instances of this issue.
<details> <summary>Expand findings</summary>File: code/system-contracts/contracts/ContractDeployer.sol 28: modifier onlySelf() { 29: require(msg.sender == address(this), "Callable only by self"); 30: _; 31: }
[28-31]
File: code/system-contracts/contracts/KnownCodesStorage.sol 19: modifier onlyCompressor() { 20: require(msg.sender == address(COMPRESSOR_CONTRACT), "Callable only by the compressor"); 21: _; 22: }
[19-22]
File: code/contracts/ethereum/contracts/bridge/L1SharedBridge.sol 74: modifier onlyBridgehubOrEra(uint256 _chainId) { 75: require( 76: msg.sender == address(bridgehub) || (_chainId == ERA_CHAIN_ID && msg.sender == ERA_DIAMOND_PROXY), 77: "L1SharedBridge: not bridgehub or era chain" 78: ); 79: _; 80: }
[74-80]
File: code/contracts/ethereum/contracts/governance/Governance.sol 64: modifier onlySecurityCouncil() { 65: require(msg.sender == securityCouncil, "Only security council is allowed to call this function"); 66: _; 67: } 70: modifier onlyOwnerOrSecurityCouncil() { 71: require( 72: msg.sender == owner() || msg.sender == securityCouncil, 73: "Only the owner and security council are allowed to call this function" 74: ); 75: _; 76: }
File: code/contracts/ethereum/contracts/state-transition/StateTransitionManager.sol 63: modifier onlyBridgehub() { 64: require(msg.sender == bridgehub, "StateTransition: only bridgehub"); 65: _; 66: }
[63-66]
File: code/contracts/zksync/contracts/bridge/L2StandardERC20.sol 134: modifier onlyNextVersion(uint8 _version) { 135: // The version should be incremented by 1. Otherwise, the governor risks disabling 136: // future reinitialization of the token by providing too large a version. 137: require(_version == _getInitializedVersion() + 1, "v"); 138: _; 139: }
[134-139]
</details>private
functions only called once can be inlined to save gasConsider removing the following private functions, and put the logic directly where they are called, as they are called only once.
There are 15 instances of this issue.
File: code/system-contracts/contracts/libraries/TransactionHelper.sol 118: function _encodeHashEIP712Transaction(Transaction calldata _transaction) private view returns (bytes32) { 147: function _encodeHashLegacyTransaction(Transaction calldata _transaction) private view returns (bytes32) { 219: function _encodeHashEIP2930Transaction(Transaction calldata _transaction) private view returns (bytes32) { 289: function _encodeHashEIP1559Transaction(Transaction calldata _transaction) private view returns (bytes32) {
File: code/contracts/ethereum/contracts/common/ReentrancyGuard.sol 46: function _initializeReentrancyGuard() private {
[46]
File: code/contracts/ethereum/contracts/upgrades/BaseZkSyncUpgrade.sol 92: function _setL2DefaultAccountBytecodeHash(bytes32 _l2DefaultAccountBytecodeHash) private { 109: function _setL2BootloaderBytecodeHash(bytes32 _l2BootloaderBytecodeHash) private { 126: function _setVerifier(IVerifier _newVerifier) private { 142: function _setVerifierParams(VerifierParams calldata _newVerifierParams) private { 222: function _verifyFactoryDeps(bytes[] calldata _factoryDeps, uint256[] calldata _expectedHashes) private pure {
File: code/contracts/ethereum/contracts/state-transition/libraries/Diamond.sol 126: function _addFunctions(address _facet, bytes4[] memory _selectors, bool _isFacetFreezable) private { 149: function _replaceFunctions(address _facet, bytes4[] memory _selectors, bool _isFacetFreezable) private { 172: function _removeFunctions(address _facet, bytes4[] memory _selectors) private { 257: function _removeFacet(address _facet) private { 278: function _initializeDiamondCut(address _init, bytes memory _calldata) private {
abi.encode()
is less efficient than abi.encodepacked()
for non-address argumentsConsider refactoring the code by using abi.encodepacked
instead of abi.encode
, as the former is cheaper when used with non-address arguments.
There are 32 instances of this issue.
<details> <summary>Expand findings</summary>File: code/system-contracts/contracts/L1Messenger.sol 106: chainedLogsHash = keccak256(abi.encode(chainedLogsHash, hashedLog)); 122: chainedMessagesHash = keccak256(abi.encode(chainedMessagesHash, hash)); 164: chainedL1BytecodesRevealDataHash = keccak256(abi.encode(chainedL1BytecodesRevealDataHash, _bytecodeHash)); 212: reconstructedChainedLogsHash = keccak256(abi.encode(reconstructedChainedLogsHash, hashedLog)); 226: abi.encode(l2ToL1LogsTreeArray[2 * i], l2ToL1LogsTreeArray[2 * i + 1]) 243: reconstructedChainedMessagesHash = keccak256(abi.encode(reconstructedChainedMessagesHash, hashedMessage)); 260: abi.encode( 261: reconstructedChainedL1BytecodesRevealDataHash, 262: Utils.hashL2Bytecode( 263: _totalL2ToL1PubdataAndStateDiffs[calldataPtr:calldataPtr + currentBytecodeLength] 264: ) 265: )
[106, 122, 164, 212, 226, 243, 260-265]
File: code/system-contracts/contracts/SystemContext.sol 159: hash = keccak256(abi.encode(_block)); 227: return keccak256(abi.encode(_blockNumber, _blockTimestamp, _prevL2BlockHash, _blockTxsRollingHash)); 403: currentL2BlockTxsRollingHash = keccak256(abi.encode(currentL2BlockTxsRollingHash, _txHash));
File: code/system-contracts/contracts/libraries/TransactionHelper.sol 120: abi.encode( 121: EIP712_TRANSACTION_TYPE_HASH, 122: _transaction.txType, 123: _transaction.from, 124: _transaction.to, 125: _transaction.gasLimit, 126: _transaction.gasPerPubdataByteLimit, 127: _transaction.maxFeePerGas, 128: _transaction.maxPriorityFeePerGas, 129: _transaction.paymaster, 130: _transaction.nonce, 131: _transaction.value, 132: EfficientCall.keccak(_transaction.data), 133: keccak256(abi.encodePacked(_transaction.factoryDeps)), 134: EfficientCall.keccak(_transaction.paymasterInput) 135: ) 139: abi.encode(EIP712_DOMAIN_TYPEHASH, keccak256("zkSync"), keccak256("2"), block.chainid)
File: code/contracts/ethereum/contracts/bridge/L1ERC20Bridge.sol 76: bytes32 constructorInputHash = keccak256(abi.encode(l2TokenBeacon, ""));
[76]
File: code/contracts/ethereum/contracts/bridge/L1SharedBridge.sol 210: bytes32 txDataHash = keccak256(abi.encode(_prevMsgSender, _l1Token, amount)); 258: bytes memory decimals = abi.encode(uint8(18)); 259: return abi.encode(name, symbol, decimals); // when depositing eth to a non-eth based chain it is an ERC20 265: return abi.encode(data1, data2, data3); 341: bytes32 txDataHash = keccak256(abi.encode(_depositSender, _l1Token, _amount)); 598: bytes32 txDataHash = keccak256(abi.encode(_prevMsgSender, _l1Token, _amount));
[210, 258, 259, 265, 341, 598]
File: code/contracts/ethereum/contracts/governance/Governance.sol 205: return keccak256(abi.encode(_operation));
[205]
File: code/contracts/ethereum/contracts/state-transition/StateTransitionManager.sol 100: storedBatchZero = keccak256(abi.encode(batchZero)); 102: initialCutHash = keccak256(abi.encode(_initializeData.diamondCut)); 138: initialCutHash = keccak256(abi.encode(_diamondCut)); 147: upgradeCutHash[_oldProtocolVersion] = keccak256(abi.encode(_cutData)); 156: upgradeCutHash[_oldProtocolVersion] = keccak256(abi.encode(_cutData));
File: code/contracts/ethereum/contracts/upgrades/BaseZkSyncUpgrade.sol 192: bytes memory encodedTransaction = abi.encode(_l2ProtocolUpgradeTx);
[192]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Admin.sol 104: bytes32 cutHashInput = keccak256(abi.encode(_diamondCut));
[104]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Executor.sol 313: concatHash = keccak256(abi.encode(concatHash, priorityOp.canonicalTxHash)); 512: return keccak256(abi.encode(passThroughDataHash, metadataHash, auxiliaryOutputHash)); 588: return keccak256(abi.encode(_storedBatchInfo)); 680: (bool success, bytes memory data) = s.blobVersionedHashRetriever.staticcall(abi.encode(_index));
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Mailbox.sol 323: bytes memory transactionEncoding = abi.encode(transaction);
[323]
</details>These comparisons should be avoided, as they are implicit. Considering refactoring by using following approach instead:
if (x == true) -> if (x), if (x == false) => if (!x)
There is 1 instance of this issue.
File: code/contracts/ethereum/contracts/state-transition/ValidatorTimelock.sol 68: require(validators[_chainId][msg.sender] == true, "ValidatorTimelock: only validator");
[68]
Consider changing the variable to be an unnamed one, since the variable is never assigned, nor is it returned by name. If the optimizer is not turned on, leaving the code as it is will also waste gas for the stack variable.
There are 3 instances of this issue.
File: code/system-contracts/contracts/BootloaderUtilities.sol // @audit bytes32 txHash 44: function encodeLegacyTransactionHash(Transaction calldata _transaction) internal view returns (bytes32 txHash) {
[44]
File: code/system-contracts/contracts/ContractDeployer.sol // @audit AccountInfo memory info 34: function getAccountInfo(address _address) external view returns (AccountInfo memory info) {
[34]
File: code/contracts/ethereum/contracts/bridgehub/Bridgehub.sol // @audit uint256 chainId 121: ) external onlyOwnerOrAdmin nonReentrant returns (uint256 chainId) {
[121]
this.<fn>()
wastes gasCalling an external function internally, through the use of this
wastes the gas overhead of calling an external function (100 gas). Instead, change the function from external
to public
, and remove the this
There are 4 instances of this issue.
File: code/system-contracts/contracts/ContractDeployer.sol 254: this.forceDeployOnAddress{value: _deployments[i].value}(_deployments[i], msg.sender);
[254]
File: code/contracts/zksync/contracts/bridge/L2StandardERC20.sol 75: try this.decodeString(nameBytes) returns (string memory nameString) { 81: try this.decodeString(symbolBytes) returns (string memory symbolString) { 93: try this.decodeUint8(decimalsBytes) returns (uint8 decimalsUint8) {
address(this)
to save gasUse Foundry's script.sol
or Solady's LibRlp.sol
to save the value in a constant, which will avoid having to spend gas to push the value on the stack every time it's used.
There are 21 instances of this issue.
<details> <summary>Expand findings</summary>File: code/system-contracts/contracts/ContractDeployer.sol 29: require(msg.sender == address(this), "Callable only by self"); 333: BASE_TOKEN_SYSTEM_CONTRACT.transferFromTo(address(this), _newAddress, value);
File: code/system-contracts/contracts/DefaultAccount.sol 47: if (codeAddress != address(this)) { 100: require(totalRequiredBalance <= address(this).balance, "Not enough balance for fee + value"); 188: return recoveredAddress == address(this) && recoveredAddress != address(0);
File: code/system-contracts/contracts/L1Messenger.sol 129: sender: address(this),
[129]
File: code/system-contracts/contracts/L2BaseToken.sol 106: balance[address(this)] -= amount;
[106]
File: code/system-contracts/contracts/MsgValueSimulator.sol 60: require(to != address(this), "MsgValueSimulator calls itself");
[60]
File: code/system-contracts/contracts/libraries/TransactionHelper.sol 377: uint256 currentAllowance = IERC20(token).allowance(address(this), paymaster);
[377]
File: code/contracts/ethereum/contracts/bridge/L1ERC20Bridge.sol 65: uint256 amount = IERC20(_token).balanceOf(address(this));
[65]
File: code/contracts/ethereum/contracts/bridge/L1SharedBridge.sol 118: uint256 balanceBefore = address(this).balance; 120: uint256 balanceAfter = address(this).balance; 127: uint256 balanceBefore = IERC20(_token).balanceOf(address(this)); 131: uint256 balanceAfter = IERC20(_token).balanceOf(address(this)); 174: uint256 balanceBefore = _token.balanceOf(address(this)); 175: _token.safeTransferFrom(_from, address(this), _amount); 176: uint256 balanceAfter = _token.balanceOf(address(this));
[118, 120, 127, 131, 174, 175, 176]
File: code/contracts/ethereum/contracts/governance/Governance.sol 59: require(msg.sender == address(this), "Only governance contract itself is allowed to call this function");
[59]
File: code/contracts/ethereum/contracts/state-transition/StateTransitionManager.sol 265: bytes32(uint256(uint160(address(this)))),
[265]
File: code/contracts/zksync/contracts/bridge/L2SharedBridge.sol 153: L2ContractHelper.computeCreate2Address(address(this), salt, l2TokenProxyBytecodeHash, constructorInputHash);
[153]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Mailbox.sol 41: uint256 amount = address(this).balance;
[41]
</details>FixedPointMathLib
Saves gas, and works to avoid unnecessary overflows.
There are 3 instances of this issue.
File: code/system-contracts/contracts/L1Messenger.sol 51: return KECCAK_ROUND_GAS_COST * (_length / KECCAK_ROUND_NUMBER_OF_BYTES + 1); 62: return SHA256_ROUND_GAS_COST * ((_length + 8) / SHA256_ROUND_NUMBER_OF_BYTES + 1);
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Mailbox.sol 159: uint256 l1GasPriceConverted = (_l1GasPrice * s.baseTokenGasPriceMultiplierNominator) / 160: s.baseTokenGasPriceMultiplierDenominator;
[159-160]
When solidity generates the bytecode for the smart contract to be deployed, it appends metadata about the compilation at the end of the bytecode.
By default, the solidity compiler appends metadata at the end of the “actual” initcode, which gets stored to the blockchain when the constructor finishes executing.
Consider tweaking the metadata to avoid this unnecessary allocation. A full guide can be found here.
There are 104 instances of this issue.
<details> <summary>Expand findings</summary>File: code/system-contracts/contracts/AccountCodeStorage.sol 22: contract AccountCodeStorage is IAccountCodeStorage {
[22]
File: code/system-contracts/contracts/BootloaderUtilities.sol 16: contract BootloaderUtilities is IBootloaderUtilities {
[16]
File: code/system-contracts/contracts/ComplexUpgrader.sol 14: contract ComplexUpgrader is IComplexUpgrader {
[14]
File: code/system-contracts/contracts/Compressor.sol 22: contract Compressor is ICompressor, ISystemContract {
[22]
File: code/system-contracts/contracts/ContractDeployer.sol 23: contract ContractDeployer is IContractDeployer, ISystemContract {
[23]
File: code/system-contracts/contracts/DefaultAccount.sol 19: contract DefaultAccount is IAccount {
[19]
File: code/system-contracts/contracts/EmptyContract.sol 10: contract EmptyContract {
[10]
File: code/system-contracts/contracts/GasBoundCaller.sol 15: contract GasBoundCaller {
[15]
File: code/system-contracts/contracts/ImmutableSimulator.sol 18: contract ImmutableSimulator is IImmutableSimulator {
[18]
File: code/system-contracts/contracts/KnownCodesStorage.sol 18: contract KnownCodesStorage is IKnownCodesStorage, ISystemContract {
[18]
File: code/system-contracts/contracts/L1Messenger.sol 25: contract L1Messenger is IL1Messenger, ISystemContract {
[25]
File: code/system-contracts/contracts/L2BaseToken.sol 18: contract L2BaseToken is IBaseToken, ISystemContract {
[18]
File: code/system-contracts/contracts/MsgValueSimulator.sol 19: contract MsgValueSimulator is ISystemContract {
[19]
File: code/system-contracts/contracts/NonceHolder.sol 27: contract NonceHolder is INonceHolder, ISystemContract {
[27]
File: code/system-contracts/contracts/PubdataChunkPublisher.sol 16: contract PubdataChunkPublisher is IPubdataChunkPublisher, ISystemContract {
[16]
File: code/system-contracts/contracts/SystemContext.sol 17: contract SystemContext is ISystemContext, ISystemContextDeprecated, ISystemContract {
[17]
File: code/contracts/zksync/contracts/ForceDeployUpgrader.sol 10: contract ForceDeployUpgrader {
[10]
File: code/contracts/zksync/contracts/L2ContractHelper.sol 18: interface IL2Messenger { 30: interface IContractDeployer { 61: interface IBaseToken { 83: library L2ContractHelper {
File: code/contracts/zksync/contracts/SystemContractsCaller.sol 26: library Utils { 36: library SystemContractsCaller {
File: code/system-contracts/contracts/interfaces/IAccount.sol 9: interface IAccount {
[9]
File: code/system-contracts/contracts/interfaces/IAccountCodeStorage.sol 5: interface IAccountCodeStorage {
[5]
File: code/system-contracts/contracts/interfaces/IBaseToken.sol 5: interface IBaseToken {
[5]
File: code/system-contracts/contracts/interfaces/IBootloaderUtilities.sol 7: interface IBootloaderUtilities {
[7]
File: code/system-contracts/contracts/interfaces/IComplexUpgrader.sol 10: interface IComplexUpgrader {
[10]
File: code/system-contracts/contracts/interfaces/ICompressor.sol 18: interface ICompressor {
[18]
File: code/system-contracts/contracts/interfaces/IContractDeployer.sol 5: interface IContractDeployer {
[5]
File: code/system-contracts/contracts/interfaces/IImmutableSimulator.sol 10: interface IImmutableSimulator {
[10]
File: code/system-contracts/contracts/interfaces/IKnownCodesStorage.sol 11: interface IKnownCodesStorage {
[11]
File: code/system-contracts/contracts/interfaces/IL1Messenger.sol 40: interface IL1Messenger {
[40]
File: code/system-contracts/contracts/interfaces/IL2StandardToken.sol 5: interface IL2StandardToken {
[5]
File: code/system-contracts/contracts/interfaces/IMailbox.sol 5: interface IMailbox {
[5]
File: code/system-contracts/contracts/interfaces/INonceHolder.sol 13: interface INonceHolder {
[13]
File: code/system-contracts/contracts/interfaces/IPaymaster.sol 14: interface IPaymaster {
[14]
File: code/system-contracts/contracts/interfaces/IPaymasterFlow.sol 12: interface IPaymasterFlow {
[12]
File: code/system-contracts/contracts/interfaces/IPubdataChunkPublisher.sol 9: interface IPubdataChunkPublisher {
[9]
File: code/system-contracts/contracts/interfaces/ISystemContext.sol 11: interface ISystemContext {
[11]
File: code/system-contracts/contracts/interfaces/ISystemContextDeprecated.sol 10: interface ISystemContextDeprecated {
[10]
File: code/system-contracts/contracts/interfaces/ISystemContract.sol 17: abstract contract ISystemContract {
[17]
File: code/system-contracts/contracts/libraries/EfficientCall.sol 32: library EfficientCall {
[32]
File: code/system-contracts/contracts/libraries/RLPEncoder.sol 10: library RLPEncoder {
[10]
File: code/system-contracts/contracts/libraries/SystemContractHelper.sol 41: library SystemContractHelper {
[41]
File: code/system-contracts/contracts/libraries/SystemContractsCaller.sol 68: library SystemContractsCaller {
[68]
File: code/system-contracts/contracts/libraries/TransactionHelper.sol 78: library TransactionHelper {
[78]
File: code/system-contracts/contracts/libraries/UnsafeBytesCalldata.sol 18: library UnsafeBytesCalldata {
[18]
File: code/system-contracts/contracts/libraries/Utils.sol 11: library Utils {
[11]
File: code/contracts/ethereum/contracts/bridge/L1ERC20Bridge.sol 19: contract L1ERC20Bridge is IL1ERC20Bridge, ReentrancyGuard {
[19]
File: code/contracts/ethereum/contracts/bridge/L1SharedBridge.sol 30: contract L1SharedBridge is IL1SharedBridge, ReentrancyGuard, Initializable, Ownable2Step {
[30]
File: code/contracts/ethereum/contracts/bridgehub/Bridgehub.sol 16: contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2Step {
[16]
File: code/contracts/ethereum/contracts/bridgehub/IBridgehub.sol 42: interface IBridgehub {
[42]
File: code/contracts/ethereum/contracts/common/ReentrancyGuard.sol 25: abstract contract ReentrancyGuard {
[25]
File: code/contracts/ethereum/contracts/governance/Governance.sol 20: contract Governance is IGovernance, Ownable2Step {
[20]
File: code/contracts/ethereum/contracts/governance/IGovernance.sol 8: interface IGovernance {
[8]
File: code/contracts/ethereum/contracts/state-transition/IStateTransitionManager.sol 26: interface IStateTransitionManager {
[26]
File: code/contracts/ethereum/contracts/state-transition/StateTransitionManager.sol 25: contract StateTransitionManager is IStateTransitionManager, ReentrancyGuard, Ownable2Step {
[25]
File: code/contracts/ethereum/contracts/state-transition/ValidatorTimelock.sol 22: contract ValidatorTimelock is IExecutor, Ownable2Step {
[22]
File: code/contracts/ethereum/contracts/upgrades/BaseZkSyncUpgrade.sol 44: abstract contract BaseZkSyncUpgrade is ZkSyncStateTransitionBase {
[44]
File: code/contracts/ethereum/contracts/upgrades/BaseZkSyncUpgradeGenesis.sol 17: abstract contract BaseZkSyncUpgradeGenesis is BaseZkSyncUpgrade {
[17]
File: code/contracts/ethereum/contracts/upgrades/DefaultUpgrade.sol 10: contract DefaultUpgrade is BaseZkSyncUpgrade {
[10]
File: code/contracts/ethereum/contracts/upgrades/GenesisUpgrade.sol 11: contract GenesisUpgrade is BaseZkSyncUpgradeGenesis {
[11]
File: code/contracts/ethereum/contracts/upgrades/IDefaultUpgrade.sol 7: interface IDefaultUpgrade {
[7]
File: code/contracts/ethereum/contracts/vendor/AddressAliasHelper.sol 21: library AddressAliasHelper {
[21]
File: code/contracts/zksync/contracts/bridge/L2SharedBridge.sol 23: contract L2SharedBridge is IL2SharedBridge, Initializable {
[23]
File: code/contracts/zksync/contracts/bridge/L2StandardERC20.sol 15: contract L2StandardERC20 is ERC20PermitUpgradeable, IL2StandardToken, ERC1967Upgrade {
[15]
File: code/contracts/zksync/contracts/interfaces/IPaymaster.sol 14: interface IPaymaster {
[14]
File: code/contracts/zksync/contracts/interfaces/IPaymasterFlow.sol 13: interface IPaymasterFlow {
[13]
File: code/contracts/zksync/contracts/vendor/AddressAliasHelper.sol 21: library AddressAliasHelper {
[21]
File: code/contracts/ethereum/contracts/bridge/interfaces/IL1ERC20Bridge.sol 11: interface IL1ERC20Bridge {
[11]
File: code/contracts/ethereum/contracts/bridge/interfaces/IL1SharedBridge.sol 12: interface IL1SharedBridge {
[12]
File: code/contracts/ethereum/contracts/bridge/interfaces/IL2Bridge.sol 6: interface IL2Bridge {
[6]
File: code/contracts/ethereum/contracts/bridge/interfaces/IWETH9.sol 4: interface IWETH9 {
[4]
File: code/contracts/ethereum/contracts/common/interfaces/IL2ContractDeployer.sol 9: interface IL2ContractDeployer {
[9]
File: code/contracts/ethereum/contracts/common/libraries/L2ContractHelper.sol 10: library L2ContractHelper {
[10]
File: code/contracts/ethereum/contracts/common/libraries/UncheckedMath.sol 10: library UncheckedMath {
[10]
File: code/contracts/ethereum/contracts/common/libraries/UnsafeBytes.sol 18: library UnsafeBytes {
[18]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/DiamondInit.sol 17: contract DiamondInit is ZkSyncStateTransitionBase, IDiamondInit {
[17]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/DiamondProxy.sol 10: contract DiamondProxy {
[10]
File: code/contracts/ethereum/contracts/state-transition/chain-interfaces/IAdmin.sol 13: interface IAdmin is IZkSyncStateTransitionBase {
[13]
File: code/contracts/ethereum/contracts/state-transition/chain-interfaces/IDiamondInit.sol 61: interface IDiamondInit {
[61]
File: code/contracts/ethereum/contracts/state-transition/chain-interfaces/IExecutor.sol 73: interface IExecutor is IZkSyncStateTransitionBase {
[73]
File: code/contracts/ethereum/contracts/state-transition/chain-interfaces/IGetters.sol 13: interface IGetters is IZkSyncStateTransitionBase {
[13]
File: code/contracts/ethereum/contracts/state-transition/chain-interfaces/ILegacyGetters.sol 11: interface ILegacyGetters is IZkSyncStateTransitionBase {
[11]
File: code/contracts/ethereum/contracts/state-transition/chain-interfaces/IMailbox.sol 11: interface IMailbox is IZkSyncStateTransitionBase {
[11]
File: code/contracts/ethereum/contracts/state-transition/chain-interfaces/IVerifier.sol 15: interface IVerifier {
[15]
File: code/contracts/ethereum/contracts/state-transition/chain-interfaces/IZkSyncStateTransition.sol 15: interface IZkSyncStateTransition is IAdmin, IExecutor, IGetters, IMailbox {
[15]
File: code/contracts/ethereum/contracts/state-transition/chain-interfaces/IZkSyncStateTransitionBase.sol 7: interface IZkSyncStateTransitionBase {
[7]
File: code/contracts/ethereum/contracts/state-transition/l2-deps/ISystemContext.sol 4: interface ISystemContext {
[4]
File: code/contracts/ethereum/contracts/state-transition/libraries/Diamond.sol 11: library Diamond {
[11]
File: code/contracts/ethereum/contracts/state-transition/libraries/LibMap.sol 8: library LibMap {
[8]
File: code/contracts/ethereum/contracts/state-transition/libraries/Merkle.sol 9: library Merkle {
[9]
File: code/contracts/ethereum/contracts/state-transition/libraries/PriorityQueue.sol 20: library PriorityQueue {
[20]
File: code/contracts/ethereum/contracts/state-transition/libraries/TransactionValidator.sol 13: library TransactionValidator {
[13]
File: code/contracts/zksync/contracts/bridge/interfaces/IL1ERC20Bridge.sol 8: interface IL1ERC20Bridge {
[8]
File: code/contracts/zksync/contracts/bridge/interfaces/IL1SharedBridge.sol 8: interface IL1SharedBridge {
[8]
File: code/contracts/zksync/contracts/bridge/interfaces/IL2SharedBridge.sol 6: interface IL2SharedBridge {
[6]
File: code/contracts/zksync/contracts/bridge/interfaces/IL2StandardToken.sol 5: interface IL2StandardToken {
[5]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Admin.sol 18: contract AdminFacet is ZkSyncStateTransitionBase, IAdmin {
[18]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Executor.sol 22: contract ExecutorFacet is ZkSyncStateTransitionBase, IExecutor {
[22]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Getters.sol 20: contract GettersFacet is ZkSyncStateTransitionBase, IGetters, ILegacyGetters {
[20]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Mailbox.sol 30: contract MailboxFacet is ZkSyncStateTransitionBase, IMailbox {
[30]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/ZkSyncStateTransitionBase.sol 11: contract ZkSyncStateTransitionBase is ReentrancyGuard {
[11]
</details>Every event parameter costs Glogdata
(8 gas) per byte. You can avoid this extra cost, in cases where you're emitting a constant, by creating a second version of the event, which doesn't have the parameter (and have users look to the contract's variables for its value instead), and using the new event in the cases shown below.
There is 1 instance of this issue.
File: code/contracts/ethereum/contracts/governance/Governance.sol // @audit 0 50: emit ChangeMinDelay(0, _minDelay);
[50]
Every release contains new gas optimizations, use the latest version to take advantage of this.
There are 22 instances of this issue.
<details> <summary>Expand findings</summary>File: code/system-contracts/contracts/libraries/TransactionHelper.sol 5: import "../openzeppelin/token/ERC20/IERC20.sol"; 6: import "../openzeppelin/token/ERC20/utils/SafeERC20.sol";
File: code/contracts/ethereum/contracts/bridge/L1ERC20Bridge.sol 5: import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 6: import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
File: code/contracts/ethereum/contracts/bridge/L1SharedBridge.sol 5: import {Initializable} from "@openzeppelin/contracts/proxy/utils/Initializable.sol"; 6: import {Ownable2Step} from "@openzeppelin/contracts/access/Ownable2Step.sol"; 8: import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; 9: import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 10: import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
File: code/contracts/ethereum/contracts/bridgehub/Bridgehub.sol 5: import {Ownable2Step} from "@openzeppelin/contracts/access/Ownable2Step.sol";
[5]
File: code/contracts/ethereum/contracts/governance/Governance.sol 5: import {Ownable2Step} from "@openzeppelin/contracts/access/Ownable2Step.sol";
[5]
File: code/contracts/ethereum/contracts/state-transition/StateTransitionManager.sol 16: import {Ownable2Step} from "@openzeppelin/contracts/access/Ownable2Step.sol";
[16]
File: code/contracts/ethereum/contracts/state-transition/ValidatorTimelock.sol 5: import {Ownable2Step} from "@openzeppelin/contracts/access/Ownable2Step.sol";
[5]
File: code/contracts/zksync/contracts/bridge/L2SharedBridge.sol 5: import {Initializable} from "@openzeppelin/contracts/proxy/utils/Initializable.sol"; 6: import {BeaconProxy} from "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol"; 7: import {UpgradeableBeacon} from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol";
File: code/contracts/zksync/contracts/bridge/L2StandardERC20.sol 5: import {ERC20PermitUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/draft-ERC20PermitUpgradeable.sol"; 6: import {UpgradeableBeacon} from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; 7: import {ERC1967Upgrade} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Upgrade.sol";
File: code/contracts/ethereum/contracts/state-transition/libraries/Diamond.sol 5: import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";
[5]
File: code/contracts/ethereum/contracts/state-transition/libraries/TransactionValidator.sol 5: import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
[5]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Mailbox.sol 5: import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
[5]
</details>Function that are public
/external
and public
state variable names can be optimized to save gas.
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. Reference
There are 80 instances of this issue.
<details> <summary>Expand findings</summary>File: code/system-contracts/contracts/AccountCodeStorage.sol 22: contract AccountCodeStorage is IAccountCodeStorage {
[22]
File: code/system-contracts/contracts/BootloaderUtilities.sol 16: contract BootloaderUtilities is IBootloaderUtilities {
[16]
File: code/system-contracts/contracts/ComplexUpgrader.sol 14: contract ComplexUpgrader is IComplexUpgrader {
[14]
File: code/system-contracts/contracts/Compressor.sol 22: contract Compressor is ICompressor, ISystemContract {
[22]
File: code/system-contracts/contracts/ContractDeployer.sol 23: contract ContractDeployer is IContractDeployer, ISystemContract {
[23]
File: code/system-contracts/contracts/DefaultAccount.sol 19: contract DefaultAccount is IAccount {
[19]
File: code/system-contracts/contracts/EmptyContract.sol 10: contract EmptyContract {
[10]
File: code/system-contracts/contracts/GasBoundCaller.sol 15: contract GasBoundCaller {
[15]
File: code/system-contracts/contracts/ImmutableSimulator.sol 18: contract ImmutableSimulator is IImmutableSimulator {
[18]
File: code/system-contracts/contracts/KnownCodesStorage.sol 18: contract KnownCodesStorage is IKnownCodesStorage, ISystemContract {
[18]
File: code/system-contracts/contracts/L1Messenger.sol 25: contract L1Messenger is IL1Messenger, ISystemContract {
[25]
File: code/system-contracts/contracts/L2BaseToken.sol 18: contract L2BaseToken is IBaseToken, ISystemContract {
[18]
File: code/system-contracts/contracts/MsgValueSimulator.sol 19: contract MsgValueSimulator is ISystemContract {
[19]
File: code/system-contracts/contracts/NonceHolder.sol 27: contract NonceHolder is INonceHolder, ISystemContract {
[27]
File: code/system-contracts/contracts/PubdataChunkPublisher.sol 16: contract PubdataChunkPublisher is IPubdataChunkPublisher, ISystemContract {
[16]
File: code/system-contracts/contracts/SystemContext.sol 17: contract SystemContext is ISystemContext, ISystemContextDeprecated, ISystemContract {
[17]
File: code/contracts/zksync/contracts/ForceDeployUpgrader.sol 10: contract ForceDeployUpgrader {
[10]
File: code/contracts/zksync/contracts/L2ContractHelper.sol 18: interface IL2Messenger { 30: interface IContractDeployer { 61: interface IBaseToken {
File: code/system-contracts/contracts/interfaces/IAccount.sol 9: interface IAccount {
[9]
File: code/system-contracts/contracts/interfaces/IAccountCodeStorage.sol 5: interface IAccountCodeStorage {
[5]
File: code/system-contracts/contracts/interfaces/IBaseToken.sol 5: interface IBaseToken {
[5]
File: code/system-contracts/contracts/interfaces/IBootloaderUtilities.sol 7: interface IBootloaderUtilities {
[7]
File: code/system-contracts/contracts/interfaces/IComplexUpgrader.sol 10: interface IComplexUpgrader {
[10]
File: code/system-contracts/contracts/interfaces/ICompressor.sol 18: interface ICompressor {
[18]
File: code/system-contracts/contracts/interfaces/IContractDeployer.sol 5: interface IContractDeployer {
[5]
File: code/system-contracts/contracts/interfaces/IImmutableSimulator.sol 10: interface IImmutableSimulator {
[10]
File: code/system-contracts/contracts/interfaces/IKnownCodesStorage.sol 11: interface IKnownCodesStorage {
[11]
File: code/system-contracts/contracts/interfaces/IL1Messenger.sol 40: interface IL1Messenger {
[40]
File: code/system-contracts/contracts/interfaces/IL2StandardToken.sol 5: interface IL2StandardToken {
[5]
File: code/system-contracts/contracts/interfaces/IMailbox.sol 5: interface IMailbox {
[5]
File: code/system-contracts/contracts/interfaces/INonceHolder.sol 13: interface INonceHolder {
[13]
File: code/system-contracts/contracts/interfaces/IPaymaster.sol 14: interface IPaymaster {
[14]
File: code/system-contracts/contracts/interfaces/IPaymasterFlow.sol 12: interface IPaymasterFlow {
[12]
File: code/system-contracts/contracts/interfaces/IPubdataChunkPublisher.sol 9: interface IPubdataChunkPublisher {
[9]
File: code/system-contracts/contracts/interfaces/ISystemContext.sol 11: interface ISystemContext {
[11]
File: code/system-contracts/contracts/interfaces/ISystemContextDeprecated.sol 10: interface ISystemContextDeprecated {
[10]
File: code/contracts/ethereum/contracts/bridge/L1ERC20Bridge.sol 19: contract L1ERC20Bridge is IL1ERC20Bridge, ReentrancyGuard {
[19]
File: code/contracts/ethereum/contracts/bridge/L1SharedBridge.sol 30: contract L1SharedBridge is IL1SharedBridge, ReentrancyGuard, Initializable, Ownable2Step {
[30]
File: code/contracts/ethereum/contracts/bridgehub/Bridgehub.sol 16: contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2Step {
[16]
File: code/contracts/ethereum/contracts/bridgehub/IBridgehub.sol 42: interface IBridgehub {
[42]
File: code/contracts/ethereum/contracts/governance/Governance.sol 20: contract Governance is IGovernance, Ownable2Step {
[20]
File: code/contracts/ethereum/contracts/governance/IGovernance.sol 8: interface IGovernance {
[8]
File: code/contracts/ethereum/contracts/state-transition/IStateTransitionManager.sol 26: interface IStateTransitionManager {
[26]
File: code/contracts/ethereum/contracts/state-transition/StateTransitionManager.sol 25: contract StateTransitionManager is IStateTransitionManager, ReentrancyGuard, Ownable2Step {
[25]
File: code/contracts/ethereum/contracts/state-transition/ValidatorTimelock.sol 22: contract ValidatorTimelock is IExecutor, Ownable2Step {
[22]
File: code/contracts/ethereum/contracts/upgrades/BaseZkSyncUpgrade.sol 44: abstract contract BaseZkSyncUpgrade is ZkSyncStateTransitionBase {
[44]
File: code/contracts/ethereum/contracts/upgrades/BaseZkSyncUpgradeGenesis.sol 17: abstract contract BaseZkSyncUpgradeGenesis is BaseZkSyncUpgrade {
[17]
File: code/contracts/ethereum/contracts/upgrades/DefaultUpgrade.sol 10: contract DefaultUpgrade is BaseZkSyncUpgrade {
[10]
File: code/contracts/ethereum/contracts/upgrades/GenesisUpgrade.sol 11: contract GenesisUpgrade is BaseZkSyncUpgradeGenesis {
[11]
File: code/contracts/ethereum/contracts/upgrades/IDefaultUpgrade.sol 7: interface IDefaultUpgrade {
[7]
File: code/contracts/zksync/contracts/bridge/L2SharedBridge.sol 23: contract L2SharedBridge is IL2SharedBridge, Initializable {
[23]
File: code/contracts/zksync/contracts/bridge/L2StandardERC20.sol 15: contract L2StandardERC20 is ERC20PermitUpgradeable, IL2StandardToken, ERC1967Upgrade {
[15]
File: code/contracts/zksync/contracts/interfaces/IPaymaster.sol 14: interface IPaymaster {
[14]
File: code/contracts/zksync/contracts/interfaces/IPaymasterFlow.sol 13: interface IPaymasterFlow {
[13]
File: code/contracts/ethereum/contracts/bridge/interfaces/IL1ERC20Bridge.sol 11: interface IL1ERC20Bridge {
[11]
File: code/contracts/ethereum/contracts/bridge/interfaces/IL1SharedBridge.sol 12: interface IL1SharedBridge {
[12]
File: code/contracts/ethereum/contracts/bridge/interfaces/IL2Bridge.sol 6: interface IL2Bridge {
[6]
File: code/contracts/ethereum/contracts/bridge/interfaces/IWETH9.sol 4: interface IWETH9 {
[4]
File: code/contracts/ethereum/contracts/common/interfaces/IL2ContractDeployer.sol 9: interface IL2ContractDeployer {
[9]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/DiamondInit.sol 17: contract DiamondInit is ZkSyncStateTransitionBase, IDiamondInit {
[17]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/DiamondProxy.sol 10: contract DiamondProxy {
[10]
File: code/contracts/ethereum/contracts/state-transition/chain-interfaces/IAdmin.sol 13: interface IAdmin is IZkSyncStateTransitionBase {
[13]
File: code/contracts/ethereum/contracts/state-transition/chain-interfaces/IDiamondInit.sol 61: interface IDiamondInit {
[61]
File: code/contracts/ethereum/contracts/state-transition/chain-interfaces/IExecutor.sol 73: interface IExecutor is IZkSyncStateTransitionBase {
[73]
File: code/contracts/ethereum/contracts/state-transition/chain-interfaces/IGetters.sol 13: interface IGetters is IZkSyncStateTransitionBase {
[13]
File: code/contracts/ethereum/contracts/state-transition/chain-interfaces/ILegacyGetters.sol 11: interface ILegacyGetters is IZkSyncStateTransitionBase {
[11]
File: code/contracts/ethereum/contracts/state-transition/chain-interfaces/IMailbox.sol 11: interface IMailbox is IZkSyncStateTransitionBase {
[11]
File: code/contracts/ethereum/contracts/state-transition/chain-interfaces/IVerifier.sol 15: interface IVerifier {
[15]
File: code/contracts/ethereum/contracts/state-transition/chain-interfaces/IZkSyncStateTransitionBase.sol 7: interface IZkSyncStateTransitionBase {
[7]
File: code/contracts/ethereum/contracts/state-transition/l2-deps/ISystemContext.sol 4: interface ISystemContext {
[4]
File: code/contracts/zksync/contracts/bridge/interfaces/IL1ERC20Bridge.sol 8: interface IL1ERC20Bridge {
[8]
File: code/contracts/zksync/contracts/bridge/interfaces/IL1SharedBridge.sol 8: interface IL1SharedBridge {
[8]
File: code/contracts/zksync/contracts/bridge/interfaces/IL2SharedBridge.sol 6: interface IL2SharedBridge {
[6]
File: code/contracts/zksync/contracts/bridge/interfaces/IL2StandardToken.sol 5: interface IL2StandardToken {
[5]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Admin.sol 18: contract AdminFacet is ZkSyncStateTransitionBase, IAdmin {
[18]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Executor.sol 22: contract ExecutorFacet is ZkSyncStateTransitionBase, IExecutor {
[22]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Getters.sol 20: contract GettersFacet is ZkSyncStateTransitionBase, IGetters, ILegacyGetters {
[20]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Mailbox.sol 30: contract MailboxFacet is ZkSyncStateTransitionBase, IMailbox {
[30]
</details>delete
instead of setting mapping/struct to 0 saves gasAvoid an assignment by deleting the value instead of setting it to zero, as it's cheaper.
There are 3 instances of this issue.
File: code/system-contracts/contracts/L1Messenger.sol 330: numberOfLogsToProcess = 0;
[330]
File: code/system-contracts/contracts/SystemContext.sol 487: txNumberInBlock = 0;
[487]
File: code/contracts/ethereum/contracts/bridgehub/Bridgehub.sol 276: baseTokenMsgValue = 0;
[276]
""
is cheaper than new bytes(0)
Saves 238 gas per instance
There are 8 instances of this issue.
File: code/contracts/ethereum/contracts/state-transition/StateTransitionManager.sol 196: signature: new bytes(0), 198: paymasterInput: new bytes(0), 199: reservedDynamic: new bytes(0) 213: l1ContractsUpgradeCalldata: new bytes(0), 214: postUpgradeCalldata: new bytes(0),
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Mailbox.sol 308: signature: new bytes(0), 310: paymasterInput: new bytes(0), 311: reservedDynamic: new bytes(0)
bool
for storage incurs overheadBooleans are more expensive than uint256
or any type that takes up a full word because each write operation emits an extra SLOAD to first read the slot's contents, replace the bits taken up by the boolean, and then write back.
This is the compiler's defense against contract upgrades and pointer aliasing, and it cannot be disabled. Use uint256(0) and uint256(1)
for true/false
to avoid a Gwarmaccess (100 gas) for the extra SLOAD
.
There are 6 instances of this issue.
File: code/contracts/ethereum/contracts/bridge/L1ERC20Bridge.sol 27: mapping(uint256 l2BatchNumber => mapping(uint256 l2ToL1MessageNumber => bool isFinalized)) 28: public isWithdrawalFinalized;
[27-28]
File: code/contracts/ethereum/contracts/bridge/L1SharedBridge.sol 57: mapping(uint256 chainId => mapping(uint256 l2BatchNumber => mapping(uint256 l2ToL1MessageNumber => bool isFinalized))) 58: public isWithdrawalFinalized; 61: mapping(uint256 chainId => bool enabled) internal hyperbridgingEnabled;
File: code/contracts/ethereum/contracts/bridgehub/Bridgehub.sol 21: mapping(address _stateTransitionManager => bool) public stateTransitionManagerIsRegistered; 23: mapping(address _token => bool) public tokenIsRegistered;
File: code/contracts/ethereum/contracts/state-transition/ValidatorTimelock.sol 50: mapping(uint256 _chainId => mapping(address _validator => bool)) public validators;
[50]
X * 2
is equivalent to X << 1
and X / 2
is the same as X >> 1
.
The MUL
and DIV
opcodes cost 5 gas, whereas SHL
and SHR
only cost 3 gas.
There are 4 instances of this issue.
File: code/system-contracts/contracts/BootloaderUtilities.sol 97: vInt += 8 + block.chainid * 2;
[97]
File: code/system-contracts/contracts/Compressor.sol 57: dictionary.length / 8 <= encodedData.length / 2,
[57]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Executor.sol 581: blobAuxOutputWords[i * 2] = _blobHashes[i]; 582: blobAuxOutputWords[i * 2 + 1] = _blobCommitments[i];
x += y
is more expensive than x = x + y
for state variablesNot inlining costs 20 to 40 gas because of two extra JUMP instructions and additional stack operations needed for function calls.
There are 3 instances of this issue.
File: code/system-contracts/contracts/L2BaseToken.sol 65: totalSupply += _amount; 107: totalSupply -= amount;
File: code/system-contracts/contracts/SystemContext.sol 483: txNumberInBlock += 1;
[483]
+=
is cheaper for mappingsUsing +=
for mappings saves 40 gas due to not having to recalculate the mapping's value's hash.
There are 2 instances of this issue.
File: code/contracts/ethereum/contracts/bridge/L1SharedBridge.sol 122: chainBalance[_targetChainId][ETH_TOKEN_ADDRESS] = 123: chainBalance[_targetChainId][ETH_TOKEN_ADDRESS] + 124: balanceAfter - 125: balanceBefore; 133: chainBalance[_targetChainId][_token] = chainBalance[_targetChainId][_token] + amount;
When using storage arrays, solidity adds an internal lookup of the array's length (a Gcoldsload 2100 gas) to ensure you don't read past the array's end.
You can avoid this lookup by using a mapping and storing the number of entries in a separate storage variable. In cases where you have sentinel values (e.g. 'zero' means invalid), you can avoid length checks.
There is 1 instance of this issue.
File: code/system-contracts/contracts/SystemContext.sol 70: bytes32[MINIBLOCK_HASHES_TO_STORE] internal l2BlockHash;
[70]
If a string can fit in 32 bytes is it better to use bytes32
instead of string
, as it is cheaper.
// @audit avoid this string constant stringVariable = "LessThan32Bytes"; // @audit as this is cheaper bytes32 constant stringVariable = "LessThan32Bytes";
There are 5 instances of this issue.
File: code/contracts/ethereum/contracts/state-transition/ValidatorTimelock.sol 26: string public constant override getName = "ValidatorTimelock";
[26]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Admin.sol 20: string public constant override getName = "AdminFacet";
[20]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Executor.sol 27: string public constant override getName = "ExecutorFacet";
[27]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Getters.sol 25: string public constant override getName = "GettersFacet";
[25]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Mailbox.sol 35: string public constant override getName = "MailboxFacet";
[35]
payable
payable
functions cost less gas to execute, since the compiler does not have to add extra checks to ensure that a payment wasn't provided.
A constructor
can safely be marked as payable
, since only the deployer would be able to pass funds, and the project itself would not pass any funds.
There are 10 instances of this issue.
<details> <summary>Expand findings</summary>File: code/contracts/ethereum/contracts/bridge/L1ERC20Bridge.sol 55: constructor(IL1SharedBridge _sharedBridge) reentrancyGuardInitializer {
[55]
File: code/contracts/ethereum/contracts/bridge/L1SharedBridge.sol 90: constructor(
[90]
File: code/contracts/ethereum/contracts/bridgehub/Bridgehub.sol 38: constructor() reentrancyGuardInitializer {}
[38]
File: code/contracts/ethereum/contracts/governance/Governance.sol 41: constructor(address _admin, address _securityCouncil, uint256 _minDelay) {
[41]
File: code/contracts/ethereum/contracts/state-transition/StateTransitionManager.sol 58: constructor(address _bridgehub) reentrancyGuardInitializer {
[58]
File: code/contracts/ethereum/contracts/state-transition/ValidatorTimelock.sol 55: constructor(address _initialOwner, uint32 _executionDelay) {
[55]
File: code/contracts/zksync/contracts/bridge/L2SharedBridge.sol 41: constructor() {
[41]
File: code/contracts/zksync/contracts/bridge/L2StandardERC20.sol 40: constructor() {
[40]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/DiamondInit.sol 19: constructor() reentrancyGuardInitializer {}
[19]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/DiamondProxy.sol 11: constructor(uint256 _chainId, Diamond.DiamondCutData memory _diamondCut) {
[11]
</details>Considering refactoring the revert message to fit in 32 bytes to avoid using more than one memory slot.
There are 104 instances of this issue.
<details> <summary>Expand findings</summary>File: code/system-contracts/contracts/AccountCodeStorage.sol 26: require(msg.sender == address(DEPLOYER_SYSTEM_CONTRACT), "Callable only by the deployer system contract"); 37: require(Utils.isContractConstructing(_hash), "Code hash is not for a contract on constructor"); 48: require(Utils.isContractConstructed(_hash), "Code hash is not for a constructed contract"); 57: require(Utils.isContractConstructing(codeHash), "Code hash is not for a contract on constructor");
File: code/system-contracts/contracts/ComplexUpgrader.sol 22: require(msg.sender == FORCE_DEPLOYER, "Can only be called by FORCE_DEPLOYER");
[22]
File: code/system-contracts/contracts/Compressor.sol 51: require( 52: encodedData.length * 4 == _bytecode.length, 53: "Encoded data length should be 4 times shorter than the original bytecode" 54: ); 56: require( 57: dictionary.length / 8 <= encodedData.length / 2, 58: "Dictionary should have at most the same number of entries as the encoded data" 59: ); 63: require(indexOfEncodedChunk < dictionary.length, "Encoded chunk index is out of bounds"); 68: require(encodedChunk == realChunk, "Encoded chunk does not match the original bytecode"); 119: require(_enumerationIndexSize <= MAX_ENUMERATION_INDEX_SIZE, "enumeration index size is too large"); 156: require(numInitialWritesProcessed == numberOfInitialWrites, "Incorrect number of initial storage diffs"); 187: require(stateDiffPtr == _compressedStateDiffs.length, "Extra data in _compressedStateDiffs"); 230: require(convertedValue == _finalValue, "transform or no compression: compressed and final mismatch"); 232: require( 233: _initialValue + convertedValue == _finalValue, 234: "add: initial plus converted not equal to final" 235: ); 237: require( 238: _initialValue - convertedValue == _finalValue, 239: "sub: initial minus converted not equal to final" 240: );
[51-54, 56-59, 63, 68, 119, 156, 187, 230, 232-235, 237-240]
File: code/system-contracts/contracts/ContractDeployer.sol 77: require( 78: _nonceOrdering == AccountNonceOrdering.Arbitrary && 79: currentInfo.nonceOrdering == AccountNonceOrdering.Sequential, 80: "It is only possible to change from sequential to arbitrary ordering" 81: ); 239: require( 240: msg.sender == FORCE_DEPLOYER || msg.sender == address(COMPLEX_UPGRADER_CONTRACT), 241: "Can only be called by FORCE_DEPLOYER or COMPLEX_UPGRADER_CONTRACT" 242: ); 251: require(msg.value == sumOfValues, "`value` provided is not equal to the combined `value`s of deployments"); 265: require(uint160(_newAddress) > MAX_SYSTEM_CONTRACT_ADDRESS, "Can not deploy contracts in kernel space"); 350: require(value == 0, "The value must be zero if we do not call the constructor");
[77-81, 239-242, 251, 265, 350]
File: code/system-contracts/contracts/DefaultAccount.sol 100: require(totalRequiredBalance <= address(this).balance, "Not enough balance for fee + value"); 202: require(success, "Failed to pay the fee to the operator");
File: code/system-contracts/contracts/ImmutableSimulator.sol 35: require(msg.sender == address(DEPLOYER_SYSTEM_CONTRACT), "Callable only by the deployer system contract");
[35]
File: code/system-contracts/contracts/KnownCodesStorage.sol 76: require(version == 1 && _bytecodeHash[1] == bytes1(0), "Incorrectly formatted bytecodeHash");
[76]
File: code/system-contracts/contracts/L1Messenger.sol 214: require( 215: reconstructedChainedLogsHash == chainedLogsHash, 216: "reconstructedChainedLogsHash is not equal to chainedLogsHash" 217: ); 245: require( 246: reconstructedChainedMessagesHash == chainedMessagesHash, 247: "reconstructedChainedMessagesHash is not equal to chainedMessagesHash" 248: ); 269: require( 270: reconstructedChainedL1BytecodesRevealDataHash == chainedL1BytecodesRevealDataHash, 271: "reconstructedChainedL1BytecodesRevealDataHash is not equal to chainedL1BytecodesRevealDataHash" 272: ); 279: require( 280: uint256(uint8(bytes1(_totalL2ToL1PubdataAndStateDiffs[calldataPtr]))) == 281: STATE_DIFF_COMPRESSION_VERSION_NUMBER, 282: "state diff compression version mismatch" 283: ); 315: require(calldataPtr == _totalL2ToL1PubdataAndStateDiffs.length, "Extra data in the totalL2ToL1Pubdata array");
[214-217, 245-248, 269-272, 279-283, 315]
File: code/system-contracts/contracts/L2BaseToken.sol 33: require( 34: msg.sender == MSG_VALUE_SYSTEM_CONTRACT || 35: msg.sender == address(DEPLOYER_SYSTEM_CONTRACT) || 36: msg.sender == BOOTLOADER_FORMAL_ADDRESS, 37: "Only system contracts with special access can call this method" 38: );
[33-38]
File: code/system-contracts/contracts/NonceHolder.sol 66: require(_value <= MAXIMAL_MIN_NONCE_INCREMENT, "The value for incrementing the nonce is too high"); 136: require( 137: msg.sender == address(DEPLOYER_SYSTEM_CONTRACT), 138: "Only the contract deployer can increment the deployment nonce" 139: );
File: code/system-contracts/contracts/SystemContext.sol 242: require(_isFirstInBatch, "Upgrade transaction must be first"); 245: require(_l2BlockNumber > 0, "L2 block number is never expected to be zero"); 249: require(correctPrevBlockHash == _expectedPrevL2BlockHash, "The previous L2 block hash is incorrect"); 287: require(_maxVirtualBlocksToCreate > 0, "Can't initialize the first virtual block"); 351: require( 352: _l2BlockTimestamp >= currentBatchTimestamp, 353: "The timestamp of the L2 block must be greater than or equal to the timestamp of the current batch" 354: ); 355: require(_maxVirtualBlocksToCreate > 0, "There must be a virtual block created at the start of the batch"); 367: require(!_isFirstInBatch, "Can not reuse L2 block number from the previous batch"); 368: require(currentL2BlockTimestamp == _l2BlockTimestamp, "The timestamp of the same L2 block must be same"); 369: require( 370: _expectedPrevL2BlockHash == _getLatest257L2blockHash(_l2BlockNumber - 1), 371: "The previous hash of the same L2 block must be same" 372: ); 373: require(_maxVirtualBlocksToCreate == 0, "Can not create virtual blocks in the middle of the miniblock"); 385: require(_expectedPrevL2BlockHash == pendingL2BlockHash, "The current L2 block hash is incorrect"); 386: require( 387: _l2BlockTimestamp > currentL2BlockTimestamp, 388: "The timestamp of the new L2 block must be greater than the timestamp of the previous L2 block" 389: ); 413: require(currentBatchNumber > 0, "The current batch number must be greater than 0"); 429: require( 430: _newTimestamp > currentBlockTimestamp, 431: "The timestamp of the batch must be greater than the timestamp of the previous block" 432: ); 452: require(previousBatchNumber + 1 == _expectedNewNumber, "The provided block number is not correct");
[242, 245, 249, 287, 351-354, 355, 367, 368, 369-372, 373, 385, 386-389, 413, 429-432, 452]
File: code/system-contracts/contracts/interfaces/ISystemContract.sol 21: require( 22: SystemContractHelper.isSystemCall() || SystemContractHelper.isSystemContract(msg.sender), 23: "This method require system call flag" 24: ); 31: require( 32: SystemContractHelper.isSystemContract(msg.sender), 33: "This method require the caller to be system contract" 34: );
File: code/system-contracts/contracts/libraries/SystemContractHelper.sol 319: require(index < 10, "There are only 10 accessible registers");
[319]
File: code/system-contracts/contracts/libraries/TransactionHelper.sol 363: require(_transaction.paymasterInput.length >= 4, "The standard paymaster input must be at least 4 bytes long"); 367: require( 368: _transaction.paymasterInput.length >= 68, 369: "The approvalBased paymaster input must be at least 68 bytes long" 370: );
File: code/contracts/ethereum/contracts/bridge/L1SharedBridge.sol 75: require( 76: msg.sender == address(bridgehub) || (_chainId == ERA_CHAIN_ID && msg.sender == ERA_DIAMOND_PROXY), 77: "L1SharedBridge: not bridgehub or era chain" 78: ); 155: require(msg.value == _amount, "L1SharedBridge: msg.value not equal to amount"); 195: require(bridgehub.baseToken(_chainId) != _l1Token, "ShB: baseToken deposit not supported"); 427: require(!alreadyFinalized, "Withdrawal is already finalized 2"); 564: require(_l1Token != l1WethAddress, "ShB: WETH deposit not supported 2");
File: code/contracts/ethereum/contracts/bridgehub/Bridgehub.sol 83: require( 84: !stateTransitionManagerIsRegistered[_stateTransitionManager], 85: "Bridgehub: state transition already registered" 86: ); 93: require( 94: stateTransitionManagerIsRegistered[_stateTransitionManager], 95: "Bridgehub: state transition not registered yet" 96: ); 102: require(!tokenIsRegistered[_token], "Bridgehub: token already registered"); 125: require( 126: stateTransitionManagerIsRegistered[_stateTransitionManager], 127: "Bridgehub: state transition not registered" 128: ); 132: require(stateTransitionManager[_chainId] == address(0), "Bridgehub: chainId already registered"); 225: require(msg.value == 0, "Bridgehub: non-eth bridge with msg.value"); 300: require( 301: _request.secondBridgeAddress > BRIDGEHUB_MIN_SECOND_BRIDGE_ADDRESS, 302: "Bridgehub: second bridge address too low" 303: ); // to avoid calls to precompiles
[83-86, 93-96, 102, 125-128, 132, 225, 300-303]
File: code/contracts/ethereum/contracts/governance/Governance.sol 59: require(msg.sender == address(this), "Only governance contract itself is allowed to call this function"); 65: require(msg.sender == securityCouncil, "Only security council is allowed to call this function"); 71: require( 72: msg.sender == owner() || msg.sender == securityCouncil, 73: "Only the owner and security council are allowed to call this function" 74: ); 172: require(isOperationReady(id), "Operation must be ready before execution"); 177: require(isOperationReady(id), "Operation must be ready after execution"); 191: require(isOperationPending(id), "Operation must be pending before execution"); 196: require(isOperationPending(id), "Operation must be pending after execution"); 216: require(!isOperation(_id), "Operation with this proposal id already exists"); 217: require(_delay >= minDelay, "Proposed delay is less than minimum delay"); 240: require(_predecessorId == bytes32(0) || isOperationDone(_predecessorId), "Predecessor operation not completed");
[59, 65, 71-74, 172, 177, 191, 196, 216, 217, 240]
File: code/contracts/ethereum/contracts/state-transition/StateTransitionManager.sol 256: require(cutHashInput == initialCutHash, "StateTransition: initial cutHash mismatch");
[256]
File: code/contracts/ethereum/contracts/state-transition/ValidatorTimelock.sol 62: require(msg.sender == stateTransitionManager.getChainAdmin(_chainId), "ValidatorTimelock: only chain admin"); 68: require(validators[_chainId][msg.sender] == true, "ValidatorTimelock: only validator");
File: code/contracts/ethereum/contracts/upgrades/BaseZkSyncUpgrade.sol 190: require(_l2ProtocolUpgradeTx.txType == SYSTEM_UPGRADE_L2_TX_TYPE, "L2 system upgrade tx type is wrong"); 205: require( 206: _l2ProtocolUpgradeTx.nonce == _newProtocolVersion, 207: "The new protocol version should be included in the L2 system upgrade tx" 208: ); 238: require( 239: _newProtocolVersion > previousProtocolVersion, 240: "New protocol version is not greater than the current one" 241: ); 242: require( 243: _newProtocolVersion - previousProtocolVersion <= MAX_ALLOWED_PROTOCOL_VERSION_DELTA, 244: "Too big protocol version difference" 245: ); 249: require(s.l2SystemContractsUpgradeTxHash == bytes32(0), "Previous upgrade has not been finalized"); 250: require( 251: s.l2SystemContractsUpgradeBatchNumber == 0, 252: "The batch number of the previous upgrade has not been cleaned" 253: );
[190, 205-208, 238-241, 242-245, 249, 250-253]
File: code/contracts/ethereum/contracts/upgrades/BaseZkSyncUpgradeGenesis.sol 22: require( 23: // Genesis Upgrade difference: Note this is the only thing change > to >= 24: _newProtocolVersion >= previousProtocolVersion, 25: "New protocol version is not greater than the current one" 26: ); 27: require( 28: _newProtocolVersion - previousProtocolVersion <= MAX_ALLOWED_PROTOCOL_VERSION_DELTA, 29: "Too big protocol version difference" 30: ); 33: require(s.l2SystemContractsUpgradeTxHash == bytes32(0), "Previous upgrade has not been finalized"); 34: require( 35: s.l2SystemContractsUpgradeBatchNumber == 0, 36: "The batch number of the previous upgrade has not been cleaned" 37: );
File: code/contracts/zksync/contracts/bridge/L2SharedBridge.sol 92: require(msg.value == 0, "Value should be 0 for ERC20 bridge");
[92]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Admin.sol 90: require(s.totalBatchesCommitted == 0, "AdminFacet: set validium only after genesis"); // Validium mode can be set only before the first batch is committed 105: require( 106: cutHashInput == IStateTransitionManager(s.stateTransitionManager).upgradeCutHash(_oldProtocolVersion), 107: "StateTransition: cutHash mismatch" 108: ); 110: require( 111: s.protocolVersion == _oldProtocolVersion, 112: "StateTransition: protocolVersion mismatch in STC when upgrading" 113: ); 116: require( 117: s.protocolVersion > _oldProtocolVersion, 118: "StateTransition: protocolVersion mismatch in STC after upgrading" 119: );
[90, 105-108, 110-113, 116-119]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Executor.sol 226: require( 227: IStateTransitionManager(s.stateTransitionManager).protocolVersion() == s.protocolVersion, 228: "Executor facet: wrong protocol version" 229: ); 616: require(success, "failed to call point evaluation precompile");
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Mailbox.sol 39: require(s.chainId == ERA_CHAIN_ID, "transferEthToSharedBridge only available for Era on mailbox"); 158: require(s.baseTokenGasPriceMultiplierDenominator > 0, "Mailbox: baseTokenGasPriceDenominator not set"); 185: require(s.chainId == ERA_CHAIN_ID, "finalizeEthWithdrawal only available for Era on mailbox"); 206: require(s.chainId == ERA_CHAIN_ID, "legacy interface only available for era token");
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/ZkSyncStateTransitionBase.sol 22: require(s.validators[msg.sender], "StateTransition Chain: not validator"); 27: require(msg.sender == s.stateTransitionManager, "StateTransition Chain: not state transition manager"); 32: require(msg.sender == s.bridgehub, "StateTransition Chain: not bridgehub"); 37: require( 38: msg.sender == s.admin || msg.sender == s.stateTransitionManager, 39: "StateTransition Chain: Only by admin or state transition manager" 40: ); 45: require( 46: s.validators[msg.sender] || msg.sender == s.stateTransitionManager, 47: "StateTransition Chain: Only by validator or state transition manager" 48: ); 53: require(msg.sender == s.baseTokenBridge, "Only shared bridge can call this function");
[22, 27, 32, 37-40, 45-48, 53]
</details>require
statements that use &&
saves gasUsing operator && on a single require costs more gas than using two require.
//expensive require(version == 1 && index == 2); //cheaper require(version == 1); require(index == 2 == bytes1(0));
There are 3 instances of this issue.
File: code/system-contracts/contracts/ContractDeployer.sol 77: require( 78: _nonceOrdering == AccountNonceOrdering.Arbitrary && 79: currentInfo.nonceOrdering == AccountNonceOrdering.Sequential, 80: "It is only possible to change from sequential to arbitrary ordering" 81: );
[77-81]
File: code/system-contracts/contracts/KnownCodesStorage.sol 76: require(version == 1 && _bytecodeHash[1] == bytes1(0), "Incorrectly formatted bytecodeHash");
[76]
File: code/contracts/ethereum/contracts/common/libraries/L2ContractHelper.sol 41: require(version == 1 && _bytecodeHash[1] == bytes1(0), "zf"); // Incorrectly formatted bytecodeHash
[41]
if
statements that use &&
saves gasUsing the &&
operator on a single if
costs more gas than using two nested if
.
There are 12 instances of this issue.
<details> <summary>Expand findings</summary>File: code/system-contracts/contracts/AccountCodeStorage.sol 102: if (codeHash == 0x00 && NONCE_HOLDER_SYSTEM_CONTRACT.getRawNonce(account) > 0) { 132: uint160(account) > CURRENT_MAX_PRECOMPILE_ADDRESS && 133: codeHash != 0x00 && 134: !Utils.isContractConstructing(codeHash)
File: code/system-contracts/contracts/ContractDeployer.sol 48: _address > address(MAX_SYSTEM_CONTRACT_ADDRESS) && 49: ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT.getRawCodeHash(_address) == 0
[48-49]
File: code/system-contracts/contracts/DefaultAccount.sol 139: if (to == address(DEPLOYER_SYSTEM_CONTRACT) && data.length >= 4) {
[139]
File: code/system-contracts/contracts/NonceHolder.sol 88: if (accountInfo.nonceOrdering == IContractDeployer.AccountNonceOrdering.Sequential && _key != 0) { 169: if (isUsed && !_shouldBeUsed) { 171: } else if (!isUsed && _shouldBeUsed) {
File: code/system-contracts/contracts/SystemContext.sol 151: _block >= currentVirtualBlockUpgradeInfo.virtualBlockFinishL2Block && 152: currentVirtualBlockUpgradeInfo.virtualBlockFinishL2Block > 0 277: if (currentVirtualL2BlockInfo.number == 0 && virtualBlockInfo.timestamp == 0) { 360: if (currentL2BlockNumber == 0 && currentL2BlockTimestamp == 0) {
File: code/contracts/ethereum/contracts/upgrades/BaseZkSyncUpgrade.sol 148: _newVerifierParams.recursionNodeLevelVkHash == bytes32(0) && 149: _newVerifierParams.recursionLeafLevelVkHash == bytes32(0) && 150: _newVerifierParams.recursionCircuitsSetVksHash == bytes32(0)
[148-150]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Executor.sol 361: if (batchWhenUpgradeHappened != 0 && batchWhenUpgradeHappened <= newTotalBatchesExecuted) {
[361]
</details>Counting down saves 6 gas per loop, since checks for zero are more efficient than checks against any other value.
There are 18 instances of this issue.
<details> <summary>Expand findings</summary>File: code/system-contracts/contracts/ContractDeployer.sol 248: for (uint256 i = 0; i < deploymentsLength; ++i) { 253: for (uint256 i = 0; i < deploymentsLength; ++i) {
File: code/system-contracts/contracts/ImmutableSimulator.sol 38: for (uint256 i = 0; i < immutablesLength; ++i) {
[38]
File: code/system-contracts/contracts/KnownCodesStorage.sol 30: for (uint256 i = 0; i < hashesLen; ++i) {
[30]
File: code/system-contracts/contracts/L1Messenger.sol 206: for (uint256 i = 0; i < numberOfL2ToL1Logs; ++i) { 218: for (uint256 i = numberOfL2ToL1Logs; i < L2_TO_L1_LOGS_MERKLE_TREE_LEAVES; ++i) { 224: for (uint256 i = 0; i < nodesOnCurrentLevel; ++i) { 236: for (uint256 i = 0; i < numberOfMessages; ++i) { 254: for (uint256 i = 0; i < numberOfBytecodes; ++i) {
File: code/system-contracts/contracts/PubdataChunkPublisher.sol 36: for (uint256 i = 0; i < MAX_NUMBER_OF_BLOBS; i++) {
[36]
File: code/contracts/ethereum/contracts/governance/Governance.sol 225: for (uint256 i = 0; i < _calls.length; ++i) {
[225]
File: code/contracts/ethereum/contracts/state-transition/ValidatorTimelock.sol 116: for (uint256 i = 0; i < _newBatchesData.length; ++i) { 135: for (uint256 i = 0; i < _newBatchesData.length; ++i) { 185: for (uint256 i = 0; i < _newBatchesData.length; ++i) { 209: for (uint256 i = 0; i < _newBatchesData.length; ++i) {
File: code/contracts/ethereum/contracts/upgrades/BaseZkSyncUpgrade.sol 226: for (uint256 i = 0; i < _factoryDeps.length; ++i) {
[226]
</details>File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Executor.sol 580: for (uint i = 0; i < MAX_NUMBER_OF_BLOBS; i++) { 667: for (uint256 i = 0; i < MAX_NUMBER_OF_BLOBS; i++) {
do-while
is cheaper than for
-loops when the initial check can be skippedExample:
for (uint256 i; i < len; ++i){ ... } -> do { ...; ++i } while (i < len);
There are 35 instances of this issue.
<details> <summary>Expand findings</summary>File: code/system-contracts/contracts/Compressor.sol 61: for (uint256 encodedDataPointer = 0; encodedDataPointer < encodedData.length; encodedDataPointer += 2) { 127: for (uint256 i = 0; i < _numberOfStateDiffs * STATE_DIFF_ENTRY_SIZE; i += STATE_DIFF_ENTRY_SIZE) { 159: for (uint256 i = 0; i < _numberOfStateDiffs * STATE_DIFF_ENTRY_SIZE; i += STATE_DIFF_ENTRY_SIZE) {
File: code/system-contracts/contracts/ContractDeployer.sol 248: for (uint256 i = 0; i < deploymentsLength; ++i) { 253: for (uint256 i = 0; i < deploymentsLength; ++i) {
File: code/system-contracts/contracts/ImmutableSimulator.sol 38: for (uint256 i = 0; i < immutablesLength; ++i) {
[38]
File: code/system-contracts/contracts/KnownCodesStorage.sol 30: for (uint256 i = 0; i < hashesLen; ++i) {
[30]
File: code/system-contracts/contracts/L1Messenger.sol 206: for (uint256 i = 0; i < numberOfL2ToL1Logs; ++i) { 218: for (uint256 i = numberOfL2ToL1Logs; i < L2_TO_L1_LOGS_MERKLE_TREE_LEAVES; ++i) { 224: for (uint256 i = 0; i < nodesOnCurrentLevel; ++i) { 236: for (uint256 i = 0; i < numberOfMessages; ++i) { 254: for (uint256 i = 0; i < numberOfBytecodes; ++i) {
File: code/system-contracts/contracts/PubdataChunkPublisher.sol 36: for (uint256 i = 0; i < MAX_NUMBER_OF_BLOBS; i++) {
[36]
File: code/contracts/ethereum/contracts/governance/Governance.sol 225: for (uint256 i = 0; i < _calls.length; ++i) {
[225]
File: code/contracts/ethereum/contracts/state-transition/ValidatorTimelock.sol 116: for (uint256 i = 0; i < _newBatchesData.length; ++i) { 135: for (uint256 i = 0; i < _newBatchesData.length; ++i) { 185: for (uint256 i = 0; i < _newBatchesData.length; ++i) { 209: for (uint256 i = 0; i < _newBatchesData.length; ++i) {
File: code/contracts/ethereum/contracts/upgrades/BaseZkSyncUpgrade.sol 226: for (uint256 i = 0; i < _factoryDeps.length; ++i) {
[226]
File: code/contracts/ethereum/contracts/state-transition/libraries/Diamond.sol 101: for (uint256 i = 0; i < facetCutsLength; i = i.uncheckedInc()) { 138: for (uint256 i = 0; i < selectorsLength; i = i.uncheckedInc()) { 158: for (uint256 i = 0; i < selectorsLength; i = i.uncheckedInc()) { 178: for (uint256 i = 0; i < selectorsLength; i = i.uncheckedInc()) {
File: code/contracts/ethereum/contracts/state-transition/libraries/Merkle.sol 29: for (uint256 i; i < pathLength; i = i.uncheckedInc()) {
[29]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Executor.sol 142: for (uint256 i = 0; i < emittedL2Logs.length; i = i.uncheckedAdd(L2_TO_L1_LOG_SERIALIZE_SIZE)) { 257: for (uint256 i = 0; i < _newBatchesData.length; i = i.uncheckedInc()) { 289: for (uint256 i = 0; i < _newBatchesData.length; i = i.uncheckedInc()) { 311: for (uint256 i = 0; i < _nPriorityOps; i = i.uncheckedInc()) { 351: for (uint256 i = 0; i < nBatches; i = i.uncheckedInc()) { 405: for (uint256 i = 0; i < committedBatchesLength; i = i.uncheckedInc()) { 580: for (uint i = 0; i < MAX_NUMBER_OF_BLOBS; i++) { 636: for (uint256 i = 0; i < _pubdataCommitments.length; i += PUBDATA_COMMITMENT_SIZE) { 667: for (uint256 i = 0; i < MAX_NUMBER_OF_BLOBS; i++) {
[142, 257, 289, 311, 351, 405, 580, 636, 667]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Getters.sol 208: for (uint256 i = 0; i < facetsLen; i = i.uncheckedInc()) {
[208]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Mailbox.sol 356: for (uint256 i = 0; i < factoryDepsLen; i = i.uncheckedInc()) {
[356]
</details>unchecked
in loopsUse unchecked
to increment the loop variable as it can save gas:
for(uint256 i; i < length;) { unchecked{ ++i; } }
There are 12 instances of this issue.
<details> <summary>Expand findings</summary>File: code/system-contracts/contracts/ContractDeployer.sol 248: for (uint256 i = 0; i < deploymentsLength; ++i) { 253: for (uint256 i = 0; i < deploymentsLength; ++i) {
File: code/system-contracts/contracts/L1Messenger.sol 206: for (uint256 i = 0; i < numberOfL2ToL1Logs; ++i) { 218: for (uint256 i = numberOfL2ToL1Logs; i < L2_TO_L1_LOGS_MERKLE_TREE_LEAVES; ++i) { 224: for (uint256 i = 0; i < nodesOnCurrentLevel; ++i) { 236: for (uint256 i = 0; i < numberOfMessages; ++i) { 254: for (uint256 i = 0; i < numberOfBytecodes; ++i) {
File: code/system-contracts/contracts/PubdataChunkPublisher.sol 36: for (uint256 i = 0; i < MAX_NUMBER_OF_BLOBS; i++) {
[36]
File: code/contracts/ethereum/contracts/governance/Governance.sol 225: for (uint256 i = 0; i < _calls.length; ++i) {
[225]
File: code/contracts/ethereum/contracts/upgrades/BaseZkSyncUpgrade.sol 226: for (uint256 i = 0; i < _factoryDeps.length; ++i) {
[226]
</details>File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Executor.sol 580: for (uint i = 0; i < MAX_NUMBER_OF_BLOBS; i++) { 667: for (uint256 i = 0; i < MAX_NUMBER_OF_BLOBS; i++) {
Consider pre-compute hashes (e.g. keccak256("transfer(address,uint256)")
-> 0xa9059cbb
) instead of calculating them inside the contract.
There are 2 instances of this issue.
File: code/system-contracts/contracts/libraries/TransactionHelper.sol // @audit keccak256("zkSync") 139: abi.encode(EIP712_DOMAIN_TYPEHASH, keccak256("zkSync"), keccak256("2"), block.chainid) // @audit keccak256("2") 139: abi.encode(EIP712_DOMAIN_TYPEHASH, keccak256("zkSync"), keccak256("2"), block.chainid)
uint
comparison with zero can be cheaperChecking for != 0
is cheaper than > 0
for unsigned integers.
There are 24 instances of this issue.
<details> <summary>Expand findings</summary>File: code/system-contracts/contracts/AccountCodeStorage.sol 102: if (codeHash == 0x00 && NONCE_HOLDER_SYSTEM_CONTRACT.getRawNonce(account) > 0) {
[102]
File: code/system-contracts/contracts/ComplexUpgrader.sol 24: require(_delegateTo.code.length > 0, "Delegatee is an EOA");
[24]
File: code/system-contracts/contracts/ContractDeployer.sol 303: require(knownCodeMarker > 0, "The code hash is not known"); 332: if (value > 0) { 339: if (value > 0) {
File: code/system-contracts/contracts/NonceHolder.sol 156: return (_nonce < getMinNonce(_address) || nonceValues[addressAsKey][_nonce] > 0);
[156]
File: code/system-contracts/contracts/SystemContext.sol 152: currentVirtualBlockUpgradeInfo.virtualBlockFinishL2Block > 0 245: require(_l2BlockNumber > 0, "L2 block number is never expected to be zero"); 287: require(_maxVirtualBlocksToCreate > 0, "Can't initialize the first virtual block"); 355: require(_maxVirtualBlocksToCreate > 0, "There must be a virtual block created at the start of the batch"); 413: require(currentBatchNumber > 0, "The current batch number must be greater than 0");
File: code/contracts/ethereum/contracts/bridge/L1SharedBridge.sol 129: require(amount > 0, "ShB: 0 amount to transfer"); 327: require(_amount > 0, "y1");
File: code/contracts/ethereum/contracts/bridgehub/Bridgehub.sol 329: } else if (_refundRecipient.code.length > 0) {
[329]
File: code/contracts/zksync/contracts/bridge/L2SharedBridge.sol 124: require(_amount > 0, "Amount cannot be zero");
[124]
File: code/contracts/ethereum/contracts/state-transition/libraries/Diamond.sol 107: require(selectors.length > 0, "B"); // no functions for diamond cut 132: require(_facet.code.length > 0, "G"); 155: require(_facet.code.length > 0, "K");
File: code/contracts/ethereum/contracts/state-transition/libraries/Merkle.sol 24: require(pathLength > 0, "xc");
[24]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Executor.sol 429: if (_proof.serializedProof.length > 0) { 593: return (_bitMap & (1 << _index)) > 0; 631: require(_pubdataCommitments.length > 0, "pl");
</details>File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Mailbox.sol 158: require(s.baseTokenGasPriceMultiplierDenominator > 0, "Mailbox: baseTokenGasPriceDenominator not set"); 277: if (refundRecipient.code.length > 0) {
address(0)
A simple zero address check can be written in assembly to save some gas.
There are 30 instances of this issue.
<details> <summary>Expand findings</summary>File: code/system-contracts/contracts/DefaultAccount.sol 188: return recoveredAddress == address(this) && recoveredAddress != address(0); 188: return recoveredAddress == address(this) && recoveredAddress != address(0);
File: code/system-contracts/contracts/libraries/TransactionHelper.sol 406: if (address(uint160(_transaction.paymaster)) != address(0)) {
[406]
File: code/contracts/ethereum/contracts/bridge/L1SharedBridge.sol 108: require(_owner != address(0), "ShB owner 0"); 188: require(l2BridgeAddress[_chainId] != address(0), "ShB l2 bridge not deployed"); 563: require(l2BridgeAddress[ERA_CHAIN_ID] != address(0), "ShB b. n dep"); 578: if (_refundRecipient == address(0)) {
File: code/contracts/ethereum/contracts/bridgehub/Bridgehub.sol 130: require(address(sharedBridge) != address(0), "Bridgehub: weth bridge not set"); 132: require(stateTransitionManager[_chainId] == address(0), "Bridgehub: chainId already registered"); 326: if (_refundRecipient == address(0)) {
File: code/contracts/ethereum/contracts/governance/Governance.sol 42: require(_admin != address(0), "Admin should be non zero address");
[42]
File: code/contracts/ethereum/contracts/state-transition/StateTransitionManager.sol 82: require(_initializeData.governor != address(0), "StateTransition: governor zero"); 246: if (stateTransition[_chainId] != address(0)) {
File: code/contracts/zksync/contracts/bridge/L2SharedBridge.sol 55: require(_l1Bridge != address(0), "bf"); 57: require(_aliasedOwner != address(0), "sf"); 68: require(_l1LegecyBridge != address(0), "bf2"); 96: if (currentL1Token == address(0)) { 129: require(l1Token != address(0), "yh");
File: code/contracts/zksync/contracts/bridge/L2StandardERC20.sol 51: require(_l1Address != address(0), "in6"); // Should be non-zero address
[51]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/DiamondInit.sol 25: require(address(_initializeData.verifier) != address(0), "vt"); 26: require(_initializeData.admin != address(0), "vy"); 27: require(_initializeData.validatorTimelock != address(0), "hc");
File: code/contracts/ethereum/contracts/state-transition/chain-deps/DiamondProxy.sol 30: require(facetAddress != address(0), "F"); // Proxy has no facet for this selector
[30]
File: code/contracts/ethereum/contracts/state-transition/libraries/Diamond.sol 141: require(oldFacet.facetAddress == address(0), "J"); // facet for this selector already exists 161: require(oldFacet.facetAddress != address(0), "L"); // it is impossible to replace the facet with zero address 175: require(_facet == address(0), "a1"); // facet address must be zero 181: require(oldFacet.facetAddress != address(0), "a2"); // Can't delete a non-existent facet 279: if (_init == address(0)) {
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Getters.sol 183: require(ds.selectorToFacet[_selector].facetAddress != address(0), "g2");
[183]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Mailbox.sol 275: address refundRecipient = _params.refundRecipient == address(0) ? _params.sender : _params.refundRecipient;
[275]
</details>If an external call's calldata can fit into two or fewer words, use the scratch space to build the calldata, rather than allowing Solidity to do a memory expansion.
There are 275 instances of this issue.
<details> <summary>Expand findings</summary>File: code/system-contracts/contracts/AccountCodeStorage.sol 37: require(Utils.isContractConstructing(_hash), "Code hash is not for a contract on constructor"); 48: require(Utils.isContractConstructed(_hash), "Code hash is not for a constructed contract"); 57: require(Utils.isContractConstructing(codeHash), "Code hash is not for a contract on constructor"); 60: bytes32 constructedBytecodeHash = Utils.constructedBytecodeHash(codeHash); 102: if (codeHash == 0x00 && NONCE_HOLDER_SYSTEM_CONTRACT.getRawNonce(account) > 0) { 107: else if (Utils.isContractConstructing(codeHash)) { 134: !Utils.isContractConstructing(codeHash) 136: codeSize = Utils.bytecodeLenInBytes(codeHash);
[37, 48, 57, 60, 102, 107, 134, 136]
File: code/system-contracts/contracts/BootloaderUtilities.sol 27: signedTxHash = _transaction.encodeHash(); 29: txHash = keccak256(bytes.concat(signedTxHash, EfficientCall.keccak(_transaction.signature))); 29: txHash = keccak256(bytes.concat(signedTxHash, EfficientCall.keccak(_transaction.signature))); 52: bytes memory encodedNonce = RLPEncoder.encodeUint256(_transaction.nonce); 56: bytes memory encodedGasPrice = RLPEncoder.encodeUint256(_transaction.maxFeePerGas); 57: bytes memory encodedGasLimit = RLPEncoder.encodeUint256(_transaction.gasLimit); 58: encodedGasParam = bytes.concat(encodedGasPrice, encodedGasLimit); 61: bytes memory encodedTo = RLPEncoder.encodeAddress(address(uint160(_transaction.to))); 62: bytes memory encodedValue = RLPEncoder.encodeUint256(_transaction.value); 71: encodedDataLength = RLPEncoder.encodeNonSingleBytesLen(txDataLen); 82: rEncoded = RLPEncoder.encodeUint256(rInt); 87: sEncoded = RLPEncoder.encodeUint256(sInt); 100: vEncoded = RLPEncoder.encodeUint256(vInt); 116: encodedListLength = RLPEncoder.encodeListLen(uint64(listLength)); 143: bytes memory encodedChainId = RLPEncoder.encodeUint256(block.chainid); 144: bytes memory encodedNonce = RLPEncoder.encodeUint256(_transaction.nonce); 145: bytes memory encodedGasPrice = RLPEncoder.encodeUint256(_transaction.maxFeePerGas); 146: bytes memory encodedGasLimit = RLPEncoder.encodeUint256(_transaction.gasLimit); 147: bytes memory encodedTo = RLPEncoder.encodeAddress(address(uint160(_transaction.to))); 148: bytes memory encodedValue = RLPEncoder.encodeUint256(_transaction.value); 167: encodedDataLength = RLPEncoder.encodeNonSingleBytesLen(txDataLen); 176: bytes memory encodedAccessListLength = RLPEncoder.encodeListLen(0); 181: rEncoded = RLPEncoder.encodeUint256(rInt); 186: sEncoded = RLPEncoder.encodeUint256(sInt); 193: vEncoded = RLPEncoder.encodeUint256(vInt - 27); 207: encodedListLength = RLPEncoder.encodeListLen(uint64(listLength)); 236: bytes memory encodedChainId = RLPEncoder.encodeUint256(block.chainid); 237: bytes memory encodedNonce = RLPEncoder.encodeUint256(_transaction.nonce); 238: bytes memory encodedMaxPriorityFeePerGas = RLPEncoder.encodeUint256(_transaction.maxPriorityFeePerGas); 239: bytes memory encodedMaxFeePerGas = RLPEncoder.encodeUint256(_transaction.maxFeePerGas); 240: bytes memory encodedGasLimit = RLPEncoder.encodeUint256(_transaction.gasLimit); 241: bytes memory encodedTo = RLPEncoder.encodeAddress(address(uint160(_transaction.to))); 242: bytes memory encodedValue = RLPEncoder.encodeUint256(_transaction.value); 262: encodedDataLength = RLPEncoder.encodeNonSingleBytesLen(txDataLen); 271: bytes memory encodedAccessListLength = RLPEncoder.encodeListLen(0); 276: rEncoded = RLPEncoder.encodeUint256(rInt); 281: sEncoded = RLPEncoder.encodeUint256(sInt); 288: vEncoded = RLPEncoder.encodeUint256(vInt - 27); 302: encodedListLength = RLPEncoder.encodeListLen(uint64(listLength));
[27, 29, 29, 52, 56, 57, 58, 61, 62, 71, 82, 87, 100, 116, 143, 144, 145, 146, 147, 148, 167, 176, 181, 186, 193, 207, 236, 237, 238, 239, 240, 241, 242, 262, 271, 276, 281, 288, 302]
File: code/system-contracts/contracts/ComplexUpgrader.sol 25: (bool success, bytes memory returnData) = _delegateTo.delegatecall(_calldata);
[25]
File: code/system-contracts/contracts/Compressor.sol 62: uint256 indexOfEncodedChunk = uint256(encodedData.readUint16(encodedDataPointer)) * 8; 65: uint64 encodedChunk = dictionary.readUint64(indexOfEncodedChunk); 66: uint64 realChunk = _bytecode.readUint64(encodedDataPointer * 4); 72: bytecodeHash = Utils.hashL2Bytecode(_bytecode); 73: L1_MESSENGER_CONTRACT.sendToL1(_rawCompressedData); 74: KNOWN_CODE_STORAGE_CONTRACT.markBytecodeAsPublished(bytecodeHash); 121: uint256 numberOfInitialWrites = uint256(_compressedStateDiffs.readUint16(0)); 129: uint64 enumIndex = stateDiff.readUint64(84); 137: bytes32 derivedKey = stateDiff.readBytes32(52); 138: uint256 initValue = stateDiff.readUint256(92); 139: uint256 finalValue = stateDiff.readUint256(124); 140: require(derivedKey == _compressedStateDiffs.readBytes32(stateDiffPtr), "iw: initial key mismatch"); 161: uint64 enumIndex = stateDiff.readUint64(84); 166: uint256 initValue = stateDiff.readUint256(92); 167: uint256 finalValue = stateDiff.readUint256(124); 189: stateDiffHash = EfficientCall.keccak(_stateDiffs); 202: uint256 dictionaryLen = uint256(_rawCompressedData.readUint16(0));
[62, 65, 66, 72, 73, 74, 121, 129, 137, 138, 139, 140, 161, 166, 167, 189, 202]
File: code/system-contracts/contracts/ContractDeployer.sol 49: ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT.getRawCodeHash(_address) == 0 103: bytes32 constructorInputHash = EfficientCall.keccak(_input); 168: NONCE_HOLDER_SYSTEM_CONTRACT.incrementDeploymentNonce(msg.sender); 188: uint256 senderNonce = NONCE_HOLDER_SYSTEM_CONTRACT.incrementDeploymentNonce(msg.sender); 269: ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT.getCodeHash(uint256(uint160(_newAddress))) == 0x0, 273: require(NONCE_HOLDER_SYSTEM_CONTRACT.getRawNonce(_newAddress) == 0x00, "Account is occupied"); 302: uint256 knownCodeMarker = KNOWN_CODE_STORAGE_CONTRACT.getMarker(_bytecodeHash); 311: bytes32 constructingBytecodeHash = Utils.constructingBytecodeHash(_bytecodeHash); 312: ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT.storeAccountConstructingCodeHash(_newAddress, constructingBytecodeHash); 341: SystemContractHelper.setValueForNextFarCall(uint128(value)); 345: ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT.markAccountCodeHashAsConstructed(_newAddress); 348: IMMUTABLE_SIMULATOR_SYSTEM_CONTRACT.setImmutables(_newAddress, immutables); 352: ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT.storeAccountConstructedCodeHash(_newAddress, _bytecodeHash);
[49, 103, 168, 188, 269, 273, 302, 311, 312, 341, 345, 348, 352]
File: code/system-contracts/contracts/DefaultAccount.sol 94: bytes32 txHash = _suggestedSignedHash != bytes32(0) ? _suggestedSignedHash : _transaction.encodeHash(); 99: uint256 totalRequiredBalance = _transaction.totalRequiredBalance(); 133: uint128 value = Utils.safeCastToU128(_transaction.value); 135: uint32 gas = Utils.safeCastToU32(gasleft()); 151: EfficientCall.propagateRevert(); 201: bool success = _transaction.payToTheBootloader(); 217: _transaction.processPaymasterInput(); 46: address codeAddress = SystemContractHelper.getCodeAddress();
[94, 99, 133, 135, 151, 201, 217, 46]
File: code/system-contracts/contracts/GasBoundCaller.sol 51: uint256 pubdataPublishedBefore = REAL_SYSTEM_CONTEXT_CONTRACT.getCurrentPubdataSpent(); 59: uint256 pubdataPublishedAfter = REAL_SYSTEM_CONTEXT_CONTRACT.getCurrentPubdataSpent(); 67: uint256 pubdataPrice = REAL_SYSTEM_CONTEXT_CONTRACT.gasPerPubdataByte();
File: code/system-contracts/contracts/KnownCodesStorage.sol 51: L1_MESSENGER_CONTRACT.requestBytecodeL1Publication(_bytecodeHash); 78: require(Utils.bytecodeLenInWords(_bytecodeHash) % 2 == 1, "Code length in words must be odd");
File: code/system-contracts/contracts/L1Messenger.sol 78: txNumberInBlock: SYSTEM_CONTEXT_CONTRACT.txNumberInBlock(), 90: SystemContractHelper.burnGas(Utils.safeCastToU32(gasToPay), 0); 90: SystemContractHelper.burnGas(Utils.safeCastToU32(gasToPay), 0); 118: hash = EfficientCall.keccak(_message); 128: txNumberInBlock: SYSTEM_CONTEXT_CONTRACT.txNumberInBlock(), 154: SystemContractHelper.burnGas(Utils.safeCastToU32(gasToPay), uint32(pubdataLen)); 154: SystemContractHelper.burnGas(Utils.safeCastToU32(gasToPay), uint32(pubdataLen)); 166: uint256 bytecodeLen = Utils.bytecodeLenInBytes(_bytecodeHash); 179: SystemContractHelper.burnGas(Utils.safeCastToU32(gasToPay), uint32(pubdataLen)); 179: SystemContractHelper.burnGas(Utils.safeCastToU32(gasToPay), uint32(pubdataLen)); 207: bytes32 hashedLog = EfficientCall.keccak( 208: _totalL2ToL1PubdataAndStateDiffs[calldataPtr:calldataPtr + L2_TO_L1_LOG_SERIALIZE_SIZE] 209: ); 239: bytes32 hashedMessage = EfficientCall.keccak( 240: _totalL2ToL1PubdataAndStateDiffs[calldataPtr:calldataPtr + currentMessageLength] 241: ); 262: Utils.hashL2Bytecode( 263: _totalL2ToL1PubdataAndStateDiffs[calldataPtr:calldataPtr + currentBytecodeLength] 264: ) 317: PUBDATA_CHUNK_PUBLISHER.chunkAndPublishPubdata(totalL2ToL1Pubdata); 324: EfficientCall.keccak(totalL2ToL1Pubdata)
[78, 90, 90, 118, 128, 154, 154, 166, 179, 179, 207-209, 239-241, 262-264, 317, 324]
File: code/system-contracts/contracts/L2BaseToken.sol 77: L1_MESSENGER_CONTRACT.sendToL1(message); 90: L1_MESSENGER_CONTRACT.sendToL1(message);
File: code/system-contracts/contracts/MsgValueSimulator.sol 26: value = SystemContractHelper.getExtraAbiData(0); 27: uint256 addressAsUint = SystemContractHelper.getExtraAbiData(1); 28: uint256 mask = SystemContractHelper.getExtraAbiData(2); 63: (bool success, ) = address(REAL_BASE_TOKEN_SYSTEM_CONTRACT).call( 64: abi.encodeCall(REAL_BASE_TOKEN_SYSTEM_CONTRACT.transferFromTo, (msg.sender, to, value)) 65: ); 79: SystemContractHelper.setValueForNextFarCall(Utils.safeCastToU128(value)); 79: SystemContractHelper.setValueForNextFarCall(Utils.safeCastToU128(value));
File: code/system-contracts/contracts/NonceHolder.sol 83: IContractDeployer.AccountInfo memory accountInfo = DEPLOYER_SYSTEM_CONTRACT.getAccountInfo(msg.sender);
[83]
File: code/system-contracts/contracts/SystemContext.sol 118: uint256 pubdataPublished = SystemContractHelper.getZkSyncMeta().pubdataPublished;
[118]
File: code/contracts/zksync/contracts/L2ContractHelper.sol 91: return L2_MESSENGER.sendToL1(_message);
[91]
File: code/contracts/zksync/contracts/SystemContractsCaller.sol 44: uint32 dataLength = uint32(Utils.safeCastToU32(data.length));
[44]
File: code/system-contracts/contracts/interfaces/ISystemContract.sol 22: SystemContractHelper.isSystemCall() || SystemContractHelper.isSystemContract(msg.sender), 22: SystemContractHelper.isSystemCall() || SystemContractHelper.isSystemContract(msg.sender), 32: SystemContractHelper.isSystemContract(msg.sender),
File: code/system-contracts/contracts/libraries/EfficientCall.sol 251: SystemContractHelper.loadCalldataIntoActivePtr(); 259: SystemContractHelper.ptrAddIntoActive(uint32(dataOffset)); 262: SystemContractHelper.ptrShrinkIntoActive(shrinkTo); 264: uint32 gas = Utils.safeCastToU32(_gas); 273: SystemContractHelper.ptrPackIntoActivePtr(farCallAbi);
File: code/system-contracts/contracts/libraries/SystemContractsCaller.sol 83: uint32 dataLength = uint32(Utils.safeCastToU32(data.length));
[83]
File: code/system-contracts/contracts/libraries/TransactionHelper.sol 132: EfficientCall.keccak(_transaction.data), 134: EfficientCall.keccak(_transaction.paymasterInput) 155: bytes memory encodedNonce = RLPEncoder.encodeUint256(_transaction.nonce); 159: bytes memory encodedGasPrice = RLPEncoder.encodeUint256(_transaction.maxFeePerGas); 160: bytes memory encodedGasLimit = RLPEncoder.encodeUint256(_transaction.gasLimit); 161: encodedGasParam = bytes.concat(encodedGasPrice, encodedGasLimit); 164: bytes memory encodedTo = RLPEncoder.encodeAddress(address(uint160(_transaction.to))); 165: bytes memory encodedValue = RLPEncoder.encodeUint256(_transaction.value); 174: encodedDataLength = RLPEncoder.encodeNonSingleBytesLen(txDataLen); 185: encodedChainId = bytes.concat(RLPEncoder.encodeUint256(block.chainid), hex"80_80"); 185: encodedChainId = bytes.concat(RLPEncoder.encodeUint256(block.chainid), hex"80_80"); 199: encodedListLength = RLPEncoder.encodeListLen(uint64(listLength)); 228: bytes memory encodedChainId = RLPEncoder.encodeUint256(block.chainid); 229: bytes memory encodedNonce = RLPEncoder.encodeUint256(_transaction.nonce); 230: bytes memory encodedGasPrice = RLPEncoder.encodeUint256(_transaction.maxFeePerGas); 231: bytes memory encodedGasLimit = RLPEncoder.encodeUint256(_transaction.gasLimit); 232: bytes memory encodedTo = RLPEncoder.encodeAddress(address(uint160(_transaction.to))); 233: bytes memory encodedValue = RLPEncoder.encodeUint256(_transaction.value); 252: encodedDataLength = RLPEncoder.encodeNonSingleBytesLen(txDataLen); 261: bytes memory encodedAccessListLength = RLPEncoder.encodeListLen(0); 271: encodedListLength = RLPEncoder.encodeListLen(uint64(listLength)); 298: bytes memory encodedChainId = RLPEncoder.encodeUint256(block.chainid); 299: bytes memory encodedNonce = RLPEncoder.encodeUint256(_transaction.nonce); 300: bytes memory encodedMaxPriorityFeePerGas = RLPEncoder.encodeUint256(_transaction.maxPriorityFeePerGas); 301: bytes memory encodedMaxFeePerGas = RLPEncoder.encodeUint256(_transaction.maxFeePerGas); 302: bytes memory encodedGasLimit = RLPEncoder.encodeUint256(_transaction.gasLimit); 303: bytes memory encodedTo = RLPEncoder.encodeAddress(address(uint160(_transaction.to))); 304: bytes memory encodedValue = RLPEncoder.encodeUint256(_transaction.value); 324: encodedDataLength = RLPEncoder.encodeNonSingleBytesLen(txDataLen); 333: bytes memory encodedAccessListLength = RLPEncoder.encodeListLen(0); 343: encodedListLength = RLPEncoder.encodeListLen(uint64(listLength)); 377: uint256 currentAllowance = IERC20(token).allowance(address(this), paymaster); 382: IERC20(token).safeApprove(paymaster, 0); 383: IERC20(token).safeApprove(paymaster, minAllowance);
[132, 134, 155, 159, 160, 161, 164, 165, 174, 185, 185, 199, 228, 229, 230, 231, 232, 233, 252, 261, 271, 298, 299, 300, 301, 302, 303, 304, 324, 333, 343, 377, 382, 383]
File: code/system-contracts/contracts/libraries/Utils.sol 90: EfficientCall.sha(_bytecode) &
[90]
File: code/contracts/ethereum/contracts/bridge/L1ERC20Bridge.sol 65: uint256 amount = IERC20(_token).balanceOf(address(this)); 67: IERC20(_token).safeTransfer(address(sharedBridge), amount); 161: uint256 balanceBefore = _token.balanceOf(address(sharedBridge)); 163: uint256 balanceAfter = _token.balanceOf(address(sharedBridge));
File: code/contracts/ethereum/contracts/bridge/L1SharedBridge.sol 119: IMailbox(_target).transferEthToSharedBridge(); 127: uint256 balanceBefore = IERC20(_token).balanceOf(address(this)); 128: uint256 amount = IERC20(_token).balanceOf(address(legacyBridge)); 130: IL1ERC20Bridge(_target).tranferTokenToSharedBridge(_token, amount); 131: uint256 balanceAfter = IERC20(_token).balanceOf(address(this)); 138: require(bridgehub.getStateTransition(_chainId) == msg.sender, "receiveEth not state transition"); 174: uint256 balanceBefore = _token.balanceOf(address(this)); 176: uint256 balanceAfter = _token.balanceOf(address(this)); 195: require(bridgehub.baseToken(_chainId) != _l1Token, "ShB: baseToken deposit not supported"); 262: (, bytes memory data1) = _token.staticcall(abi.encodeCall(IERC20Metadata.name, ())); 263: (, bytes memory data2) = _token.staticcall(abi.encodeCall(IERC20Metadata.symbol, ())); 264: (, bytes memory data3) = _token.staticcall(abi.encodeCall(IERC20Metadata.decimals, ())); 362: IERC20(_l1Token).safeTransfer(_depositSender, _amount); 396: require(!legacyBridge.isWithdrawalFinalized(_l2BatchNumber, _l2MessageIndex), "ShB: legacy withdrawal"); 423: bool alreadyFinalized = IGetters(ERA_DIAMOND_PROXY).isEthWithdrawalFinalized( 424: _l2BatchNumber, 425: _l2MessageIndex 426: ); 452: IERC20(l1Token).safeTransfer(l1Receiver, amount); 467: bool baseTokenWithdrawal = (l1Token == bridgehub.baseToken(_chainId)); 503: (uint32 functionSignature, uint256 offset) = UnsafeBytes.readUint32(_l2ToL1message, 0); 506: (l1Receiver, offset) = UnsafeBytes.readAddress(_l2ToL1message, offset); 507: (amount, offset) = UnsafeBytes.readUint256(_l2ToL1message, offset); 508: l1Token = bridgehub.baseToken(_chainId); 518: (l1Receiver, offset) = UnsafeBytes.readAddress(_l2ToL1message, offset); 519: (l1Token, offset) = UnsafeBytes.readAddress(_l2ToL1message, offset); 520: (amount, offset) = UnsafeBytes.readUint256(_l2ToL1message, offset); 580: ? AddressAliasHelper.applyL1ToL2Alias(_prevMsgSender)
[119, 127, 128, 130, 131, 138, 174, 176, 195, 262, 263, 264, 362, 396, 423-426, 452, 467, 503, 506, 507, 508, 518, 519, 520, 580]
File: code/contracts/ethereum/contracts/bridgehub/Bridgehub.sol 76: return IStateTransitionManager(stateTransitionManager[_chainId]).stateTransition(_chainId); 238: canonicalTxHash = IZkSyncStateTransition(stateTransition).bridgehubRequestL2Transaction( 239: BridgehubL2TransactionRequest({ 240: sender: msg.sender, 241: contractL2: _request.l2Contract, 242: mintValue: _request.mintValue, 243: l2Value: _request.l2Value, 244: l2Calldata: _request.l2Calldata, 245: l2GasLimit: _request.l2GasLimit, 246: l2GasPerPubdataByteLimit: _request.l2GasPerPubdataByteLimit, 247: factoryDeps: _request.factoryDeps, 248: refundRecipient: refundRecipient 249: }) 250: ); 304: canonicalTxHash = IZkSyncStateTransition(stateTransition).bridgehubRequestL2Transaction( 305: BridgehubL2TransactionRequest({ 306: sender: _request.secondBridgeAddress, 307: contractL2: outputRequest.l2Contract, 308: mintValue: _request.mintValue, 309: l2Value: _request.l2Value, 310: l2Calldata: outputRequest.l2Calldata, 311: l2GasLimit: _request.l2GasLimit, 312: l2GasPerPubdataByteLimit: _request.l2GasPerPubdataByteLimit, 313: factoryDeps: outputRequest.factoryDeps, 314: refundRecipient: refundRecipient 315: }) 316: ); 328: _recipient = msg.sender == tx.origin ? msg.sender : AddressAliasHelper.applyL1ToL2Alias(msg.sender); 331: _recipient = AddressAliasHelper.applyL1ToL2Alias(_refundRecipient);
[76, 238-250, 304-316, 328, 331]
File: code/contracts/ethereum/contracts/state-transition/StateTransitionManager.sol 75: return IZkSyncStateTransition(stateTransition[_chainId]).getAdmin(); 161: IZkSyncStateTransition(stateTransition[_chainId]).freezeDiamond(); 166: IZkSyncStateTransition(stateTransition[_chainId]).freezeDiamond(); 171: IZkSyncStateTransition(stateTransition[_chainId]).revertBatches(_newLastBatch); 226: IAdmin(_chainContract).executeUpgrade(cutData);
File: code/contracts/ethereum/contracts/state-transition/ValidatorTimelock.sol 103: return committedBatchTimestamp[_chainId].get(_l2BatchNumber); 117: committedBatchTimestamp[ERA_CHAIN_ID].set(_newBatchesData[i].batchNumber, timestamp); 136: committedBatchTimestamp[_chainId].set(_newBatchesData[i].batchNumber, timestamp); 186: uint256 commitBatchTimestamp = committedBatchTimestamp[ERA_CHAIN_ID].get( 187: _newBatchesData[i].batchNumber 188: ); 210: uint256 commitBatchTimestamp = committedBatchTimestamp[_chainId].get(_newBatchesData[i].batchNumber); 226: address contractAddress = stateTransitionManager.stateTransition(_chainId); 62: require(msg.sender == stateTransitionManager.getChainAdmin(_chainId), "ValidatorTimelock: only chain admin");
[103, 117, 136, 186-188, 210, 226, 62]
File: code/contracts/ethereum/contracts/upgrades/BaseZkSyncUpgrade.sol 97: L2ContractHelper.validateBytecodeHash(_l2DefaultAccountBytecodeHash); 114: L2ContractHelper.validateBytecodeHash(_l2BootloaderBytecodeHash); 201: TransactionValidator.validateUpgradeTransaction(_l2ProtocolUpgradeTx); 228: L2ContractHelper.hashL2Bytecode(_factoryDeps[i]) == bytes32(_expectedHashes[i]),
File: code/contracts/zksync/contracts/bridge/L2SharedBridge.sol 66: l2TokenBeacon.transferOwnership(_aliasedOwner); 88: AddressAliasHelper.undoL1ToL2Alias(msg.sender) == l1Bridge || 89: AddressAliasHelper.undoL1ToL2Alias(msg.sender) == l1LegacyBridge, 104: IL2StandardToken(expectedL2Token).bridgeMint(_l2Receiver, _amount); 113: L2StandardERC20(address(l2Token)).bridgeInitialize(_l1Token, _data); 126: IL2StandardToken(_l2Token).bridgeBurn(msg.sender, _amount); 132: L2ContractHelper.sendMessageToL1(message);
[66, 88, 89, 104, 113, 126, 132]
File: code/contracts/zksync/contracts/bridge/L2StandardERC20.sol 120: require(msg.sender == UpgradeableBeacon(beaconAddress).owner(), "tt");
[120]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/DiamondProxy.sol 15: Diamond.diamondCut(_diamondCut); 21: Diamond.DiamondStorage storage diamondStorage = Diamond.getDiamondStorage();
File: code/contracts/ethereum/contracts/state-transition/libraries/Diamond.sol 101: for (uint256 i = 0; i < facetCutsLength; i = i.uncheckedInc()) { 138: for (uint256 i = 0; i < selectorsLength; i = i.uncheckedInc()) { 158: for (uint256 i = 0; i < selectorsLength; i = i.uncheckedInc()) { 178: for (uint256 i = 0; i < selectorsLength; i = i.uncheckedInc()) { 195: ds.facetToSelectors[_facet].facetPosition = ds.facets.length.toUint16(); 208: uint16 selectorPosition = (ds.facetToSelectors[_facet].selectors.length).toUint16(); 240: ds.selectorToFacet[lastSelector].selectorPosition = selectorPosition.toUint16(); 269: ds.facetToSelectors[lastFacet].facetPosition = facetPosition.toUint16(); 283: (bool success, bytes memory data) = _init.delegatecall(_calldata);
[101, 138, 158, 178, 195, 208, 240, 269, 283]
File: code/contracts/ethereum/contracts/state-transition/libraries/Merkle.sol 29: for (uint256 i; i < pathLength; i = i.uncheckedInc()) {
[29]
File: code/contracts/ethereum/contracts/state-transition/libraries/PriorityQueue.sol 65: require(!_queue.isEmpty(), "D"); // priority queue is empty 73: require(!_queue.isEmpty(), "s"); // priority queue is empty
File: code/contracts/ethereum/contracts/state-transition/libraries/TransactionValidator.sol 83: costForComputation += Math.ceilDiv(_encodingLength * L1_TX_DELTA_544_ENCODING_BYTES, 544); 89: costForComputation = Math.max(costForComputation, L1_TX_MIN_L2_GAS_BASE); 136: batchOverheadForTransaction = Math.max(batchOverheadForTransaction, overheadForLength);
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Admin.sol 106: cutHashInput == IStateTransitionManager(s.stateTransitionManager).upgradeCutHash(_oldProtocolVersion), 114: Diamond.diamondCut(_diamondCut); 124: Diamond.diamondCut(_diamondCut); 134: Diamond.DiamondStorage storage diamondStorage = Diamond.getDiamondStorage(); 144: Diamond.DiamondStorage storage diamondStorage = Diamond.getDiamondStorage();
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Executor.sol 142: for (uint256 i = 0; i < emittedL2Logs.length; i = i.uncheckedAdd(L2_TO_L1_LOG_SERIALIZE_SIZE)) { 144: (address logSender, ) = UnsafeBytes.readAddress(emittedL2Logs, i + L2_LOG_ADDRESS_OFFSET); 145: (uint256 logKey, ) = UnsafeBytes.readUint256(emittedL2Logs, i + L2_LOG_KEY_OFFSET); 146: (bytes32 logValue, ) = UnsafeBytes.readBytes32(emittedL2Logs, i + L2_LOG_VALUE_OFFSET); 227: IStateTransitionManager(s.stateTransitionManager).protocolVersion() == s.protocolVersion, 257: for (uint256 i = 0; i < _newBatchesData.length; i = i.uncheckedInc()) { 289: for (uint256 i = 0; i < _newBatchesData.length; i = i.uncheckedInc()) { 311: for (uint256 i = 0; i < _nPriorityOps; i = i.uncheckedInc()) { 312: PriorityOperation memory priorityOp = s.priorityQueue.popFront(); 351: for (uint256 i = 0; i < nBatches; i = i.uncheckedInc()) { 405: for (uint256 i = 0; i < committedBatchesLength; i = i.uncheckedInc()) { 406: currentTotalBatchesVerified = currentTotalBatchesVerified.uncheckedInc(); 612: (bool success, bytes memory data) = POINT_EVALUATION_PRECOMPILE_ADDR.staticcall(precompileInput); 680: (bool success, bytes memory data) = s.blobVersionedHashRetriever.staticcall(abi.encode(_index));
[142, 144, 145, 146, 227, 257, 289, 311, 312, 351, 405, 406, 612, 680]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Getters.sol 93: return s.priorityQueue.getTotalPriorityTxs(); 98: return s.priorityQueue.getFirstUnprocessedPriorityTx(); 103: return s.priorityQueue.getSize(); 108: return s.priorityQueue.front(); 158: Diamond.DiamondStorage storage ds = Diamond.getDiamondStorage(); 164: Diamond.DiamondStorage storage ds = Diamond.getDiamondStorage(); 182: Diamond.DiamondStorage storage ds = Diamond.getDiamondStorage(); 203: Diamond.DiamondStorage storage ds = Diamond.getDiamondStorage(); 208: for (uint256 i = 0; i < facetsLen; i = i.uncheckedInc()) { 218: Diamond.DiamondStorage storage ds = Diamond.getDiamondStorage(); 224: Diamond.DiamondStorage storage ds = Diamond.getDiamondStorage(); 230: Diamond.DiamondStorage storage ds = Diamond.getDiamondStorage();
[93, 98, 103, 108, 158, 164, 182, 203, 208, 218, 224, 230]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Mailbox.sol 174: return Math.max(l2GasPrice, minL2GasPriceBaseToken); 235: l2Sender = AddressAliasHelper.applyL1ToL2Alias(_request.sender); 265: _params.txId = s.priorityQueue.getTotalPriorityTxs(); 278: refundRecipient = AddressAliasHelper.applyL1ToL2Alias(refundRecipient); 334: s.priorityQueue.pushBack( 335: PriorityOperation({ 336: canonicalTxHash: canonicalTxHash, 337: expirationTimestamp: _priorityOpParams.expirationTimestamp, 338: layer2Tip: uint192(0) // TODO: Restore after fee modeling will be stable. (SMA-1230) 339: }) 340: ); 356: for (uint256 i = 0; i < factoryDepsLen; i = i.uncheckedInc()) { 357: bytes32 hashedBytecode = L2ContractHelper.hashL2Bytecode(_factoryDeps[i]);
[174, 235, 265, 278, 334-340, 356, 357]
</details>Considering using assembly to write hashes, as it's possible to save a considerable amount of gas.
There are 56 instances of this issue.
<details> <summary>Expand findings</summary>File: code/system-contracts/contracts/BootloaderUtilities.sol 29: txHash = keccak256(bytes.concat(signedTxHash, EfficientCall.keccak(_transaction.signature))); 120: keccak256( 121: bytes.concat( 122: encodedListLength, 123: encodedNonce, 124: encodedGasParam, 125: encodedTo, 126: encodedValue, 127: encodedDataLength, 128: _transaction.data, 129: vEncoded, 130: rEncoded, 131: sEncoded 132: ) 133: ); 211: keccak256( 212: bytes.concat( 213: "\x01", 214: encodedListLength, 215: encodedFixedLengthParams, 216: encodedDataLength, 217: _transaction.data, 218: encodedAccessListLength, 219: vEncoded, 220: rEncoded, 221: sEncoded 222: ) 223: ); 306: keccak256( 307: bytes.concat( 308: "\x02", 309: encodedListLength, 310: encodedFixedLengthParams, 311: encodedDataLength, 312: _transaction.data, 313: encodedAccessListLength, 314: vEncoded, 315: rEncoded, 316: sEncoded 317: ) 318: );
[29, 120-133, 211-223, 306-318]
File: code/system-contracts/contracts/ContractDeployer.sol 105: bytes32 hash = keccak256( 106: bytes.concat(CREATE2_PREFIX, bytes32(uint256(uint160(_sender))), _salt, _bytecodeHash, constructorInputHash) 107: ); 121: bytes32 hash = keccak256( 122: bytes.concat(CREATE_PREFIX, bytes32(uint256(uint160(_sender))), bytes32(_senderNonce)) 123: );
File: code/system-contracts/contracts/L1Messenger.sol 95: bytes32 hashedLog = keccak256( 96: abi.encodePacked( 97: _l2ToL1Log.l2ShardId, 98: _l2ToL1Log.isService, 99: _l2ToL1Log.txNumberInBlock, 100: _l2ToL1Log.sender, 101: _l2ToL1Log.key, 102: _l2ToL1Log.value 103: ) 104: ); 106: chainedLogsHash = keccak256(abi.encode(chainedLogsHash, hashedLog)); 122: chainedMessagesHash = keccak256(abi.encode(chainedMessagesHash, hash)); 164: chainedL1BytecodesRevealDataHash = keccak256(abi.encode(chainedL1BytecodesRevealDataHash, _bytecodeHash)); 212: reconstructedChainedLogsHash = keccak256(abi.encode(reconstructedChainedLogsHash, hashedLog)); 225: l2ToL1LogsTreeArray[i] = keccak256( 226: abi.encode(l2ToL1LogsTreeArray[2 * i], l2ToL1LogsTreeArray[2 * i + 1]) 227: ); 243: reconstructedChainedMessagesHash = keccak256(abi.encode(reconstructedChainedMessagesHash, hashedMessage)); 259: reconstructedChainedL1BytecodesRevealDataHash = keccak256( 260: abi.encode( 261: reconstructedChainedL1BytecodesRevealDataHash, 262: Utils.hashL2Bytecode( 263: _totalL2ToL1PubdataAndStateDiffs[calldataPtr:calldataPtr + currentBytecodeLength] 264: ) 265: ) 266: );
[95-104, 106, 122, 164, 212, 225-227, 243, 259-266]
File: code/system-contracts/contracts/SystemContext.sol 159: hash = keccak256(abi.encode(_block)); 227: return keccak256(abi.encode(_blockNumber, _blockTimestamp, _prevL2BlockHash, _blockTxsRollingHash)); 234: return keccak256(abi.encodePacked(uint32(_blockNumber))); 403: currentL2BlockTxsRollingHash = keccak256(abi.encode(currentL2BlockTxsRollingHash, _txHash));
File: code/contracts/zksync/contracts/L2ContractHelper.sol 108: bytes32 data = keccak256( 109: bytes.concat(CREATE2_PREFIX, senderBytes, _salt, _bytecodeHash, _constructorInputHash) 110: );
[108-110]
File: code/system-contracts/contracts/libraries/TransactionHelper.sol 119: bytes32 structHash = keccak256( 120: abi.encode( 121: EIP712_TRANSACTION_TYPE_HASH, 122: _transaction.txType, 123: _transaction.from, 124: _transaction.to, 125: _transaction.gasLimit, 126: _transaction.gasPerPubdataByteLimit, 127: _transaction.maxFeePerGas, 128: _transaction.maxPriorityFeePerGas, 129: _transaction.paymaster, 130: _transaction.nonce, 131: _transaction.value, 132: EfficientCall.keccak(_transaction.data), 133: keccak256(abi.encodePacked(_transaction.factoryDeps)), 134: EfficientCall.keccak(_transaction.paymasterInput) 135: ) 136: ); 133: keccak256(abi.encodePacked(_transaction.factoryDeps)), 138: bytes32 domainSeparator = keccak256( 139: abi.encode(EIP712_DOMAIN_TYPEHASH, keccak256("zkSync"), keccak256("2"), block.chainid) 140: ); 139: abi.encode(EIP712_DOMAIN_TYPEHASH, keccak256("zkSync"), keccak256("2"), block.chainid) 139: abi.encode(EIP712_DOMAIN_TYPEHASH, keccak256("zkSync"), keccak256("2"), block.chainid) 142: return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); 203: keccak256( 204: bytes.concat( 205: encodedListLength, 206: encodedNonce, 207: encodedGasParam, 208: encodedTo, 209: encodedValue, 210: encodedDataLength, 211: _transaction.data, 212: encodedChainId 213: ) 214: ); 275: keccak256( 276: bytes.concat( 277: "\x01", 278: encodedListLength, 279: encodedFixedLengthParams, 280: encodedDataLength, 281: _transaction.data, 282: encodedAccessListLength 283: ) 284: ); 347: keccak256( 348: bytes.concat( 349: "\x02", 350: encodedListLength, 351: encodedFixedLengthParams, 352: encodedDataLength, 353: _transaction.data, 354: encodedAccessListLength 355: ) 356: );
[119-136, 133, 138-140, 139, 139, 142, 203-214, 275-284, 347-356]
File: code/contracts/ethereum/contracts/bridge/L1ERC20Bridge.sol 76: bytes32 constructorInputHash = keccak256(abi.encode(l2TokenBeacon, ""));
[76]
File: code/contracts/ethereum/contracts/bridge/L1SharedBridge.sol 210: bytes32 txDataHash = keccak256(abi.encode(_prevMsgSender, _l1Token, amount)); 341: bytes32 txDataHash = keccak256(abi.encode(_depositSender, _l1Token, _amount)); 598: bytes32 txDataHash = keccak256(abi.encode(_prevMsgSender, _l1Token, _amount));
File: code/contracts/ethereum/contracts/governance/Governance.sol 205: return keccak256(abi.encode(_operation));
[205]
File: code/contracts/ethereum/contracts/state-transition/StateTransitionManager.sol 100: storedBatchZero = keccak256(abi.encode(batchZero)); 102: initialCutHash = keccak256(abi.encode(_initializeData.diamondCut)); 138: initialCutHash = keccak256(abi.encode(_diamondCut)); 147: upgradeCutHash[_oldProtocolVersion] = keccak256(abi.encode(_cutData)); 156: upgradeCutHash[_oldProtocolVersion] = keccak256(abi.encode(_cutData)); 255: bytes32 cutHashInput = keccak256(_diamondCut);
[100, 102, 138, 147, 156, 255]
File: code/contracts/ethereum/contracts/upgrades/BaseZkSyncUpgrade.sol 212: bytes32 l2ProtocolUpgradeTxHash = keccak256(encodedTransaction);
[212]
File: code/contracts/zksync/contracts/bridge/L2SharedBridge.sol 150: bytes32 constructorInputHash = keccak256(abi.encode(address(l2TokenBeacon), ""));
[150]
File: code/contracts/ethereum/contracts/common/libraries/L2ContractHelper.sol 67: bytes32 data = keccak256( 68: bytes.concat(CREATE2_PREFIX, senderBytes, _salt, _bytecodeHash, _constructorInputHash) 69: );
[67-69]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Admin.sol 104: bytes32 cutHashInput = keccak256(abi.encode(_diamondCut));
[104]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Executor.sol 63: keccak256(_newBatch.pubdataCommitments[1:_newBatch.pubdataCommitments.length - 32]), 313: concatHash = keccak256(abi.encode(concatHash, priorityOp.canonicalTxHash)); 460: keccak256( 461: abi.encodePacked( 462: _prevBatchCommitment, 463: _currentBatchCommitment, 464: _verifierParams.recursionNodeLevelVkHash, 465: _verifierParams.recursionLeafLevelVkHash 466: ) 467: ) 506: bytes32 passThroughDataHash = keccak256(_batchPassThroughData(_newBatchData)); 507: bytes32 metadataHash = keccak256(_batchMetaParameters()); 508: bytes32 auxiliaryOutputHash = keccak256( 509: _batchAuxiliaryOutput(_newBatchData, _stateDiffHash, _blobCommitments, _blobHashes) 510: ); 512: return keccak256(abi.encode(passThroughDataHash, metadataHash, auxiliaryOutputHash)); 545: bytes32 l2ToL1LogsHash = keccak256(_batch.systemLogs); 588: return keccak256(abi.encode(_storedBatchInfo)); 654: blobCommitments[versionedHashIndex] = keccak256( 655: abi.encodePacked(blobVersionedHash, _pubdataCommitments[i:i + PUBDATA_COMMITMENT_COMMITMENT_OFFSET]) 656: );
[63, 313, 460-467, 506, 507, 508-510, 512, 545, 588, 654-656]
</details>File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Mailbox.sol 112: bytes32 hashedLog = keccak256( 113: abi.encodePacked(_log.l2ShardId, _log.isService, _log.txNumberInBatch, _log.sender, _log.key, _log.value) 114: ); 138: value: keccak256(_message.data) 332: canonicalTxHash = keccak256(transactionEncoding);
msg.sender
It is possible to use assembly to efficiently validate msg.sender
with the least amount of opcodes. For more details check the following report.
There are 42 instances of this issue.
<details> <summary>Expand findings</summary>File: code/system-contracts/contracts/AccountCodeStorage.sol 26: require(msg.sender == address(DEPLOYER_SYSTEM_CONTRACT), "Callable only by the deployer system contract");
[26]
File: code/system-contracts/contracts/ComplexUpgrader.sol 22: require(msg.sender == FORCE_DEPLOYER, "Can only be called by FORCE_DEPLOYER");
[22]
File: code/system-contracts/contracts/ContractDeployer.sol 29: require(msg.sender == address(this), "Callable only by self"); 239: require( 240: msg.sender == FORCE_DEPLOYER || msg.sender == address(COMPLEX_UPGRADER_CONTRACT), 241: "Can only be called by FORCE_DEPLOYER or COMPLEX_UPGRADER_CONTRACT" 242: );
File: code/system-contracts/contracts/DefaultAccount.sol 29: if (msg.sender != BOOTLOADER_FORMAL_ADDRESS) { 222: assert(msg.sender != BOOTLOADER_FORMAL_ADDRESS);
File: code/system-contracts/contracts/ImmutableSimulator.sol 35: require(msg.sender == address(DEPLOYER_SYSTEM_CONTRACT), "Callable only by the deployer system contract");
[35]
File: code/system-contracts/contracts/KnownCodesStorage.sol 20: require(msg.sender == address(COMPRESSOR_CONTRACT), "Callable only by the compressor");
[20]
File: code/system-contracts/contracts/L2BaseToken.sol 33: require( 34: msg.sender == MSG_VALUE_SYSTEM_CONTRACT || 35: msg.sender == address(DEPLOYER_SYSTEM_CONTRACT) || 36: msg.sender == BOOTLOADER_FORMAL_ADDRESS, 37: "Only system contracts with special access can call this method" 38: );
[33-38]
File: code/system-contracts/contracts/NonceHolder.sol 89: require(isNonceUsed(msg.sender, _key - 1), "Previous nonce has not been used"); 136: require( 137: msg.sender == address(DEPLOYER_SYSTEM_CONTRACT), 138: "Only the contract deployer can increment the deployment nonce" 139: );
File: code/system-contracts/contracts/interfaces/ISystemContract.sol 21: require( 22: SystemContractHelper.isSystemCall() || SystemContractHelper.isSystemContract(msg.sender), 23: "This method require system call flag" 24: ); 31: require( 32: SystemContractHelper.isSystemContract(msg.sender), 33: "This method require the caller to be system contract" 34: ); 41: require(msg.sender == caller, "Inappropriate caller"); 48: require(msg.sender == BOOTLOADER_FORMAL_ADDRESS, "Callable only by the bootloader"); 55: require(msg.sender == FORCE_DEPLOYER);
File: code/contracts/ethereum/contracts/bridge/L1ERC20Bridge.sol 64: require(msg.sender == address(sharedBridge), "Not shared bridge");
[64]
File: code/contracts/ethereum/contracts/bridge/L1SharedBridge.sol 69: require(msg.sender == address(bridgehub), "ShB not BH"); 75: require( 76: msg.sender == address(bridgehub) || (_chainId == ERA_CHAIN_ID && msg.sender == ERA_DIAMOND_PROXY), 77: "L1SharedBridge: not bridgehub or era chain" 78: ); 84: require(msg.sender == address(legacyBridge), "ShB not legacy bridge"); 138: require(bridgehub.getStateTransition(_chainId) == msg.sender, "receiveEth not state transition");
File: code/contracts/ethereum/contracts/bridgehub/Bridgehub.sol 46: require(msg.sender == admin || msg.sender == owner(), "Bridgehub: not owner or admin"); 62: require(msg.sender == currentPendingAdmin, "n42"); // Only proposed by current admin address can claim the admin rights
File: code/contracts/ethereum/contracts/governance/Governance.sol 59: require(msg.sender == address(this), "Only governance contract itself is allowed to call this function"); 65: require(msg.sender == securityCouncil, "Only security council is allowed to call this function"); 71: require( 72: msg.sender == owner() || msg.sender == securityCouncil, 73: "Only the owner and security council are allowed to call this function" 74: );
File: code/contracts/ethereum/contracts/state-transition/StateTransitionManager.sol 64: require(msg.sender == bridgehub, "StateTransition: only bridgehub"); 70: require(msg.sender == admin || msg.sender == owner(), "Bridgehub: not owner or admin"); 121: require(msg.sender == currentPendingAdmin, "n42"); // Only proposed by current admin address can claim the admin rights
File: code/contracts/ethereum/contracts/state-transition/ValidatorTimelock.sol 62: require(msg.sender == stateTransitionManager.getChainAdmin(_chainId), "ValidatorTimelock: only chain admin"); 68: require(validators[_chainId][msg.sender] == true, "ValidatorTimelock: only validator");
File: code/contracts/zksync/contracts/bridge/L2SharedBridge.sol 87: require( 88: AddressAliasHelper.undoL1ToL2Alias(msg.sender) == l1Bridge || 89: AddressAliasHelper.undoL1ToL2Alias(msg.sender) == l1LegacyBridge, 90: "mq" 91: );
[87-91]
File: code/contracts/zksync/contracts/bridge/L2StandardERC20.sol 120: require(msg.sender == UpgradeableBeacon(beaconAddress).owner(), "tt"); 130: require(msg.sender == l2Bridge, "xnt"); // Only L2 bridge can call this method
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Admin.sol 34: require(msg.sender == pendingAdmin, "n4"); // Only proposed by current admin address can claim the admin rights
[34]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/ZkSyncStateTransitionBase.sol 16: require(msg.sender == s.admin, "StateTransition Chain: not admin"); 22: require(s.validators[msg.sender], "StateTransition Chain: not validator"); 27: require(msg.sender == s.stateTransitionManager, "StateTransition Chain: not state transition manager"); 32: require(msg.sender == s.bridgehub, "StateTransition Chain: not bridgehub"); 37: require( 38: msg.sender == s.admin || msg.sender == s.stateTransitionManager, 39: "StateTransition Chain: Only by admin or state transition manager" 40: ); 45: require( 46: s.validators[msg.sender] || msg.sender == s.stateTransitionManager, 47: "StateTransition Chain: Only by validator or state transition manager" 48: ); 53: require(msg.sender == s.baseTokenBridge, "Only shared bridge can call this function");
[16, 22, 27, 32, 37-40, 45-48, 53]
</details>address
storage valuesUsing assembly { sstore(state.slot, addr)
instead of state = addr
can save gas.
There are 16 instances of this issue.
<details> <summary>Expand findings</summary>File: code/system-contracts/contracts/SystemContext.sol 100: origin = _newOrigin;
[100]
File: code/contracts/ethereum/contracts/bridge/L1SharedBridge.sol 96: l1WethAddress = _l1WethAddress;
[96]
File: code/contracts/ethereum/contracts/bridgehub/Bridgehub.sol 55: pendingAdmin = _newPendingAdmin; 65: admin = currentPendingAdmin; 109: sharedBridge = IL1SharedBridge(_sharedBridge);
File: code/contracts/ethereum/contracts/governance/Governance.sol 46: securityCouncil = _securityCouncil; 258: securityCouncil = _newSecurityCouncil;
File: code/contracts/ethereum/contracts/state-transition/StateTransitionManager.sol 59: bridgehub = _bridgehub; 85: genesisUpgrade = _initializeData.genesisUpgrade; 87: validatorTimelock = _initializeData.validatorTimelock; 114: pendingAdmin = _newPendingAdmin; 124: admin = currentPendingAdmin; 133: validatorTimelock = _validatorTimelock;
File: code/contracts/zksync/contracts/bridge/L2SharedBridge.sol 60: l1Bridge = _l1Bridge;
[60]
</details>File: code/contracts/zksync/contracts/bridge/L2StandardERC20.sol 52: l1Address = _l1Address; 54: l2Bridge = msg.sender;
event
To efficiently emit events, it's possible to utilize assembly by making use of scratch space and the free memory pointer. This approach has the advantage of potentially avoiding the costs associated with memory expansion.
However, it's important to note that in order to safely optimize this process, it is preferable to cache and restore the free memory pointer.
A good example of such practice can be seen in Solady's codebase.
There are 77 instances of this issue.
<details> <summary>Expand findings</summary>File: code/system-contracts/contracts/ContractDeployer.sol 68: emit AccountVersionUpdated(msg.sender, _version); 86: emit AccountNonceOrderingUpdated(msg.sender, _nonceOrdering); 355: emit ContractDeployed(_sender, _bytecodeHash, _newAddress);
File: code/system-contracts/contracts/KnownCodesStorage.sol 59: emit MarkedAsKnown(_bytecodeHash, _shouldSendToL1);
[59]
File: code/system-contracts/contracts/L1Messenger.sol 111: emit L2ToL1LogSent(_l2ToL1Log); 156: emit L1MessageSent(msg.sender, hash, _message); 181: emit BytecodeL1PublicationRequested(_bytecodeHash);
File: code/system-contracts/contracts/L2BaseToken.sol 49: emit Transfer(_from, _to, _amount); 67: emit Mint(_account, _amount); 79: emit Withdrawal(msg.sender, _l1Receiver, amount); 92: emit WithdrawalWithMessage(msg.sender, _l1Receiver, amount, _additionalData);
File: code/system-contracts/contracts/NonceHolder.sol 96: emit ValueSetUnderNonce(msg.sender, _key, _value);
[96]
File: code/contracts/ethereum/contracts/bridge/L1ERC20Bridge.sol 155: emit DepositInitiated(l2TxHash, msg.sender, _l2Receiver, _l1Token, _amount); 199: emit ClaimedFailedDeposit(_depositSender, _l1Token, amount); 225: emit WithdrawalFinalized(l1Receiver, l1Token, amount);
File: code/contracts/ethereum/contracts/bridge/L1SharedBridge.sol 168: emit BridgehubDepositBaseTokenInitiated(_chainId, _prevMsgSender, _l1Token, _amount); 227: emit BridgehubDepositInitiated(_chainId, txDataHash, _prevMsgSender, _l2Receiver, _l1Token, amount); 239: emit BridgehubDepositFinalized(_chainId, _txDataHash, _txHash); 367: emit ClaimedFailedDepositSharedBridge(_chainId, _depositSender, _l1Token, _amount); 454: emit WithdrawalFinalizedSharedBridge(_chainId, l1Receiver, l1Token, amount); 602: emit LegacyDepositInitiated(ERA_CHAIN_ID, l2TxHash, _prevMsgSender, _l2Receiver, _l1Token, _amount);
[168, 227, 239, 367, 454, 602]
File: code/contracts/ethereum/contracts/bridgehub/Bridgehub.sol 56: emit NewPendingAdmin(oldPendingAdmin, _newPendingAdmin); 68: emit NewPendingAdmin(currentPendingAdmin, address(0)); 69: emit NewAdmin(previousAdmin, pendingAdmin); 145: emit NewChain(_chainId, _stateTransitionManager, _admin);
File: code/contracts/ethereum/contracts/governance/Governance.sol 47: emit ChangeSecurityCouncil(address(0), _securityCouncil); 50: emit ChangeMinDelay(0, _minDelay); 132: emit TransparentOperationScheduled(id, _delay, _operation); 144: emit ShadowOperationScheduled(_id, _delay); 157: emit OperationCancelled(_id); 180: emit OperationExecuted(id); 199: emit OperationExecuted(id); 250: emit ChangeMinDelay(minDelay, _newDelay); 257: emit ChangeSecurityCouncil(securityCouncil, _newSecurityCouncil);
[47, 50, 132, 144, 157, 180, 199, 250, 257]
File: code/contracts/ethereum/contracts/state-transition/StateTransitionManager.sol 115: emit NewPendingAdmin(oldPendingAdmin, _newPendingAdmin); 127: emit NewPendingAdmin(currentPendingAdmin, address(0)); 128: emit NewAdmin(previousAdmin, pendingAdmin); 227: emit SetChainIdUpgrade(_chainContract, l2ProtocolUpgradeTx, protocolVersion); 235: emit StateTransitionNewChain(_chainId, _stateTransitionContract); 287: emit StateTransitionNewChain(_chainId, stateTransitionAddress);
[115, 127, 128, 227, 235, 287]
File: code/contracts/ethereum/contracts/state-transition/ValidatorTimelock.sol 83: emit ValidatorAdded(_chainId, _newValidator); 92: emit ValidatorRemoved(_chainId, _validator); 98: emit NewExecutionDelay(_executionDelay);
File: code/contracts/ethereum/contracts/upgrades/BaseZkSyncUpgrade.sol 87: emit UpgradeComplete(_proposedUpgrade.newProtocolVersion, txHash, _proposedUpgrade); 104: emit NewL2DefaultAccountBytecodeHash(previousDefaultAccountBytecodeHash, _l2DefaultAccountBytecodeHash); 121: emit NewL2BootloaderBytecodeHash(previousBootloaderBytecodeHash, _l2BootloaderBytecodeHash); 137: emit NewVerifier(address(oldVerifier), address(_newVerifier)); 157: emit NewVerifierParams(oldVerifierParams, _newVerifierParams); 256: emit NewProtocolVersion(previousProtocolVersion, _newProtocolVersion);
File: code/contracts/ethereum/contracts/upgrades/BaseZkSyncUpgradeGenesis.sol 40: emit NewProtocolVersion(previousProtocolVersion, _newProtocolVersion); 67: emit UpgradeComplete(_proposedUpgrade.newProtocolVersion, txHash, _proposedUpgrade);
File: code/contracts/zksync/contracts/bridge/L2SharedBridge.sol 105: emit FinalizeDeposit(_l1Sender, _l2Receiver, expectedL2Token, _amount); 134: emit WithdrawalInitiated(msg.sender, _l1Receiver, _l2Token, _amount);
File: code/contracts/zksync/contracts/bridge/L2StandardERC20.sol 101: emit BridgeInitialize(_l1Address, decodedName, decodedSymbol, decimals_); 126: emit BridgeInitialize(l1Address, _newName, _newSymbol, decimals_); 147: emit BridgeMint(_to, _amount); 156: emit BridgeBurn(_from, _amount);
File: code/contracts/ethereum/contracts/state-transition/libraries/Diamond.sol 121: emit DiamondCut(facetCuts, initAddress, initCalldata);
[121]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Admin.sol 28: emit NewPendingAdmin(oldPendingAdmin, _newPendingAdmin); 40: emit NewPendingAdmin(pendingAdmin, address(0)); 41: emit NewAdmin(previousAdmin, pendingAdmin); 47: emit ValidatorStatusUpdate(_validator, _active); 54: emit IsPorterAvailableStatusUpdate(_zkPorterIsAvailable); 63: emit NewPriorityTxMaxGasLimit(oldPriorityTxMaxGasLimit, _newPriorityTxMaxGasLimit); 75: emit NewFeeParams(oldFeeParams, _newFeeParams); 86: emit NewBaseTokenMultiplier(oldNominator, oldDenominator, _nominator, _denominator); 92: emit ValidiumModeStatusUpdate(_validiumMode); 115: emit ExecuteUpgrade(_diamondCut); 125: emit ExecuteUpgrade(_diamondCut); 139: emit Freeze(); 149: emit Unfreeze();
[28, 40, 41, 47, 54, 63, 75, 86, 92, 115, 125, 139, 149]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Executor.sol 261: emit BlockCommit( 262: _lastCommittedBatchData.batchNumber, 263: _lastCommittedBatchData.batchHash, 264: _lastCommittedBatchData.commitment 265: ); 299: emit BlockCommit( 300: _lastCommittedBatchData.batchNumber, 301: _lastCommittedBatchData.batchHash, 302: _lastCommittedBatchData.commitment 303: ); 353: emit BlockExecution(_batchesData[i].batchNumber, _batchesData[i].batchHash, _batchesData[i].commitment); 436: emit BlocksVerification(s.totalBatchesVerified, currentTotalBatchesVerified); 496: emit BlocksRevert(s.totalBatchesCommitted, s.totalBatchesVerified, s.totalBatchesExecuted);
[261-265, 299-303, 353, 436, 496]
File: code/contracts/ethereum/contracts/state-transition/chain-deps/facets/Mailbox.sol 343: emit NewPriorityRequest( 344: _priorityOpParams.txId, 345: canonicalTxHash, 346: _priorityOpParams.expirationTimestamp, 347: transaction, 348: _factoryDeps 349: );
[343-349]
</details>#0 - saxenism
2024-04-18T02:18:46Z
High Quality Report. Covered a lot of ground.
#1 - c4-sponsor
2024-04-18T02:18:48Z
saxenism (sponsor) confirmed
#2 - c4-judge
2024-04-29T13:49:59Z
alex-ppg marked the issue as grade-a