code-423n4 / 2022-06-canto-v2-findings

0 stars 0 forks source link

Gas Optimizations #57

Open code423n4 opened 2 years ago

code423n4 commented 2 years ago

Overview

Risk Rating Number of issues
Gas Issues 14

Table of Contents:

1. Cheap Contract Deployment Through Clones

67:     function _executeTransfer(address _owner, uint256 _idx) internal {
68:         (bytes32 salt, ) = precompute(_owner, _idx);
69:         new FlashEscrow{salt: salt}( //@audit gas: deployment can cost less through clones
70:             nftAddress,
71:             _encodeFlashEscrowPayload(_idx)
72:         );
73:     }
lending-market-v2/contracts/Stableswap/BaseV1-core.sol:530:        pair = address(new BaseV1Pair{salt:salt}());

There's a way to save a significant amount of gas on deployment using Clones: https://www.youtube.com/watch?v=3Mw-pMmJ7TA .

This is a solution that was adopted, as an example, by Porter Finance. They realized that deploying using clones was 10x cheaper:

Consider applying a similar pattern, here with a cloneDeterministic method to mimic the current create2

2. Reduce the size of error messages (Long revert Strings)

Shortening revert strings to fit in 32 bytes will decrease deployment time gas and will decrease runtime gas when the revert condition is met.

Revert strings that are longer than 32 bytes require at least one additional mstore, along with additional overhead for computing memory offset, etc.

Revert strings > 32 bytes:

