Canto contest - Dravee's results

Execution layer for original work.

General Information

Platform: Code4rena

Start Date: 14/06/2022

Pot Size: $100,000 USDC

Total HM: 26

Participants: 59

Period: 7 days

Judge: GalloDaSballo

Total Solo HM: 9

Id: 133

League: ETH

Canto

Findings Distribution

Researcher Performance

Rank: 28/59

Findings: 2

Award: $661.15

🌟 Selected for report: 0

🚀 Solo Findings: 0

Awards

687.9945 CANTO - $111.11

425.0359 USDC - $425.04

Labels

bug
QA (Quality Assurance)

External Links

Overview

Risk RatingNumber of issues
Low Risk6
Non-Critical Risk16

Table of Contents

Low Risk Issues

1. Known vulnerabilities exist in currently used @openzeppelin/contracts version

As some known vulnerabilities exist in the current @openzeppelin/contracts version, consider updating package.json with at least @openzeppelin/contracts@3.4.2 here:

    "@openzeppelin/contracts": "^3.1.0",

While vulnerabilities are known, the current scope isn't affected (this might not hold true for the whole solution)

2. Unsafe casting may overflow

SafeMath and Solidity 0.8.* handles overflows for basic math operations but not for casting. Consider using OpenZeppelin's SafeCast library (or custom safe-casting functions) to prevent unexpected overflows when casting here:

lending-market/contracts/Governance/Comp.sol:64:        balances[account] = uint96(totalSupply);
zeroswap/contracts/uniswapv2/UniswapV2Oracle.sol:59:        return uint8(epochPeriod % granularity);
zeroswap/contracts/uniswapv2/UniswapV2Oracle.sol:120:            uint224((priceCumulativeEnd - priceCumulativeStart) / timeElapsed)

3. Funds could be locked (unused/empty receive() function)

If the intention is for the Ether to be used, the function should call another function, otherwise it should revert. Furthermore, in those contracts, no withdraw mechanism exist to retrieve locked ether (Ether sent by mistake).

lending-market/contracts/Accountant/AccountantDelegate.sol:94:    receive() external override payable {}
lending-market/contracts/Treasury/TreasuryDelegator.sol:130:    receive() external override payable  {}

4. Unbounded loop on array can lead to DoS

As these arrays can grow quite large (only push operations, no pop), the transaction's gas cost could exceed the block gas limit and make it impossible to call the impacted functions at all.

