Canto contest - TomJ'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: 36/59

Findings: 2

Award: $306.41

🌟 Selected for report: 0

🚀 Solo Findings: 0

Awards

89.9424 USDC - $89.94

687.9945 CANTO - $111.11

Labels

bug
QA (Quality Assurance)

External Links

[L-01] Immutable addresses should 0-Check

I recommend adding check of 0-address for immutable addresses. Not doing so might lead to non-functional contract when it is updated to 0-address accidentally.

Issue found at

BaseV1-periphery.sol 75: constructor(address _factory, address _wcanto) { 76: factory = _factory; 77: pairCodeHash = IBaseV1Factory(_factory).pairCodeHash(); 78: wcanto = IWCANTO(_wcanto); 79: }

[L-02] require should be used instead of assert

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

[N-01] Event is missing indexed fields

Each event should have 3 indexed fields if there are 3 or more fields.

Issue found at

./Comptroller.sol:19: event MarketListed(CToken cToken); ./Comptroller.sol:22: event MarketEntered(CToken cToken, address account); ./Comptroller.sol:25: event MarketExited(CToken cToken, address account); ./Comptroller.sol:28: event NewCloseFactor(uint oldCloseFactorMantissa, uint newCloseFactorMantissa); ./Comptroller.sol:31: event NewCollateralFactor(CToken cToken, uint oldCollateralFactorMantissa, uint newCollateralFactorMantissa); ./Comptroller.sol:34: event NewLiquidationIncentive(uint oldLiquidationIncentiveMantissa, uint newLiquidationIncentiveMantissa); ./Comptroller.sol:37: event NewPriceOracle(PriceOracle oldPriceOracle, PriceOracle newPriceOracle); ./Comptroller.sol:40: event NewPauseGuardian(address oldPauseGuardian, address newPauseGuardian); ./Comptroller.sol:43: event ActionPaused(string action, bool pauseState); ./Comptroller.sol:46: event ActionPaused(CToken cToken, string action, bool pauseState); ./Comptroller.sol:49: event CompBorrowSpeedUpdated(CToken indexed cToken, uint newSpeed); ./Comptroller.sol:52: event CompSupplySpeedUpdated(CToken indexed cToken, uint newSpeed); ./Comptroller.sol:55: event ContributorCompSpeedUpdated(address indexed contributor, uint newSpeed); ./Comptroller.sol:58: event DistributedSupplierComp(CToken indexed cToken, address indexed supplier, uint compDelta, uint compSupplyIndex); ./Comptroller.sol:61: event DistributedBorrowerComp(CToken indexed cToken, address indexed borrower, uint compDelta, uint compBorrowIndex); ./Comptroller.sol:64: event NewBorrowCap(CToken indexed cToken, uint newBorrowCap); ./Comptroller.sol:67: event NewBorrowCapGuardian(address oldBorrowCapGuardian, address newBorrowCapGuardian); ./Comptroller.sol:70: event CompGranted(address recipient, uint amount); ./Comptroller.sol:73: event CompAccruedAdjusted(address indexed user, uint oldCompAccrued, uint newCompAccrued); ./Comptroller.sol:76: event CompReceivableUpdated(address indexed user, uint oldCompReceivable, uint newCompReceivable); ./AccountantInterfaces.sol:15: event AcctInit(address lendingMarketAddress); ./AccountantInterfaces.sol:16: event AcctSupplied(uint amount, uint err); ./AccountantInterfaces.sol:25: event NewImplementation(address oldImplementation, address newImplementation); ./NoteInterest.sol:17: event NewInterestParams(uint baserateperblock); ./NoteInterest.sol:61: event NewBaseRate(uint oldBaseRateMantissa, uint newBaseRateMantissa); ./NoteInterest.sol:64: event NewAdjusterCoefficient(uint oldAdjusterCoefficient, uint newAdjusterCoefficient); ./NoteInterest.sol:67: event NewUpdateFrequency(uint oldUpdateFrequency, uint newUpdateFrequency); ./CNote.sol:10: event AccountantSet(address accountant, address accountantPrior); ./BaseV1-core.sol:88: event Mint(address indexed sender, uint amount0, uint amount1); ./BaseV1-core.sol:89: event Burn(address indexed sender, uint amount0, uint amount1, address indexed to); ./BaseV1-core.sol:90: event Swap(address indexed sender, uint amount0in, uint amount1in, uint amount0out, uint amount1out, address indexed to); ./BaseV1-core.sol:98: event Sync(uint reserve0, uint reserve1); ./BaseV1-core.sol:99: event Claim(address indexed sender, address indexed recipient, uint amount0, uint amount1); ./BaseV1-core.sol:101: event Transfer(address indexed from, address indexed to, uint amount); ./BaseV1-core.sol:102: event Approval(address indexed owner, address indexed spender, uint amount); ./BaseV1-core.sol:486: event PairCreated(address indexed token0, address indexed token1, bool stable, address pair, uint); ./TreasuryInterfaces.sol:17: event NewImplementation(address oldImplementation, address newImplementation);

[N-02] Unnecessary use of named returns

Several function adds return statement even thought named returns variable are used. Remove unnecessary named returns variable to improve code readability. Also keeping the use of named returns or return statement consistent through out the whole project if possible is recommended.

Issue found at BaseV1-periphery.sol