lending-market-v2/contracts/Accountant/AccountantDelegate.sol:87:       require(cNoteAmt >= noteDiff, "AccountantDelegate::sweepInterest:Error calculating interest to sweep");
lending-market-v2/contracts/Accountant/AccountantDelegate.sol:101:      require(cnote.balanceOf(treasury) == 0, "AccountantDelegate::sweepInterestError");
lending-market-v2/contracts/Accountant/AccountantDelegator.sol:42:        require(msg.sender == admin, "AccountantDelegator::_setImplementation: admin only");
lending-market-v2/contracts/Accountant/AccountantDelegator.sol:43:        require(implementation_ != address(0), "AccountantDelegator::_setImplementation: invalid implementation address");
lending-market-v2/contracts/Accountant/AccountantDelegator.sol:123:        require(msg.value == 0,"AccountantDelegator:fallback: cannot send value to fallback");
lending-market-v2/contracts/Governance/Comp.sol:166:        require(signatory != address(0), "Comp::delegateBySig: invalid signature");
lending-market-v2/contracts/Governance/Comp.sol:167:        require(nonce == nonces[signatory]++, "Comp::delegateBySig: invalid nonce");
lending-market-v2/contracts/Governance/Comp.sol:168:        require(block.timestamp <= expiry, "Comp::delegateBySig: signature expired");
lending-market-v2/contracts/Governance/Comp.sol:190:        require(blockNumber < block.number, "Comp::getPriorVotes: not yet determined");
lending-market-v2/contracts/Governance/Comp.sol:234:        require(src != address(0), "Comp::_transferTokens: cannot transfer from the zero address");
lending-market-v2/contracts/Governance/Comp.sol:235:        require(dst != address(0), "Comp::_transferTokens: cannot transfer to the zero address");
lending-market-v2/contracts/Governance/GovernorAlpha.sol:137:        require(comp.getPriorVotes(msg.sender, sub256(block.number, 1)) > proposalThreshold(), "GovernorAlpha::propose: proposer votes below proposal threshold");
lending-market-v2/contracts/Governance/GovernorAlpha.sol:138:        require(targets.length == values.length && targets.length == signatures.length && targets.length == calldatas.length, "GovernorAlpha::propose: proposal function information arity mismatch");
lending-market-v2/contracts/Governance/GovernorAlpha.sol:139:        require(targets.length != 0, "GovernorAlpha::propose: must provide actions");
lending-market-v2/contracts/Governance/GovernorAlpha.sol:140:        require(targets.length <= proposalMaxOperations(), "GovernorAlpha::propose: too many actions");
lending-market-v2/contracts/Governance/GovernorAlpha.sol:145:          require(proposersLatestProposalState != ProposalState.Active, "GovernorAlpha::propose: one live proposal per proposer, found an already active proposal");
lending-market-v2/contracts/Governance/GovernorAlpha.sol:146:          require(proposersLatestProposalState != ProposalState.Pending, "GovernorAlpha::propose: one live proposal per proposer, found an already pending proposal");
lending-market-v2/contracts/Governance/GovernorAlpha.sol:156:        require(newProposal.id == 0, "GovernorAlpha::propose: ProposalID collsion");
lending-market-v2/contracts/Governance/GovernorAlpha.sol:178:        require(state(proposalId) == ProposalState.Succeeded, "GovernorAlpha::queue: proposal can only be queued if it is succeeded");
lending-market-v2/contracts/Governance/GovernorAlpha.sol:189:        require(!timelock.queuedTransactions(keccak256(abi.encode(target, value, signature, data, eta))), "GovernorAlpha::_queueOrRevert: proposal action already queued at eta");
lending-market-v2/contracts/Governance/GovernorAlpha.sol:194:        require(state(proposalId) == ProposalState.Queued, "GovernorAlpha::execute: proposal can only be executed if it is queued");
lending-market-v2/contracts/Governance/GovernorAlpha.sol:205:        require(state != ProposalState.Executed, "GovernorAlpha::cancel: cannot cancel executed proposal");
lending-market-v2/contracts/Governance/GovernorAlpha.sol:208:        require(msg.sender == guardian || comp.getPriorVotes(proposal.proposer, sub256(block.number, 1)) < proposalThreshold(), "GovernorAlpha::cancel: proposer above threshold");
lending-market-v2/contracts/Governance/GovernorAlpha.sol:228:        require(proposalCount >= proposalId && proposalId > 0, "GovernorAlpha::state: invalid proposal id");
lending-market-v2/contracts/Governance/GovernorAlpha.sol:258:        require(signatory != address(0), "GovernorAlpha::castVoteBySig: invalid signature");
lending-market-v2/contracts/Governance/GovernorAlpha.sol:263:        require(state(proposalId) == ProposalState.Active, "GovernorAlpha::_castVote: voting is closed");
lending-market-v2/contracts/Governance/GovernorAlpha.sol:266:        require(receipt.hasVoted == false, "GovernorAlpha::_castVote: voter already voted");
lending-market-v2/contracts/Governance/GovernorAlpha.sol:283:        require(msg.sender == guardian, "GovernorAlpha::__acceptAdmin: sender must be gov guardian");
lending-market-v2/contracts/Governance/GovernorAlpha.sol:288:        require(msg.sender == guardian, "GovernorAlpha::__abdicate: sender must be gov guardian");
lending-market-v2/contracts/Governance/GovernorAlpha.sol:293:        require(msg.sender == guardian, "GovernorAlpha::__queueSetTimelockPendingAdmin: sender must be gov guardian");
lending-market-v2/contracts/Governance/GovernorAlpha.sol:298:        require(msg.sender == guardian, "GovernorAlpha::__executeSetTimelockPendingAdmin: sender must be gov guardian");
lending-market-v2/contracts/Governance/GovernorBravoDelegate.sol:25:        require(address(timelock) == address(0), "GovernorBravo::initialize: can only initialize once");
lending-market-v2/contracts/Governance/GovernorBravoDelegate.sol:26:        require(msg.sender == admin, "GovernorBravo::initialize: admin only");
lending-market-v2/contracts/Governance/GovernorBravoDelegate.sol:27:        require(timelock_ != address(0), "GovernorBravo::initialize: invalid timelock address");
lending-market-v2/contracts/Governance/GovernorBravoDelegate.sol:45:                "GovernorBravo::propose: proposal function information arity mismatch");
lending-market-v2/contracts/Governance/GovernorBravoDelegate.sol:46:        require(unigovProposal.targets.length != 0, "GovernorBravo::propose: must provide actions");
lending-market-v2/contracts/Governance/GovernorBravoDelegate.sol:47:        require(unigovProposal.targets.length <= proposalMaxOperations, "GovernorBravo::propose: too many actions");
lending-market-v2/contracts/Governance/GovernorBravoDelegate.sol:76:        require(!timelock.queuedTransactions(keccak256(abi.encode(target, value, signature, data, eta))), "GovernorBravo::queueOrRevertInternal: identical proposal action already queued at eta");
lending-market-v2/contracts/Governance/GovernorBravoDelegate.sol:85:        require(state(proposalId) == ProposalState.Queued, "GovernorBravo::execute: proposal can only be executed if it is queued");
lending-market-v2/contracts/Governance/GovernorBravoDelegate.sol:128:        require(msg.sender == admin, "GovernorBravo::_initiate: admin only");
lending-market-v2/contracts/Governance/GovernorBravoDelegate.sol:129:        require(initialProposalId == 0, "GovernorBravo::_initiate: can only initiate once");
lending-market-v2/contracts/Governance/GovernorBravoDelegate.sol:140:        require(msg.sender == admin, "GovernorBravo:_setPendingAdmin: admin only");
lending-market-v2/contracts/Governance/GovernorBravoDelegate.sol:158:        require(msg.sender == pendingAdmin, "GovernorBravo:_acceptAdmin: pending admin only");
lending-market-v2/contracts/Governance/GovernorBravoDelegator.sol:27:        require(msg.sender == admin, "GovernorBravoDelegator::_setImplementation: admin only");
lending-market-v2/contracts/Governance/GovernorBravoDelegator.sol:28:        require(implementation_ != address(0), "GovernorBravoDelegator::_setImplementation: invalid implementation address");
lending-market-v2/contracts/Governance/GovernorBravoDelegator.sol:40:        require(msg.sender == admin, "GovernorBravoDelegator::_initiateDelegated: admin only");
lending-market-v2/contracts/Governance/GovernorBravoDelegator.sol:48:        require(msg.sender == admin, "GovernorBravoDelegator::_acceptInitialAdminDelegated: admin only");
lending-market-v2/contracts/Governance/GovernorBravoDelegator.sol:56:        require(msg.sender == admin, "GovernorBravoDelegator::_setPendingAdminDelegated: admin only");
lending-market-v2/contracts/Stableswap/BaseV1-periphery.sol:104:        require(tokenA != tokenB, "BaseV1Router: IDENTICAL_ADDRESSES");
lending-market-v2/contracts/Stableswap/BaseV1-periphery.sol:122:        require(amountA > 0, "BaseV1Router: INSUFFICIENT_AMOUNT");
lending-market-v2/contracts/Stableswap/BaseV1-periphery.sol:123:        require(reserveA > 0 && reserveB > 0, "BaseV1Router: INSUFFICIENT_LIQUIDITY");
lending-market-v2/contracts/Stableswap/BaseV1-periphery.sol:241:                require(amountBOptimal >= amountBMin, "BaseV1Router: INSUFFICIENT_B_AMOUNT");
lending-market-v2/contracts/Stableswap/BaseV1-periphery.sol:246:                require(amountAOptimal >= amountAMin, "BaseV1Router: INSUFFICIENT_A_AMOUNT");
lending-market-v2/contracts/Stableswap/BaseV1-periphery.sol:313:        require(amountA >= amountAMin, "BaseV1Router: INSUFFICIENT_A_AMOUNT");
lending-market-v2/contracts/Stableswap/BaseV1-periphery.sol:314:        require(amountB >= amountBMin, "BaseV1Router: INSUFFICIENT_B_AMOUNT");
lending-market-v2/contracts/Stableswap/BaseV1-periphery.sol:405:        require(amounts[amounts.length - 1] >= amountOutMin, "BaseV1Router: INSUFFICIENT_OUTPUT_AMOUNT");
lending-market-v2/contracts/Stableswap/BaseV1-periphery.sol:420:        require(amounts[amounts.length - 1] >= amountOutMin, "BaseV1Router: INSUFFICIENT_OUTPUT_AMOUNT");
lending-market-v2/contracts/Stableswap/BaseV1-periphery.sol:435:        require(amounts[amounts.length - 1] >= amountOutMin, "BaseV1Router: INSUFFICIENT_OUTPUT_AMOUNT");
lending-market-v2/contracts/Stableswap/BaseV1-periphery.sol:448:        require(amounts[amounts.length - 1] >= amountOutMin, "BaseV1Router: INSUFFICIENT_OUTPUT_AMOUNT");
lending-market-v2/contracts/Stableswap/BaseV1-periphery.sol:470:        require(success, "TransferHelper: ETH_TRANSFER_FAILED");
lending-market-v2/contracts/Treasury/TreasuryDelegator.sol:31:        require(msg.sender == admin, "GovernorBravoDelegator::setImplementation: admin only");
lending-market-v2/contracts/Treasury/TreasuryDelegator.sol:32:        require(implementation_ != address(0), "GovernorBravoDelegator::setImplementation: invalid implementation address");
lending-market-v2/contracts/Treasury/TreasuryDelegator.sol:121:        require(msg.value == 0, "TreasuryDelegator::fallback:cannot send value to fallback");
lending-market-v2/contracts/BaseJumpRateModelV2.sol:68:        require(msg.sender == owner, "only the owner may call this function.");
lending-market-v2/contracts/CDaiDelegate.sol:32:        require(msg.sender == admin, "only the admin may initialize the implementation");
lending-market-v2/contracts/CDaiDelegate.sol:49:        require(address(dai) == underlying, "DAI must be the same as underlying");
lending-market-v2/contracts/CDaiDelegate.sol:74:        require(msg.sender == admin, "only the admin may abandon the implementation");
lending-market-v2/contracts/CDaiDelegate.sol:134:        require(token.transferFrom(from, address(this), amount), "unexpected EIP-20 transfer in return");
lending-market-v2/contracts/CErc20.sol:129:        require(msg.sender == admin, "CErc20::sweepToken: only admin can sweep tokens");
lending-market-v2/contracts/CErc20.sol:130:        require(address(token) != underlying, "CErc20::sweepToken: can not sweep underlying token");
lending-market-v2/contracts/CErc20.sol:229:        require(msg.sender == admin, "only the admin may set the comp-like delegate");
lending-market-v2/contracts/CErc20Delegate.sol:30:        require(msg.sender == admin, "only the admin may call _becomeImplementation");
lending-market-v2/contracts/CErc20Delegate.sol:42:        require(msg.sender == admin, "only the admin may call _resignImplementation");
lending-market-v2/contracts/CErc20Delegator.sol:63:        require(msg.sender == admin, "CErc20Delegator::_setImplementation: Caller must be admin");
lending-market-v2/contracts/CErc20Delegator.sol:463:        require(msg.value == 0,"CErc20Delegator:fallback: cannot send value to fallback");
lending-market-v2/contracts/CNote.sol:17:        require(msg.sender == admin, "CNote::_setAccountantContract:Only admin may call this function");
lending-market-v2/contracts/CNote.sol:105:            require(balanceCur == 0, "Accountant has not been correctly supplied");
lending-market-v2/contracts/CNote.sol:148:        require(token.balanceOf(address(this)) == 0, "cNote::doTransferOut: TransferOut Failed");
lending-market-v2/contracts/CToken.sol:33:        require(msg.sender == admin, "only admin may initialize the market");
lending-market-v2/contracts/CToken.sol:34:        require(accrualBlockNumber == 0 && borrowIndex == 0, "market may only be initialized once");
lending-market-v2/contracts/CToken.sol:37:        require(initialExchangeRateMantissa > 0, "initial exchange rate must be greater than zero.");
lending-market-v2/contracts/CToken.sol:50:        require(err == NO_ERROR, "setting interest rate model failed");
lending-market-v2/contracts/CToken.sol:485:        require(redeemTokensIn == 0 || redeemAmountIn == 0, "one of redeemTokensIn or redeemAmountIn must be zero");
lending-market-v2/contracts/CToken.sol:765:        require(amountSeizeError == NO_ERROR, "LIQUIDATE_COMPTROLLER_CALCULATE_AMOUNT_SEIZE_FAILED");
lending-market-v2/contracts/DAIInterestRateModelV3.sol:55:        require(msg.sender == owner, "only the owner may call this function.");
lending-market-v2/contracts/ERC20.sol:183:        require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
lending-market-v2/contracts/ERC20.sol:208:        require(from != address(0), "ERC20: transfer from the zero address");
lending-market-v2/contracts/ERC20.sol:209:        require(to != address(0), "ERC20: transfer to the zero address");
lending-market-v2/contracts/ERC20.sol:212:        require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
lending-market-v2/contracts/ERC20.sol:248:        require(account != address(0), "ERC20: burn from the zero address");
lending-market-v2/contracts/ERC20.sol:251:        require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
lending-market-v2/contracts/ERC20.sol:276:        require(owner != address(0), "ERC20: approve from the zero address");
lending-market-v2/contracts/ERC20.sol:277:        require(spender != address(0), "ERC20: approve to the zero address");
lending-market-v2/contracts/NoteInterest.sol:167:        require(msg.sender == admin, "only the admin may set the base rate");
lending-market-v2/contracts/NoteInterest.sol:180:        require(msg.sender == admin, "only the admin may set the adjuster coefficient");
lending-market-v2/contracts/NoteInterest.sol:193:        require(msg.sender == admin, "only the admin may set the update frequency");
lending-market-v2/contracts/SafeMath.sol:98:        require(c / a == b, "SafeMath: multiplication overflow");
lending-market-v2/contracts/Timelock.sol:28:        require(delay_ >= MINIMUM_DELAY, "Timelock::constructor: Delay must exceed minimum delay.");
lending-market-v2/contracts/Timelock.sol:29:        require(delay_ <= MAXIMUM_DELAY, "Timelock::setDelay: Delay must not exceed maximum delay.");
lending-market-v2/contracts/Timelock.sol:38:        require(msg.sender == address(this), "Timelock::setDelay: Call must come from Timelock.");
lending-market-v2/contracts/Timelock.sol:39:        require(delay_ >= MINIMUM_DELAY, "Timelock::setDelay: Delay must exceed minimum delay.");
lending-market-v2/contracts/Timelock.sol:40:        require(delay_ <= MAXIMUM_DELAY, "Timelock::setDelay: Delay must not exceed maximum delay.");
lending-market-v2/contracts/Timelock.sol:47:        require(msg.sender == pendingAdmin, "Timelock::acceptAdmin: Call must come from pendingAdmin.");
lending-market-v2/contracts/Timelock.sol:55:        require(msg.sender == address(this), "Timelock::setPendingAdmin: Call must come from Timelock.");
lending-market-v2/contracts/Timelock.sol:62:        require(msg.sender == admin, "Timelock::queueTransaction: Call must come from admin.");
lending-market-v2/contracts/Timelock.sol:63:        require(eta >= getBlockTimestamp().add(delay), "Timelock::queueTransaction: Estimated execution block must satisfy delay.");
lending-market-v2/contracts/Timelock.sol:73:        require(msg.sender == admin, "Timelock::cancelTransaction: Call must come from admin.");
lending-market-v2/contracts/Timelock.sol:82:        require(msg.sender == admin, "Timelock::executeTransaction: Call must come from admin.");
lending-market-v2/contracts/Timelock.sol:85:        require(queuedTransactions[txHash], "Timelock::executeTransaction: Transaction hasn't been queued.");
lending-market-v2/contracts/Timelock.sol:86:        require(getBlockTimestamp() >= eta, "Timelock::executeTransaction: Transaction hasn't surpassed time lock.");
lending-market-v2/contracts/Timelock.sol:87:        require(getBlockTimestamp() <= eta.add(GRACE_PERIOD), "Timelock::executeTransaction: Transaction is stale.");
lending-market-v2/contracts/Timelock.sol:101:        require(success, "Timelock::executeTransaction: Transaction execution reverted.");
lending-market-v2/contracts/WETH.sol:29:        require(_balanceOf[msg.sender] >= wamount, "sender balance insufficient for withdrawal");
lending-market-v2/contracts/WETH.sol:72:            require(_allowance[src][msg.sender] >= wad, "WETH::transferFrom:allowance insufficient");
lending-market-v2/contracts/WETH.sol:89:        require(owner != address(0), "ERC20: approve from the zero address");
lending-market-v2/contracts/WETH.sol:90:        require(spender != address(0), "ERC20: approve to the zero address");
manifest-v2/contracts/ERC20Burnable.sol:37:        require(currentAllowance >= amount, "ERC20: burn amount exceeds allowance");
manifest-v2/contracts/ERC20MinterBurnerDecimals.sol:72:      require(hasRole(MINTER_ROLE, _msgSender()), "ERC20MinterBurnerDecimals: must have minter role to mint");
manifest-v2/contracts/ERC20MinterBurnerDecimals.sol:86:      require(hasRole(BURNER_ROLE, _msgSender()), "ERC20MinterBurnerDecimals: must have burner role to burn");
manifest-v2/contracts/ERC20MinterBurnerDecimals.sol:100:      require(hasRole(PAUSER_ROLE, _msgSender()), "ERC20MinterBurnerDecimals: must have pauser role to pause");
manifest-v2/contracts/ERC20MinterBurnerDecimals.sol:114:      require(hasRole(PAUSER_ROLE, _msgSender()), "ERC20MinterBurnerDecimals: must have pauser role to unpause");