lending-market/contracts/Comptroller.sol:160:        accountAssets[borrower].push(cToken);
lending-market/contracts/Comptroller.sol:735:        for (uint i = 0; i < assets.length; i++) {
lending-market/contracts/Comptroller.sol:959:        for (uint i = 0; i < allMarkets.length; i ++) {
lending-market/contracts/Comptroller.sol:962:        allMarkets.push(CToken(cToken));

Consider introducing a reasonable upper limit based on block gas limits and adding a method to remove elements in the array.

5. Prevent accidentally burning tokens

Transferring tokens to the zero address is usually prohibited to accidentally avoid "burning" tokens by sending them to an unrecoverable zero address.

Consider adding a check to prevent accidentally burning tokens here:

lending-market/contracts/Treasury/TreasuryDelegate.sol:52:            to.transfer(amount);
lending-market/contracts/Treasury/TreasuryDelegate.sol:56:            note.transfer(recipient, amount);
lending-market/contracts/Comptroller.sol:1380:            comp.transfer(user, amount);

6. abi.encodePacked() should not be used with dynamic types when passing the result to a hash function such as keccak256()

Similar issue in the past: here

Use abi.encode() instead which will pad items to 32 bytes, which will prevent hash collisions (e.g. abi.encodePacked(0x123,0x456) => 0x123456 => abi.encodePacked(0x1,0x23456), but abi.encode(0x123,0x456) => 0x0...1230...456). If there is only one argument to abi.encodePacked() it can often be cast to bytes() or bytes32() instead.

lending-market/contracts/Governance/Comp.sol:164:        bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
lending-market/contracts/Governance/GovernorAlpha.sol:256:        bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
lending-market/contracts/NoteInterest.sol:95:        uint rand = uint(keccak256(abi.encodePacked(msg.sender))) % 100;
stableswap/contracts/BaseV1-core.sol:424:            abi.encodePacked(
stableswap/contracts/BaseV1-core.sol:525:        bytes32 salt = keccak256(abi.encodePacked(token0, token1, stable)); // notice salt includes stable as well, 3 parameters
stableswap/contracts/BaseV1-periphery.sol:94:        pair = address(uint160(uint256(keccak256(abi.encodePacked(
stableswap/contracts/BaseV1-periphery.sol:97:            keccak256(abi.encodePacked(token0, token1, stable)),
zeroswap/contracts/uniswapv2/libraries/UniswapV2Library.sol:22:        pair = address(uint(keccak256(abi.encodePacked(
zeroswap/contracts/uniswapv2/libraries/UniswapV2Library.sol:25:                keccak256(abi.encodePacked(token0, token1)),

Non-Critical Issues

1. require() should be used for checking error conditions on inputs and return values while assert() should be used for invariant checking

Properly functioning code should never reach a failing assert statement, unless there is a bug in your contract you should fix. Here, I believe the assert should be a require or a revert:

lending-market/contracts/Comptroller.sol:214:        assert(assetIndex < len);
lending-market/contracts/Comptroller.sol:360:            assert(markets[cToken].accountMembership[borrower]);
stableswap/contracts/BaseV1-periphery.sol:82:        assert(msg.sender == address(wcanto)); // only accept ETH via fallback from the WETH contract
stableswap/contracts/BaseV1-periphery.sol:227:                assert(amountAOptimal <= amountADesired);
stableswap/contracts/BaseV1-periphery.sol:273:        assert(wcanto.transfer(pair, amountCANTO));
stableswap/contracts/BaseV1-periphery.sol:419:        assert(wcanto.transfer(pairFor(routes[0].from, routes[0].to, routes[0].stable), amounts[0]));

As the Solidity version is > 0.8.* the remaining gas would still be refunded in case of failure.

2. It's better to emit after all processing is done

  • lending-market/contracts/CNote.sol:
   18:         emit AccountantSet(accountant_, address(_accountant));
   19       _accountant = AccountantInterface(accountant_);
   20          admin = accountant_;
  • lending-market/contracts/Comptroller.sol:
  1124:                 emit CompReceivableUpdated(user, oldReceivable, newReceivable);
  1125  
  1126                  amountToSubtract = currentAccrual;
  • lending-market/contracts/NoteInterest.sol:
   73      constructor(uint baseRatePerYear) {
   74          baseRatePerBlock = baseRatePerYear.div(blocksPerYear);
   75:         emit NewInterestParams(baseRatePerBlock);
   76          admin = msg.sender;
   77      }
  • lending-market/contracts/Accountant/AccountantDelegate.sol:
  35:   emit AcctInit(cnoteAddress_);
  36  
  37    cnote._setAccountantContract(payable(this));
  • lending-market/contracts/Accountant/AccountantDelegator.sol:
  45:         emit NewImplementation(implementation, implementation_);
  46         
  47          implementation = implementation_;
  • lending-market/contracts/Governance/Comp.sol:
  228:         emit DelegateChanged(delegator, currentDelegate, delegatee);
  229  
  230          _moveDelegates(currentDelegate, delegatee, delegatorBalance);
  239:         emit Transfer(src, dst, amount);
  240  
  241          _moveDelegates(delegates[src], delegates[dst], amount);

3. Typos in revert strings

  • initiatlize
lending-market/contracts/Accountant/AccountantDelegate.sol:29:  require(note.balanceOf(msg.sender) == note._initialSupply(), "AccountantDelegate::initiatlize: Accountant has not received payment");
  • arity
lending-market/contracts/Governance/GovernorBravoDelegate.sol:45:                "GovernorBravo::propose: proposal function information arity mismatch");
  • faialure
stableswap/contracts/BaseV1-periphery.sol:463:        require(token.code.length > 0, "token code length faialure");

4. Typos in comments

  • contructor
lending-market/contracts/Accountant/AccountantDelegate.sol:10:      * @notice Method used to initialize the contract during delegator contructor
  • undelrying
lending-market/contracts/CNote.sol:93:     * @param repayAmount the amount of undelrying tokens being returned
  • irrelevent
lending-market/contracts/NoteInterest.sol:106:     * @notice The following parameters are irrelevent for calculating Note's interest rate. They are passed in to align with the standard function definition `getSupplyRate` in InterestRateModel
lending-market/contracts/NoteInterest.sol:89:     * @notice The following parameters are irrelevent for calculating Note's interest rate. They are passed in to align with the standard function definition `getBorrowRate` ```
- obervations
```solidity
stableswap/contracts/BaseV1-core.sol:62:    // Structure to capture time period obervations every 30 minutes, used for local oracles

5. Deprecated library used for Solidity >= 0.8 : SafeMath

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

6. Missing friendly revert strings

lending-market/contracts/Governance/Comp.sol:276:        require(n < 2**32, errorMessage);
lending-market/contracts/Governance/Comp.sol:281:        require(n < 2**96, errorMessage);
lending-market/contracts/Governance/Comp.sol:287:        require(c >= a, errorMessage);
lending-market/contracts/Governance/Comp.sol:292:        require(b <= a, errorMessage);
lending-market/contracts/Treasury/TreasuryDelegate.sol:16:        require(msg.sender == admin);
lending-market/contracts/Treasury/TreasuryDelegate.sol:17:     require(note_ != address(0));
lending-market/contracts/WETH.sol:69:        require(_balanceOf[src] >= wad);
lending-market/contracts/WETH.sol:72:            require(_allowance[src][msg.sender] >= wad);
manifest/contracts/Proposal-Store.sol:31: require(msg.sender == UniGovModAcct);
stableswap/contracts/BaseV1-core.sol:285:        require(!BaseV1Factory(factory).isPaused());
stableswap/contracts/BaseV1-core.sol:465:        require(token.code.length > 0);
stableswap/contracts/BaseV1-core.sol:468:        require(success && (data.length == 0 || abi.decode(data, (bool))));
stableswap/contracts/BaseV1-core.sol:498:        require(msg.sender == pauser);
stableswap/contracts/BaseV1-core.sol:503:        require(msg.sender == pendingPauser);
stableswap/contracts/BaseV1-core.sol:508:        require(msg.sender == pauser);
stableswap/contracts/BaseV1-periphery.sol:210:        require(amountADesired >= amountAMin);
stableswap/contracts/BaseV1-periphery.sol:211:        require(amountBDesired >= amountBMin);
stableswap/contracts/BaseV1-periphery.sol:291:        require(IBaseV1Pair(pair).transferFrom(msg.sender, pair, liquidity)); // send liquidity to pair
stableswap/contracts/BaseV1-periphery.sol:456:        require(token.code.length > 0);
stableswap/contracts/BaseV1-periphery.sol:459:        require(success && (data.length == 0 || abi.decode(data, (bool))));

7. Open TODOS

Consider resolving the TODOs before deploying.

lending-market/contracts/Comptroller.sol:1232:        // TODO: Don't distribute supplier COMP if the user is not in the supplier market.
lending-market/contracts/Comptroller.sol:1271:        // TODO: Don't distribute supplier COMP if the user is not in the borrower market.

8. 10000000e18 should be changed to 1e25 for readability reasons

1e25is already well understandable. Writing 10000000e18 is actually confusing

lending-market/contracts/Governance/Comp.sol:15:    uint public constant totalSupply = 10000000e18; // 10 million Comp

9. 400000e18 should be changed to 4e23 for readability reasons

1e23is already well understandable. Writing 400000e18 is actually confusing

lending-market/contracts/Governance/GovernorAlpha.sol:9:    function quorumVotes() public pure returns (uint) { return 400000e18; } // 400,000 = 4% of Comp

10. 100000e18 should be changed to 1e23 for readability reasons

1e23is already well understandable. Writing 100000e18 is actually confusing

lending-market/contracts/Governance/GovernorAlpha.sol:12:    function proposalThreshold() public pure returns (uint) { return 100000e18; } // 100,000 = 1% of Comp

11. Use solidity version of at least 0.8.12 to get string.concat()

Use a solidity version of at least 0.8.12 to get string.concat() instead of abi.encodePacked(<str>,<str>)

lending-market/contracts/Governance/Comp.sol:2:pragma solidity ^0.8.10;
lending-market/contracts/Governance/Comp.sol:164:        bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
lending-market/contracts/Governance/GovernorAlpha.sol:2:pragma solidity ^0.8.10;
lending-market/contracts/Governance/GovernorAlpha.sol:256:        bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
stableswap/contracts/BaseV1-core.sol:2:pragma solidity 0.8.11;
stableswap/contracts/BaseV1-core.sol:109:            name = string(abi.encodePacked("StableV1 AMM - ", erc20(_token0).symbol(), "/", erc20(_token1).symbol()));
stableswap/contracts/BaseV1-core.sol:110:            symbol = string(abi.encodePacked("sAMM-", erc20(_token0).symbol(), "/", erc20(_token1).symbol()));
stableswap/contracts/BaseV1-core.sol:112:            name = string(abi.encodePacked("VolatileV1 AMM - ", erc20(_token0).symbol(), "/", erc20(_token1).symbol()));
stableswap/contracts/BaseV1-core.sol:113:            symbol = string(abi.encodePacked("vAMM-", erc20(_token0).symbol(), "/", erc20(_token1).symbol()));
stableswap/contracts/BaseV1-core.sol:424:            abi.encodePacked(
stableswap/contracts/BaseV1-core.sol:525:        bytes32 salt = keccak256(abi.encodePacked(token0, token1, stable)); // notice salt includes stable as well, 3 parameters
stableswap/contracts/BaseV1-periphery.sol:3:pragma solidity 0.8.11;
stableswap/contracts/BaseV1-periphery.sol:94:        pair = address(uint160(uint256(keccak256(abi.encodePacked(
stableswap/contracts/BaseV1-periphery.sol:97:            keccak256(abi.encodePacked(token0, token1, stable)),

12. Use bytes.concat() instead of abi.encodePacked(<bytes>,<bytes>)

Solidity version 0.8.4 introduces bytes.concat() (vs abi.encodePacked(<bytes>,<bytes>))

stableswap/contracts/BaseV1-core.sol:2:pragma solidity 0.8.11;
stableswap/contracts/BaseV1-core.sol:525:        bytes32 salt = keccak256(abi.encodePacked(token0, token1, stable)); // notice salt includes stable as well, 3 parameters
stableswap/contracts/BaseV1-periphery.sol:3:pragma solidity 0.8.11;
stableswap/contracts/BaseV1-periphery.sol:97:            keccak256(abi.encodePacked(token0, token1, stable)),

13. Use scientific notation (e.g. 1e3, or even just 1000) rather than exponentiation (e.g. 10**3)

stableswap/contracts/BaseV1-core.sol:56:    uint internal constant MINIMUM_LIQUIDITY = 10**3;
stableswap/contracts/BaseV1-periphery.sol:67:    uint internal constant MINIMUM_LIQUIDITY = 10**3;

14. Adding a return statement when the function defines a named return variable, is redundant

While not consuming more gas with the Optimizer enabled: using both named returns and a return statement isn't necessary. Removing one of those can improve code clarity.

Affected code:

  • lending-market/contracts/Governance/GovernorAlpha.sol:
  218:     function getActions(uint proposalId) public view returns (address[] memory targets, uint[] memory values, string[] memory signatures, bytes[] memory calldatas) {
  220:         return (p.targets, p.values, p.signatures, p.calldatas);
  • lending-market/contracts/Governance/GovernorBravoDelegate.sol:
  104:     function getActions(uint proposalId) external view returns (address[] memory targets, uint[] memory values, string[] memory signatures, bytes[] memory calldatas) {
  106:         return (p.targets, p.values, p.signatures, p.calldatas);
  • stableswap/contracts/BaseV1-periphery.sol:
  117:     function getAmountOut(uint amountIn, address tokenIn, address tokenOut) external view returns (uint amount, bool stable) {
  128:         return amountStable > amountVolatile ? (amountStable, true) : (amountVolatile, false);
  • zeroswap/contracts/uniswapv2/UniswapV2Oracle.sol:
   57:     function observationIndexOf(uint timestamp) public view returns (uint8 index) {
   59:         return uint8(epochPeriod % granularity);
  128:     function consult(address tokenIn, uint amountIn, address tokenOut) external view returns (uint amountOut) {
  141:             return computeAmountOut(firstObservation.price0Cumulative, price0Cumulative, timeElapsed, amountIn);
  143:             return computeAmountOut(firstObservation.price1Cumulative, price1Cumulative, timeElapsed, amountIn);

15. The pragmas used are not the same everywhere

lending-market/contracts/Accountant/AccountantDelegate.sol:1:pragma solidity ^0.8.10;
manifest/contracts/Proposal-Store.sol:3:pragma solidity ^0.8.0;
stableswap/contracts/BaseV1-core.sol:2:pragma solidity 0.8.11;
zeroswap/contracts/uniswapv2/libraries/UniswapV2Library.sol:3:pragma solidity >=0.5.0;
zeroswap/contracts/uniswapv2/libraries/UniswapV2OracleLibrary.sol:3:pragma solidity =0.6.12;

16. Non-library/interface files should use fixed compiler versions, not floating ones

lending-market/contracts/Accountant/AccountantDelegate.sol:1:pragma solidity ^0.8.10;
lending-market/contracts/Accountant/AccountantDelegator.sol:1:pragma solidity ^0.8.10;
lending-market/contracts/Accountant/AccountantInterfaces.sol:1:pragma solidity ^0.8.10;
lending-market/contracts/Governance/Comp.sol:2:pragma solidity ^0.8.10;
lending-market/contracts/Governance/GovernorAlpha.sol:2:pragma solidity ^0.8.10;
lending-market/contracts/Governance/GovernorBravoDelegate.sol:2:pragma solidity ^0.8.10;
lending-market/contracts/Governance/GovernorBravoDelegator.sol:2:pragma solidity ^0.8.10;
lending-market/contracts/Governance/GovernorBravoInterfaces.sol:2:pragma solidity ^0.8.10;
lending-market/contracts/Treasury/TreasuryDelegate.sol:1:pragma solidity ^0.8.10;
lending-market/contracts/Treasury/TreasuryDelegator.sol:1:pragma solidity ^0.8.10;
lending-market/contracts/Treasury/TreasuryInterfaces.sol:1:pragma solidity ^0.8.10;
lending-market/contracts/CNote.sol:1:pragma solidity ^0.8.10;
lending-market/contracts/Comptroller.sol:2:pragma solidity ^0.8.10;
lending-market/contracts/NoteInterest.sol:1:pragma solidity ^0.8.10;
lending-market/contracts/WETH.sol:1:pragma solidity ^0.8.10;
manifest/contracts/Proposal-Store.sol:3:pragma solidity ^0.8.0;

#0 - GalloDaSballo

2022-08-02T20:11:55Z

1. Known vulnerabilities exist in currently used @openzeppelin/contracts version

Not a fan of the blanket statement, in lack of any risk am downgrading to NC

2. Unsafe casting may overflow

Valid Low, esp on uint96

3. Funds could be locked (unused/empty receive() function)

Valid Low

4. Unbounded loop on array can lead to DoS

I think this is highly unlikely, am very conflicted. Marking Low for now

5. Prevent accidentally burning tokens

Valid Low

6. abi.encodePacked() should not be used with dynamic types when passing the result to a hash function such as keccak256()

I don't see any risk of clash of hashes, so in lack of further detail am marking invalid, consider finding an instance of clash and submit as higher severity in the future

1. require() should be used for checking error conditions on inputs and return values while assert() should be used for invariant checking

Valid Ref

2. It's better to emit after all processing is done

Disagree as long as usage is consistent (+ slither gives false positives is you emit after a transfer or w/e)

3. Typos in revert strings and 4.

NC

5. Deprecated library used for Solidity >= 0.8 : SafeMath

Valid R

6. Missing friendly revert strings

Valid NC

7. Open TODOS

Valid NC

8. 10000000e18 should be changed to 1e25 for readability reasons and others

Valid Ref

11. Use solidity version of at least 0.8.12 to get string.concat() and bytes.concat

NC

14. Adding a return statement when the function defines a named return variable, is redundant

Valid Ref

15. The pragmas used are not the same everywhere & Interfaces

Valid NC

Neat report

4L 4R 6NC

Awards

60.8952 USDC - $60.90

396.9199 CANTO - $64.10

Labels

bug
G (Gas Optimization)

External Links

Overview

Risk RatingNumber of issues
Gas Issues21

Table of Contents:

1. Avoid emitting a storage variable when a memory value is available

When they are the same, consider emitting the memory value instead of the storage value:

  • File: Comptroller.sol
854:         uint oldCloseFactorMantissa = closeFactorMantissa;
855:         closeFactorMantissa = newCloseFactorMantissa;
- 856:         emit NewCloseFactor(oldCloseFactorMantissa, closeFactorMantissa);
+ 856:         emit NewCloseFactor(oldCloseFactorMantissa, newCloseFactorMantissa);
1038:         // Save current value for inclusion in log
1039:         address oldPauseGuardian = pauseGuardian;
1040: 
1041:         // Store pauseGuardian with value newPauseGuardian
1042:         pauseGuardian = newPauseGuardian;
1043: 
1044:         // Emit NewPauseGuardian(OldPauseGuardian, NewPauseGuardian)
- 1045:         emit NewPauseGuardian(oldPauseGuardian, pauseGuardian);
+ 1045:         emit NewPauseGuardian(oldPauseGuardian, newPauseGuardian);
  • File: NoteInterest.sol
125:             baseRatePerYear = newBaseRatePerYear;
126:             lastUpdateBlock = blockNumber;
- 127:             emit NewInterestParams(baseRatePerYear);
+ 127:             emit NewInterestParams(newBaseRatePerYear);
143:         baseRatePerYear = newBaseRateMantissa;
- 144:         emit NewBaseRate(oldBaseRatePerYear, baseRatePerYear);
+ 144:         emit NewBaseRate(oldBaseRatePerYear, newBaseRateMantissa);
156:         adjusterCoefficient = newAdjusterCoefficient;
- 157:         emit NewAdjusterCoefficient(oldAdjusterCoefficient, adjusterCoefficient);
+ 157:         emit NewAdjusterCoefficient(oldAdjusterCoefficient, newAdjusterCoefficient);
169:         updateFrequency = newUpdateFrequency;
- 170:         emit NewUpdateFrequency(oldUpdateFrequency, updateFrequency);
+ 170:         emit NewUpdateFrequency(oldUpdateFrequency, newUpdateFrequency);
  • File: GovernorBravoDelegate.sol
170:         // Store admin with value pendingAdmin
171:         admin = pendingAdmin;
172: 
173:         // Clear the pending value
174:         pendingAdmin = address(0);
175: 
176:         emit NewAdmin(oldAdmin, admin);
- 177:         emit NewPendingAdmin(oldPendingAdmin, pendingAdmin);
+ 177:         emit NewPendingAdmin(oldPendingAdmin, address(0));
31:         implementation = implementation_;
32: 
- 33:         emit NewImplementation(oldImplementation, implementation);
+ 33:         emit NewImplementation(oldImplementation, implementation_);
  • File: BaseV1-core.sol
167:         reserve0 = balance0;
168:         reserve1 = balance1;
169:         blockTimestampLast = blockTimestamp;
- 170:         emit Sync(reserve0, reserve1);
+ 170:         emit Sync(balance0, balance1);

2. Cheap Contract Deployment Through Clones

stableswap/contracts/BaseV1-core.sol:527:        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

3. Unchecking arithmetics operations that can't underflow/overflow

Solidity version 0.8+ comes with implicit overflow and underflow checks on unsigned integers. When an overflow or an underflow isn't possible (as an example, when a comparison is made before the arithmetic operation), some gas can be saved by using an unchecked block: https://docs.soliditylang.org/en/v0.8.10/control-structures.html#checked-or-unchecked-arithmetic

Consider wrapping with an unchecked block here (see @audit tags for more details):

lending-market/contracts/WETH.sol:

  29          require(_balanceOf[msg.sender] >= wad, "sender balance insufficient for withdrawal");
  30:         _balanceOf[msg.sender] -= wad; //@audit should be unchecked, see L29
  72              require(_allowance[src][msg.sender] >= wad);
  73:             _allowance[src][msg.sender] -= wad; //@audit should be unchecked, see L72
  69          require(_balanceOf[src] >= wad);
  ...
  76:         _balanceOf[src] -= wad; //@audit should be unchecked, see L69
  • stableswap/contracts/BaseV1-periphery.sol:
  276:         if (msg.value > amountCANTO) _safeTransferCANTO(msg.sender, msg.value - amountCANTO); //@audit should be unchecked, see this line itself

4. Some variables should be immutable

These variables are only set in the constructor and are never edited after that:

  • File: NoteInterest.sol
22:    address private admin;
...
73:     constructor(uint baseRatePerYear) {
...
76:         admin = msg.sender;
77:     }
  • File: Proposal-Store.sol
35:    address private UniGovModAcct;
...
39:     constructor(uint propId, string memory title, string memory desc, address[] memory targets, 
40:                         uint[] memory values, string[] memory signatures, bytes[] memory calldatas) {
41:  UniGovModAcct = msg.sender;
...
44:     }

Consider marking them as immutable, as it would avoid the expensive storage-writing operation (around 20 000 gas)

5. 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/contracts/Accountant/AccountantDelegate.sol:17:  require(msg.sender == admin, "AccountantDelegate::initialize: only admin can call this function");
lending-market/contracts/Accountant/AccountantDelegate.sol:18:  require(noteAddress_ != address(0), "AccountantDelegate::initialize: note Address invalid");
lending-market/contracts/Accountant/AccountantDelegate.sol:29:  require(note.balanceOf(msg.sender) == note._initialSupply(), "AccountantDelegate::initiatlize: Accountant has not received payment");
lending-market/contracts/Accountant/AccountantDelegate.sol:48:  require(msg.sender == address(cnote), "AccountantDelegate::supplyMarket: Only the CNote contract can supply market");
lending-market/contracts/Accountant/AccountantDelegate.sol:60:  require(msg.sender == address(cnote), "AccountantDelegate::redeemMarket: Only the CNote contract can redeem market");
lending-market/contracts/Accountant/AccountantDelegate.sol:83:  require(cNoteConverted >= noteDifferential, "Note Loaned to LendingMarket must increase in value");
lending-market/contracts/Accountant/AccountantDelegator.sol:43:        require(msg.sender == admin, "AccountantDelegator::_setImplementation: admin only");
lending-market/contracts/Accountant/AccountantDelegator.sol:44:        require(implementation_ != address(0), "AccountantDelegator::_setImplementation: invalid implementation address");
lending-market/contracts/Accountant/AccountantDelegator.sol:124:        require(msg.value == 0,"AccountantDelegator:fallback: cannot send value to fallback");
lending-market/contracts/Governance/Comp.sol:166:        require(signatory != address(0), "Comp::delegateBySig: invalid signature");
lending-market/contracts/Governance/Comp.sol:167:        require(nonce == nonces[signatory]++, "Comp::delegateBySig: invalid nonce");
lending-market/contracts/Governance/Comp.sol:168:        require(block.timestamp <= expiry, "Comp::delegateBySig: signature expired");
lending-market/contracts/Governance/Comp.sol:190:        require(blockNumber < block.number, "Comp::getPriorVotes: not yet determined");
lending-market/contracts/Governance/Comp.sol:234:        require(src != address(0), "Comp::_transferTokens: cannot transfer from the zero address");
lending-market/contracts/Governance/Comp.sol:235:        require(dst != address(0), "Comp::_transferTokens: cannot transfer to the zero address");
lending-market/contracts/Governance/GovernorAlpha.sol:137:        require(comp.getPriorVotes(msg.sender, sub256(block.number, 1)) > proposalThreshold(), "GovernorAlpha::propose: proposer votes below proposal threshold");
lending-market/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/contracts/Governance/GovernorAlpha.sol:139:        require(targets.length != 0, "GovernorAlpha::propose: must provide actions");
lending-market/contracts/Governance/GovernorAlpha.sol:140:        require(targets.length <= proposalMaxOperations(), "GovernorAlpha::propose: too many actions");
lending-market/contracts/Governance/GovernorAlpha.sol:145:          require(proposersLatestProposalState != ProposalState.Active, "GovernorAlpha::propose: one live proposal per proposer, found an already active proposal");
lending-market/contracts/Governance/GovernorAlpha.sol:146:          require(proposersLatestProposalState != ProposalState.Pending, "GovernorAlpha::propose: one live proposal per proposer, found an already pending proposal");
lending-market/contracts/Governance/GovernorAlpha.sol:156:        require(newProposal.id == 0, "GovernorAlpha::propose: ProposalID collsion");
lending-market/contracts/Governance/GovernorAlpha.sol:178:        require(state(proposalId) == ProposalState.Succeeded, "GovernorAlpha::queue: proposal can only be queued if it is succeeded");
lending-market/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/contracts/Governance/GovernorAlpha.sol:194:        require(state(proposalId) == ProposalState.Queued, "GovernorAlpha::execute: proposal can only be executed if it is queued");
lending-market/contracts/Governance/GovernorAlpha.sol:205:        require(state != ProposalState.Executed, "GovernorAlpha::cancel: cannot cancel executed proposal");
lending-market/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/contracts/Governance/GovernorAlpha.sol:228:        require(proposalCount >= proposalId && proposalId > 0, "GovernorAlpha::state: invalid proposal id");
lending-market/contracts/Governance/GovernorAlpha.sol:258:        require(signatory != address(0), "GovernorAlpha::castVoteBySig: invalid signature");
lending-market/contracts/Governance/GovernorAlpha.sol:263:        require(state(proposalId) == ProposalState.Active, "GovernorAlpha::_castVote: voting is closed");
lending-market/contracts/Governance/GovernorAlpha.sol:266:        require(receipt.hasVoted == false, "GovernorAlpha::_castVote: voter already voted");
lending-market/contracts/Governance/GovernorAlpha.sol:283:        require(msg.sender == guardian, "GovernorAlpha::__acceptAdmin: sender must be gov guardian");
lending-market/contracts/Governance/GovernorAlpha.sol:288:        require(msg.sender == guardian, "GovernorAlpha::__abdicate: sender must be gov guardian");
lending-market/contracts/Governance/GovernorAlpha.sol:293:        require(msg.sender == guardian, "GovernorAlpha::__queueSetTimelockPendingAdmin: sender must be gov guardian");
lending-market/contracts/Governance/GovernorAlpha.sol:298:        require(msg.sender == guardian, "GovernorAlpha::__executeSetTimelockPendingAdmin: sender must be gov guardian");
lending-market/contracts/Governance/GovernorBravoDelegate.sol:25:        require(address(timelock) == address(0), "GovernorBravo::initialize: can only initialize once");
lending-market/contracts/Governance/GovernorBravoDelegate.sol:26:        require(msg.sender == admin, "GovernorBravo::initialize: admin only");
lending-market/contracts/Governance/GovernorBravoDelegate.sol:27:        require(timelock_ != address(0), "GovernorBravo::initialize: invalid timelock address");
lending-market/contracts/Governance/GovernorBravoDelegate.sol:45:                "GovernorBravo::propose: proposal function information arity mismatch");
lending-market/contracts/Governance/GovernorBravoDelegate.sol:46:        require(unigovProposal.targets.length != 0, "GovernorBravo::propose: must provide actions");
lending-market/contracts/Governance/GovernorBravoDelegate.sol:47:        require(unigovProposal.targets.length <= proposalMaxOperations, "GovernorBravo::propose: too many actions");
lending-market/contracts/Governance/GovernorBravoDelegate.sol:78:        require(!timelock.queuedTransactions(keccak256(abi.encode(target, value, signature, data, eta))), "GovernorBravo::queueOrRevertInternal: identical proposal action already queued at eta");
lending-market/contracts/Governance/GovernorBravoDelegate.sol:87:        require(state(proposalId) == ProposalState.Queued, "GovernorBravo::execute: proposal can only be executed if it is queued");
lending-market/contracts/Governance/GovernorBravoDelegate.sol:115:        require(proposalCount >= proposalId && proposalId > initialProposalId, "GovernorBravo::state: invalid proposal id");
lending-market/contracts/Governance/GovernorBravoDelegate.sol:132:        require(msg.sender == admin, "GovernorBravo::_initiate: admin only");
lending-market/contracts/Governance/GovernorBravoDelegate.sol:133:        require(initialProposalId == 0, "GovernorBravo::_initiate: can only initiate once");
lending-market/contracts/Governance/GovernorBravoDelegate.sol:146:        require(msg.sender == admin, "GovernorBravo:_setPendingAdmin: admin only");
lending-market/contracts/Governance/GovernorBravoDelegate.sol:164:        require(msg.sender == pendingAdmin && msg.sender != address(0), "GovernorBravo:_acceptAdmin: pending admin only");
lending-market/contracts/Governance/GovernorBravoDelegator.sol:27:        require(msg.sender == admin, "GovernorBravoDelegator::_setImplementation: admin only");
lending-market/contracts/Governance/GovernorBravoDelegator.sol:28:        require(implementation_ != address(0), "GovernorBravoDelegator::_setImplementation: invalid implementation address");
lending-market/contracts/Governance/GovernorBravoDelegator.sol:40:        require(msg.sender == admin, "GovernorBravoDelegator::_initiateDelegated: admin only");
lending-market/contracts/Governance/GovernorBravoDelegator.sol:48:        require(msg.sender == admin, "GovernorBravoDelegator::_acceptInitialAdminDelegated: admin only");
lending-market/contracts/Governance/GovernorBravoDelegator.sol:56:        require(msg.sender == admin, "GovernorBravoDelegator::_setPendingAdminDelegated: admin only");
lending-market/contracts/Treasury/TreasuryDelegate.sol:47:        require(msg.sender == admin, "Treasury::sendFund can only be called by admin");
lending-market/contracts/Treasury/TreasuryDelegator.sol:31:        require(msg.sender == admin, "GovernorBravoDelegator::_setImplementation: admin only");
lending-market/contracts/Treasury/TreasuryDelegator.sol:32:        require(implementation_ != address(0), "GovernorBravoDelegator::_setImplementation: invalid implementation address");
lending-market/contracts/CNote.sol:16:            require(msg.sender == admin, "CNote::_setAccountantContract:Only admin may call this function");
lending-market/contracts/CNote.sol:43:     require(getCashPrior() == 0, "CNote::borrowFresh:Impossible reserves in CNote market Place");
lending-market/contracts/CNote.sol:45:     require(address(_accountant) != address(0), "CNote::borrowFresh:Accountant has not been initialized");
lending-market/contracts/CNote.sol:54:        require(getCashPrior() == borrowAmount, "CNote::borrowFresh:Error in Accountant supply");
lending-market/contracts/CNote.sol:77:     require(getCashPrior() == 0,"CNote::borrowFresh: Error in doTransferOut, impossible Liquidity in LendingMarket");
lending-market/contracts/CNote.sol:114:        require(getCashPrior() == 0, "CNote::repayBorrowFresh:Liquidity in Note Lending Market is always flashed");
lending-market/contracts/CNote.sol:130:        require(getCashPrior() >= actualRepayAmount, "CNote::repayBorrowFresh: doTransferIn supplied incorrect amount"); //sanity check that Accountant has some thing to redeem
lending-market/contracts/CNote.sol:146:        require(getCashPrior() == 0, "CNote::repayBorrowFresh: Error in Accountant.redeemMarket");
lending-market/contracts/CNote.sol:198:     require(getCashPrior() == 0, "CNote::mintFresh: Any Liquidity in the Lending Market is flashed");
lending-market/contracts/CNote.sol:214:        require(getCashPrior() == actualMintAmount, "CNote::mintFresh: Error in doTransferIn, CNote reserves >= mint Amount"); //sanity check that Accountant has some thing to redeem
lending-market/contracts/CNote.sol:264:        require(redeemTokensIn == 0 || redeemAmountIn == 0, "one of redeemTokensIn or redeemAmountIn must be zero");
lending-market/contracts/CNote.sol:310:     require(getCashPrior() == 0, "CNote::redeemFresh, LendingMarket has > 0 Cash before Accountant Supplies");
lending-market/contracts/CNote.sol:330:        require(getCashPrior() == redeemAmount, "CNote::redeemFresh: Accountant has supplied incorrect Amount");
lending-market/contracts/Comptroller.sol:178:        require(oErr == 0, "exitMarket: getAccountSnapshot failed"); // semi-opaque error code
lending-market/contracts/Comptroller.sol:491:            require(borrowBalance >= repayAmount, "Can not repay more than the total borrow");
lending-market/contracts/Comptroller.sol:998:     require(msg.sender == admin || msg.sender == borrowCapGuardian, "only admin or borrow cap guardian can set borrow caps");
lending-market/contracts/Comptroller.sol:1016:        require(msg.sender == admin, "only admin can set borrow cap guardian");
lending-market/contracts/Comptroller.sol:1051:        require(markets[address(cToken)].isListed, "cannot pause a market that is not listed");
lending-market/contracts/Comptroller.sol:1052:        require(msg.sender == pauseGuardian || msg.sender == admin, "only pause guardian and admin can pause");
lending-market/contracts/Comptroller.sol:1061:        require(markets[address(cToken)].isListed, "cannot pause a market that is not listed");
lending-market/contracts/Comptroller.sol:1062:        require(msg.sender == pauseGuardian || msg.sender == admin, "only pause guardian and admin can pause");
lending-market/contracts/Comptroller.sol:1071:        require(msg.sender == pauseGuardian || msg.sender == admin, "only pause guardian and admin can pause");
lending-market/contracts/Comptroller.sol:1080:        require(msg.sender == pauseGuardian || msg.sender == admin, "only pause guardian and admin can pause");
lending-market/contracts/Comptroller.sol:1089:        require(msg.sender == unitroller.admin(), "only unitroller admin can change brains");
lending-market/contracts/Comptroller.sol:1095:        require(msg.sender == admin, "Only admin can call this function"); // Only the timelock can call this function
lending-market/contracts/Comptroller.sol:1096:        require(!proposal65FixExecuted, "Already executed this one-off function"); // Require that this function is only called once
lending-market/contracts/Comptroller.sol:1411:        require(numTokens == supplySpeeds.length && numTokens == borrowSpeeds.length, "Comptroller::_setCompSpeeds invalid input");
lending-market/contracts/NoteInterest.sol:141:        require(msg.sender == admin, "only the admin may set the base rate");
lending-market/contracts/NoteInterest.sol:154:        require(msg.sender == admin, "only the admin may set the adjuster coefficient");
lending-market/contracts/NoteInterest.sol:167:        require(msg.sender == admin, "only the admin may set the update frequency");
lending-market/contracts/WETH.sol:29:        require(_balanceOf[msg.sender] >= wad, "sender balance insufficient for withdrawal");
lending-market/contracts/WETH.sol:96:        require(owner != address(0), "ERC20: approve from the zero address");
lending-market/contracts/WETH.sol:97:        require(spender != address(0), "ERC20: approve to the zero address");

Consider shortening the revert strings to fit in 32 bytes.

6. 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/contracts/NoteInterest.sol:1:pragma solidity ^0.8.10;
lending-market/contracts/NoteInterest.sol:5:import "./SafeMath.sol";
lending-market/contracts/NoteInterest.sol:15:    using SafeMath for uint;

7. 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/contracts/Governance/GovernorAlpha.sol:266:        require(receipt.hasVoted == false, "GovernorAlpha::_castVote: voter already voted");
lending-market/contracts/Comptroller.sol:149:        if (marketToJoin.accountMembership[borrower] == true) {
lending-market/contracts/Comptroller.sol:1053:        require(msg.sender == admin || state == true, "only admin can unpause");
lending-market/contracts/Comptroller.sol:1063:        require(msg.sender == admin || state == true, "only admin can unpause");
lending-market/contracts/Comptroller.sol:1072:        require(msg.sender == admin || state == true, "only admin can unpause");
lending-market/contracts/Comptroller.sol:1081:        require(msg.sender == admin || state == true, "only admin can unpause");
lending-market/contracts/Comptroller.sol:1350:            if (borrowers == true) {
lending-market/contracts/Comptroller.sol:1357:            if (suppliers == true) {
lending-market/contracts/Comptroller.sol:1456:            borrowGuardianPaused[address(cToken)] == true &&

8. Bytes constants are more efficient than string constants

From the Solidity doc:

If you can limit the length to a certain number of bytes, always use one of bytes1 to bytes32 because they are much cheaper.

Why do Solidity examples use bytes32 type instead of string?

bytes32 uses less gas because it fits in a single word of the EVM, and string is a dynamically sized-type which has current limitations in Solidity (such as can't be returned from a function to a contract).

If data can fit into 32 bytes, then you should use bytes32 datatype rather than bytes or strings as it is cheaper in solidity. Basically, any fixed size variable in solidity is cheaper than variable size. That will save gas on the contract.

Instances of string constant that can be replaced by bytes(1..32) constant :

lending-market/contracts/Governance/Comp.sol:6:    string public constant name = "Compound";
lending-market/contracts/Governance/Comp.sol:9:    string public constant symbol = "COMP";
lending-market/contracts/Governance/GovernorAlpha.sol:6:    string public constant name = "Compound Governor Alpha";
lending-market/contracts/Governance/GovernorBravoDelegate.sol:9:    string public constant name = "Compound Governor Bravo";

9. 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/contracts/Governance/GovernorAlpha.sol:228:        require(proposalCount >= proposalId && proposalId > 0, "GovernorAlpha::state: invalid proposal id");
lending-market/contracts/CNote.sol:310:     require(getCashPrior() == 0, "CNote::redeemFresh, LendingMarket has > 0 Cash before Accountant Supplies");
stableswap/contracts/BaseV1-core.sol:253:        require(liquidity > 0, 'ILM'); // BaseV1: INSUFFICIENT_LIQUIDITY_MINTED
stableswap/contracts/BaseV1-core.sol:272:        require(amount0 > 0 && amount1 > 0, 'ILB'); // BaseV1: INSUFFICIENT_LIQUIDITY_BURNED
stableswap/contracts/BaseV1-core.sol:286:        require(amount0Out > 0 || amount1Out > 0, 'IOA'); // BaseV1: INSUFFICIENT_OUTPUT_AMOUNT
stableswap/contracts/BaseV1-core.sol:303:        require(amount0In > 0 || amount1In > 0, 'IIA'); // BaseV1: INSUFFICIENT_INPUT_AMOUNT
stableswap/contracts/BaseV1-core.sol:309:        require(_k(_balance0, _balance1) >= _k(_reserve0, _reserve1), 'K'); // BaseV1: K
stableswap/contracts/BaseV1-core.sol:465:        require(token.code.length > 0);
stableswap/contracts/BaseV1-periphery.sol:104:        require(amountA > 0, 'BaseV1Router: INSUFFICIENT_AMOUNT');
stableswap/contracts/BaseV1-periphery.sol:105:        require(reserveA > 0 && reserveB > 0, 'BaseV1Router: INSUFFICIENT_LIQUIDITY');
stableswap/contracts/BaseV1-periphery.sol:456:        require(token.code.length > 0);
stableswap/contracts/BaseV1-periphery.sol:463:        require(token.code.length > 0, "token code length faialure");
zeroswap/contracts/uniswapv2/libraries/UniswapV2Library.sol:39:        require(amountA > 0, 'UniswapV2Library: INSUFFICIENT_AMOUNT');
zeroswap/contracts/uniswapv2/libraries/UniswapV2Library.sol:40:        require(reserveA > 0 && reserveB > 0, 'UniswapV2Library: INSUFFICIENT_LIQUIDITY');
zeroswap/contracts/uniswapv2/libraries/UniswapV2Library.sol:46:        require(amountIn > 0, 'UniswapV2Library: INSUFFICIENT_INPUT_AMOUNT');
zeroswap/contracts/uniswapv2/libraries/UniswapV2Library.sol:47:        require(reserveIn > 0 && reserveOut > 0, 'UniswapV2Library: INSUFFICIENT_LIQUIDITY');
zeroswap/contracts/uniswapv2/libraries/UniswapV2Library.sol:56:        require(amountOut > 0, 'UniswapV2Library: INSUFFICIENT_OUTPUT_AMOUNT');
zeroswap/contracts/uniswapv2/libraries/UniswapV2Library.sol:57:        require(reserveIn > 0 && reserveOut > 0, 'UniswapV2Library: INSUFFICIENT_LIQUIDITY');

Also, please enable the Optimizer.

10. >= 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/contracts/Comptroller.sol:1197:            Double memory ratio = supplyTokens > 0 ? fraction(compAccrued, supplyTokens) : Double({mantissa: 0});
lending-market/contracts/Comptroller.sol:1218:            Double memory ratio = borrowAmount > 0 ? fraction(compAccrued, borrowAmount) : Double({mantissa: 0});
stableswap/contracts/BaseV1-core.sol:16:        return a < b ? a : b;
stableswap/contracts/BaseV1-core.sol:301:        uint amount0In = _balance0 > _reserve0 - amount0Out ? _balance0 - (_reserve0 - amount0Out) : 0;
stableswap/contracts/BaseV1-core.sol:302:        uint amount1In = _balance1 > _reserve1 - amount1Out ? _balance1 - (_reserve1 - amount1Out) : 0;
stableswap/contracts/BaseV1-core.sol:522:        (address token0, address token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
stableswap/contracts/BaseV1-periphery.sol:35:        return a < b ? a : b;
stableswap/contracts/BaseV1-periphery.sol:87:        (token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
zeroswap/contracts/uniswapv2/libraries/UniswapV2Library.sol:15:        (token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);

11. 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/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/contracts/Governance/GovernorAlpha.sol:228:        require(proposalCount >= proposalId && proposalId > 0, "GovernorAlpha::state: invalid proposal id");
lending-market/contracts/Governance/GovernorBravoDelegate.sol:42:        require(unigovProposal.targets.length == unigovProposal.values.length && 
lending-market/contracts/Governance/GovernorBravoDelegate.sol:43:                unigovProposal.targets.length == unigovProposal.signatures.length && 
lending-market/contracts/Governance/GovernorBravoDelegate.sol:115:        require(proposalCount >= proposalId && proposalId > initialProposalId, "GovernorBravo::state: invalid proposal id");
lending-market/contracts/Governance/GovernorBravoDelegate.sol:164:        require(msg.sender == pendingAdmin && msg.sender != address(0), "GovernorBravo:_acceptAdmin: pending admin only");
lending-market/contracts/Comptroller.sol:1003:        require(numMarkets != 0 && numMarkets == numBorrowCaps, "invalid input");
lending-market/contracts/Comptroller.sol:1411:        require(numTokens == supplySpeeds.length && numTokens == borrowSpeeds.length, "Comptroller::_setCompSpeeds invalid input");
stableswap/contracts/BaseV1-core.sol:272:        require(amount0 > 0 && amount1 > 0, 'ILB'); // BaseV1: INSUFFICIENT_LIQUIDITY_BURNED
stableswap/contracts/BaseV1-core.sol:288:        require(amount0Out < _reserve0 && amount1Out < _reserve1, 'IL'); // BaseV1: INSUFFICIENT_LIQUIDITY
stableswap/contracts/BaseV1-core.sol:294:        require(to != _token0 && to != _token1, 'IT'); // BaseV1: INVALID_TO
stableswap/contracts/BaseV1-core.sol:431:        require(recoveredAddress != address(0) && recoveredAddress == owner, 'BaseV1: INVALID_SIGNATURE');
stableswap/contracts/BaseV1-core.sol:468:        require(success && (data.length == 0 || abi.decode(data, (bool))));
stableswap/contracts/BaseV1-periphery.sol:105:        require(reserveA > 0 && reserveB > 0, 'BaseV1Router: INSUFFICIENT_LIQUIDITY');
stableswap/contracts/BaseV1-periphery.sol:459:        require(success && (data.length == 0 || abi.decode(data, (bool))));
stableswap/contracts/BaseV1-periphery.sol:466:        require(success && (data.length == 0 || abi.decode(data, (bool))), "failing here");
zeroswap/contracts/uniswapv2/libraries/UniswapV2Library.sol:40:        require(reserveA > 0 && reserveB > 0, 'UniswapV2Library: INSUFFICIENT_LIQUIDITY');
zeroswap/contracts/uniswapv2/libraries/UniswapV2Library.sol:47:        require(reserveIn > 0 && reserveOut > 0, 'UniswapV2Library: INSUFFICIENT_LIQUIDITY');
zeroswap/contracts/uniswapv2/libraries/UniswapV2Library.sol:57:        require(reserveIn > 0 && reserveOut > 0, 'UniswapV2Library: INSUFFICIENT_LIQUIDITY');

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.

12. Using private rather than public for constants saves gas

If needed, the value can be read from the verified contract source code. Savings are due to the compiler not having to create non-payable getter functions for deployment calldata, and not adding another entry to the method ID table

lending-market/contracts/Comptroller.sol:79:    uint224 public constant compInitialIndex = 1e36;
lending-market/contracts/NoteInterest.sol:27:    uint public constant blocksPerYear = 5256000;

13. 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.

  • Use >> 1 instead of / 2
  • Use >> 2 instead of / 4
  • Use << 3 instead of * 8

Affected code:

lending-market/contracts/Governance/Comp.sol:210:            uint32 center = upper - (upper - lower) / 2; // ceil, avoiding overflow
stableswap/contracts/BaseV1-core.sol:21:            uint x = y / 2 + 1;
stableswap/contracts/BaseV1-core.sol:24:                x = (y / x + x) / 2;
stableswap/contracts/BaseV1-periphery.sol:40:            uint x = y / 2 + 1;
stableswap/contracts/BaseV1-periphery.sol:43:                x = (y / x + x) / 2;

14. <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/contracts/Governance/GovernorAlpha.sol:181:        for (uint i = 0; i < proposal.targets.length; i++) {
lending-market/contracts/Governance/GovernorAlpha.sol:197:        for (uint i = 0; i < proposal.targets.length; i++) {
lending-market/contracts/Governance/GovernorAlpha.sol:211:        for (uint i = 0; i < proposal.targets.length; i++) {
lending-market/contracts/Governance/GovernorBravoDelegate.sol:68:        for (uint i = 0; i < newProposal.targets.length; i++) {
lending-market/contracts/Governance/GovernorBravoDelegate.sol:90:        for (uint i = 0; i < proposal.targets.length; i++) {
lending-market/contracts/Comptroller.sol:735:        for (uint i = 0; i < assets.length; i++) {
lending-market/contracts/Comptroller.sol:959:        for (uint i = 0; i < allMarkets.length; i ++) {
lending-market/contracts/Comptroller.sol:1106:        for (uint i = 0; i < affectedUsers.length; ++i) {
lending-market/contracts/Comptroller.sol:1347:        for (uint i = 0; i < cTokens.length; i++) {
lending-market/contracts/Comptroller.sol:1353:                for (uint j = 0; j < holders.length; j++) {
lending-market/contracts/Comptroller.sol:1359:                for (uint j = 0; j < holders.length; j++) {
lending-market/contracts/Comptroller.sol:1364:        for (uint j = 0; j < holders.length; j++) {
stableswap/contracts/BaseV1-core.sol:207:        for (uint i = 0; i < _prices.length; i++) {
stableswap/contracts/BaseV1-periphery.sol:136:        for (uint i = 0; i < routes.length; i++) {
stableswap/contracts/BaseV1-periphery.sol:362:        for (uint i = 0; i < routes.length; i++) {
zeroswap/contracts/uniswapv2/libraries/UniswapV2Library.sol:68:        for (uint i; i < path.length - 1; i++) {
zeroswap/contracts/uniswapv2/libraries/UniswapV2Library.sol:79:        for (uint i = path.length - 1; i > 0; i--) {
zeroswap/contracts/uniswapv2/UniswapV2Oracle.sol:83:        for (uint i = pairObservations[pair].length; i < granularity; i++) {

15. ++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:

  • i += 1 is the most expensive form
  • i++ costs 6 gas less than i += 1
  • ++i costs 5 gas less than i++ (11 gas less than i += 1)

Decrement:

  • i -= 1 is the most expensive form
  • i-- costs 11 gas less than i -= 1
  • --i costs 5 gas less than i-- (16 gas less than i -= 1)

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/contracts/Governance/GovernorAlpha.sol:152:        proposalCount++;
lending-market/contracts/Governance/GovernorAlpha.sol:181:        for (uint i = 0; i < proposal.targets.length; i++) {
lending-market/contracts/Governance/GovernorAlpha.sol:197:        for (uint i = 0; i < proposal.targets.length; i++) {
lending-market/contracts/Governance/GovernorAlpha.sol:211:        for (uint i = 0; i < proposal.targets.length; i++) {
lending-market/contracts/Governance/GovernorBravoDelegate.sol:68:        for (uint i = 0; i < newProposal.targets.length; i++) {
lending-market/contracts/Governance/GovernorBravoDelegate.sol:90:        for (uint i = 0; i < proposal.targets.length; i++) {
lending-market/contracts/Comptroller.sol:126:        for (uint i = 0; i < len; i++) {
lending-market/contracts/Comptroller.sol:206:        for (uint i = 0; i < len; i++) {
lending-market/contracts/Comptroller.sol:735:        for (uint i = 0; i < assets.length; i++) {
lending-market/contracts/Comptroller.sol:959:        for (uint i = 0; i < allMarkets.length; i ++) {
lending-market/contracts/Comptroller.sol:1005:        for(uint i = 0; i < numMarkets; i++) {
lending-market/contracts/Comptroller.sol:1347:        for (uint i = 0; i < cTokens.length; i++) {
lending-market/contracts/Comptroller.sol:1353:                for (uint j = 0; j < holders.length; j++) {
lending-market/contracts/Comptroller.sol:1359:                for (uint j = 0; j < holders.length; j++) {
lending-market/contracts/Comptroller.sol:1364:        for (uint j = 0; j < holders.length; j++) {
stableswap/contracts/BaseV1-core.sol:207:        for (uint i = 0; i < _prices.length; i++) {
stableswap/contracts/BaseV1-core.sol:337:        for (uint i = 0; i < 255; i++) {
stableswap/contracts/BaseV1-periphery.sol:136:        for (uint i = 0; i < routes.length; i++) {
stableswap/contracts/BaseV1-periphery.sol:362:        for (uint i = 0; i < routes.length; i++) {
zeroswap/contracts/uniswapv2/libraries/UniswapV2Library.sol:68:        for (uint i; i < path.length - 1; i++) {
zeroswap/contracts/uniswapv2/libraries/UniswapV2Library.sol:79:        for (uint i = path.length - 1; i > 0; i--) {
zeroswap/contracts/uniswapv2/UniswapV2Oracle.sol:83:        for (uint i = pairObservations[pair].length; i < granularity; i++) {

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

Notice that this is already applied here:

lending-market/contracts/Comptroller.sol:1106:        for (uint i = 0; i < affectedUsers.length; ++i) {
lending-market/contracts/Comptroller.sol:1413:        for (uint i = 0; i < numTokens; ++i) {

16. 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/contracts/Governance/GovernorAlpha.sol:181:        for (uint i = 0; i < proposal.targets.length; i++) {
lending-market/contracts/Governance/GovernorAlpha.sol:197:        for (uint i = 0; i < proposal.targets.length; i++) {
lending-market/contracts/Governance/GovernorAlpha.sol:211:        for (uint i = 0; i < proposal.targets.length; i++) {
lending-market/contracts/Governance/GovernorBravoDelegate.sol:68:        for (uint i = 0; i < newProposal.targets.length; i++) {
lending-market/contracts/Governance/GovernorBravoDelegate.sol:90:        for (uint i = 0; i < proposal.targets.length; i++) {
lending-market/contracts/Comptroller.sol:126:        for (uint i = 0; i < len; i++) {
lending-market/contracts/Comptroller.sol:206:        for (uint i = 0; i < len; i++) {
lending-market/contracts/Comptroller.sol:735:        for (uint i = 0; i < assets.length; i++) {
lending-market/contracts/Comptroller.sol:959:        for (uint i = 0; i < allMarkets.length; i ++) {
lending-market/contracts/Comptroller.sol:1005:        for(uint i = 0; i < numMarkets; i++) {
lending-market/contracts/Comptroller.sol:1106:        for (uint i = 0; i < affectedUsers.length; ++i) {
lending-market/contracts/Comptroller.sol:1347:        for (uint i = 0; i < cTokens.length; i++) {
lending-market/contracts/Comptroller.sol:1353:                for (uint j = 0; j < holders.length; j++) {
lending-market/contracts/Comptroller.sol:1359:                for (uint j = 0; j < holders.length; j++) {
lending-market/contracts/Comptroller.sol:1364:        for (uint j = 0; j < holders.length; j++) {
lending-market/contracts/Comptroller.sol:1413:        for (uint i = 0; i < numTokens; ++i) {
stableswap/contracts/BaseV1-core.sol:207:        for (uint i = 0; i < _prices.length; i++) {
stableswap/contracts/BaseV1-core.sol:337:        for (uint i = 0; i < 255; i++) {
stableswap/contracts/BaseV1-periphery.sol:136:        for (uint i = 0; i < routes.length; i++) {
stableswap/contracts/BaseV1-periphery.sol:362:        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.

17. 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/contracts/Governance/GovernorAlpha.sol:294:        timelock.queueTransaction(address(timelock), 0, "setPendingAdmin(address)", abi.encode(newPendingAdmin), eta);
lending-market/contracts/Governance/GovernorAlpha.sol:299:        timelock.executeTransaction(address(timelock), 0, "setPendingAdmin(address)", abi.encode(newPendingAdmin), eta);

Keep in mind that abi.encodePacked() should not be used with dynamic types when passing the result to a hash function such as keccak256() (see the QA Report)

18. 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/contracts/Governance/Comp.sol:207:        uint32 lower = 0;
lending-market/contracts/Governance/GovernorAlpha.sol:181:        for (uint i = 0; i < proposal.targets.length; i++) {
lending-market/contracts/Governance/GovernorAlpha.sol:197:        for (uint i = 0; i < proposal.targets.length; i++) {
lending-market/contracts/Governance/GovernorAlpha.sol:211:        for (uint i = 0; i < proposal.targets.length; i++) {
lending-market/contracts/Governance/GovernorBravoDelegate.sol:68:        for (uint i = 0; i < newProposal.targets.length; i++) {
lending-market/contracts/Governance/GovernorBravoDelegate.sol:90:        for (uint i = 0; i < proposal.targets.length; i++) {
lending-market/contracts/Comptroller.sol:126:        for (uint i = 0; i < len; i++) {
lending-market/contracts/Comptroller.sol:206:        for (uint i = 0; i < len; i++) {
lending-market/contracts/Comptroller.sol:735:        for (uint i = 0; i < assets.length; i++) {
lending-market/contracts/Comptroller.sol:959:        for (uint i = 0; i < allMarkets.length; i ++) {
lending-market/contracts/Comptroller.sol:1005:        for(uint i = 0; i < numMarkets; i++) {
lending-market/contracts/Comptroller.sol:1106:        for (uint i = 0; i < affectedUsers.length; ++i) {
lending-market/contracts/Comptroller.sol:1347:        for (uint i = 0; i < cTokens.length; i++) {
lending-market/contracts/Comptroller.sol:1353:                for (uint j = 0; j < holders.length; j++) {
lending-market/contracts/Comptroller.sol:1359:                for (uint j = 0; j < holders.length; j++) {
lending-market/contracts/Comptroller.sol:1364:        for (uint j = 0; j < holders.length; j++) {
lending-market/contracts/Comptroller.sol:1413:        for (uint i = 0; i < numTokens; ++i) {
stableswap/contracts/BaseV1-core.sol:46:    uint public totalSupply = 0;
stableswap/contracts/BaseV1-core.sol:207:        for (uint i = 0; i < _prices.length; i++) {
stableswap/contracts/BaseV1-core.sol:223:        uint nextIndex = 0;
stableswap/contracts/BaseV1-core.sol:224:        uint index = 0;
stableswap/contracts/BaseV1-core.sol:337:        for (uint i = 0; i < 255; i++) {
stableswap/contracts/BaseV1-periphery.sol:136:        for (uint i = 0; i < routes.length; i++) {
stableswap/contracts/BaseV1-periphery.sol:158:        uint _totalSupply = 0;
stableswap/contracts/BaseV1-periphery.sol:362:        for (uint i = 0; i < routes.length; i++) {

Consider removing explicit initializations for default values.

19. Upgrade pragma

Using newer compiler versions and the optimizer give gas optimizations. Also, additional safety checks are available for free.

The advantages here are:

  • Low level inliner (>= 0.8.2): Cheaper runtime gas (especially relevant when the contract has small functions).
  • Optimizer improvements in packed structs (>= 0.8.3)
  • Custom errors (>= 0.8.4): cheaper deployment cost and runtime cost. Note: the runtime cost is only relevant when the revert condition is met. In short, replace revert strings by custom errors.
  • Contract existence checks (>= 0.8.10): external calls skip contract existence checks if the external call has a return value

Consider upgrading here :

manifest/contracts/Proposal-Store.sol:3:pragma solidity ^0.8.0;

20. 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/contracts/Accountant/AccountantDelegate.sol:17:  require(msg.sender == admin, "AccountantDelegate::initialize: only admin can call this function");
lending-market/contracts/Accountant/AccountantDelegate.sol:18:  require(noteAddress_ != address(0), "AccountantDelegate::initialize: note Address invalid");
lending-market/contracts/Accountant/AccountantDelegate.sol:29:  require(note.balanceOf(msg.sender) == note._initialSupply(), "AccountantDelegate::initiatlize: Accountant has not received payment");
lending-market/contracts/Accountant/AccountantDelegate.sol:48:  require(msg.sender == address(cnote), "AccountantDelegate::supplyMarket: Only the CNote contract can supply market");
lending-market/contracts/Accountant/AccountantDelegate.sol:60:  require(msg.sender == address(cnote), "AccountantDelegate::redeemMarket: Only the CNote contract can redeem market");
lending-market/contracts/Accountant/AccountantDelegate.sol:83:  require(cNoteConverted >= noteDifferential, "Note Loaned to LendingMarket must increase in value");
lending-market/contracts/Accountant/AccountantDelegator.sol:43:        require(msg.sender == admin, "AccountantDelegator::_setImplementation: admin only");
lending-market/contracts/Accountant/AccountantDelegator.sol:44:        require(implementation_ != address(0), "AccountantDelegator::_setImplementation: invalid implementation address");
lending-market/contracts/Accountant/AccountantDelegator.sol:124:        require(msg.value == 0,"AccountantDelegator:fallback: cannot send value to fallback");
lending-market/contracts/Governance/Comp.sol:166:        require(signatory != address(0), "Comp::delegateBySig: invalid signature");
lending-market/contracts/Governance/Comp.sol:167:        require(nonce == nonces[signatory]++, "Comp::delegateBySig: invalid nonce");
lending-market/contracts/Governance/Comp.sol:168:        require(block.timestamp <= expiry, "Comp::delegateBySig: signature expired");
lending-market/contracts/Governance/Comp.sol:190:        require(blockNumber < block.number, "Comp::getPriorVotes: not yet determined");
lending-market/contracts/Governance/Comp.sol:234:        require(src != address(0), "Comp::_transferTokens: cannot transfer from the zero address");
lending-market/contracts/Governance/Comp.sol:235:        require(dst != address(0), "Comp::_transferTokens: cannot transfer to the zero address");
lending-market/contracts/Governance/Comp.sol:276:        require(n < 2**32, errorMessage);
lending-market/contracts/Governance/Comp.sol:281:        require(n < 2**96, errorMessage);
lending-market/contracts/Governance/Comp.sol:287:        require(c >= a, errorMessage);
lending-market/contracts/Governance/Comp.sol:292:        require(b <= a, errorMessage);
lending-market/contracts/Governance/GovernorAlpha.sol:137:        require(comp.getPriorVotes(msg.sender, sub256(block.number, 1)) > proposalThreshold(), "GovernorAlpha::propose: proposer votes below proposal threshold");
lending-market/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/contracts/Governance/GovernorAlpha.sol:139:        require(targets.length != 0, "GovernorAlpha::propose: must provide actions");
lending-market/contracts/Governance/GovernorAlpha.sol:140:        require(targets.length <= proposalMaxOperations(), "GovernorAlpha::propose: too many actions");
lending-market/contracts/Governance/GovernorAlpha.sol:145:          require(proposersLatestProposalState != ProposalState.Active, "GovernorAlpha::propose: one live proposal per proposer, found an already active proposal");
lending-market/contracts/Governance/GovernorAlpha.sol:146:          require(proposersLatestProposalState != ProposalState.Pending, "GovernorAlpha::propose: one live proposal per proposer, found an already pending proposal");
lending-market/contracts/Governance/GovernorAlpha.sol:156:        require(newProposal.id == 0, "GovernorAlpha::propose: ProposalID collsion");
lending-market/contracts/Governance/GovernorAlpha.sol:178:        require(state(proposalId) == ProposalState.Succeeded, "GovernorAlpha::queue: proposal can only be queued if it is succeeded");
lending-market/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/contracts/Governance/GovernorAlpha.sol:194:        require(state(proposalId) == ProposalState.Queued, "GovernorAlpha::execute: proposal can only be executed if it is queued");
lending-market/contracts/Governance/GovernorAlpha.sol:205:        require(state != ProposalState.Executed, "GovernorAlpha::cancel: cannot cancel executed proposal");
lending-market/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/contracts/Governance/GovernorAlpha.sol:228:        require(proposalCount >= proposalId && proposalId > 0, "GovernorAlpha::state: invalid proposal id");
lending-market/contracts/Governance/GovernorAlpha.sol:258:        require(signatory != address(0), "GovernorAlpha::castVoteBySig: invalid signature");
lending-market/contracts/Governance/GovernorAlpha.sol:263:        require(state(proposalId) == ProposalState.Active, "GovernorAlpha::_castVote: voting is closed");
lending-market/contracts/Governance/GovernorAlpha.sol:266:        require(receipt.hasVoted == false, "GovernorAlpha::_castVote: voter already voted");
lending-market/contracts/Governance/GovernorAlpha.sol:283:        require(msg.sender == guardian, "GovernorAlpha::__acceptAdmin: sender must be gov guardian");
lending-market/contracts/Governance/GovernorAlpha.sol:288:        require(msg.sender == guardian, "GovernorAlpha::__abdicate: sender must be gov guardian");
lending-market/contracts/Governance/GovernorAlpha.sol:293:        require(msg.sender == guardian, "GovernorAlpha::__queueSetTimelockPendingAdmin: sender must be gov guardian");
lending-market/contracts/Governance/GovernorAlpha.sol:298:        require(msg.sender == guardian, "GovernorAlpha::__executeSetTimelockPendingAdmin: sender must be gov guardian");
lending-market/contracts/Governance/GovernorAlpha.sol:304:        require(c >= a, "addition overflow");
lending-market/contracts/Governance/GovernorAlpha.sol:309:        require(b <= a, "subtraction underflow");
lending-market/contracts/Governance/GovernorBravoDelegate.sol:25:        require(address(timelock) == address(0), "GovernorBravo::initialize: can only initialize once");
lending-market/contracts/Governance/GovernorBravoDelegate.sol:26:        require(msg.sender == admin, "GovernorBravo::initialize: admin only");
lending-market/contracts/Governance/GovernorBravoDelegate.sol:27:        require(timelock_ != address(0), "GovernorBravo::initialize: invalid timelock address");
lending-market/contracts/Governance/GovernorBravoDelegate.sol:42:        require(unigovProposal.targets.length == unigovProposal.values.length && 
lending-market/contracts/Governance/GovernorBravoDelegate.sol:46:        require(unigovProposal.targets.length != 0, "GovernorBravo::propose: must provide actions");
lending-market/contracts/Governance/GovernorBravoDelegate.sol:47:        require(unigovProposal.targets.length <= proposalMaxOperations, "GovernorBravo::propose: too many actions");
lending-market/contracts/Governance/GovernorBravoDelegate.sol:53:        require(proposals[unigovProposal.id].id == 0);
lending-market/contracts/Governance/GovernorBravoDelegate.sol:78:        require(!timelock.queuedTransactions(keccak256(abi.encode(target, value, signature, data, eta))), "GovernorBravo::queueOrRevertInternal: identical proposal action already queued at eta");
lending-market/contracts/Governance/GovernorBravoDelegate.sol:87:        require(state(proposalId) == ProposalState.Queued, "GovernorBravo::execute: proposal can only be executed if it is queued");
lending-market/contracts/Governance/GovernorBravoDelegate.sol:115:        require(proposalCount >= proposalId && proposalId > initialProposalId, "GovernorBravo::state: invalid proposal id");
lending-market/contracts/Governance/GovernorBravoDelegate.sol:132:        require(msg.sender == admin, "GovernorBravo::_initiate: admin only");
lending-market/contracts/Governance/GovernorBravoDelegate.sol:133:        require(initialProposalId == 0, "GovernorBravo::_initiate: can only initiate once");
lending-market/contracts/Governance/GovernorBravoDelegate.sol:146:        require(msg.sender == admin, "GovernorBravo:_setPendingAdmin: admin only");
lending-market/contracts/Governance/GovernorBravoDelegate.sol:164:        require(msg.sender == pendingAdmin && msg.sender != address(0), "GovernorBravo:_acceptAdmin: pending admin only");
lending-market/contracts/Governance/GovernorBravoDelegate.sol:182:        require(c >= a, "addition overflow");
lending-market/contracts/Governance/GovernorBravoDelegate.sol:187:        require(b <= a, "subtraction underflow");
lending-market/contracts/Governance/GovernorBravoDelegator.sol:27:        require(msg.sender == admin, "GovernorBravoDelegator::_setImplementation: admin only");
lending-market/contracts/Governance/GovernorBravoDelegator.sol:28:        require(implementation_ != address(0), "GovernorBravoDelegator::_setImplementation: invalid implementation address");
lending-market/contracts/Governance/GovernorBravoDelegator.sol:40:        require(msg.sender == admin, "GovernorBravoDelegator::_initiateDelegated: admin only");
lending-market/contracts/Governance/GovernorBravoDelegator.sol:48:        require(msg.sender == admin, "GovernorBravoDelegator::_acceptInitialAdminDelegated: admin only");
lending-market/contracts/Governance/GovernorBravoDelegator.sol:56:        require(msg.sender == admin, "GovernorBravoDelegator::_setPendingAdminDelegated: admin only");
lending-market/contracts/Treasury/TreasuryDelegate.sol:16:        require(msg.sender == admin);
lending-market/contracts/Treasury/TreasuryDelegate.sol:17:     require(note_ != address(0));
lending-market/contracts/Treasury/TreasuryDelegate.sol:47:        require(msg.sender == admin, "Treasury::sendFund can only be called by admin");
lending-market/contracts/Treasury/TreasuryDelegator.sol:31:        require(msg.sender == admin, "GovernorBravoDelegator::_setImplementation: admin only");
lending-market/contracts/Treasury/TreasuryDelegator.sol:32:        require(implementation_ != address(0), "GovernorBravoDelegator::_setImplementation: invalid implementation address");
lending-market/contracts/CNote.sol:16:            require(msg.sender == admin, "CNote::_setAccountantContract:Only admin may call this function");
lending-market/contracts/CNote.sol:43:     require(getCashPrior() == 0, "CNote::borrowFresh:Impossible reserves in CNote market Place");
lending-market/contracts/CNote.sol:45:     require(address(_accountant) != address(0), "CNote::borrowFresh:Accountant has not been initialized");
lending-market/contracts/CNote.sol:54:        require(getCashPrior() == borrowAmount, "CNote::borrowFresh:Error in Accountant supply");
lending-market/contracts/CNote.sol:77:     require(getCashPrior() == 0,"CNote::borrowFresh: Error in doTransferOut, impossible Liquidity in LendingMarket");
lending-market/contracts/CNote.sol:114:        require(getCashPrior() == 0, "CNote::repayBorrowFresh:Liquidity in Note Lending Market is always flashed");
lending-market/contracts/CNote.sol:130:        require(getCashPrior() >= actualRepayAmount, "CNote::repayBorrowFresh: doTransferIn supplied incorrect amount"); //sanity check that Accountant has some thing to redeem
lending-market/contracts/CNote.sol:146:        require(getCashPrior() == 0, "CNote::repayBorrowFresh: Error in Accountant.redeemMarket");
lending-market/contracts/CNote.sol:198:     require(getCashPrior() == 0, "CNote::mintFresh: Any Liquidity in the Lending Market is flashed");
lending-market/contracts/CNote.sol:214:        require(getCashPrior() == actualMintAmount, "CNote::mintFresh: Error in doTransferIn, CNote reserves >= mint Amount"); //sanity check that Accountant has some thing to redeem
lending-market/contracts/CNote.sol:229:        require(getCashPrior() == 0);
lending-market/contracts/CNote.sol:264:        require(redeemTokensIn == 0 || redeemAmountIn == 0, "one of redeemTokensIn or redeemAmountIn must be zero");
lending-market/contracts/CNote.sol:310:     require(getCashPrior() == 0, "CNote::redeemFresh, LendingMarket has > 0 Cash before Accountant Supplies");
lending-market/contracts/CNote.sol:330:        require(getCashPrior() == redeemAmount, "CNote::redeemFresh: Accountant has supplied incorrect Amount");
lending-market/contracts/CNote.sol:353:            require(_notEntered, "re-entered");
lending-market/contracts/Comptroller.sol:178:        require(oErr == 0, "exitMarket: getAccountSnapshot failed"); // semi-opaque error code
lending-market/contracts/Comptroller.sol:237:        require(!mintGuardianPaused[cToken], "mint is paused");
lending-market/contracts/Comptroller.sol:343:        require(!borrowGuardianPaused[cToken], "borrow is paused");
lending-market/contracts/Comptroller.sol:351:            require(msg.sender == cToken, "sender must be cToken");
lending-market/contracts/Comptroller.sol:373:            require(nextTotalBorrows < borrowCap, "market borrow cap reached");
lending-market/contracts/Comptroller.sol:491:            require(borrowBalance >= repayAmount, "Can not repay more than the total borrow");
lending-market/contracts/Comptroller.sol:556:        require(!seizeGuardianPaused, "seize is paused");
lending-market/contracts/Comptroller.sol:614:        require(!transferGuardianPaused, "transfer is paused");
lending-market/contracts/Comptroller.sol:

#0 - GalloDaSballo

2022-08-04T00:22:54Z

4.2k from immutable, rest is probably less than 500 gas

AuditHub

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

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter