Platform: Code4rena
Start Date: 12/08/2022
Pot Size: $35,000 USDC
Total HM: 10
Participants: 126
Period: 3 days
Judge: Justin Goro
Total Solo HM: 3
Id: 154
League: ETH
Rank: 13/126
Findings: 4
Award: $512.55
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: CertoraInc
Also found by: cccz, csanuragjain, jonatascm, scaraven
https://github.com/code-423n4/2022-08-fiatdao/blob/main/contracts/VotingEscrow.sol#L426
Accounting is not done correctly in case the ERC20 token used in this contract have fees on transfer
Same need to applied for all functions using transfer/transferFrom
function createLock(uint256 _value, uint256 _unlockTime) external override nonReentrant checkBlocklist { ... locked_.amount += int128(int256(_value)); ... require( token.transferFrom(msg.sender, address(this), _value), "Transfer failed" ); }
As we can see locked amount for the calling user is updated fully by _value
However if the token is charging fees on transfer then contract will receive amount < _value, hence contract will lose funds
require( token.transferFrom(msg.sender, address(this), _value), "Transfer failed" );
Store the balance before and after transfer and then subtract them to obtain the actual amount received by contract. This actual amount should then be updated as locked amount
#0 - bahurum
2022-08-16T22:17:28Z
Duplicate of #229
#1 - lacoop6tu
2022-08-17T10:30:29Z
Duplicate of #229
🌟 Selected for report: Aymen0909
Also found by: 0xSky, 0xf15ers, CertoraInc, JohnSmith, auditor0517, bin2chen, csanuragjain, scaraven, tabish, wagmi, yixxas
https://github.com/code-423n4/2022-08-fiatdao/blob/main/contracts/VotingEscrow.sol#L513
The end time of oldLocked is not updated correctly and instead is pointing to new updated time
function increaseUnlockTime(uint256 _unlockTime) external override nonReentrant checkBlocklist { ... LockedBalance memory locked_ = locked[msg.sender]; uint256 unlock_time = _floorToWeek(_unlockTime); ... locked_.end = unlock_time; locked[msg.sender] = locked_; ... LockedBalance memory oldLocked = _copyLock(locked_); oldLocked.end = unlock_time; _checkpoint(msg.sender, oldLocked, locked_); ... }
_unlockTime is the new increased unlock time which is then parsed and kept within unlock_time variable
locked end time is updated to unlock_time
locked_.end = unlock_time; locked[msg.sender] = locked_;
LockedBalance memory oldLocked = _copyLock(locked_);
oldLocked.end = unlock_time; _checkpoint(msg.sender, oldLocked, locked_);
Revise the old time like below:
oldLocked.end = oldUnlockTime;
#0 - csanuragjain
2022-08-16T18:06:47Z
#1 - lacoop6tu
2022-08-17T10:30:05Z
Duplicate of #217
🌟 Selected for report: oyc_109
Also found by: 0x1f8b, 0x52, 0xDjango, 0xLovesleep, 0xNazgul, 0xNineDec, 0xbepresent, 0xmatt, 0xsolstars, Aymen0909, Bahurum, Bnke0x0, CertoraInc, Chom, CodingNameKiki, DecorativePineapple, Deivitto, Dravee, ElKu, Funen, GalloDaSballo, IllIllI, JC, JohnSmith, Junnon, KIntern_NA, Lambda, LeoS, MiloTruck, Noah3o6, PaludoX0, RedOneN, Respx, ReyAdmirado, Rohan16, RoiEvenHaim, Rolezn, Ruhum, Sm4rty, TomJ, Vexjon, Waze, Yiko, __141345__, a12jmx, ajtra, ak1, apostle0x01, asutorufos, auditor0517, bin2chen, bobirichman, brgltd, bulej93, byndooa, c3phas, cRat1st0s, cryptphi, csanuragjain, d3e4, defsec, delfin454000, djxploit, durianSausage, ellahi, erictee, exd0tpy, fatherOfBlocks, gogo, jonatascm, ladboy233, medikko, mics, natzuu, neumo, p_crypt0, paribus, pfapostol, rbserver, reassor, ret2basic, robee, rokinot, rvierdiiev, sach1r0, saneryee, seyni, sikorico, simon135, sseefried, wagmi, wastewa
29.8918 USDC - $29.89
Contract: https://github.com/code-423n4/2022-08-fiatdao/blob/main/contracts/VotingEscrow.sol#L139
Function: transferOwnership
Issue: The function is lacking any check to see if passed _addr is not zero address. This is necessary as all admin operation (updateBlocklist/updatePenaltyRecipient/unlock) will get halted on updating with wrong address
Recommendation: Check that _addr is not a zero address
require(_addr!=address(0), "Invalid address");
Contract: https://github.com/code-423n4/2022-08-fiatdao/blob/main/contracts/interfaces/IBlocklist.sol
Issue: Mismatch between interface and implementation as listed below:
Recommendation: As per requirement change modifier to either public/external in both interface and implementation
🌟 Selected for report: IllIllI
Also found by: 0x040, 0x1f8b, 0xDjango, 0xHarry, 0xLovesleep, 0xNazgul, 0xNineDec, 0xSmartContract, 0xackermann, 0xbepresent, 2997ms, Amithuddar, Aymen0909, Bnke0x0, CRYP70, CertoraInc, Chom, CodingNameKiki, Deivitto, Dravee, ElKu, Fitraldys, Funen, GalloDaSballo, JC, JohnSmith, Junnon, LeoS, Metatron, MiloTruck, Noah3o6, NoamYakov, PaludoX0, RedOneN, Respx, ReyAdmirado, Rohan16, Rolezn, Ruhum, Sm4rty, SooYa, SpaceCake, TomJ, Tomio, Waze, Yiko, __141345__, a12jmx, ajtra, ak1, apostle0x01, asutorufos, bobirichman, brgltd, bulej93, c3phas, cRat1st0s, carlitox477, chrisdior4, csanuragjain, d3e4, defsec, delfin454000, djxploit, durianSausage, ellahi, erictee, fatherOfBlocks, gerdusx, gogo, ignacio, jag, ladboy233, m_Rassska, medikko, mics, natzuu, newfork01, oyc_109, paribus, pfapostol, rbserver, reassor, ret2basic, robee, rokinot, rvierdiiev, sach1r0, saian, sashik_eth, sikorico, simon135
14.9459 USDC - $14.95
Contract: VotingEscrow.sol
Function: createLock
Issue: locked_.amount += int128(int256(value)); is not required since locked.amount will always be 0 because of below require statement:
require(locked_.amount == 0, "Lock exists");