Consider shortening the revert strings to fit in 32 bytes.

3. SafeMath is not needed when using Solidity version 0.8+

Solidity version 0.8+ already implements overflow and underflow checks by default. Using the SafeMath library from OpenZeppelin (which is more gas expensive than the 0.8+ overflow checks) is therefore redundant.

Consider using the built-in checks instead of SafeMath and remove SafeMath here:

lending-market-v2/contracts/NoteInterest.sol:1:pragma solidity ^0.8.10;
lending-market-v2/contracts/NoteInterest.sol:5:import "./SafeMath.sol";
lending-market-v2/contracts/NoteInterest.sol:15:    using SafeMath for uint;
lending-market-v2/contracts/Timelock.sol:2:pragma solidity ^0.8.10;
lending-market-v2/contracts/Timelock.sol:4:import "./SafeMath.sol";
lending-market-v2/contracts/Timelock.sol:7:    using SafeMath for uint;

4. Boolean comparisons

Comparing to a constant (true or false) is a bit more expensive than directly checking the returned boolean value. Consider using if(directValue) instead of if(directValue == true) and if(!directValue) instead of if(directValue == false) here:

lending-market-v2/contracts/Governance/GovernorAlpha.sol:266:        require(receipt.hasVoted == false, "GovernorAlpha::_castVote: voter already voted");

5. Pre-Solidity 0.8.13: > 0 is less efficient than != 0 for unsigned integers (with proof)

Up until Solidity 0.8.13: != 0 costs less gas compared to > 0 for unsigned integers in require statements with the optimizer enabled (6 gas)

Proof: While it may seem that > 0 is cheaper than !=, this is only true without the optimizer enabled and outside a require statement. If you enable the optimizer AND you're in a require statement, this will save gas. You can see this tweet for more proofs: https://twitter.com/gzeon/status/1485428085885640706

Consider changing > 0 with != 0 here:

lending-market-v2/contracts/Governance/GovernorAlpha.sol:228:        require(proposalCount >= proposalId && proposalId > 0, "GovernorAlpha::state: invalid proposal id");
lending-market-v2/contracts/Stableswap/BaseV1-core.sol:256:        require(liquidity > 0, 'ILM'); // BaseV1: INSUFFICIENT_LIQUIDITY_MINTED
lending-market-v2/contracts/Stableswap/BaseV1-core.sol:275:        require(amount0 > 0 && amount1 > 0, 'ILB'); // BaseV1: INSUFFICIENT_LIQUIDITY_BURNED
lending-market-v2/contracts/Stableswap/BaseV1-core.sol:289:        require(amount0Out > 0 || amount1Out > 0, 'IOA'); // BaseV1: INSUFFICIENT_OUTPUT_AMOUNT
lending-market-v2/contracts/Stableswap/BaseV1-core.sol:306:        require(amount0In > 0 || amount1In > 0, 'IIA'); // BaseV1: INSUFFICIENT_INPUT_AMOUNT
lending-market-v2/contracts/Stableswap/BaseV1-core.sol:468:        require(token.code.length > 0);
lending-market-v2/contracts/Stableswap/BaseV1-periphery.sol:122:        require(amountA > 0, "BaseV1Router: INSUFFICIENT_AMOUNT");
lending-market-v2/contracts/Stableswap/BaseV1-periphery.sol:123:        require(reserveA > 0 && reserveB > 0, "BaseV1Router: INSUFFICIENT_LIQUIDITY");
lending-market-v2/contracts/Stableswap/BaseV1-periphery.sol:474:        require(token.code.length > 0);
lending-market-v2/contracts/Stableswap/BaseV1-periphery.sol:481:        require(token.code.length > 0, "token code length failure");
lending-market-v2/contracts/CToken.sol:37:        require(initialExchangeRateMantissa > 0, "initial exchange rate must be greater than zero.");
lending-market-v2/contracts/SafeMath.sol:154:        require(b > 0, errorMessage);

