Platform: Code4rena
Start Date: 21/06/2022
Pot Size: $30,000 USDC
Total HM: 12
Participants: 96
Period: 3 days
Judge: HardlyDifficult
Total Solo HM: 5
Id: 140
League: ETH
Rank: 31/96
Findings: 2
Award: $49.70
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: IllIllI
Also found by: 0x1f8b, 0x29A, 0x52, 0xNazgul, 0xNineDec, 0xc0ffEE, 0xf15ers, 0xkatana, BowTiedWardens, Chom, ElKu, Funen, GalloDaSballo, JC, JMukesh, JohnSmith, Lambda, Limbooo, MadWookie, MiloTruck, Nethermind, Noah3o6, Nyamcil, Picodes, PwnedNoMore, Randyyy, RoiEvenHaim, SmartSek, StErMi, Tadashi, TerrierLover, TomJ, Tomio, Treasure-Seeker, UnusualTurtle, Varun_Verma, Wayne, Waze, _Adam, apostle0x01, asutorufos, berndartmueller, c3phas, catchup, cccz, cloudjunky, codexploder, cryptphi, defsec, delfin454000, dipp, ellahi, exd0tpy, fatherOfBlocks, hansfriese, hyh, joestakey, kebabsec, kenta, masterchief, minhquanym, naps62, oyc_109, pashov, peritoflores, reassor, rfa, robee, sach1r0, saian, sashik_eth, shenwilly, simon135, slywaters, sorrynotsorry, sseefried, unforgiven, xiaoming90, ych18, zuhaibmohd, zzzitron
28.2928 USDC - $28.29
The permit
function of NibblVault calls the Solidity ecrecover function directly to verify the given signatures. However, the ecrecover EVM opcode allows malleable (non-unique) signatures and thus is susceptible to replay attacks.
Although a replay attack seems not possible here since the nonce is increased each time, ensuring the signatures are not malleable is considered a best practice (and so is checking _signer != address(0)
, where address(0)
means an invalid signature).
SWC-117: Signature Malleability SWC-121: Missing Protection against Signature Replay Attacks
Recommended Mitigation Steps Use the recover function from OpenZeppelin's ECDSA library for signature verification.
In initiateBuyout()
function, it check if block.timestamp >= minBuyoutTime
and revert with message minBuyoutTime < now
. But the revert message should be
minBuyoutTime > now
initialize
in Vault and initialise
in Basket#0 - HardlyDifficult
2022-07-03T23:03:45Z
#1 - HardlyDifficult
2022-07-04T00:53:15Z
#2 - HardlyDifficult
2022-07-04T00:56:09Z
#3 - HardlyDifficult
2022-07-04T18:14:58Z
Some decent suggestions, incl the merged reports
🌟 Selected for report: IllIllI
Also found by: 0v3rf10w, 0x1f8b, 0x29A, 0xKitsune, 0xNazgul, 0xf15ers, 0xkatana, 8olidity, ACai, BowTiedWardens, Chandr, Chom, ElKu, Fitraldys, Funen, IgnacioB, JC, Lambda, Limbooo, MiloTruck, Noah3o6, Nyamcil, Picodes, Randyyy, SmartSek, StErMi, TerrierLover, TomJ, Tomio, UnusualTurtle, Waze, _Adam, ajtra, c3phas, cRat1st0s, catchup, codexploder, cryptphi, defsec, delfin454000, ellahi, exd0tpy, fatherOfBlocks, hansfriese, joestakey, kebabsec, kenta, m_Rassska, minhquanym, oyc_109, pashov, reassor, rfa, robee, sach1r0, saian, sashik_eth, simon135, slywaters, ych18, ynnad, zuhaibmohd
21.4057 USDC - $21.41
secondaryReserveBalance
in _chargeFee()
function can save gasIn NibbleVault._chargeFee()
function, secondaryReserveBalance
is loaded from storage 2 times and stored in storage 1 time. We can save gas by loading it to memory to do calculation and only save to storage after all. For example
uint256 _tmp = secondaryReserveBalance; uint256 _maxSecondaryBalanceIncrease = fictitiousPrimaryReserveBalance - _tmp; _tmp += _feeCurve; secondaryReserveRatio = uint32((_tmp * SCALE * 1e18) / (initialTokenSupply * initialTokenPrice)); secondaryReserveBalance = _tmp;
_feeAdmin
in check before transfer ETH to factory
In _chargeFee()
function, it checks if _adminFeeAmt > 0
before sending ETH to factory
. _adminFeeAmt
is not sent amount but _feeAdmin
and when _amount
is small, _feeAdmin
can be 0 even though _adminFeeAmt > 0
due to division round down. In that case, transfer 0 wei is unnecessary and cost gas. So we should check _feeAdmin > 0
instead.
UPDATE_TIME
to save gasUPDATE_TIME
is not changed anywhere in the codebase, so it should be marked constant
to save gas