Skip to content

Instantly share code, notes, and snippets.

@Picodes
Created October 28, 2022 20:10
Show Gist options
  • Save Picodes/1f87a82e954cc749dea9d9961d5f4dff to your computer and use it in GitHub Desktop.
Save Picodes/1f87a82e954cc749dea9d9961d5f4dff to your computer and use it in GitHub Desktop.

Report

Gas Optimizations

Issue Instances
[GAS-1] Cache array length outside of loop 1
[GAS-2] Use != 0 instead of > 0 for unsigned integer comparison 5
[GAS-3] Don't initialize variables with default value 14
[GAS-4] Use shift Right/Left instead of division/multiplication if possible 3
[GAS-5] Using bools for storage incurs overhead 6
[GAS-6] Use Custom Errors 93

[GAS-1] Cache array length outside of loop

If not cached, the solidity compiler will always read the length of the array during each iteration. That is, if it is a storage array, this is an extra sload operation (100 additional extra gas for each iteration except for the first) and if it is a memory array, this is an extra mload operation (3 additional gas for each iteration except for the first).

Instances (1):

File: ethereum/contracts/zksync/facets/Executor.sol

111:         for (uint256 i = 0; i < emittedL2Logs.length; i = i.uncheckedAdd(L2_TO_L1_LOG_SERIALIZE_SIZE)) {

[GAS-2] Use != 0 instead of > 0 for unsigned integer comparison

Instances (5):

File: ethereum/contracts/bridge/L1ERC20Bridge.sol

117:         require(amount > 0, "1T"); // empty deposit amount

210:         require(amount > 0, "y1");
File: ethereum/contracts/zksync/facets/DiamondCut.sol

16:         assert(SECURITY_COUNCIL_APPROVALS_FOR_EMERGENCY_UPGRADE > 0);
File: ethereum/contracts/zksync/libraries/Diamond.sol

100:             require(selectors.length > 0, "B"); // no functions for diamond cut
File: ethereum/contracts/zksync/libraries/Merkle.sol

23:         require(pathLength > 0, "xc");

[GAS-3] Don't initialize variables with default value

Instances (14):

File: ethereum/contracts/common/AllowList.sol

69:         for (uint256 i = 0; i < targetsLength; i = i.uncheckedInc()) {

103:         for (uint256 i = 0; i < callersLength; i = i.uncheckedInc()) {
File: ethereum/contracts/zksync/facets/Executor.sol

111:         for (uint256 i = 0; i < emittedL2Logs.length; i = i.uncheckedAdd(L2_TO_L1_LOG_SERIALIZE_SIZE)) {

162:         for (uint256 i = 0; i < blocksLength; i = i.uncheckedInc()) {

180:         for (uint256 i = 0; i < _nPriorityOps; i = i.uncheckedInc()) {

210:         for (uint256 i = 0; i < nBlocks; i = i.uncheckedInc()) {

240:         for (uint256 i = 0; i < committedBlocksLength; i = i.uncheckedInc()) {
File: ethereum/contracts/zksync/facets/Getters.sol

163:         for (uint256 i = 0; i < facetsLen; i = i.uncheckedInc()) {
File: ethereum/contracts/zksync/facets/Mailbox.sol

223:         for (uint256 i = 0; i < factoryDepsLen; i = i.uncheckedInc()) {
File: ethereum/contracts/zksync/libraries/Diamond.sol

94:         for (uint256 i = 0; i < facetCutsLength; ++i) {

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

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

173:         for (uint256 i = 0; i < selectorsLength; ++i) {
File: ethereum/contracts/zksync/libraries/Merkle.sol

28:         for (uint256 i = 0; i < pathLength; i = i.uncheckedInc()) {

[GAS-4] Use shift Right/Left instead of division/multiplication if possible

Instances (3):

File: zksync/contracts/bridge/L2ERC20Bridge.sol

12: import "./L2StandardERC20.sol";

13: import {L2ContractHelper} from "../L2ContractHelper.sol";
File: zksync/contracts/bridge/L2ETHBridge.sol

5: import {L2ContractHelper} from "../L2ContractHelper.sol";

[GAS-5] Using bools for storage incurs overhead

Use uint256(1) and uint256(2) for true/false to avoid a Gwarmaccess (100 gas), and to avoid Gsset (20000 gas) when changing from ‘false’ to ‘true’, after having been ‘true’ in the past. See source.

Instances (6):

File: ethereum/contracts/bridge/L1ERC20Bridge.sol

40:     mapping(uint256 => mapping(uint256 => bool)) public isWithdrawalFinalized;
File: ethereum/contracts/bridge/L1EthBridge.sol

37:     mapping(uint256 => mapping(uint256 => bool)) public isWithdrawalFinalized;
File: ethereum/contracts/common/AllowList.sol

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

30:     mapping(address => mapping(address => mapping(bytes4 => bool))) public hasSpecialAccessToCall;
File: ethereum/contracts/zksync/Storage.sol

22:     mapping(address => bool) securityCouncilMembers;

77:     mapping(address => bool) validators;

[GAS-6] Use Custom Errors

Source Instead of using error strings, to reduce deployment and runtime cost, you should use Custom Errors. This would save both deployment and runtime cost.

Instances (93):

File: ethereum/contracts/bridge/L1ERC20Bridge.sol

77:         require(_factoryDeps.length == 2, "mk");

117:         require(amount > 0, "1T"); // empty deposit amount

207:         require(success, "yn");

210:         require(amount > 0, "y1");

232:         require(!isWithdrawalFinalized[_l2BlockNumber][_l2MessageIndex], "pw");

249:             require(success, "nq");

271:         require(_l2ToL1message.length == 76, "kk");

274:         require(bytes4(functionSignature) == this.finalizeWithdrawal.selector, "nt");
File: ethereum/contracts/bridge/L1EthBridge.sol

93:         require(_l1Token == CONVENTIONAL_ETH_ADDRESS, "bx");

141:         require(_l1Token == CONVENTIONAL_ETH_ADDRESS, "sj");

166:         require(success, "ju");

189:         require(!isWithdrawalFinalized[_l2BlockNumber][_l2MessageIndex], "jj");

205:         require(success, "rj");
File: ethereum/contracts/common/AllowList.sol

33:         require(_owner != address(0), "kq");

38:         require(msg.sender == owner, "kx");

67:         require(targetsLength == _enables.length, "yg"); // The size of arrays should be equal

155:         require(msg.sender == newOwner, "n0"); // Only proposed by current owner address can claim the owner rights
File: ethereum/contracts/common/AllowListed.sol

15:             require(_allowList.canCall(msg.sender, address(this), functionSig), "nr");
File: ethereum/contracts/common/L2ContractHelper.sol

50:         require(_bytecode.length % 32 == 0, "po");

53:         require(bytecodeLenInWords < 2**16, "pp"); // bytecode length must be less than 2^16 words

54:         require(bytecodeLenInWords % 2 == 1, "pr"); // bytecode length in words must be odd

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

67:         require(bytecodeLen(_bytecodeHash) % 2 == 1, "uy"); // Code length in words must be odd
File: ethereum/contracts/common/ReentrancyGuard.sol

55:         require(lockSlotOldValue == 0, "1B");

72:         require(_status == _NOT_ENTERED, "r1");
File: ethereum/contracts/zksync/DiamondProxy.sol

13:         require(_chainId == block.chainid, "pr");

24:         require(msg.data.length >= 4 || msg.data.length == 0, "Ut");

29:         require(facetAddress != address(0), "F"); // Proxy has no facet for this selector

30:         require(!diamondStorage.isFrozen || !facet.isFreezable, "q1"); // Facet is frozen
File: ethereum/contracts/zksync/facets/Base.sol

16:         require(msg.sender == s.governor, "1g"); // only by governor

22:         require(s.validators[msg.sender], "1h"); // validator is not active
File: ethereum/contracts/zksync/facets/DiamondCut.sol

23:         require(s.diamondCutStorage.proposedDiamondCutTimestamp == 0, "a3"); // proposal already exists

40:         require(_resetProposal(), "g1"); // failed cancel diamond cut

55:         require(approvedBySecurityCouncil || upgradeNoticePeriodPassed, "a6"); // notice period should expire

56:         require(approvedBySecurityCouncil || !diamondStorage.isFrozen, "f3");

65:         require(_resetProposal(), "a5"); // failed reset proposal

80:         require(!diamondStorage.isFrozen, "a9"); // diamond proxy is frozen already

94:         require(diamondStorage.isFrozen, "a7"); // diamond proxy is not frozen

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

108:         require(s.diamondCutStorage.securityCouncilMemberLastApprovedProposalId[msg.sender] < currentProposalId, "ao"); // already approved this proposal

111:         require(s.diamondCutStorage.proposedDiamondCutTimestamp != 0, "f0"); // there is no proposed diamond cut

112:         require(s.diamondCutStorage.proposedDiamondCutHash == _diamondCutHash, "f1"); // proposed diamond cut do not match to the approved
File: ethereum/contracts/zksync/facets/Executor.sol

28:         require(_newBlock.blockNumber == _previousBlock.blockNumber + 1, "f"); // only commit next block

39:         require(_previousBlock.blockHash == previousBlockHash, "l");

41:         require(expectedPriorityOperationsHash == _newBlock.priorityOperationsHash, "t");

52:             require(timestampNotTooSmall, "h"); // New block timestamp is too small

53:             require(timestampNotTooBig, "h1"); // New block timestamp is too big

117:                 require(keccak256(l2Messages[currentMessage]) == hashedMessage, "k2");

129:                 require(!isSystemContextLogProcessed, "fx");

136:                 require(bytecodeHash == L2ContractHelper.hashL2Bytecode(factoryDeps[currentBytecode]), "k3");

142:         require(currentBytecode == factoryDeps.length, "ym");

143:         require(currentMessage == l2Messages.length, "pl");

145:         require(isSystemContextLogProcessed, "by");

159:         require(s.storedBlockHashes[s.totalBlocksCommitted] == _hashStoredBlockInfo(_lastCommittedBlockData), "i"); // incorrect previous block data

192:         require(currentBlockNumber == s.totalBlocksExecuted + _executedBlockIdx + 1, "k"); // Execute blocks in order

199:         require(priorityOperationsHash == _storedBlock.priorityOperationsHash, "x"); // priority operations hash does not match to expected

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

237:         require(_hashStoredBlockInfo(_prevBlock) == s.storedBlockHashes[currentTotalBlocksVerified], "t1");

261:         require(successVerifyProof, "p"); // Proof verification fail

265:         require(successProofAggregation, "hh"); // Proof aggregation must be valid

268:         require(currentTotalBlocksVerified <= s.totalBlocksCommitted, "q");

337:         require(s.totalBlocksCommitted > _newLastBlock, "v1"); // the last committed block is less new last block
File: ethereum/contracts/zksync/facets/Getters.sol

148:         require(ds.selectorToFacet[_selector].facetAddress != address(0), "g2");
File: ethereum/contracts/zksync/facets/Governance.sol

30:         require(msg.sender == pendingGovernor, "n4"); // Only proposed by current governor address can claim the governor rights
File: ethereum/contracts/zksync/facets/Mailbox.sol

56:         require(_blockNumber <= s.totalBlocksExecuted, "xx");

63:         require(hashedLog != L2_L1_LOGS_TREE_DEFAULT_LEAF_HASH, "tw");

66:         require(_proof.length == L2_TO_L1_LOG_MERKLE_TREE_HEIGHT, "rz");

124:         require(_ergsLimit <= PRIORITY_TX_MAX_ERGS_LIMIT, "ui");
File: ethereum/contracts/zksync/libraries/Diamond.sol

100:             require(selectors.length > 0, "B"); // no functions for diamond cut

109:                 revert("C"); // undefined diamond cut action

126:         require(_facet != address(0), "G"); // facet with zero address cannot be added

135:             require(oldFacet.facetAddress == address(0), "J"); // facet for this selector already exists

150:         require(_facet != address(0), "K"); // cannot replace facet with zero address

156:             require(oldFacet.facetAddress != address(0), "L"); // it is impossible to replace the facet with zero address

170:         require(_facet == address(0), "a1"); // facet address must be zero

176:             require(oldFacet.facetAddress != address(0), "a2"); // Can't delete a non-existent facet

214:             require(_isSelectorFreezable == ds.selectorToFacet[selector0].isFreezable, "J1");

279:             require(_calldata.length == 0, "H"); // Non-empty calldata for zero address

283:             require(success, "I"); // delegatecall failed

287:             require(data.length == 32, "lp");

288:             require(abi.decode(data, (bytes32)) == DIAMOND_INIT_SUCCESS_RETURN_VALUE, "lp1");
File: ethereum/contracts/zksync/libraries/Merkle.sol

23:         require(pathLength > 0, "xc");

24:         require(pathLength < 256, "bt");

25:         require(_index < 2**pathLength, "pz");
File: ethereum/contracts/zksync/libraries/PriorityQueue.sol

64:         require(!_queue.isEmpty(), "D"); // priority queue is empty

72:         require(!_queue.isEmpty(), "s"); // priority queue is empty
File: zksync/contracts/bridge/L2ERC20Bridge.sol

58:         require(msg.sender == l1Bridge, "mq");

63:             require(deployedToken == expectedL2Token, "mt");

95:         require(l1Token != address(0), "yh");
File: zksync/contracts/bridge/L2ETHBridge.sol

49:         require(msg.sender == l1Bridge, "ni");

64:         require(_l2Token == CONVENTIONAL_ETH_ADDRESS, "zn");
File: zksync/contracts/bridge/L2StandardERC20.sol

44:         require(l1Address == address(0), "in5"); // Is already initialized

45:         require(_l1Address != address(0), "in6"); // Should be non-zero address
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment