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: 64/126
Findings: 2
Award: $45.02
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 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
30.066 USDC - $30.07
check for address 0 is a best practice for smart contract development, recalling the <a href=https://twitter.com/samczsun/status/1554252024723546112>Nomad hack</a> in some case lack of checking 0 value can be fatal.
Instance: Blocklist.sol
VotingEscrow.sol
Solution: using this following code
require(addr != address(0))
Some role can make this contract unoperateable, make it a one step change can be risky if we transfer the role to wrong address. it's a best practice to make several role changes function to two step instead.
Instance: VotingEscrow::transferOwnership()
function transferOwnership(address _addr) external { require(msg.sender == owner, "Only owner"); owner = _addr; emit TransferOwnership(_addr); }
Solution: make it two steps
address pendingOwner; function changePendingOwner(address _addr) external { require(_addr != address(0), "zero address"); //according to report above require(msg.sender == owner, "Only owner"); pendingOwner = _addr; emit PendingOwnerChange(_addr); } function transferOwnership(address _addr) external { require(msg.sender == owner, "Only owner"); require(_addr == pendingOwner, "address mismatch"); owner = _addr; emit TransferOwnership(_addr); }
Both Blocklist.sol
and VotingEscrow.sol
using a floating pragma ^0.8.3
it's recommended to change it become a fixed pragma to mitigated the bug that can be dangerous caused by outdated compiler. i personally recommended pragma solidity '0.8.15`.
Instance: VotingEscrow.sol https://github.com/code-423n4/2022-08-fiatdao/blob/main/contracts/VotingEscrow.sol Blocklist.sol https://github.com/code-423n4/2022-08-fiatdao/blob/main/contracts/features/Blocklist.sol
Mitigation:
can use fixed pragma soldiity, like pragma solidity 0.8.15
;
Naming of a function in Blocklist.sol
shadow the builtin symbol. that's function is block()
(line 23), block is a builtin symbol of solidity that point to block onchain. In this case, Blocklist not calling a builtin block.
instance: Blocklist.sol Blocklist::block() https://github.com/code-423n4/2022-08-fiatdao/blob/main/contracts/features/Blocklist.sol#L23
Mitigation:
rename the function be blockUser
or blockAddress
,etc.
event have a useage for frontend indexing and monitoring. In checkpoint
theres no event emitted.
instance VotingEscrow.sol VotingEscrow::checkpoint() or VotingEscrow::_checkpoint() https://github.com/code-423n4/2022-08-fiatdao/blob/fece3bdb79ccacb501099c24b60312cd0b2e4bb2/contracts/VotingEscrow.sol#L397 https://github.com/code-423n4/2022-08-fiatdao/blob/fece3bdb79ccacb501099c24b60312cd0b2e4bb2/contracts/VotingEscrow.sol#L222
Mitigation:
make an event for checkpoint then emit it in _checkpoint()
or checkpoint()
🌟 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
constructor
can be marked as immutable
Instance : VotingEscrow.sol
Blocklist.sol
public
function that never called inside the contract should marked as external
insteadInstance: VotingEscrow.sol
uint
/int
variable has default value 0, it is cheaper to leave it empty than passing it with 0 value.Example
uint a //is cheaper than uint a = 0
Instance VotingEscrow.sol
229 int128 oldSlopeDelta = 0; 230 int128 newSlopeDelta = 0;
309 for (uint256 i = 0; i < 255; i++) {
714 uint256 min = 0; 717 for (uint256 i = 0; i < 128; i++) {
https://github.com/code-423n4/2022-08-fiatdao/blob/fece3bdb79ccacb501099c24b60312cd0b2e4bb2/contracts/VotingEscrow.sol#L714 https://github.com/code-423n4/2022-08-fiatdao/blob/fece3bdb79ccacb501099c24b60312cd0b2e4bb2/contracts/VotingEscrow.sol#L717
737 uint256 min = 0; 739 for (uint256 i = 0; i < 128; i++) {
https://github.com/code-423n4/2022-08-fiatdao/blob/fece3bdb79ccacb501099c24b60312cd0b2e4bb2/contracts/VotingEscrow.sol#L737 https://github.com/code-423n4/2022-08-fiatdao/blob/fece3bdb79ccacb501099c24b60312cd0b2e4bb2/contracts/VotingEscrow.sol#L739
793 uint256 dBlock = 0; 794 uint256 dTime = 0;
834 for (uint256 i = 0; i < 255; i++) {
Loop is gas costly, but we can decrease the gas by this step
uint i = 0
can leave default be uint i
insteadi++
can changed to ++i
, since ++i
is cheaperunchecked
for ++i
operationExample
// Existing loop by fiatdao for (uint256 i = 0; i < 255; i++) { ... //implementation } // Cheaper version of Loop for (uint256 i; i < 255 ){ ...//implementation unchecked{ //at the end of the loop ++i } }
Instance: VotingEscrow.sol