Platform: Code4rena
Start Date: 21/06/2022
Pot Size: $55,000 USDC
Total HM: 29
Participants: 88
Period: 5 days
Judge: gzeon
Total Solo HM: 7
Id: 134
League: ETH
Rank: 46/88
Findings: 2
Award: $142.95
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: defsec
Also found by: 0x1f8b, 0x29A, 0xDjango, 0xNazgul, 0xNineDec, 0xf15ers, 0xkowloon, 0xmint, Bnke0x0, BowTiedWardens, Chom, ElKu, Funen, GalloDaSballo, GimelSec, IllIllI, JC, Kenshin, Kulk0, Lambda, Limbooo, MadWookie, Metatron, Picodes, Soosh, StErMi, TomJ, WatchPug, Waze, Yiko, _Adam, ak1, asutorufos, aysha, bardamu, catchup, datapunk, delfin454000, dipp, fatherOfBlocks, grGred, hake, hansfriese, hyh, joestakey, kebabsec, kenzo, kirk-baird, oyc_109, pashov, poirots, rfa, robee, saian, sashik_eth, shenwilly, simon135, slywaters, z3s, zeesaw, zer0dot
63.9425 USDC - $63.94
When implementing access control changes recommend two step processes to protect against mistakenly transfering to the wrong address.
Redeemer.sol#L62-L65 MarketPlace.sol#L109-L112 Lender.sol#L129-L131
Recommend adding event for when contract is paused/unpaused. Lender.sol#L734-L737
Redeemer.sol#L89 - @notice is for the wrong function
🌟 Selected for report: BowTiedWardens
Also found by: 0v3rf10w, 0x1f8b, 0x29A, 0xKitsune, 0xNazgul, 0xf15ers, 0xkatana, 0xkowloon, Bnke0x0, ElKu, Fitraldys, Funen, GalloDaSballo, IllIllI, JC, Kaiziron, Lambda, MadWookie, Noah3o6, Nyamcil, RoiEvenHaim, TomJ, Tomio, UnusualTurtle, Waze, _Adam, ajtra, asutorufos, bardamu, c3phas, catchup, datapunk, defsec, delfin454000, fatherOfBlocks, grGred, hake, hansfriese, hyh, ignacio, joestakey, kebabsec, ladboy233, oyc_109, pashov, poirots, rfa, robee, sach1r0, samruna, sashik_eth, simon135, slywaters, z3s, zer0dot
79.0114 USDC - $79.01
Based on this test in remix you can save ~511 gas in deployment costs and ~6 gas on each function call by using delete instead of setting to default value.
contract Test { mapping (address => uint256) public withdrawals; function test(address a) external { withdrawals[a] = 0; (Deployment cost: 180,368, Execution cost: 27,820) vs delete withdrawals[a]; (Deployment cost: 179,857, Execution cost: 27,814) } }
Instances where delete mapping[x]; can be implemented: Lender.sol#L670 Lender.sol#L699 Lender.sol#L714
The variable amount isn't used in the revert statement and can be moved after. This will save an external call if the revert statement fails. Redeemer.sol#L120-L123
The variable principle isn't used in the revert statement and can be moved after. This will save an external call if the revert statement fails. Redeemer.sol#L164-L174
In for loops pre increments can also be used to save a small amount of gas per iteration. 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) } } }
For loops that can use pre increments: Lender.sol#L96 Lender.sol#L120 Lender.sol#L289
Custom Errors are used throughout most of the contracts but their are still a couple of revert strings that can be replaced. Based on a test in remix, 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)
Instances where custom errors can be used instead of require statements: Lender.sol#L710 Lender.sol#L712
Functions that will always revert when regular users call them (Such as any with the authorized modifier) can be marked payable to save a small amount of gas (~24 Gas when function is called based on remix test)
MarketPlace.sol#L74 MarketPlace.sol#L98 MarketPlace.sol#L109 MarketPlace.sol#L123 Lender.sol#L78-L82 Lender.sol#L107 Lender.sol#L129 Lender.sol#L137 Lender.sol#L145 Lender.sol#L156 Lender.sol#L662 Lender.sol#L687 Lender.sol#L698 Lender.sol#L708 Lender.sol#L734 Redeemer.sol#L62 Redeemer.sol#L70 Redeemer.sol#L81 Redeemer.sol#L92 Redeemer.sol#L281
Based on test in remix you can save ~1,007 gas on deployment and ~15 gas on execution cost if you use x = x + y over x += y (Is only true for storage variables).
contract Test { uint256 x = 1; function test() external { x += 3; (Deployment Cost: 153,124, Execution Cost: 30,369) vs x = x + 1; (Deployment Cost: 152,117, Execution Cost: 30,354) } }
Instances where x = x + y can be implemented: Lender.sol#L294 Lender.sol#L340 Lender.sol#L404 Lender.sol#L461 Lender.sol#L514 Lender.sol#L572 Lender.sol#L621
Whenever using a storage variable more than once in a function you can save ~97 gas per use. (normally 100 gas each use vs 103 gas to SLOAD/MSTORE for the first use and then only 3 gas for further uses)
Lender.sol#L146-L147 - can cache marketPlace (save ~97 gas) Lender.sol#L681 - can cache feenominator (save ~97 gas) Redeemer.sol#L134-L136 - can cache lender (save ~97 gas) Redeemer.sol#L177-L180 - can cache lender (save ~97 gas) Redeemer.sol#L221-L224 - can cache lender (save ~97 gas) Redeemer.sol#L256-L259 - can cache lender (save ~97 gas)
The following function is never called in their contracts and can be switched to external to save gas: Redeemer.sol#L275-L281