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: 30/59
Findings: 2
Award: $635.83
🌟 Selected for report: 1
🚀 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
72.7276 USDC - $72.73
687.9945 CANTO - $111.11
Recommend using static solidity version and not floating pragma in the following contracts: Proposal-Store.sol#L3 WETH.sol#L1 GovernorBravoDelegate.sol#L2 Comptroller.sol#L2 AccountantDelegator.sol#L1 AccountantInterfaces.sol#L1 TreasuryDelegator.sol#L1 TreasuryDelegate.sol#L1 TreasuryInterfaces.sol#L1 CNote.sol#L1 NoteInterest.sol#L1
Recommend removing open todos before deploying contract Comptroller.sol#L1232 Comptroller.sol#L1271
This appears to be old code/unused code, recommend removing. NoteInterest.sol#L79-L85 NoteInterest.sol#L93-L94
Recommend adding != address(0) checks for the following 2 variables: BaseV1-periphery.sol#L76 BaseV1-periphery.sol#L78
#0 - GalloDaSballo
2022-07-30T02:26:54Z
NC in lack of POC
NC
Valid Low
1L 2 NC
🌟 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
396.9199 CANTO - $64.10
387.8897 USDC - $387.89
When initialising a variable to its default variable, it is cheaper to leave blank. I ran a test in remix that initialises a single variable and got a saving of 2,246 gas.
contract Test { uint256 public variable = 0; (69,312 gas) vs uint256 public variable; (67,066 gas) }
BaseV1-core.sol#L46 - can change to: uint public totalSupply;
You can save an SLOAD (~100 gas) by emiting local variables over storage variables when they have the same value.
BaseV1-core.sol#L170 - can emit balance0 & balance1 over reserve0 & reserve0 (save 2 SLOADS) Comptroller.sol#L856 - can emit newCloseFactorMantissa instead of closeFactorMantissa Comptroller.sol#L1045 - can emit newPauseGuardian instead of pauseGuardian NoteInterest.sol#L127 - can emit newBaseRatePerYear instead of baseRatePerYear NoteInterest.sol#L144 - can emit newBaseRateMantissa instead of baseRatePerYear NoteInterest.sol#L144 - can emit newAdjusterCoefficient instead of adjusterCoefficient NoteInterest.sol#L170 - can emit newUpdateFrequency instead of updateFrequency
The following variables are initialised either when created or in the constructor and then never modified and can be changed to immutable/constant to save gas.
Proposal-Store.sol#L35 BaseV1-core.sol#L39-L40 WETH.sol#L6-L8 NoteInterest.sol#L22
When incrementing i in for loops there is no chance of overflow so unchecked can be used to save gas. I ran a simple test in remix and found deployment savings of 31,653 gas and on each function call saved ~141 gas per iteration.
contract Test { function loopTest() external { for (uint256 i; i < 1; ++i) { Deployment Cost: 125,637, Cost on function call: 24,601 vs for (uint256 i; i < 1; ) { // for loop body unchecked { ++i } Deployment Cost: 93,984, Cost on function call: 24,460 } } }
In for loops pre increments can also be used to save a small amount of gas per iteraition. I ran a test in remix using a for loop and found the deployment savings of 497 gas and ~5 gas per iteration.
contract Test { function loopTest() external { for (uint256 i; i < 1; i++) { (Deployment cost: 118,408, Cost on function call: 24,532) vs for (uint256 i; i < 1; ++i) { (Deployment cost: 117,911, Cost on function call: 24,527) } } }
Looping over memory/storage variable lengths will cost ~3 gas/~100 gas per iteration, i recommend cacheing their value and looping over that instead.
Instances of for loops that can be optimised: BaseV1-core.sol#L207 BaseV1-core.sol#L337 BaseV1-periphery.sol#L136 BaseV1-periphery.sol#L362 GovernorBravoDelegate.sol#L68 GovernorBravoDelegate.sol#L90 Comptroller.sol#L126 Comptroller.sol#L206 Comptroller.sol#L735 Comptroller.sol#L959 Comptroller.sol#L1005 Comptroller.sol#L1106 Comptroller.sol#L1347 Comptroller.sol#L1353 Comptroller.sol#L1359 Comptroller.sol#L1364 Comptroller.sol#L1413
You can save 1 SLOAD (~97 gas) per use by cacheing a storage variable that is used more than once.
CNote.sol#L15-L18 - can cache address(_ accountant) CNote.sol#L179-L180 - can cache address(_ accountant)
You can save some gas (~1000 gas on deployment and ~10 gas on function call, based on remix test) by removing == True from the following locations.
Comptroller.sol#L149
Comptroller.sol#L1053
Comptroller.sol#L1063
Comptroller.sol#L1072
Comptroller.sol#L1081
Comptroller.sol#L1350
Comptroller.sol#L1357
Comptroller.sol#L1456
As your using a solidity version greater 0.8.4 you can replace revert strings with custom errors. This will save in deployment costs and runtime costs. I ran a test in remix comparing a revert string vs custom errors and found that replacing a single revert string with a custom error saved 12,404 gas in deployment cost and 86 gas on each function call.
contract Test { uint256 a; function check() external { require(a != 0, "check failed"); } } (Deployment cost: 114,703, Cost on Function call: 23,392) vs contract Test { uint256 a; error checkFailed(); function check() external { if (a != 0) revert checkFailed(); } } (Deployment cost: 102,299, Cost on Function call: 23,306)
I recommend replacing all revert strings with custom errors.
If opting not to update revert strings to custom errors, keeping revert strings <= 32 bytes in length will save gas. I ran a test in remix and found the savings for a single short revert string vs long string to be 9,377 gas in deployment cost and 18 gas on function call.
contract Test { uint256 a; function check() external { require(a != 0, "short error message"); (Deployment cost: 114,799, Cost on function call: 23,392) vs require(a != 0, "A longer Error Message over 32 bytes in length"); (Deployment cost: 124,176, Cost on function call: 23,410) } }
I recommend shortenning the following revert strings to <= 32 bytes in length:
BaseV1-periphery.sol#L86 BaseV1-periphery.sol#L104-L105 BaseV1-periphery.sol#L223 BaseV1-periphery.sol#L228 BaseV1-periphery.sol#L295-L296 BaseV1-periphery.sol#L387 BaseV1-periphery.sol#L402 BaseV1-periphery.sol#L417 BaseV1-periphery.sol#L430 BaseV1-periphery.sol#L452 WETH.sol#L29 WETH.sol#L96-L97 GovernorBravoDelegate.sol#L25-L27 GovernorBravoDelegate.sol#L42-L47 GovernorBravoDelegate.sol#L78 GovernorBravoDelegate.sol#L87 GovernorBravoDelegate.sol#L115 GovernorBravoDelegate.sol#L132-L133 GovernorBravoDelegate.sol#L146 GovernorBravoDelegate.sol#L164 Comptroller.sol#L178 Comptroller.sol#L491 Comptroller.sol#L998 Comptroller.sol#L1016 Comptroller.sol#L1051-L1052 Comptroller.sol#L1061-L1062 Comptroller.sol#L1071 Comptroller.sol#L1080 Comptroller.sol#L1089 Comptroller.sol#L1095-L1096 Comptroller.sol#L1411 AccountantDelegator.sol#L43-L44 AccountantDelegator.sol#L124 TreasuryDelegator.sol#L31-L32 TreasuryDelegate.sol#L47 CNote.sol#L16 CNote.sol#L43 CNote.sol#L45 CNote.sol#L54 CNote.sol#L77 CNote.sol#L114 CNote.sol#L130 CNote.sol#L146 CNote.sol#L198 CNote.sol#L214 CNote.sol#L264 CNote.sol#L310 CNote.sol#L330 NoteInterest.sol#L141 NoteInterest.sol#L154 NoteInterest.sol#L167
If opting not to use custom errors when checking whether a uint is > 0 in a requrie functions you can save a small amount of gas by replacing with != 0. This is only true if optimiser is turned on and in a require statement. I ran a test in remix with optimisation set to 10,000 and found the savings for a single occurance is 632 in deployment cost and 6 gas on each function call.
contract Test { uint256 a; function check() external { require(a > 0); (Deployment cost: 79,763, Cost on function call: 23,305) vs require(a != 0); (Deployment cost: 79,331, Cost on function call: 23,299) } }
Instances where a uint is compared > 0: BaseV1-core.sol#L253 BaseV1-core.sol#L272 BaseV1-core.sol#L286 BaseV1-core.sol#L303 BaseV1-core.sol#L465 BaseV1-periphery.sol#L104-L105 BaseV1-periphery.sol#L456 BaseV1-periphery.sol#L463
If not opting to use custom errors and If optimising for running costs over deployment costs you can seperate && in require functions into 2 parts. I ran a basic test in remix and it cost an extra 234 gas to deploy but will save ~9 gas everytime the require function is called.
contract Test { uint256 a = 0; uint256 b = 1; function test() external { require(a == 0 && b > a) (Deployment cost: 123,291, Cost on function call: 29,371) vs require(a == 0); require(b > a); (Deployment cost: 123,525, Cost on function call: 29,362) } }
Instances where require statements can be split into seperate statements:
BaseV1-core.sol#L272 BaseV1-core.sol#L288 BaseV1-core.sol#L294 BaseV1-core.sol#L431 BaseV1-core.sol#L468 BaseV1-periphery.sol#L105 BaseV1-periphery.sol#L459 BaseV1-periphery.sol#L466 GovernorBravoDelegate.sol#L42-L45 GovernorBravoDelegate.sol#L115 GovernorBravoDelegate.sol#L164 Comptroller.sol#L1003 Comptroller.sol#L1411
#0 - GalloDaSballo
2022-08-04T00:02:47Z
Over 10k between immutables and stuff
#1 - GalloDaSballo
2022-08-04T17:25:38Z
Valid but I only count runtime gas vs deployment
8 * 97 (3 gas for the MLOAD)
776
7 * 2100 14700
I think the benchmark is correct but may be unfairly skewed against the non-optimized (perhaps no optimizer) Will give 25 points per instance
17 * 25 425
94 each (6 gas for setup of cache) 188
6 gas per instance (3 for check, 3 for MLOAD of the hardcoded value) 8 * 6 48
#2 - GalloDaSballo
2022-08-07T23:44:46Z
Because you benchmarked it, I'll keep that in mind in scoring
6 per instance 51 * 6 301
6 per instance 8 * 6 48
9 per instance (because you benchmarked, would have given 3 normally) 13 * 9 117
Total Gas Saved 16603