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
Rank: 36/59
Findings: 2
Award: $306.41
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: joestakey
Also found by: 0x1f8b, 0x29A, 0x52, 0xDjango, 0xNazgul, 0xf15ers, 0xmint, Bronicle, Dravee, Funen, JMukesh, Limbooo, MadWookie, Picodes, Ruhum, TerrierLover, TomJ, Tutturu, WatchPug, Waze, _Adam, asutorufos, c3phas, catchup, cccz, codexploder, cryptphi, csanuragjain, defsec, fatherOfBlocks, gzeon, hake, hansfriese, hyh, ignacio, k, nxrblsrpr, oyc_109, robee, sach1r0, saian, simon135, technicallyty, zzzitron
89.9424 USDC - $89.94
687.9945 CANTO - $111.11
[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
L
NC (agree for tokens should be indexed)
NC
NC
NC
NC
2L 5NC
🌟 Selected for report: _Adam
Also found by: 0v3rf10w, 0x1f8b, 0x29A, 0xKitsune, 0xNazgul, 0xf15ers, 0xkatana, 0xmint, Chom, Dravee, Fitraldys, Funen, JC, Limbooo, MadWookie, Picodes, Ruhum, TerrierLover, TomJ, Tomio, Waze, ak1, c3phas, catchup, defsec, fatherOfBlocks, gzeon, hake, hansfriese, joestakey, k, oyc_109, rfa, robee, sach1r0, saian, simon135, ynnad
41.2642 USDC - $41.26
396.9199 CANTO - $64.10
[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:
[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
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
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);
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));
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
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
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;
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);
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_;
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);
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