Remove returns variable "amount" and "stable" 117: function getAmountOut(uint amountIn, address tokenIn, address tokenOut) external view returns (uint amount, bool stable) { 128: return amountStable > amountVolatile ? (amountStable, true) : (amountVolatile, false);

BaseV1-core.sol

Remove returns variable "dec0", "dec1", "r0", "r1", "st", "t0" and "t1" 139: function metadata() external view returns (uint dec0, uint dec1, uint r0, uint r1, bool st, address t0, address t1) { 140: return (decimals0, decimals1, reserve0, reserve1, stable, token0, token1);
Remove returns variable "amountOut" 204: function quote(address tokenIn, uint amountIn, uint granularity) external view returns (uint amountOut) { 210: return priceAverageCumulative / granularity;

GovernorBravoDelegate.sol

Remove returns variable "targets", "values", "signatures" and "calldatas" 104: function getActions(uint proposalId) external view returns (address[] memory targets, uint[] memory values, string[] memory signatures, bytes[] memory calldatas) { 105: Proposal storage p = proposals[proposalId]; 106: return (p.targets, p.values, p.signatures, p.calldatas);

[N-03] TYPO

Type found at Comptroller.sol

1371: * @dev Note: If there is not enough WETH, we do not perform the transfer all.

Change it to

1371: * @dev Note: If there is not enough WETH, we do not perform the transfer at all.

[N-04] require statements should have descriptive revert strings

./CNote.sol:229: require(getCashPrior() == 0); ./GovernorBravoDelegate.sol:53: require(proposals[unigovProposal.id].id == 0); ./BaseV1-core.sol:125: require(_unlocked == 1); ./BaseV1-core.sol:285: require(!BaseV1Factory(factory).isPaused()); ./BaseV1-core.sol:465: require(token.code.length > 0); ./BaseV1-core.sol:468: require(success && (data.length == 0 || abi.decode(data, (bool)))); ./BaseV1-core.sol:498: require(msg.sender == pauser); ./BaseV1-core.sol:503: require(msg.sender == pendingPauser); ./BaseV1-core.sol:508: require(msg.sender == pauser); ./WETH.sol:69: require(_balanceOf[src] >= wad); ./WETH.sol:72: require(_allowance[src][msg.sender] >= wad); ./TreasuryDelegate.sol:16: require(msg.sender == admin); ./TreasuryDelegate.sol:17: require(note_ != address(0)); ./BaseV1-periphery.sol:210: require(amountADesired >= amountAMin); ./BaseV1-periphery.sol:211: require(amountBDesired >= amountBMin); ./BaseV1-periphery.sol:291: require(IBaseV1Pair(pair).transferFrom(msg.sender, pair, liquidity)); // send liquidity to pair ./BaseV1-periphery.sol:456: require(token.code.length > 0); ./BaseV1-periphery.sol:459: require(success && (data.length == 0 || abi.decode(data, (bool))));

[N-04] Use fixed compiler versions instead of floating version

./AccountantDelegator.sol:1:pragma solidity ^0.8.10; ./Comptroller.sol:2:pragma solidity ^0.8.10; ./AccountantInterfaces.sol:1:pragma solidity ^0.8.10; ./NoteInterest.sol:1:pragma solidity ^0.8.10; ./CNote.sol:1:pragma solidity ^0.8.10; ./TreasuryDelegator.sol:1:pragma solidity ^0.8.10; ./GovernorBravoDelegate.sol:2:pragma solidity ^0.8.10; ./TreasuryInterfaces.sol:1:pragma solidity ^0.8.10; ./WETH.sol:1:pragma solidity ^0.8.10; ./AccountantDelegate.sol:1:pragma solidity ^0.8.10; ./TreasuryDelegate.sol:1:pragma solidity ^0.8.10;

#0 - GalloDaSballo

2022-08-03T23:44:13Z

[L-01] Immutable addresses should 0-Check

L

[L-02] require should be used instead of assert

L

[N-01] Event is missing indexed fields

NC (agree for tokens should be indexed)

[N-02] Unnecessary use of named returns

NC

[N-03] Typos

NC

[N-04] require statements should have descriptive revert strings

NC

[N-04] Use fixed compiler versions instead of floating version

NC

2L 5NC

Awards

41.2642 USDC - $41.26

396.9199 CANTO - $64.10

Labels

bug
G (Gas Optimization)

External Links

[G-01] Unnecessary Default Value Initialization

When variable is not initialized, it will have its default values. Example: 0 for uint, false for bool and address(0) for address

I suggest removing default value initialization for following variables.

./Comptroller.sol:126: for (uint i = 0; i < len; i++) { ./Comptroller.sol:206: for (uint i = 0; i < len; i++) { ./Comptroller.sol:735: for (uint i = 0; i < assets.length; i++) { ./Comptroller.sol:947: newMarket.isComped = false; ./Comptroller.sol:959: for (uint i = 0; i < allMarkets.length; i ++) { ./Comptroller.sol:1005: for(uint i = 0; i < numMarkets; i++) { ./Comptroller.sol:1106: for (uint i = 0; i < affectedUsers.length; ++i) { ./Comptroller.sol:1347: for (uint i = 0; i < cTokens.length; i++) { ./Comptroller.sol:1353: for (uint j = 0; j < holders.length; j++) { ./Comptroller.sol:1359: for (uint j = 0; j < holders.length; j++) { ./Comptroller.sol:1364: for (uint j = 0; j < holders.length; j++) { ./Comptroller.sol:1413: for (uint i = 0; i < numTokens; ++i) { ./CNote.sol:355: _notEntered = false; ./GovernorBravoDelegate.sol:62: newProposal.canceled = false; ./GovernorBravoDelegate.sol:68: for (uint i = 0; i < newProposal.targets.length; i++) { ./GovernorBravoDelegate.sol:90: for (uint i = 0; i < proposal.targets.length; i++) { ./BaseV1-core.sol:46: uint public totalSupply = 0; ./BaseV1-core.sol:207: for (uint i = 0; i < _prices.length; i++) { ./BaseV1-core.sol:223: uint nextIndex = 0; ./BaseV1-core.sol:224: uint index = 0; ./BaseV1-core.sol:337: for (uint i = 0; i < 255; i++) { ./BaseV1-core.sol:490: isPaused = false; ./BaseV1-periphery.sol:136: for (uint i = 0; i < routes.length; i++) { ./BaseV1-periphery.sol:158: uint _totalSupply = 0; ./BaseV1-periphery.sol:362: for (uint i = 0; i < routes.length; i++) {

For example these can change to:

  • uint16 public constant AAVE_REFERRAL_CODE;
  • for (uint8 i; i < _pooledTokens.length; i++) {

[G-02] Save Gas in For-Loops by storing array's length as a variable

3 gas per iteration can be saved by storing an array's length as a variable before the for-loop.

Issue found at:

./Comptroller.sol:735: for (uint i = 0; i < assets.length; i++) { ./Comptroller.sol:959: for (uint i = 0; i < allMarkets.length; i ++) { ./Comptroller.sol:1106: for (uint i = 0; i < affectedUsers.length; ++i) { ./Comptroller.sol:1347: for (uint i = 0; i < cTokens.length; i++) { ./Comptroller.sol:1353: for (uint j = 0; j < holders.length; j++) { ./Comptroller.sol:1359: for (uint j = 0; j < holders.length; j++) { ./Comptroller.sol:1364: for (uint j = 0; j < holders.length; j++) { ./GovernorBravoDelegate.sol:68: for (uint i = 0; i < newProposal.targets.length; i++) { ./GovernorBravoDelegate.sol:90: for (uint i = 0; i < proposal.targets.length; i++) { ./BaseV1-core.sol:207: for (uint i = 0; i < _prices.length; i++) { ./BaseV1-periphery.sol:136: for (uint i = 0; i < routes.length; i++) { ./BaseV1-periphery.sol:362: for (uint i = 0; i < routes.length; i++) {

For example, I suggest changing it to:

length = assets.length for (uint i; i < length; i++) {

[G-03] ++i costs less gas than i++

It is better to use ++i than i++ when possible since it costs less gas.

Issue found at:

./Comptroller.sol:126: for (uint i = 0; i < len; i++) { ./Comptroller.sol:206: for (uint i = 0; i < len; i++) { ./Comptroller.sol:735: for (uint i = 0; i < assets.length; i++) { ./Comptroller.sol:959: for (uint i = 0; i < allMarkets.length; i ++) { ./Comptroller.sol:1005: for(uint i = 0; i < numMarkets; i++) { ./Comptroller.sol:1347: for (uint i = 0; i < cTokens.length; i++) { ./Comptroller.sol:1353: for (uint j = 0; j < holders.length; j++) { ./Comptroller.sol:1359: for (uint j = 0; j < holders.length; j++) { ./Comptroller.sol:1364: for (uint j = 0; j < holders.length; j++) { ./GovernorBravoDelegate.sol:68: for (uint i = 0; i < newProposal.targets.length; i++) { ./GovernorBravoDelegate.sol:90: for (uint i = 0; i < proposal.targets.length; i++) { ./BaseV1-core.sol:207: for (uint i = 0; i < _prices.length; i++) { ./BaseV1-core.sol:337: for (uint i = 0; i < 255; i++) { ./BaseV1-core.sol:427: keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline)) ./BaseV1-periphery.sol:136: for (uint i = 0; i < routes.length; i++) { ./BaseV1-periphery.sol:362: for (uint i = 0; i < routes.length; i++) {

[G-04] Defined Variables Used Only Once

Certain variables is defined even though they are used only once. Remove these unnecessary variables to save gas. For cases where it will reduce the readability, one can use comments to help describe what the code is doing.

Issue found at

  1. AccountantDelegate.sol
  • Remove "expRate" and "amtToRedeem" variable of redeemMarket function
62: Exp memory expRate = Exp({mantissa: cnote.exchangeRateStored()}); // return exchangeRate scaled by 1e18 64: uint amtToRedeem = div_(amount, expRate); // convert exchangeRateStored Internal to Exp, multiply by scalar amount, and truncate, to return correct amount to redeem 66: return cnote.redeem(amtToRedeem); // redeem the amount of Note calculated via current CNote -> Note exchange rate
  • Remove "noteBalance" and "amtToSweep" variable of sweepInterest function
76: uint noteBalance = note.balanceOf(address(this)); 81: uint noteDifferential = sub_(note.totalSupply(), noteBalance); //cannot underflow, subtraction first to prevent against overflow, subtraction as integers 85: uint amtToSweep = sub_(cNoteConverted, noteDifferential); 87: note.transfer(treasury, amtToSweep);
  1. AccountantDelegator.sol
  • Remove "data" variable of supplyMarket, redeemMarket and sweepInterest function
55: bytes memory data = delegateToImplementation(abi.encodeWithSignature("supplyMarket(uint256)", amount)); 56: return abi.decode(data, (uint)); 64: bytes memory data = delegateToImplementation(abi.encodeWithSignature("redeemMarket(uint256)", amount)); 65: return abi.decode(data, (uint)); 72: bytes memory data = delegateToImplementation(abi.encodeWithSignature("sweepInterest()")); 73: return abi.decode(data, (uint));
  1. BaseV1-core.sol
  • Remove "_point" variable of _update function
162: Observation memory _point = lastObservation(); 163: timeElapsed = blockTimestamp - _point.timestamp; // compare the last observation with current timestamp, if greater than 30 minutes, record a new event
  1. Comptroller.sol
  • Remove "assetsIn" variable of getAssetsIn function
102: CToken[] memory assetsIn = accountAssets[account]; 104: return assetsIn;

For mitigation, simply don't define variable that is used only once. Below is mitigation example of above 1.

return cnote.redeem(div_(amount, Exp({mantissa: cnote.exchangeRateStored()}))

[G-05] Redundant Use of Variable

Below has redundant use of variables. Instead of defining a new variable, emit the event first and then update the variable.

Issue found in

  1. GovernorBravoDelegate.sol
144: function _setPendingAdmin(address newPendingAdmin) external { 146: require(msg.sender == admin, "GovernorBravo:_setPendingAdmin: admin only"); 149: address oldPendingAdmin = pendingAdmin; 152: pendingAdmin = newPendingAdmin; 155: emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin);

Change it to

function _setPendingAdmin(address newPendingAdmin) external { require(msg.sender == admin, "GovernorBravo:_setPendingAdmin: admin only"); emit NewPendingAdmin(pendingAdmin, newPendingAdmin); pendingAdmin = newPendingAdmin;
  1. GovernorBravoDelegate.sol
162: function _acceptadmin() external { 164: require(msg.sender == pendingadmin && msg.sender != address(0), "governorbravo:_acceptadmin: pending admin only"); 167: address oldadmin = admin; 168: address oldpendingadmin = pendingadmin; 171: admin = pendingadmin; 174: pendingadmin = address(0); 176: emit newadmin(oldadmin, admin); 177: emit newpendingadmin(oldpendingadmin, pendingadmin);

Change it to

function _acceptadmin() external { require(msg.sender == pendingadmin && msg.sender != address(0), "governorbravo:_acceptadmin: pending admin only"); emit newadmin(admin, pendingadmin); emit newpendingadmin(pendingadmin, address(0)); admin = pendingadmin; pendingadmin = address(0);
  1. TreasuryDelegator.sol
29: function _setImplementation(address implementation_) override public { 31: require(msg.sender == admin, "GovernorBravoDelegator::_setImplementation: admin only"); 32: require(implementation_ != address(0), "GovernorBravoDelegator::_setImplementation: invalid implementation address"); 34: address oldImplementation = implementation; 35: implementation = implementation_; 37: emit NewImplementation(oldImplementation, implementation_);

Change it to

function _setImplementation(address implementation_) override public { require(msg.sender == admin, "GovernorBravoDelegator::_setImplementation: admin only"); require(implementation_ != address(0), "GovernorBravoDelegator::_setImplementation: invalid implementation address"); emit NewImplementation(implementation, implementation_); implementation = implementation_;
  1. Comptroller.sol
826: function _setPriceOracle(PriceOracle newOracle) public returns (uint) { 828: if (msg.sender != admin) { 829: return fail(Error.UNAUTHORIZED, FailureInfo.SET_PRICE_ORACLE_OWNER_CHECK); 833: PriceOracle oldOracle = oracle; 836: oracle = newOracle; 839: emit NewPriceOracle(oldOracle, newOracle); 841: return uint(Error.NO_ERROR);

Change it to

function _setPriceOracle(PriceOracle newOracle) public returns (uint) { if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SET_PRICE_ORACLE_OWNER_CHECK); emit NewPriceOracle(oracle, newOracle); oracle = newOracle; return uint(Error.NO_ERROR);
  1. Comptroller.sol
850: function _setclosefactor(uint newclosefactormantissa) external returns (uint) { 852: require(msg.sender == admin, "only admin can set close factor"); 854: uint oldCloseFactorMantissa = closeFactorMantissa; 855: closeFactorMantissa = newCloseFactorMantissa; 856: emit NewCloseFactor(oldCloseFactorMantissa, closeFactorMantissa); 858: return uint(Error.NO_ERROR);

Change it to

function _setclosefactor(uint newclosefactormantissa) external returns (uint) { require(msg.sender == admin, "only admin can set close factor"); emit NewCloseFactor(closeFactorMantissa, newCloseFactorMantissa); closeFactorMantissa = newCloseFactorMantissa; return uint(Error.NO_ERROR);

[G-06] != 0 costs less gass then > 0

!= 0 costs less gas when optimizer is enabled and is used for unsigned integers in "require" statement. I suggest changing > 0 to != 0

Issue found at:

./CNote.sol:310: require(getCashPrior() == 0, "CNote::redeemFresh, LendingMarket has > 0 Cash before Accountant Supplies"); ./BaseV1-core.sol:253: require(liquidity > 0, 'ILM'); // BaseV1: INSUFFICIENT_LIQUIDITY_MINTED ./BaseV1-core.sol:272: require(amount0 > 0 && amount1 > 0, 'ILB'); // BaseV1: INSUFFICIENT_LIQUIDITY_BURNED ./BaseV1-core.sol:286: require(amount0Out > 0 || amount1Out > 0, 'IOA'); // BaseV1: INSUFFICIENT_OUTPUT_AMOUNT ./BaseV1-core.sol:303: require(amount0In > 0 || amount1In > 0, 'IIA'); // BaseV1: INSUFFICIENT_INPUT_AMOUNT ./BaseV1-core.sol:465: require(token.code.length > 0); ./BaseV1-periphery.sol:104: require(amountA > 0, 'BaseV1Router: INSUFFICIENT_AMOUNT'); ./BaseV1-periphery.sol:105: require(reserveA > 0 && reserveB > 0, 'BaseV1Router: INSUFFICIENT_LIQUIDITY'); ./BaseV1-periphery.sol:456: require(token.code.length > 0); ./BaseV1-periphery.sol:463: require(token.code.length > 0, "token code length faialure");

[G-07] Reduce the long revert strings of error messages

By keeping the revert strings within 32 bytes will save you gas since each slot is 32 bytes.

Following are revert strings that are more than 32 bytes.

./AccountantDelegator.sol:43: require(msg.sender == admin, "AccountantDelegator::_setImplementation: admin only"); ./AccountantDelegator.sol:44: require(implementation_ != address(0), "AccountantDelegator::_setImplementation: invalid implementation address"); ./AccountantDelegator.sol:124: require(msg.value == 0,"AccountantDelegator:fallback: cannot send value to fallback"); ./Comptroller.sol:178: require(oErr == 0, "exitMarket: getAccountSnapshot failed"); // semi-opaque error code ./Comptroller.sol:491: require(borrowBalance >= repayAmount, "Can not repay more than the total borrow"); ./Comptroller.sol:998: require(msg.sender == admin || msg.sender == borrowCapGuardian, "only admin or borrow cap guardian can set borrow caps"); ./Comptroller.sol:1016: require(msg.sender == admin, "only admin can set borrow cap guardian"); ./Comptroller.sol:1051: require(markets[address(cToken)].isListed, "cannot pause a market that is not listed"); ./Comptroller.sol:1052: require(msg.sender == pauseGuardian || msg.sender == admin, "only pause guardian and admin can pause"); ./Comptroller.sol:1061: require(markets[address(cToken)].isListed, "cannot pause a market that is not listed"); ./Comptroller.sol:1062: require(msg.sender == pauseGuardian || msg.sender == admin, "only pause guardian and admin can pause"); ./Comptroller.sol:1071: require(msg.sender == pauseGuardian || msg.sender == admin, "only pause guardian and admin can pause"); ./Comptroller.sol:1080: require(msg.sender == pauseGuardian || msg.sender == admin, "only pause guardian and admin can pause"); ./Comptroller.sol:1089: require(msg.sender == unitroller.admin(), "only unitroller admin can change brains"); ./Comptroller.sol:1095: require(msg.sender == admin, "Only admin can call this function"); // Only the timelock can call this function ./Comptroller.sol:1096: require(!proposal65FixExecuted, "Already executed this one-off function"); // Require that this function is only called once ./Comptroller.sol:1411: require(numTokens == supplySpeeds.length && numTokens == borrowSpeeds.length, "Comptroller::_setCompSpeeds invalid input"); ./NoteInterest.sol:141: require(msg.sender == admin, "only the admin may set the base rate"); ./NoteInterest.sol:154: require(msg.sender == admin, "only the admin may set the adjuster coefficient"); ./NoteInterest.sol:167: require(msg.sender == admin, "only the admin may set the update frequency"); ./CNote.sol:16: require(msg.sender == admin, "CNote::_setAccountantContract:Only admin may call this function"); ./CNote.sol:43: require(getCashPrior() == 0, "CNote::borrowFresh:Impossible reserves in CNote market Place"); ./CNote.sol:45: require(address(_accountant) != address(0), "CNote::borrowFresh:Accountant has not been initialized"); ./CNote.sol:54: require(getCashPrior() == borrowAmount, "CNote::borrowFresh:Error in Accountant supply"); ./CNote.sol:77: require(getCashPrior() == 0,"CNote::borrowFresh: Error in doTransferOut, impossible Liquidity in LendingMarket"); ./CNote.sol:114: require(getCashPrior() == 0, "CNote::repayBorrowFresh:Liquidity in Note Lending Market is always flashed"); ./CNote.sol:130: require(getCashPrior() >= actualRepayAmount, "CNote::repayBorrowFresh: doTransferIn supplied incorrect amount"); //sanity check that Accountant has some thing to redeem ./CNote.sol:146: require(getCashPrior() == 0, "CNote::repayBorrowFresh: Error in Accountant.redeemMarket"); ./CNote.sol:198: require(getCashPrior() == 0, "CNote::mintFresh: Any Liquidity in the Lending Market is flashed"); ./CNote.sol:214: require(getCashPrior() == actualMintAmount, "CNote::mintFresh: Error in doTransferIn, CNote reserves >= mint Amount"); //sanity check that Accountant has some thing to redeem ./CNote.sol:264: require(redeemTokensIn == 0 || redeemAmountIn == 0, "one of redeemTokensIn or redeemAmountIn must be zero"); ./CNote.sol:310: require(getCashPrior() == 0, "CNote::redeemFresh, LendingMarket has > 0 Cash before Accountant Supplies"); ./CNote.sol:330: require(getCashPrior() == redeemAmount, "CNote::redeemFresh: Accountant has supplied incorrect Amount"); ./TreasuryDelegator.sol:31: require(msg.sender == admin, "GovernorBravoDelegator::_setImplementation: admin only"); ./TreasuryDelegator.sol:32: require(implementation_ != address(0), "GovernorBravoDelegator::_setImplementation: invalid implementation address"); ./GovernorBravoDelegate.sol:25: require(address(timelock) == address(0), "GovernorBravo::initialize: can only initialize once"); ./GovernorBravoDelegate.sol:26: require(msg.sender == admin, "GovernorBravo::initialize: admin only"); ./GovernorBravoDelegate.sol:27: require(timelock_ != address(0), "GovernorBravo::initialize: invalid timelock address"); ./GovernorBravoDelegate.sol:42-45: require(unigovProposal.targets.length == unigovProposal.values.length && unigovProposal.targets.length == unigovProposal.signatures.length && unigovProposal.targets.length == unigovProposal.calldatas.length, "GovernorBravo::propose: proposal function information arity mismatch"); ./GovernorBravoDelegate.sol:46: require(unigovProposal.targets.length != 0, "GovernorBravo::propose: must provide actions"); ./GovernorBravoDelegate.sol:47: require(unigovProposal.targets.length <= proposalMaxOperations, "GovernorBravo::propose: too many actions"); ./GovernorBravoDelegate.sol:78: require(!timelock.queuedTransactions(keccak256(abi.encode(target, value, signature, data, eta))), "GovernorBravo::queueOrRevertInternal: identical proposal action already queued at eta"); ./GovernorBravoDelegate.sol:87: require(state(proposalId) == ProposalState.Queued, "GovernorBravo::execute: proposal can only be executed if it is queued"); ./GovernorBravoDelegate.sol:115: require(proposalCount >= proposalId && proposalId > initialProposalId, "GovernorBravo::state: invalid proposal id"); ./GovernorBravoDelegate.sol:132: require(msg.sender == admin, "GovernorBravo::_initiate: admin only"); ./GovernorBravoDelegate.sol:133: require(initialProposalId == 0, "GovernorBravo::_initiate: can only initiate once"); ./GovernorBravoDelegate.sol:146: require(msg.sender == admin, "GovernorBravo:_setPendingAdmin: admin only"); ./GovernorBravoDelegate.sol:164: require(msg.sender == pendingAdmin && msg.sender != address(0), "GovernorBravo:_acceptAdmin: pending admin only"); ./WETH.sol:29: require(_balanceOf[msg.sender] >= wad, "sender balance insufficient for withdrawal"); ./WETH.sol:96: require(owner != address(0), "ERC20: approve from the zero address"); ./WETH.sol:97: require(spender != address(0), "ERC20: approve to the zero address"); ./AccountantDelegate.sol:17: require(msg.sender == admin, "AccountantDelegate::initialize: only admin can call this function"); ./AccountantDelegate.sol:18: require(noteAddress_ != address(0), "AccountantDelegate::initialize: note Address invalid"); ./AccountantDelegate.sol:29: require(note.balanceOf(msg.sender) == note._initialSupply(), "AccountantDelegate::initiatlize: Accountant has not received payment"); ./AccountantDelegate.sol:48: require(msg.sender == address(cnote), "AccountantDelegate::supplyMarket: Only the CNote contract can supply market"); ./AccountantDelegate.sol:60: require(msg.sender == address(cnote), "AccountantDelegate::redeemMarket: Only the CNote contract can redeem market"); ./AccountantDelegate.sol:83: require(cNoteConverted >= noteDifferential, "Note Loaned to LendingMarket must increase in value"); ./TreasuryDelegate.sol:47: require(msg.sender == admin, "Treasury::sendFund can only be called by admin"); ./BaseV1-periphery.sol:86: require(tokenA != tokenB, 'BaseV1Router: IDENTICAL_ADDRESSES'); ./BaseV1-periphery.sol:104: require(amountA > 0, 'BaseV1Router: INSUFFICIENT_AMOUNT'); ./BaseV1-periphery.sol:105: require(reserveA > 0 && reserveB > 0, 'BaseV1Router: INSUFFICIENT_LIQUIDITY'); ./BaseV1-periphery.sol:223: require(amountBOptimal >= amountBMin, 'BaseV1Router: INSUFFICIENT_B_AMOUNT'); ./BaseV1-periphery.sol:228: require(amountAOptimal >= amountAMin, 'BaseV1Router: INSUFFICIENT_A_AMOUNT'); ./BaseV1-periphery.sol:295: require(amountA >= amountAMin, 'BaseV1Router: INSUFFICIENT_A_AMOUNT'); ./BaseV1-periphery.sol:296: require(amountB >= amountBMin, 'BaseV1Router: INSUFFICIENT_B_AMOUNT'); ./BaseV1-periphery.sol:387: require(amounts[amounts.length - 1] >= amountOutMin, 'BaseV1Router: INSUFFICIENT_OUTPUT_AMOUNT'); ./BaseV1-periphery.sol:402: require(amounts[amounts.length - 1] >= amountOutMin, 'BaseV1Router: INSUFFICIENT_OUTPUT_AMOUNT'); ./BaseV1-periphery.sol:417: require(amounts[amounts.length - 1] >= amountOutMin, 'BaseV1Router: INSUFFICIENT_OUTPUT_AMOUNT'); ./BaseV1-periphery.sol:430: require(amounts[amounts.length - 1] >= amountOutMin, 'BaseV1Router: INSUFFICIENT_OUTPUT_AMOUNT'); ./BaseV1-periphery.sol:452: require(success, 'TransferHelper: ETH_TRANSFER_FAILED');

[G-08] Use Custom Errors to Save Gas

Custom errors from Solidity 0.8.4 are cheaper than revert strings. Details are explained here: https://blog.soliditylang.org/2021/04/21/custom-errors/

I recommend using custom errors.

./AccountantDelegator.sol:43: require(msg.sender == admin, "AccountantDelegator::_setImplementation: admin only"); ./AccountantDelegator.sol:44: require(implementation_ != address(0), "AccountantDelegator::_setImplementation: invalid implementation address"); ./AccountantDelegator.sol:124: require(msg.value == 0,"AccountantDelegator:fallback: cannot send value to fallback"); ./Comptroller.sol:178: require(oErr == 0, "exitMarket: getAccountSnapshot failed"); // semi-opaque error code ./Comptroller.sol:237: require(!mintGuardianPaused[cToken], "mint is paused"); ./Comptroller.sol:343: require(!borrowGuardianPaused[cToken], "borrow is paused"); ./Comptroller.sol:351: require(msg.sender == cToken, "sender must be cToken"); ./Comptroller.sol:373: require(nextTotalBorrows < borrowCap, "market borrow cap reached"); ./Comptroller.sol:491: require(borrowBalance >= repayAmount, "Can not repay more than the total borrow"); ./Comptroller.sol:556: require(!seizeGuardianPaused, "seize is paused"); ./Comptroller.sol:614: require(!transferGuardianPaused, "transfer is paused"); ./Comptroller.sol:852: require(msg.sender == admin, "only admin can set close factor"); ./Comptroller.sol:960: require(allMarkets[i] != CToken(cToken), "market already added"); ./Comptroller.sol:998: require(msg.sender == admin || msg.sender == borrowCapGuardian, "only admin or borrow cap guardian can set borrow caps"); ./Comptroller.sol:1003: require(numMarkets != 0 && numMarkets == numBorrowCaps, "invalid input"); ./Comptroller.sol:1016: require(msg.sender == admin, "only admin can set borrow cap guardian"); ./Comptroller.sol:1051: require(markets[address(cToken)].isListed, "cannot pause a market that is not listed"); ./Comptroller.sol:1052: require(msg.sender == pauseGuardian || msg.sender == admin, "only pause guardian and admin can pause"); ./Comptroller.sol:1053: require(msg.sender == admin || state == true, "only admin can unpause"); ./Comptroller.sol:1061: require(markets[address(cToken)].isListed, "cannot pause a market that is not listed"); ./Comptroller.sol:1062: require(msg.sender == pauseGuardian || msg.sender == admin, "only pause guardian and admin can pause"); ./Comptroller.sol:1063: require(msg.sender == admin || state == true, "only admin can unpause"); ./Comptroller.sol:1071: require(msg.sender == pauseGuardian || msg.sender == admin, "only pause guardian and admin can pause"); ./Comptroller.sol:1072: require(msg.sender == admin || state == true, "only admin can unpause"); ./Comptroller.sol:1080: require(msg.sender == pauseGuardian || msg.sender == admin, "only pause guardian and admin can pause"); ./Comptroller.sol:1081: require(msg.sender == admin || state == true, "only admin can unpause"); ./Comptroller.sol:1089: require(msg.sender == unitroller.admin(), "only unitroller admin can change brains"); ./Comptroller.sol:1090: require(unitroller._acceptImplementation() == 0, "change not authorized"); ./Comptroller.sol:1095: require(msg.sender == admin, "Only admin can call this function"); // Only the timelock can call this function ./Comptroller.sol:1096: require(!proposal65FixExecuted, "Already executed this one-off function"); // Require that this function is only called once ./Comptroller.sol:1097: require(affectedUsers.length == amounts.length, "Invalid input"); ./Comptroller.sol:1158: require(market.isListed, "comp market is not listed"); ./Comptroller.sol:1349: require(markets[address(cToken)].isListed, "market must be listed"); ./Comptroller.sol:1395: require(adminOrInitializing(), "only admin can grant comp"); ./Comptroller.sol:1397: require(amountLeft == 0, "insufficient comp for grant"); ./Comptroller.sol:1408: require(adminOrInitializing(), "only admin can set comp speed"); ./Comptroller.sol:1411: require(numTokens == supplySpeeds.length && numTokens == borrowSpeeds.length, "Comptroller::_setCompSpeeds invalid input"); ./Comptroller.sol:1424: require(adminOrInitializing(), "only admin can set comp speed"); ./NoteInterest.sol:141: require(msg.sender == admin, "only the admin may set the base rate"); ./NoteInterest.sol:154: require(msg.sender == admin, "only the admin may set the adjuster coefficient"); ./NoteInterest.sol:167: require(msg.sender == admin, "only the admin may set the update frequency"); ./CNote.sol:16: require(msg.sender == admin, "CNote::_setAccountantContract:Only admin may call this function"); ./CNote.sol:43: require(getCashPrior() == 0, "CNote::borrowFresh:Impossible reserves in CNote market Place"); ./CNote.sol:45: require(address(_accountant) != address(0), "CNote::borrowFresh:Accountant has not been initialized"); ./CNote.sol:54: require(getCashPrior() == borrowAmount, "CNote::borrowFresh:Error in Accountant supply"); ./CNote.sol:77: require(getCashPrior() == 0,"CNote::borrowFresh: Error in doTransferOut, impossible Liquidity in LendingMarket"); ./CNote.sol:113: //this cannot be a require statement, it must be accounted for if this is the case ./CNote.sol:114: require(getCashPrior() == 0, "CNote::repayBorrowFresh:Liquidity in Note Lending Market is always flashed"); ./CNote.sol:130: require(getCashPrior() >= actualRepayAmount, "CNote::repayBorrowFresh: doTransferIn supplied incorrect amount"); //sanity check that Accountant has some thing to redeem ./CNote.sol:146: require(getCashPrior() == 0, "CNote::repayBorrowFresh: Error in Accountant.redeemMarket"); ./CNote.sol:198: require(getCashPrior() == 0, "CNote::mintFresh: Any Liquidity in the Lending Market is flashed"); ./CNote.sol:214: require(getCashPrior() == actualMintAmount, "CNote::mintFresh: Error in doTransferIn, CNote reserves >= mint Amount"); //sanity check that Accountant has some thing to redeem ./CNote.sol:229: require(getCashPrior() == 0); ./CNote.sol:264: require(redeemTokensIn == 0 || redeemAmountIn == 0, "one of redeemTokensIn or redeemAmountIn must be zero"); ./CNote.sol:310: require(getCashPrior() == 0, "CNote::redeemFresh, LendingMarket has > 0 Cash before Accountant Supplies"); ./CNote.sol:330: require(getCashPrior() == redeemAmount, "CNote::redeemFresh: Accountant has supplied incorrect Amount"); ./CNote.sol:353: require(_notEntered, "re-entered"); ./TreasuryDelegator.sol:31: require(msg.sender == admin, "GovernorBravoDelegator::_setImplementation: admin only"); ./TreasuryDelegator.sol:32: require(implementation_ != address(0), "GovernorBravoDelegator::_setImplementation: invalid implementation address"); ./GovernorBravoDelegate.sol:25: require(address(timelock) == address(0), "GovernorBravo::initialize: can only initialize once"); ./GovernorBravoDelegate.sol:26: require(msg.sender == admin, "GovernorBravo::initialize: admin only"); ./GovernorBravoDelegate.sol:27: require(timelock_ != address(0), "GovernorBravo::initialize: invalid timelock address"); ./GovernorBravoDelegate.sol:42: require(unigovProposal.targets.length == unigovProposal.values.length && ./GovernorBravoDelegate.sol:46: require(unigovProposal.targets.length != 0, "GovernorBravo::propose: must provide actions"); ./GovernorBravoDelegate.sol:47: require(unigovProposal.targets.length <= proposalMaxOperations, "GovernorBravo::propose: too many actions"); ./GovernorBravoDelegate.sol:53: require(proposals[unigovProposal.id].id == 0); ./GovernorBravoDelegate.sol:78: require(!timelock.queuedTransactions(keccak256(abi.encode(target, value, signature, data, eta))), "GovernorBravo::queueOrRevertInternal: identical proposal action already queued at eta"); ./GovernorBravoDelegate.sol:87: require(state(proposalId) == ProposalState.Queued, "GovernorBravo::execute: proposal can only be executed if it is queued"); ./GovernorBravoDelegate.sol:115: require(proposalCount >= proposalId && proposalId > initialProposalId, "GovernorBravo::state: invalid proposal id"); ./GovernorBravoDelegate.sol:132: require(msg.sender == admin, "GovernorBravo::_initiate: admin only"); ./GovernorBravoDelegate.sol:133: require(initialProposalId == 0, "GovernorBravo::_initiate: can only initiate once"); ./GovernorBravoDelegate.sol:146: require(msg.sender == admin, "GovernorBravo:_setPendingAdmin: admin only"); ./GovernorBravoDelegate.sol:164: require(msg.sender == pendingAdmin && msg.sender != address(0), "GovernorBravo:_acceptAdmin: pending admin only"); ./GovernorBravoDelegate.sol:182: require(c >= a, "addition overflow"); ./GovernorBravoDelegate.sol:187: require(b <= a, "subtraction underflow"); ./BaseV1-core.sol:125: require(_unlocked == 1); ./BaseV1-core.sol:253: require(liquidity > 0, 'ILM'); // BaseV1: INSUFFICIENT_LIQUIDITY_MINTED ./BaseV1-core.sol:272: require(amount0 > 0 && amount1 > 0, 'ILB'); // BaseV1: INSUFFICIENT_LIQUIDITY_BURNED ./BaseV1-core.sol:285: require(!BaseV1Factory(factory).isPaused()); ./BaseV1-core.sol:286: require(amount0Out > 0 || amount1Out > 0, 'IOA'); // BaseV1: INSUFFICIENT_OUTPUT_AMOUNT ./BaseV1-core.sol:288: require(amount0Out < _reserve0 && amount1Out < _reserve1, 'IL'); // BaseV1: INSUFFICIENT_LIQUIDITY ./BaseV1-core.sol:294: require(to != _token0 && to != _token1, 'IT'); // BaseV1: INVALID_TO ./BaseV1-core.sol:303: require(amount0In > 0 || amount1In > 0, 'IIA'); // BaseV1: INSUFFICIENT_INPUT_AMOUNT ./BaseV1-core.sol:309: require(_k(_balance0, _balance1) >= _k(_reserve0, _reserve1), 'K'); // BaseV1: K ./BaseV1-core.sol:413: require(deadline >= block.timestamp, 'BaseV1: EXPIRED'); ./BaseV1-core.sol:431: require(recoveredAddress != address(0) && recoveredAddress == owner, 'BaseV1: INVALID_SIGNATURE'); ./BaseV1-core.sol:465: require(token.code.length > 0); ./BaseV1-core.sol:468: require(success && (data.length == 0 || abi.decode(data, (bool)))); ./BaseV1-core.sol:498: require(msg.sender == pauser); ./BaseV1-core.sol:503: require(msg.sender == pendingPauser); ./BaseV1-core.sol:508: require(msg.sender == pauser); ./BaseV1-core.sol:521: require(tokenA != tokenB, 'IA'); // BaseV1: IDENTICAL_ADDRESSES ./BaseV1-core.sol:523: require(token0 != address(0), 'ZA'); // BaseV1: ZERO_ADDRESS ./BaseV1-core.sol:524: require(getPair[token0][token1][stable] == address(0), 'PE'); // BaseV1: PAIR_EXISTS - single check is sufficient ./WETH.sol:29: require(_balanceOf[msg.sender] >= wad, "sender balance insufficient for withdrawal"); ./WETH.sol:69: require(_balanceOf[src] >= wad); ./WETH.sol:72: require(_allowance[src][msg.sender] >= wad); ./WETH.sol:96: require(owner != address(0), "ERC20: approve from the zero address"); ./WETH.sol:97: require(spender != address(0), "ERC20: approve to the zero address"); ./AccountantDelegate.sol:17: require(msg.sender == admin, "AccountantDelegate::initialize: only admin can call this function"); ./AccountantDelegate.sol:18: require(noteAddress_ != address(0), "AccountantDelegate::initialize: note Address invalid"); ./AccountantDelegate.sol:29: require(note.balanceOf(msg.sender) == note._initialSupply(), "AccountantDelegate::initiatlize: Accountant has not received payment"); ./AccountantDelegate.sol:48: require(msg.sender == address(cnote), "AccountantDelegate::supplyMarket: Only the CNote contract can supply market"); ./AccountantDelegate.sol:60: require(msg.sender == address(cnote), "AccountantDelegate::redeemMarket: Only the CNote contract can redeem market"); ./AccountantDelegate.sol:83: require(cNoteConverted >= noteDifferential, "Note Loaned to LendingMarket must increase in value"); ./TreasuryDelegate.sol:16: require(msg.sender == admin); ./TreasuryDelegate.sol:17: require(note_ != address(0)); ./TreasuryDelegate.sol:47: require(msg.sender == admin, "Treasury::sendFund can only be called by admin"); ./BaseV1-periphery.sol:71: require(deadline >= block.timestamp, 'BaseV1Router: EXPIRED'); ./BaseV1-periphery.sol:86: require(tokenA != tokenB, 'BaseV1Router: IDENTICAL_ADDRESSES'); ./BaseV1-periphery.sol:88: require(token0 != address(0), 'BaseV1Router: ZERO_ADDRESS'); ./BaseV1-periphery.sol:104: require(amountA > 0, 'BaseV1Router: INSUFFICIENT_AMOUNT'); ./BaseV1-periphery.sol:105: require(reserveA > 0 && reserveB > 0, 'BaseV1Router: INSUFFICIENT_LIQUIDITY'); ./BaseV1-periphery.sol:133: require(routes.length >= 1, 'BaseV1Router: INVALID_PATH'); ./BaseV1-periphery.sol:210: require(amountADesired >= amountAMin); ./BaseV1-periphery.sol:211: require(amountBDesired >= amountBMin); ./BaseV1-periphery.sol:223: require(amountBOptimal >= amountBMin, 'BaseV1Router: INSUFFICIENT_B_AMOUNT'); ./BaseV1-periphery.sol:228: require(amountAOptimal >= amountAMin, 'BaseV1Router: INSUFFICIENT_A_AMOUNT'); ./BaseV1-periphery.sol:291: require(IBaseV1Pair(pair).transferFrom(msg.sender, pair, liquidity)); // send liquidity to pair ./BaseV1-periphery.sol:295: require(amountA >= amountAMin, 'BaseV1Router: INSUFFICIENT_A_AMOUNT'); ./BaseV1-periphery.sol:296: require(amountB >= amountBMin, 'BaseV1Router: INSUFFICIENT_B_AMOUNT'); ./BaseV1-periphery.sol:387: require(amounts[amounts.length - 1] >= amountOutMin, 'BaseV1Router: INSUFFICIENT_OUTPUT_AMOUNT'); ./BaseV1-periphery.sol:402: require(amounts[amounts.length - 1] >= amountOutMin, 'BaseV1Router: INSUFFICIENT_OUTPUT_AMOUNT'); ./BaseV1-periphery.sol:415: require(routes[0].from == address(wcanto), 'BaseV1Router: INVALID_PATH'); ./BaseV1-periphery.sol:417: require(amounts[amounts.length - 1] >= amountOutMin, 'BaseV1Router: INSUFFICIENT_OUTPUT_AMOUNT'); ./BaseV1-periphery.sol:428: require(routes[routes.length - 1].to == address(wcanto), 'BaseV1Router: INVALID_PATH'); ./BaseV1-periphery.sol:430: require(amounts[amounts.length - 1] >= amountOutMin, 'BaseV1Router: INSUFFICIENT_OUTPUT_AMOUNT'); ./BaseV1-periphery.sol:452: require(success, 'TransferHelper: ETH_TRANSFER_FAILED'); ./BaseV1-periphery.sol:456: require(token.code.length > 0); ./BaseV1-periphery.sol:459: require(success && (data.length == 0 || abi.decode(data, (bool)))); ./BaseV1-periphery.sol:463: require(token.code.length > 0, "token code length faialure"); ./BaseV1-periphery.sol:466: require(success && (data.length == 0 || abi.decode(data, (bool))), "failing here");

[G-09] Duplicate require() checks should be a modifier or a function

Since below require checks are used more than once, I recommend making these to a modifier or a function.

./Comptroller.sol:1051: require(markets[address(cToken)].isListed, "cannot pause a market that is not listed"); ./Comptroller.sol:1061: require(markets[address(cToken)].isListed, "cannot pause a market that is not listed");
./Comptroller.sol:1052: require(msg.sender == pauseGuardian || msg.sender == admin, "only pause guardian and admin can pause"); ./Comptroller.sol:1062: require(msg.sender == pauseGuardian || msg.sender == admin, "only pause guardian and admin can pause"); ./Comptroller.sol:1071: require(msg.sender == pauseGuardian || msg.sender == admin, "only pause guardian and admin can pause"); ./Comptroller.sol:1080: require(msg.sender == pauseGuardian || msg.sender == admin, "only pause guardian and admin can pause");
./Comptroller.sol:1053: require(msg.sender == admin || state == true, "only admin can unpause"); ./Comptroller.sol:1063: require(msg.sender == admin || state == true, "only admin can unpause"); ./Comptroller.sol:1072: require(msg.sender == admin || state == true, "only admin can unpause"); ./Comptroller.sol:1081: require(msg.sender == admin || state == true, "only admin can unpause");
./Comptroller.sol:1408: require(adminOrInitializing(), "only admin can set comp speed"); ./Comptroller.sol:1424: require(adminOrInitializing(), "only admin can set comp speed");
./BaseV1-core.sol:465: require(token.code.length > 0); ./BaseV1-periphery.sol:456: require(token.code.length > 0);
./BaseV1-core.sol:468: require(success && (data.length == 0 || abi.decode(data, (bool)))); ./BaseV1-periphery.sol:459: require(success && (data.length == 0 || abi.decode(data, (bool))));
./BaseV1-core.sol:498: require(msg.sender == pauser); ./BaseV1-core.sol:508: require(msg.sender == pauser);
./BaseV1-periphery.sol:387: require(amounts[amounts.length - 1] >= amountOutMin, 'BaseV1Router: INSUFFICIENT_OUTPUT_AMOUNT'); ./BaseV1-periphery.sol:402: require(amounts[amounts.length - 1] >= amountOutMin, 'BaseV1Router: INSUFFICIENT_OUTPUT_AMOUNT'); ./BaseV1-periphery.sol:417: require(amounts[amounts.length - 1] >= amountOutMin, 'BaseV1Router: INSUFFICIENT_OUTPUT_AMOUNT'); ./BaseV1-periphery.sol:430: require(amounts[amounts.length - 1] >= amountOutMin, 'BaseV1Router: INSUFFICIENT_OUTPUT_AMOUNT');

[G-10] Both named returns and return statement are used Removing unused named returns variable in below code can save gas and improve code readability.

Issue found at BaseV1-periphery.sol

Remove returns variable "amount" and "stable" 117: function getAmountOut(uint amountIn, address tokenIn, address tokenOut) external view returns (uint amount, bool stable) { 128: return amountStable > amountVolatile ? (amountStable, true) : (amountVolatile, false);

BaseV1-core.sol

Remove returns variable "dec0", "dec1", "r0", "r1", "st", "t0" and "t1" 139: function metadata() external view returns (uint dec0, uint dec1, uint r0, uint r1, bool st, address t0, address t1) { 140: return (decimals0, decimals1, reserve0, reserve1, stable, token0, token1);
Remove returns variable "amountOut" 204: function quote(address tokenIn, uint amountIn, uint granularity) external view returns (uint amountOut) { 210: return priceAverageCumulative / granularity;

GovernorBravoDelegate.sol

Remove returns variable "targets", "values", "signatures" and "calldatas" 104: function getActions(uint proposalId) external view returns (address[] memory targets, uint[] memory values, string[] memory signatures, bytes[] memory calldatas) { 105: Proposal storage p = proposals[proposalId]; 106: return (p.targets, p.values, p.signatures, p.calldatas);

[G-11] Use require instead of && When there are multiple conditions in require statement, break down the require statement into multiple require statements instead of using && can save gas.

Issue found at

./Comptroller.sol:1003: require(numMarkets != 0 && numMarkets == numBorrowCaps, "invalid input"); ./Comptroller.sol:1411: require(numTokens == supplySpeeds.length && numTokens == borrowSpeeds.length, "Comptroller::_setCompSpeeds invalid input"); ./GovernorBravoDelegate.sol:42-45: require(unigovProposal.targets.length == unigovProposal.values.length && unigovProposal.targets.length == unigovProposal.signatures.length && unigovProposal.targets.length == unigovProposal.calldatas.length, "GovernorBravo::propose: proposal function information arity mismatch"); ./GovernorBravoDelegate.sol:115: require(proposalCount >= proposalId && proposalId > initialProposalId, "GovernorBravo::state: invalid proposal id"); ./GovernorBravoDelegate.sol:164: require(msg.sender == pendingAdmin && msg.sender != address(0), "GovernorBravo:_acceptAdmin: pending admin only"); ./BaseV1-core.sol:272: require(amount0 > 0 && amount1 > 0, 'ILB'); // BaseV1: INSUFFICIENT_LIQUIDITY_BURNED ./BaseV1-core.sol:288: require(amount0Out < _reserve0 && amount1Out < _reserve1, 'IL'); // BaseV1: INSUFFICIENT_LIQUIDITY ./BaseV1-core.sol:294: require(to != _token0 && to != _token1, 'IT'); // BaseV1: INVALID_TO ./BaseV1-core.sol:431: require(recoveredAddress != address(0) && recoveredAddress == owner, 'BaseV1: INVALID_SIGNATURE'); ./BaseV1-core.sol:468: require(success && (data.length == 0 || abi.decode(data, (bool)))); ./BaseV1-periphery.sol:105: require(reserveA > 0 && reserveB > 0, 'BaseV1Router: INSUFFICIENT_LIQUIDITY'); ./BaseV1-periphery.sol:459: require(success && (data.length == 0 || abi.decode(data, (bool)))); ./BaseV1-periphery.sol:466: require(success && (data.length == 0 || abi.decode(data, (bool))), "failing here");

For example these can be changed to

require(numMarkets != 0); require(numMarkets == numBorrowCaps, "invalid input");

[G-12] Use Calldata instead of Memory for Read Only Function Parameters

It is cheaper gas to use calldata than memory if the function parameter is read only. Calldata is a non-modifiable, non-persistent area where function arguments are stored, and behaves mostly like memory.

I recommend changing following memory to calldata

./AccountantDelegator.sol:82: function delegateTo(address callee, bytes memory data) internal returns(bytes memory) { ./AccountantDelegator.sol:98: function delegateToImplementation(bytes memory data) public returns (bytes memory) { ./AccountantDelegator.sol:109: function delegateToViewImplementation(bytes memory data) public view returns (bytes memory) { ./Comptroller.sol:122: function enterMarkets(address[] memory cTokens) override public returns (uint[] memory) { ./Comptroller.sol:1210: function updateCompBorrowIndex(address cToken, Exp memory marketBorrowIndex) internal { ./Comptroller.sol:1270: function distributeBorrowerComp(address cToken, address borrower, Exp memory marketBorrowIndex) internal { ./Comptroller.sol:1333: function claimComp(address holder, CToken[] memory cTokens) public { ./Comptroller.sol:1346: function claimComp(address[] memory holders, CToken[] memory cTokens, bool borrowers, bool suppliers) public { ./Comptroller.sol:1407: function _setCompSpeeds(CToken[] memory cTokens, uint[] memory supplySpeeds, uint[] memory borrowSpeeds) public { ./TreasuryDelegator.sol:73: function delegateToImplementation(bytes memory data) public returns (bytes memory) { ./TreasuryDelegator.sol:84: function delegateToViewImplementation(bytes memory data) public view returns (bytes memory) { ./TreasuryDelegator.sol:101: function delegateTo(address callee, bytes memory data) internal returns(bytes memory) { ./GovernorBravoDelegate.sol:77: function queueOrRevertInternal(address target, uint value, string memory signature, bytes memory data, uint eta) internal { ./BaseV1-periphery.sol:132: function getAmountsOut(uint amountIn, route[] memory routes) public view returns (uint[] memory amounts) { ./BaseV1-periphery.sol:361: function _swap(uint[] memory amounts, route[] memory routes, address _to) internal virtual { ./BaseV1-periphery.sol:439-444: function UNSAFE_swapExactTokensForTokens(uint[] memory amounts, route[] calldata routes, address to, uint deadline) external ensure(deadline) returns (uint[] memory) {

#0 - GalloDaSballo

2022-08-04T00:45:53Z

Less than 500 gas saved

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