Also, please enable the Optimizer.

6. >= is cheaper than > (and <= cheaper than <)

Strict inequalities (>) are more expensive than non-strict ones (>=). This is due to some supplementary checks (ISZERO, 3 gas). This also holds true between <= and <.

Consider replacing strict inequalities with non-strict ones to save some gas here:

lending-market-v2/contracts/Accountant/AccountantDelegate.sol:92:       cNoteToSweep = (cNoteToSweep > cNoteBal) ? cNoteBal :  cNoteToSweep; 
lending-market-v2/contracts/Stableswap/BaseV1-core.sol:18:        return a < b ? a : b;
lending-market-v2/contracts/Stableswap/BaseV1-core.sol:304:        uint amount0In = _balance0 > _reserve0 - amount0Out ? _balance0 - (_reserve0 - amount0Out) : 0;
lending-market-v2/contracts/Stableswap/BaseV1-core.sol:305:        uint amount1In = _balance1 > _reserve1 - amount1Out ? _balance1 - (_reserve1 - amount1Out) : 0;
lending-market-v2/contracts/Stableswap/BaseV1-core.sol:525:        (address token0, address token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
lending-market-v2/contracts/Stableswap/BaseV1-periphery.sol:43:        return a < b ? a : b;
lending-market-v2/contracts/Stableswap/BaseV1-periphery.sol:105:        (token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);

7. Splitting require() statements that use && saves gas

If you're using the Optimizer at 200, instead of using the && operator in a single require statement to check multiple conditions, Consider using multiple require statements with 1 condition per require statement:

lending-market-v2/contracts/Governance/GovernorAlpha.sol:138:        require(targets.length == values.length && targets.length == signatures.length && targets.length == calldatas.length, "GovernorAlpha::propose: proposal function information arity mismatch");
lending-market-v2/contracts/Governance/GovernorAlpha.sol:228:        require(proposalCount >= proposalId && proposalId > 0, "GovernorAlpha::state: invalid proposal id");
lending-market-v2/contracts/Governance/GovernorBravoDelegate.sol:42:        require(unigovProposal.targets.length == unigovProposal.values.length && 
lending-market-v2/contracts/Stableswap/BaseV1-core.sol:275:        require(amount0 > 0 && amount1 > 0, 'ILB'); // BaseV1: INSUFFICIENT_LIQUIDITY_BURNED
lending-market-v2/contracts/Stableswap/BaseV1-core.sol:291:        require(amount0Out < _reserve0 && amount1Out < _reserve1, 'IL'); // BaseV1: INSUFFICIENT_LIQUIDITY
lending-market-v2/contracts/Stableswap/BaseV1-core.sol:297:        require(to != _token0 && to != _token1, 'IT'); // BaseV1: INVALID_TO
lending-market-v2/contracts/Stableswap/BaseV1-core.sol:434:        require(recoveredAddress != address(0) && recoveredAddress == owner, 'BaseV1: INVALID_SIGNATURE');
lending-market-v2/contracts/Stableswap/BaseV1-core.sol:471:        require(success && (data.length == 0 || abi.decode(data, (bool))));
lending-market-v2/contracts/Stableswap/BaseV1-periphery.sol:123:        require(reserveA > 0 && reserveB > 0, "BaseV1Router: INSUFFICIENT_LIQUIDITY");
lending-market-v2/contracts/Stableswap/BaseV1-periphery.sol:477:        require(success && (data.length == 0 || abi.decode(data, (bool))));
lending-market-v2/contracts/CToken.sol:34:        require(accrualBlockNumber == 0 && borrowIndex == 0, "market may only be initialized once");

Please, note that this might not hold true at a higher number of runs for the Optimizer (10k). However, it indeed is true at 200.

8. Use shift right/left instead of division/multiplication if possible

While the DIV / MUL opcode uses 5 gas, the SHR / SHL opcode only uses 3 gas. Furthermore, beware that Solidity's division operation also includes a division-by-0 prevention which is bypassed using shifting. Eventually, overflow checks are never performed for shift operations as they are done for arithmetic operations. Instead, the result is always truncated.

Affected code:

lending-market-v2/contracts/Governance/Comp.sol:210:            uint32 center = upper - (upper - lower) / 2; // ceil, avoiding overflow
lending-market-v2/contracts/Stableswap/BaseV1-core.sol:23:            uint x = y / 2 + 1;
lending-market-v2/contracts/Stableswap/BaseV1-core.sol:26:                x = (y / x + x) / 2;
lending-market-v2/contracts/Stableswap/BaseV1-periphery.sol:48:            uint x = y / 2 + 1;
lending-market-v2/contracts/Stableswap/BaseV1-periphery.sol:51:                x = (y / x + x) / 2;
lending-market-v2/contracts/ExponentialNoError.sol:14:    uint constant halfExpScale = expScale/2;
manifest-v2/contracts/ERC20DirectBalanceManipulation.sol:18:    uint256 half = amount / 2;

9. <array>.length should not be looked up in every loop of a for-loop

Reading array length at each iteration of the loop consumes more gas than necessary.

In the best case scenario (length read on a memory variable), caching the array length in the stack saves around 3 gas per iteration. In the worst case scenario (external calls at each iteration), the amount of gas wasted can be massive.

Here, Consider storing the array's length in a variable before the for-loop, and use this new variable instead:

lending-market-v2/contracts/Governance/GovernorAlpha.sol:181:        for (uint i = 0; i < proposal.targets.length; i++) {
lending-market-v2/contracts/Governance/GovernorAlpha.sol:197:        for (uint i = 0; i < proposal.targets.length; i++) {
lending-market-v2/contracts/Governance/GovernorAlpha.sol:211:        for (uint i = 0; i < proposal.targets.length; i++) {
lending-market-v2/contracts/Governance/GovernorBravoDelegate.sol:66:        for (uint i = 0; i < newProposal.targets.length; i++) {
lending-market-v2/contracts/Governance/GovernorBravoDelegate.sol:88:        for (uint i = 0; i < proposal.targets.length; i++) {
lending-market-v2/contracts/Stableswap/BaseV1-core.sol:210:        for (uint i = 0; i < _prices.length; i++) {
lending-market-v2/contracts/Stableswap/BaseV1-periphery.sol:154:        for (uint i = 0; i < routes.length; i++) {
lending-market-v2/contracts/Stableswap/BaseV1-periphery.sol:380:        for (uint i = 0; i < routes.length; i++) {

10. ++i costs less gas compared to i++ or i += 1 (same for --i vs i-- or i -= 1)

Pre-increments and pre-decrements are cheaper.

For a uint256 i variable, the following is true with the Optimizer enabled at 10k:

Increment:

Decrement:

Note that post-increments (or post-decrements) return the old value before incrementing or decrementing, hence the name post-increment:

uint i = 1;  
uint j = 2;
require(j == i++, "This will be false as i is incremented after the comparison");

However, pre-increments (or pre-decrements) return the new value:

uint i = 1;  
uint j = 2;
require(j == ++i, "This will be true as i is incremented before the comparison");

In the pre-increment case, the compiler has to create a temporary variable (when used) for returning 1 instead of 2.

Affected code:

lending-market-v2/contracts/Governance/GovernorAlpha.sol:152:        proposalCount++;
lending-market-v2/contracts/Governance/GovernorAlpha.sol:181:        for (uint i = 0; i < proposal.targets.length; i++) {
lending-market-v2/contracts/Governance/GovernorAlpha.sol:197:        for (uint i = 0; i < proposal.targets.length; i++) {
lending-market-v2/contracts/Governance/GovernorAlpha.sol:211:        for (uint i = 0; i < proposal.targets.length; i++) {
lending-market-v2/contracts/Governance/GovernorBravoDelegate.sol:66:        for (uint i = 0; i < newProposal.targets.length; i++) {
lending-market-v2/contracts/Governance/GovernorBravoDelegate.sol:88:        for (uint i = 0; i < proposal.targets.length; i++) {
lending-market-v2/contracts/Stableswap/BaseV1-core.sol:210:        for (uint i = 0; i < _prices.length; i++) {
lending-market-v2/contracts/Stableswap/BaseV1-core.sol:340:        for (uint i = 0; i < 255; i++) {
lending-market-v2/contracts/Stableswap/BaseV1-periphery.sol:154:        for (uint i = 0; i < routes.length; i++) {
lending-market-v2/contracts/Stableswap/BaseV1-periphery.sol:380:        for (uint i = 0; i < routes.length; i++) {

Consider using pre-increments and pre-decrements where they are relevant (meaning: not where post-increments/decrements logic are relevant).

11. Increments/decrements can be unchecked in for-loops

In Solidity 0.8+, there's a default overflow check on unsigned integers. It's possible to uncheck this in for-loops and save some gas at each iteration, but at the cost of some code readability, as this uncheck cannot be made inline.

ethereum/solidity#10695

Affected code:

lending-market-v2/contracts/Governance/GovernorAlpha.sol:181:        for (uint i = 0; i < proposal.targets.length; i++) {
lending-market-v2/contracts/Governance/GovernorAlpha.sol:197:        for (uint i = 0; i < proposal.targets.length; i++) {
lending-market-v2/contracts/Governance/GovernorAlpha.sol:211:        for (uint i = 0; i < proposal.targets.length; i++) {
lending-market-v2/contracts/Governance/GovernorBravoDelegate.sol:66:        for (uint i = 0; i < newProposal.targets.length; i++) {
lending-market-v2/contracts/Governance/GovernorBravoDelegate.sol:88:        for (uint i = 0; i < proposal.targets.length; i++) {
lending-market-v2/contracts/Stableswap/BaseV1-core.sol:210:        for (uint i = 0; i < _prices.length; i++) {
lending-market-v2/contracts/Stableswap/BaseV1-core.sol:340:        for (uint i = 0; i < 255; i++) {
lending-market-v2/contracts/Stableswap/BaseV1-periphery.sol:154:        for (uint i = 0; i < routes.length; i++) {
lending-market-v2/contracts/Stableswap/BaseV1-periphery.sol:380:        for (uint i = 0; i < routes.length; i++) {

The change would be:

- for (uint256 i; i < numIterations; i++) {
+ for (uint256 i; i < numIterations;) {
 // ...  
+   unchecked { ++i; }
}  

The same can be applied with decrements (which should use break when i == 0).

The risk of overflow is non-existant for uint256 here.

12. abi.encode() is less efficient than abi.encodePacked()

Changing abi.encode function to abi.encodePacked can save gas since the abi.encode function pads extra null bytes at the end of the call data, which is unnecessary. Also, in general, abi.encodePacked is more gas-efficient (see Solidity-Encode-Gas-Comparison).

Consider using abi.encodePacked() here:

lending-market-v2/contracts/Governance/GovernorAlpha.sol:294:        timelock.queueTransaction(address(timelock), 0, "setPendingAdmin(address)", abi.encode(newPendingAdmin), eta);

Notice that this is already used at other places for a similar situation:

lending-market-v2/contracts/Stableswap/BaseV1-core.sol:111:            name = string(abi.encodePacked("StableV1 AMM - ", erc20(_token0).symbol(), "/", erc20(_token1).symbol()));
lending-market-v2/contracts/Stableswap/BaseV1-core.sol:112:            symbol = string(abi.encodePacked("sAMM-", erc20(_token0).symbol(), "/", erc20(_token1).symbol()));
lending-market-v2/contracts/Stableswap/BaseV1-core.sol:114:            name = string(abi.encodePacked("VolatileV1 AMM - ", erc20(_token0).symbol(), "/", erc20(_token1).symbol()));
lending-market-v2/contracts/Stableswap/BaseV1-core.sol:115:            symbol = string(abi.encodePacked("vAMM-", erc20(_token0).symbol(), "/", erc20(_token1).symbol()));

13. It costs more gas to initialize variables with their default value than letting the default value be applied

If a variable is not set/initialized, it is assumed to have the default value (0 for uint, false for bool, address(0) for address...). Explicitly initializing it with its default value is an anti-pattern and wastes gas.

As an example: for (uint256 i = 0; i < numIterations; ++i) { should be replaced with for (uint256 i; i < numIterations; ++i) {

Affected code:

lending-market-v2/contracts/Governance/Comp.sol:207:        uint32 lower = 0;
lending-market-v2/contracts/Governance/GovernorAlpha.sol:181:        for (uint i = 0; i < proposal.targets.length; i++) {
lending-market-v2/contracts/Governance/GovernorAlpha.sol:197:        for (uint i = 0; i < proposal.targets.length; i++) {
lending-market-v2/contracts/Governance/GovernorAlpha.sol:211:        for (uint i = 0; i < proposal.targets.length; i++) {
lending-market-v2/contracts/Governance/GovernorBravoDelegate.sol:66:        for (uint i = 0; i < newProposal.targets.length; i++) {
lending-market-v2/contracts/Governance/GovernorBravoDelegate.sol:88:        for (uint i = 0; i < proposal.targets.length; i++) {
lending-market-v2/contracts/Stableswap/BaseV1-core.sol:48:    uint public totalSupply = 0;
lending-market-v2/contracts/Stableswap/BaseV1-core.sol:210:        for (uint i = 0; i < _prices.length; i++) {
lending-market-v2/contracts/Stableswap/BaseV1-core.sol:226:        uint nextIndex = 0;
lending-market-v2/contracts/Stableswap/BaseV1-core.sol:227:        uint index = 0;
lending-market-v2/contracts/Stableswap/BaseV1-core.sol:340:        for (uint i = 0; i < 255; i++) {
lending-market-v2/contracts/Stableswap/BaseV1-periphery.sol:154:        for (uint i = 0; i < routes.length; i++) {
lending-market-v2/contracts/Stableswap/BaseV1-periphery.sol:176:        uint _totalSupply = 0;
lending-market-v2/contracts/Stableswap/BaseV1-periphery.sol:380:        for (uint i = 0; i < routes.length; i++) {
lending-market-v2/contracts/CToken.sol:82:        uint startingAllowance = 0;

Consider removing explicit initializations for default values.

14. Use Custom Errors instead of Revert Strings to save Gas

Solidity 0.8.4 introduced custom errors. They are more gas efficient than revert strings, when it comes to deploy cost as well as runtime cost when the revert condition is met. Use custom errors instead of revert strings for gas savings.

Custom errors from Solidity 0.8.4 are cheaper than revert strings (cheaper deployment cost and runtime cost when the revert condition is met)

Source: https://blog.soliditylang.org/2021/04/21/custom-errors/:

Starting from Solidity v0.8.4, there is a convenient and gas-efficient way to explain to users why an operation failed through the use of custom errors. Until now, you could already use strings to give more information about failures (e.g., revert("Insufficient funds.");), but they are rather expensive, especially when it comes to deploy cost, and it is difficult to use dynamic information in them.

Custom errors are defined using the error statement, which can be used inside and outside of contracts (including interfaces and libraries).

Consider replacing all revert strings with custom errors in the solution.


lending-market-v2/contracts/Accountant/AccountantDelegate.sol:87:       require(cNoteAmt >= noteDiff, "AccountantDelegate::sweepInterest:Error calculating interest to sweep");
lending-market-v2/contracts/Accountant/AccountantDelegate.sol:101:      require(cnote.balanceOf(treasury) == 0, "AccountantDelegate::sweepInterestError");
lending-market-v2/contracts/Accountant/AccountantDelegator.sol:22:        require(admin_ != address(0));
lending-market-v2/contracts/Accountant/AccountantDelegator.sol:42:        require(msg.sender == admin, "AccountantDelegator::_setImplementation: admin only");
lending-market-v2/contracts/Accountant/AccountantDelegator.sol:43:        require(implementation_ != address(0), "AccountantDelegator::_setImplementation: invalid implementation address");
lending-market-v2/contracts/Accountant/AccountantDelegator.sol:123:        require(msg.value == 0,"AccountantDelegator:fallback: cannot send value to fallback");
lending-market-v2/contracts/Governance/Comp.sol:166:        require(signatory != address(0), "Comp::delegateBySig: invalid signature");
lending-market-v2/contracts/Governance/Comp.sol:167:        require(nonce == nonces[signatory]++, "Comp::delegateBySig: invalid nonce");
lending-market-v2/contracts/Governance/Comp.sol:168:        require(block.timestamp <= expiry, "Comp::delegateBySig: signature expired");
lending-market-v2/contracts/Governance/Comp.sol:190:        require(blockNumber < block.number, "Comp::getPriorVotes: not yet determined");
lending-market-v2/contracts/Governance/Comp.sol:234:        require(src != address(0), "Comp::_transferTokens: cannot transfer from the zero address");
lending-market-v2/contracts/Governance/Comp.sol:235:        require(dst != address(0), "Comp::_transferTokens: cannot transfer to the zero address");
lending-market-v2/contracts/Governance/Comp.sol:276:        require(n < 2**32, errorMessage);
lending-market-v2/contracts/Governance/Comp.sol:281:        require(n < 2**96, errorMessage);
lending-market-v2/contracts/Governance/Comp.sol:287:        require(c >= a, errorMessage);
lending-market-v2/contracts/Governance/Comp.sol:292:        require(b <= a, errorMessage);
lending-market-v2/contracts/Governance/GovernorAlpha.sol:137:        require(comp.getPriorVotes(msg.sender, sub256(block.number, 1)) > proposalThreshold(), "GovernorAlpha::propose: proposer votes below proposal threshold");
lending-market-v2/contracts/Governance/GovernorAlpha.sol:138:        require(targets.length == values.length && targets.length == signatures.length && targets.length == calldatas.length, "GovernorAlpha::propose: proposal function information arity mismatch");
lending-market-v2/contracts/Governance/GovernorAlpha.sol:139:        require(targets.length != 0, "GovernorAlpha::propose: must provide actions");
lending-market-v2/contracts/Governance/GovernorAlpha.sol:140:        require(targets.length <= proposalMaxOperations(), "GovernorAlpha::propose: too many actions");
lending-market-v2/contracts/Governance/GovernorAlpha.sol:145:          require(proposersLatestProposalState != ProposalState.Active, "GovernorAlpha::propose: one live proposal per proposer, found an already active proposal");
lending-market-v2/contracts/Governance/GovernorAlpha.sol:146:          require(proposersLatestProposalState != ProposalState.Pending, "GovernorAlpha::propose: one live proposal per proposer, found an already pending proposal");
lending-market-v2/contracts/Governance/GovernorAlpha.sol:156:        require(newProposal.id == 0, "GovernorAlpha::propose: ProposalID collsion");
lending-market-v2/contracts/Governance/GovernorAlpha.sol:178:        require(state(proposalId) == ProposalState.Succeeded, "GovernorAlpha::queue: proposal can only be queued if it is succeeded");
lending-market-v2/contracts/Governance/GovernorAlpha.sol:189:        require(!timelock.queuedTransactions(keccak256(abi.encode(target, value, signature, data, eta))), "GovernorAlpha::_queueOrRevert: proposal action already queued at eta");
lending-market-v2/contracts/Governance/GovernorAlpha.sol:194:        require(state(proposalId) == ProposalState.Queued, "GovernorAlpha::execute: proposal can only be executed if it is queued");
lending-market-v2/contracts/Governance/GovernorAlpha.sol:205:        require(state != ProposalState.Executed, "GovernorAlpha::cancel: cannot cancel executed proposal");
lending-market-v2/contracts/Governance/GovernorAlpha.sol:208:        require(msg.sender == guardian || comp.getPriorVotes(proposal.proposer, sub256(block.number, 1)) < proposalThreshold(), "GovernorAlpha::cancel: proposer above threshold");
lending-market-v2/contracts/Governance/GovernorAlpha.sol:228:        require(proposalCount >= proposalId && proposalId > 0, "GovernorAlpha::state: invalid proposal id");
lending-market-v2/contracts/Governance/GovernorAlpha.sol:258:        require(signatory != address(0), "GovernorAlpha::castVoteBySig: invalid signature");
lending-market-v2/contracts/Governance/GovernorAlpha.sol:263:        require(state(proposalId) == ProposalState.Active, "GovernorAlpha::_castVote: voting is closed");
lending-market-v2/contracts/Governance/GovernorAlpha.sol:266:        require(receipt.hasVoted == false, "GovernorAlpha::_castVote: voter already voted");
lending-market-v2/contracts/Governance/GovernorAlpha.sol:283:        require(msg.sender == guardian, "GovernorAlpha::__acceptAdmin: sender must be gov guardian");
lending-market-v2/contracts/Governance/GovernorAlpha.sol:288:        require(msg.sender == guardian, "GovernorAlpha::__abdicate: sender must be gov guardian");
lending-market-v2/contracts/Governance/GovernorAlpha.sol:293:        require(msg.sender == guardian, "GovernorAlpha::__queueSetTimelockPendingAdmin: sender must be gov guardian");
lending-market-v2/contracts/Governance/GovernorAlpha.sol:298:        require(msg.sender == guardian, "GovernorAlpha::__executeSetTimelockPendingAdmin: sender must be gov guardian");
lending-market-v2/contracts/Governance/GovernorAlpha.sol:304:        require(c >= a, "addition overflow");
lending-market-v2/contracts/Governance/GovernorAlpha.sol:309:        require(b <= a, "subtraction underflow");
lending-market-v2/contracts/Governance/GovernorBravoDelegate.sol:25:        require(address(timelock) == address(0), "GovernorBravo::initialize: can only initialize once");
lending-market-v2/contracts/Governance/GovernorBravoDelegate.sol:26:        require(msg.sender == admin, "GovernorBravo::initialize: admin only");
lending-market-v2/contracts/Governance/GovernorBravoDelegate.sol:27:        require(timelock_ != address(0), "GovernorBravo::initialize: invalid timelock address");
lending-market-v2/contracts/Governance/GovernorBravoDelegate.sol:42:        require(unigovProposal.targets.length == unigovProposal.values.length && 
lending-market-v2/contracts/Governance/GovernorBravoDelegate.sol:46:        require(unigovProposal.targets.length != 0, "GovernorBravo::propose: must provide actions");
lending-market-v2/contracts/Governance/GovernorBravoDelegate.sol:47:        require(unigovProposal.targets.length <= proposalMaxOperations, "GovernorBravo::propose: too many actions");
lending-market-v2/contracts/Governance/GovernorBravoDelegate.sol:53:        require(proposals[unigovProposal.id].id == 0);
lending-market-v2/contracts/Governance/GovernorBravoDelegate.sol:76:        require(!timelock.queuedTransactions(keccak256(abi.encode(target, value, signature, data, eta))), "GovernorBravo::queueOrRevertInternal: identical proposal action already queued at eta");
lending-market-v2/contracts/Governance/GovernorBravoDelegate.sol:85:        require(state(proposalId) == ProposalState.Queued, "GovernorBravo::execute: proposal can only be executed if it is queued");
lending-market-v2/contracts/Governance/GovernorBravoDelegate.sol:128:        require(msg.sender == admin, "GovernorBravo::_initiate: admin only");
lending-market-v2/contracts/Governance/GovernorBravoDelegate.sol:129:        require(initialProposalId == 0, "GovernorBravo::_initiate: can only initiate once");
lending-market-v2/contracts/Governance/GovernorBravoDelegate.sol:140:        require(msg.sender == admin, "GovernorBravo:_setPendingAdmin: admin only");
lending-market-v2/contracts/Governance/GovernorBravoDelegate.sol:158:        require(msg.sender == pendingAdmin, "GovernorBravo:_acceptAdmin: pending admin only");
lending-market-v2/contracts/Governance/GovernorBravoDelegate.sol:176:        require(c >= a, "addition overflow");
lending-market-v2/contracts/Governance/GovernorBravoDelegate.sol:181:        require(b <= a, "subtraction underflow");
lending-market-v2/contracts/Governance/GovernorBravoDelegator.sol:27:        require(msg.sender == admin, "GovernorBravoDelegator::_setImplementation: admin only");
lending-market-v2/contracts/Governance/GovernorBravoDelegator.sol:28:        require(implementation_ != address(0), "GovernorBravoDelegator::_setImplementation: invalid implementation address");
lending-market-v2/contracts/Governance/GovernorBravoDelegator.sol:40:        require(msg.sender == admin, "GovernorBravoDelegator::_initiateDelegated: admin only");
lending-market-v2/contracts/Governance/GovernorBravoDelegator.sol:48:        require(msg.sender == admin, "GovernorBravoDelegator::_acceptInitialAdminDelegated: admin only");
lending-market-v2/contracts/Governance/GovernorBravoDelegator.sol:56:        require(msg.sender == admin, "GovernorBravoDelegator::_setPendingAdminDelegated: admin only");
lending-market-v2/contracts/Stableswap/BaseV1-core.sol:127:        require(_unlocked == 1);
lending-market-v2/contracts/Stableswap/BaseV1-core.sol:256:        require(liquidity > 0, 'ILM'); // BaseV1: INSUFFICIENT_LIQUIDITY_MINTED
lending-market-v2/contracts/Stableswap/BaseV1-core.sol:275:        require(amount0 > 0 && amount1 > 0, 'ILB'); // BaseV1: INSUFFICIENT_LIQUIDITY_BURNED
lending-market-v2/contracts/Stableswap/BaseV1-core.sol:288:        require(!BaseV1Factory(factory).isPaused());
lending-market-v2/contracts/Stableswap/BaseV1-core.sol:289:        require(amount0Out > 0 || amount1Out > 0, 'IOA'); // BaseV1: INSUFFICIENT_OUTPUT_AMOUNT
lending-market-v2/contracts/Stableswap/BaseV1-core.sol:291:        require(amount0Out < _reserve0 && amount1Out < _reserve1, 'IL'); // BaseV1: INSUFFICIENT_LIQUIDITY
lending-market-v2/contracts/Stableswap/BaseV1-core.sol:297:        require(to != _token0 && to != _token1, 'IT'); // BaseV1: INVALID_TO
lending-market-v2/contracts/Stableswap/BaseV1-core.sol:306:        require(amount0In > 0 || amount1In > 0, 'IIA'); // BaseV1: INSUFFICIENT_INPUT_AMOUNT
lending-market-v2/contracts/Stableswap/BaseV1-core.sol:312:        require(_k(_balance0, _balance1) >= _k(_reserve0, _reserve1), 'K'); // BaseV1: K
lending-market-v2/contracts/Stableswap/BaseV1-core.sol:416:        require(deadline >= block.timestamp, 'BaseV1: EXPIRED');
lending-market-v2/contracts/Stableswap/BaseV1-core.sol:434:        require(recoveredAddress != address(0) && recoveredAddress == owner, 'BaseV1: INVALID_SIGNATURE');
lending-market-v2/contracts/Stableswap/BaseV1-core.sol:468:        require(token.code.length > 0);
lending-market-v2/contracts/Stableswap/BaseV1-core.sol:471:        require(success && (data.length == 0 || abi.decode(data, (bool))));
lending-market-v2/contracts/Stableswap/BaseV1-core.sol:501:        require(msg.sender == pauser);
lending-market-v2/contracts/Stableswap/BaseV1-core.sol:506:        require(msg.sender == pendingPauser);
lending-market-v2/contracts/Stableswap/BaseV1-core.sol:511:        require(msg.sender == pauser);
lending-market-v2/contracts/Stableswap/BaseV1-core.sol:524:        require(tokenA != tokenB, 'IA'); // BaseV1: IDENTICAL_ADDRESSES
lending-market-v2/contracts/Stableswap/BaseV1-core.sol:526:        require(token0 != address(0), 'ZA'); // BaseV1: ZERO_ADDRESS
lending-market-v2/contracts/Stableswap/BaseV1-core.sol:527:        require(getPair[token0][token1][stable] == address(0), 'PE'); // BaseV1: PAIR_EXISTS - single check is sufficient
lending-market-v2/contracts/Stableswap/BaseV1-periphery.sol:104:        require(tokenA != tokenB, "BaseV1Router: IDENTICAL_ADDRESSES");
lending-market-v2/contracts/Stableswap/BaseV1-periphery.sol:106:        require(token0 != address(0), "BaseV1Router: ZERO_ADDRESS");
lending-market-v2/contracts/Stableswap/BaseV1-periphery.sol:122:        require(amountA > 0, "BaseV1Router: INSUFFICIENT_AMOUNT");
lending-market-v2/contracts/Stableswap/BaseV1-periphery.sol:123:        require(reserveA > 0 && reserveB > 0, "BaseV1Router: INSUFFICIENT_LIQUIDITY");
lending-market-v2/contracts/Stableswap/BaseV1-periphery.sol:151:        require(routes.length >= 1, "BaseV1Router: INVALID_PATH");
lending-market-v2/contracts/Stableswap/BaseV1-periphery.sol:228:        require(amountADesired >= amountAMin);
lending-market-v2/contracts/Stableswap/BaseV1-periphery.sol:229:        require(amountBDesired >= amountBMin);
lending-market-v2/contracts/Stableswap/BaseV1-periphery.sol:241:                require(amountBOptimal >= amountBMin, "BaseV1Router: INSUFFICIENT_B_AMOUNT");
lending-market-v2/contracts/Stableswap/BaseV1-periphery.sol:246:                require(amountAOptimal >= amountAMin, "BaseV1Router: INSUFFICIENT_A_AMOUNT");
lending-market-v2/contracts/Stableswap/BaseV1-periphery.sol:309:        require(IBaseV1Pair(pair).transferFrom(msg.sender, pair, liquidity)); // send liquidity to pair
lending-market-v2/contracts/Stableswap/BaseV1-periphery.sol:313:        require(amountA >= amountAMin, "BaseV1Router: INSUFFICIENT_A_AMOUNT");
lending-market-v2/contracts/Stableswap/BaseV1-periphery.sol:314:        require(amountB >= amountBMin, "BaseV1Router: INSUFFICIENT_B_AMOUNT");
lending-market-v2/contracts/Stableswap/BaseV1-periphery.sol:405:        require(amounts[amounts.length - 1] >= amountOutMin, "BaseV1Router: INSUFFICIENT_OUTPUT_AMOUNT");
lending-market-v2/contracts/Stableswap/BaseV1-periphery.sol:420:        require(amounts[amounts.length - 1] >= amountOutMin, "BaseV1Router: INSUFFICIENT_OUTPUT_AMOUNT");
lending-market-v2/contracts/Stableswap/BaseV1-periphery.sol:433:        require(routes[0].from == address(wcanto), "BaseV1Router: INVALID_PATH");
lending-market-v2/contracts/Stableswap/BaseV1-periphery.sol:435:        require(amounts[amounts.length - 1] >= amountOutMin, "BaseV1Router: INSUFFICIENT_OUTPUT_AMOUNT");
lending-market-v2/contracts/Stableswap/BaseV1-periphery.sol:446:        require(routes[routes.length - 1].to == address(wcanto), "BaseV1Router: INVALID_PATH");
lending-market-v2/contracts/Stableswap/BaseV1-periphery.sol:448:        require(amounts[amounts.length - 1] >= amountOutMin, "BaseV1Router: INSUFFICIENT_OUTPUT_AMOUNT");
lending-market-v2/contracts/Stableswap/BaseV1-periphery.sol:470:        require(success, "TransferHelper: ETH_TRANSFER_FAILED");
lending-market-v2/contracts/Stableswap/BaseV1-periphery.sol:474:        require(token.code.length > 0);
lending-market-v2/contracts/Stableswap/BaseV1-periphery.sol:477:        require(success && (data.length == 0 || abi.decode(data, (bool))));
lending-market-v2/contracts/Stableswap/BaseV1-periphery.sol:481:        require(token.code.length > 0, "token code length failure");
lending-market-v2/contracts/Treasury/TreasuryDelegator.sol:13:        require(admin_ != address(0));
lending-market-v2/contracts/Treasury/TreasuryDelegator.sol:31:        require(msg.sender == admin, "GovernorBravoDelegator::setImplementation: admin only");
lending-market-v2/contracts/Treasury/TreasuryDelegator.sol:32:        require(implementation_ != address(0), "GovernorBravoDelegator::setImplementation: invalid implementation address");
lending-market-v2/contracts/Treasury/TreasuryDelegator.sol:121:        require(msg.value == 0, "TreasuryDelegator::fallback:cannot send value to fallback");
lending-market-v2/contracts/BaseJumpRateModelV2.sol:68:        require(msg.sender == owner, "only the owner may call this function.");
lending-market-v2/contracts/CDaiDelegate.sol:32:        require(msg.sender == admin, "only the admin may initialize the implementation");
lending-market-v2/contracts/CDaiDelegate.sol:49:        require(address(dai) == underlying, "DAI must be the same as underlying");
lending-market-v2/contracts/CDaiDelegate.sol:74:        require(msg.sender == admin, "only the admin may abandon the implementation");
lending-market-v2/contracts/CDaiDelegate.sol:134:        require(token.transferFrom(from, address(this), amount), "unexpected EIP-20 transfer in return");
lending-market-v2/contracts/CDaiDelegate.sol:177:        require((z = x + y) >= x, "add-overflow");
lending-market-v2/contracts/CDaiDelegate.sol:181:        require(y == 0 || (z = x * y) / y == x, "mul-overflow");
lending-market-v2/contracts/CErc20.sol:129:        require(msg.sender == admin, "CErc20::sweepToken: only admin can sweep tokens");
lending-market-v2/contracts/CErc20.sol:130:        require(address(token) != underlying, "CErc20::sweepToken: can not sweep underlying token");
lending-market-v2/contracts/CErc20.sol:186:        require(success, "TOKEN_TRANSFER_IN_FAILED");
lending-market-v2/contracts/CErc20.sol:220:        require(success, "TOKEN_TRANSFER_OUT_FAILED");
lending-market-v2/contracts/CErc20.sol:229:        require(msg.sender == admin, "only the admin may set the comp-like delegate");
lending-market-v2/contracts/CErc20Delegate.sol:30:        require(msg.sender == admin, "only the admin may call _becomeImplementation");
lending-market-v2/contracts/CErc20Delegate.sol:42:        require(msg.sender == admin, "only the admin may call _resignImplementation");
lending-market-v2/contracts/CErc20Delegator.sol:63:        require(msg.sender == admin, "CErc20Delegator::_setImplementation: Caller must be admin");
lending-market-v2/contracts/CErc20Delegator.sol:463:        require(msg.value == 0,"CErc20Delegator:fallback: cannot send value to fallback");
lending-market-v2/contracts/CEther.sol:143:        require(msg.sender == from, "sender mismatch");
lending-market-v2/contracts/CEther.sol:144:        require(msg.value == amount, "value mismatch");
lending-market-v2/contracts/CNote.sol:17:        require(msg.sender == admin, "CNote::_setAccountantContract:Only admin may call this function");
lending-market-v2/contracts/CNote.sol:74:        require(address(_accountant) != address(0)); //check that the accountant has been set
lending-market-v2/contracts/CNote.sol:94:        require(success, "TOKEN_TRANSFER_IN_FAILED");
lending-market-v2/contracts/CNote.sol:105:            require(balanceCur == 0, "Accountant has not been correctly supplied");
lending-market-v2/contracts/CNote.sol:121:        require(address(_accountant) != address(0)); //check that the accountant has been set
lending-market-v2/contracts/CNote.sol:147:        require(success, "TOKEN_TRANSFER_OUT_FAILED");
lending-market-v2/contracts/CNote.sol:148:        require(token.balanceOf(address(this)) == 0, "cNote::doTransferOut: TransferOut Failed");
lending-market-v2/contracts/CNote.sol:157:            require(_notEntered, "re-entered");
lending-market-v2/contracts/CToken.sol:33:        require(msg.sender == admin, "only admin may initialize the market");
lending-market-v2/contracts/CToken.sol:34:        require(accrualBlockNumber == 0 && borrowIndex == 0, "market may only be initialized once");
lending-market-v2/contracts/CToken.sol:37:        require(initialExchangeRateMantissa > 0, "initial exchange rate must be greater than zero.");
lending-market-v2/contracts/CToken.sol:42:        require(err == NO_ERROR, "setting comptroller failed");
lending-market-v2/contracts/CToken.sol:50:        require(err == NO_ERROR, "setting interest rate model failed");
lending-market-v2/contracts/CToken.sol:349:        require(borrowRateMantissa <= borrowRateMaxMantissa, "borrow rate is absurdly high");
lending-market-v2/contracts/CToken.sol:485:        require(redeemTokensIn == 0 || redeemAmountIn == 0, "one of redeemTokensIn or redeemAmountIn must be zero");
lending-market-v2/contracts/CToken.sol:765:        require(amountSeizeError == NO_ERROR, "LIQUIDATE_COMPTROLLER_CALCULATE_AMOUNT_SEIZE_FAILED");
lending-market-v2/contracts/CToken.sol:768:        require(cTokenCollateral.balanceOf(borrower) >= seizeTokens, "LIQUIDATE_SEIZE_TOO_MUCH");
lending-market-v2/contracts/CToken.sol:774:            require(cTokenCollateral.seize(liquidator, borrower, seizeTokens) == NO_ERROR, "token seizure failed");
lending-market-v2/contracts/CToken.sol:912:        require(newComptroller.isComptroller(), "marker method returned false");
lending-market-v2/contracts/CToken.sol:1114:        require(newInterestRateModel.isInterestRateModel(), "marker method returned false");
lending-market-v2/contracts/CToken.sol:1154:        require(_notEntered, "re-entered");
lending-market-v2/contracts/DAIInterestRateModelV3.sol:55:        require(msg.sender == owner, "only the owner may call this function.");
lending-market-v2/contracts/ERC20.sol:183:        require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
lending-market-v2/contracts/ERC20.sol:208:        require(from != address(0), "ERC20: transfer from the zero address");
lending-market-v2/contracts/ERC20.sol:209:        require(to != address(0), "ERC20: transfer to the zero address");
lending-market-v2/contracts/ERC20.sol:212:        require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
lending-market-v2/contracts/ERC20.sol:229:        require(account != address(0), "ERC20: mint to the zero address");
lending-market-v2/contracts/ERC20.sol:248:        require(account != address(0), "ERC20: burn from the zero address");
lending-market-v2/contracts/ERC20.sol:251:        require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
lending-market-v2/contracts/ERC20.sol:276:        require(owner != address(0), "ERC20: approve from the zero address");
lending-market-v2/contracts/ERC20.sol:277:        require(spender != address(0), "ERC20: approve to the zero address");
lending-market-v2/contracts/ERC20.sol:298:            require(currentAllowance >= amount, "ERC20: insufficient allowance");
lending-market-v2/contracts/ExponentialNoError.sol:79:        require(n < 2**224, errorMessage);
lending-market-v2/contracts/ExponentialNoError.sol:84:        require(n < 2**32, errorMessage);
lending-market-v2/contracts/Note.sol:22:        require(msg.sender == admin);
lending-market-v2/contracts/Note.sol:23:        require(address(accountant) == address(0));
lending-market-v2/contracts/NoteInterest.sol:167:        require(msg.sender == admin, "only the admin may set the base rate");
lending-market-v2/contracts/NoteInterest.sol:180:        require(msg.sender == admin, "only the admin may set the adjuster coefficient");
lending-market-v2/contracts/NoteInterest.sol:193:        require(msg.sender == admin, "only the admin may set the update frequency");
lending-market-v2/contracts/Reservoir.sol:74:    require(c >= a, errorMessage);
lending-market-v2/contracts/Reservoir.sol:79:    require(b <= a, errorMessage);
lending-market-v2/contracts/Reservoir.sol:90:    require(c / a == b, errorMessage);
lending-market-v2/contracts/SafeMath.sol:32:        require(c >= a, "SafeMath: addition overflow");
lending-market-v2/contracts/SafeMath.sol:48:        require(c >= a, errorMessage);
lending-market-v2/contracts/SafeMath.sol:74:        require(b <= a, errorMessage);
lending-market-v2/contracts/SafeMath.sol:98:        require(c / a == b, "SafeMath: multiplication overflow");
lending-market-v2/contracts/SafeMath.sol:121:        require(c / a == b, errorMessage);
lending-market-v2/contracts/SafeMath.sol:154:        require(b > 0, errorMessage);
lending-market-v2/contracts/SafeMath.sol:188:        require(b != 0, errorMessage);
lending-market-v2/contracts/Timelock.sol:28:        require(delay_ >= MINIMUM_DELAY, "Timelock::constructor: Delay must exceed minimum delay.");
lending-market-v2/contracts/Timelock.sol:29:        require(delay_ <= MAXIMUM_DELAY, "Timelock::setDelay: Delay must not exceed maximum delay.");
lending-market-v2/contracts/Timelock.sol:38:        require(msg.sender == address(this), "Timelock::setDelay: Call must come from Timelock.");
lending-market-v2/contracts/Timelock.sol:39:        require(delay_ >= MINIMUM_DELAY, "Timelock::setDelay: Delay must exceed minimum delay.");
lending-market-v2/contracts/Timelock.sol:40:        require(delay_ <= MAXIMUM_DELAY, "Timelock::setDelay: Delay must not exceed maximum delay.");
lending-
GalloDaSballo commented 2 years ago

No immutables, so the report will save less than 500 gas overall (some minor SLOAD caching + the usual)