Platform: Code4rena
Start Date: 04/05/2022
Pot Size: $50,000 DAI
Total HM: 24
Participants: 71
Period: 5 days
Judge: Justin Goro
Total Solo HM: 14
Id: 119
League: ETH
Rank: 44/71
Findings: 2
Award: $116.22
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: horsefacts
Also found by: 0x1f8b, 0xYamiDancho, 0xf15ers, 0xkatana, ACai, AlleyCat, Bruhhh, Dravee, Funen, GimelSec, Hawkeye, IllIllI, MaratCerby, PPrieditis, Picodes, Ruhum, TerrierLover, VAD37, berndartmueller, csanuragjain, defsec, delfin454000, eccentricexit, ellahi, fatherOfBlocks, gzeon, hansfriese, hickuphh3, hyh, ilan, joestakey, juicy, kebabsec, oyc_109, rajatbeladiya, reassor, rfa, robee, samruna, simon135, sorrynotsorry, throttle
74.6487 DAI - $74.65
A require() statement that checks the return value of token transfers should be used to prevent silent transfer failures with non-compliant ERC20 tokens. Failure to do so can cause silent failures of transfers and affect token accounting in contract, possibly causing loss of value for the user or the protocol.
One unsafe transfer() exists without return value checks: https://github.com/code-423n4/2022-05-factorydao/blob/main/contracts/MerkleVesting.sol#L173
Use require statements to check return values consistently or replace transfer/transferFrom with safeTransfer/safeTransferFrom from Open Zeppelin's SafeERC20 library
isContract
logic can be bypassedThe logic in isContract
can be bypassed. It uses the same approached described in https://solidity-by-example.org/hacks/contract-size
The logic that can be bypassed is for isContract
https://github.com/code-423n4/2022-05-factorydao/blob/main/contracts/VoterID.sol#L343
One solution that works for now is to check tx.origin == msg.sender
. This is true for EOAs and false for contracts, but this can change with later EIPs.
#0 - illuzen
2022-05-12T08:51:48Z
all duplicates
#1 - gititGoro
2022-06-05T10:35:17Z
L2 is security related. A new issue has been opened https://github.com/code-423n4/2022-05-factorydao-findings/issues/287
🌟 Selected for report: IllIllI
Also found by: 0x1f8b, 0xNazgul, 0xYamiDancho, 0xf15ers, 0xkatana, ACai, CertoraInc, Dravee, Funen, GimelSec, Hawkeye, PPrieditis, Picodes, Ruhum, TerrierLover, Tomio, VAD37, Waze, csanuragjain, defsec, delfin454000, eccentricexit, ellahi, fatherOfBlocks, gzeon, hansfriese, horsefacts, ilan, joestakey, juicy, minhquanym, oyc_109, rajatbeladiya, reassor, rfa, robee, samruna, simon135, z3s
41.5699 DAI - $41.57
Strings in solidity are handled in 32 byte chunks. A require string longer than 32 bytes uses more gas. Shortening these strings will save gas.
https://github.com/code-423n4/2022-05-factorydao/blob/main/contracts/MerkleVesting.sol#L141 https://github.com/code-423n4/2022-05-factorydao/blob/main/contracts/MerkleVesting.sol#L145 https://github.com/code-423n4/2022-05-factorydao/blob/main/contracts/PermissionlessBasicPoolFactory.sol#L214 https://github.com/code-423n4/2022-05-factorydao/blob/main/contracts/VoterID.sol#L217 https://github.com/code-423n4/2022-05-factorydao/blob/main/contracts/VoterID.sol#L305 https://github.com/code-423n4/2022-05-factorydao/blob/main/contracts/VoterID.sol#L306
Shorten all require strings to less than 32 characters
Using > 0
uses slightly more gas than using != 0
. Use != 0
when comparing uint variables to zero, which cannot hold values below zero
Locations where this was found include https://github.com/code-423n4/2022-05-factorydao/blob/main/contracts/VoterID.sol#L348 https://github.com/code-423n4/2022-05-factorydao/blob/main/contracts/SpeedBumpPriceGate.sol#L77 https://github.com/code-423n4/2022-05-factorydao/blob/main/contracts/FixedPricePassThruGate.sol#L51
Replace > 0
with != 0
to save gas
Solidity errors introduced in version 0.8.4 can save gas on revert conditions https://blog.soliditylang.org/2021/04/21/custom-errors/ https://twitter.com/PatrickAlphaC/status/1505197417884528640
Error messages are not used at all in the contracts. Some example of require usage that can be replaced with errors includes https://github.com/code-423n4/2022-05-factorydao/blob/main/contracts/MerkleVesting.sol#L141 https://github.com/code-423n4/2022-05-factorydao/blob/main/contracts/MerkleVesting.sol#L145 https://github.com/code-423n4/2022-05-factorydao/blob/main/contracts/PermissionlessBasicPoolFactory.sol#L214
Replace require blocks with new solidity errors described in https://blog.soliditylang.org/2021/04/21/custom-errors/
Using a prefix increment (++i) instead of a postfix increment (i++) saves gas for each loop cycle and so can have a big gas impact when the loop executes on a large number of elements.
There are many examples of this in for loops https://github.com/code-423n4/2022-05-factorydao/blob/main/contracts/PermissionlessBasicPoolFactory.sol#L115 https://github.com/code-423n4/2022-05-factorydao/blob/main/contracts/PermissionlessBasicPoolFactory.sol#L141 https://github.com/code-423n4/2022-05-factorydao/blob/main/contracts/PermissionlessBasicPoolFactory.sol#L168 https://github.com/code-423n4/2022-05-factorydao/blob/main/contracts/PermissionlessBasicPoolFactory.sol#L224 https://github.com/code-423n4/2022-05-factorydao/blob/main/contracts/PermissionlessBasicPoolFactory.sol#L249 https://github.com/code-423n4/2022-05-factorydao/blob/main/contracts/PermissionlessBasicPoolFactory.sol#L266
Use prefix not postfix to increment in a loop
The comparison operators >= and <= use more gas than >, <, or ==. Replacing the >= and ≤ operators with a comparison operator that has an opcode in the EVM saves gas
The existing code is https://github.com/code-423n4/2022-05-factorydao/blob/main/contracts/MerkleResistor.sol#L179-L185
if (block.timestamp >= tranche.endTime) { currentWithdrawal = tranche.currentCoins; } else { // compute allowed withdrawal // secondsElapsedSinceLastWithdrawal * coinsPerSecond == coinsAccumulatedSinceLastWithdrawal currentWithdrawal = (block.timestamp - tranche.lastWithdrawalTime) * tranche.coinsPerSecond; }
A simple comparison can be used for gas savings by reversing the logic
if (block.timestamp < tranche.endTime) { // compute allowed withdrawal // secondsElapsedSinceLastWithdrawal * coinsPerSecond == coinsAccumulatedSinceLastWithdrawal currentWithdrawal = (block.timestamp - tranche.lastWithdrawalTime) * tranche.coinsPerSecond; } else { currentWithdrawal = tranche.currentCoins; }
The same change can be used here https://github.com/code-423n4/2022-05-factorydao/blob/main/contracts/MerkleVesting.sol#L153-L158
Replace the comparison operator and reverse the logic to save gas using the suggestions above
Solidity does not recognize null as a value, so uint variables are initialized to zero. Setting a uint variable to zero is redundant and can waste gas.
There was one place where an int is initialized to zero https://github.com/code-423n4/2022-05-factorydao/blob/main/contracts/MerkleDropFactory.sol#L17 https://github.com/code-423n4/2022-05-factorydao/blob/main/contracts/VoterID.sol#L69 https://github.com/code-423n4/2022-05-factorydao/blob/main/contracts/MerkleEligibility.sol#L31 https://github.com/code-423n4/2022-05-factorydao/blob/main/contracts/MerkleResistor.sol#L24 https://github.com/code-423n4/2022-05-factorydao/blob/main/contracts/MerkleResistor.sol#L176 https://github.com/code-423n4/2022-05-factorydao/blob/main/contracts/MerkleVesting.sol#L16 https://github.com/code-423n4/2022-05-factorydao/blob/main/contracts/MerkleVesting.sol#L150 https://github.com/code-423n4/2022-05-factorydao/blob/main/contracts/PermissionlessBasicPoolFactory.sol#L115 https://github.com/code-423n4/2022-05-factorydao/blob/main/contracts/PermissionlessBasicPoolFactory.sol#L141 https://github.com/code-423n4/2022-05-factorydao/blob/main/contracts/PermissionlessBasicPoolFactory.sol#L168 https://github.com/code-423n4/2022-05-factorydao/blob/main/contracts/PermissionlessBasicPoolFactory.sol#L224 https://github.com/code-423n4/2022-05-factorydao/blob/main/contracts/PermissionlessBasicPoolFactory.sol#L249 https://github.com/code-423n4/2022-05-factorydao/blob/main/contracts/PermissionlessBasicPoolFactory.sol#L266
Remove the redundant zero initialization
uint256 i;
instead of uint256 i = 0;
Caching the array length outside a loop saves reading it on each iteration, as long as the array's length is not changed during the loop. This saves gas.
This was found in many places https://github.com/code-423n4/2022-05-factorydao/blob/main/contracts/MerkleLib.sol#L22 https://github.com/code-423n4/2022-05-factorydao/blob/main/contracts/PermissionlessBasicPoolFactory.sol#L115 https://github.com/code-423n4/2022-05-factorydao/blob/main/contracts/PermissionlessBasicPoolFactory.sol#L141 https://github.com/code-423n4/2022-05-factorydao/blob/main/contracts/PermissionlessBasicPoolFactory.sol#L168 https://github.com/code-423n4/2022-05-factorydao/blob/main/contracts/PermissionlessBasicPoolFactory.sol#L224 https://github.com/code-423n4/2022-05-factorydao/blob/main/contracts/PermissionlessBasicPoolFactory.sol#L249 https://github.com/code-423n4/2022-05-factorydao/blob/main/contracts/PermissionlessBasicPoolFactory.sol#L266
Cache the array length before the for loop
#0 - illuzen
2022-05-12T08:51:16Z
all duplicates