Platform: Code4rena
Start Date: 26/07/2022
Pot Size: $75,000 USDC
Total HM: 29
Participants: 179
Period: 6 days
Judge: LSDan
Total Solo HM: 6
Id: 148
League: ETH
Rank: 118/179
Findings: 2
Award: $56.49
๐ Selected for report: 0
๐ Solo Findings: 0
๐ Selected for report: IllIllI
Also found by: 0x1f8b, 0x4non, 0x52, 0xA5DF, 0xDjango, 0xLovesleep, 0xNazgul, 0xNineDec, 0xSmartContract, 0xackermann, 0xc0ffEE, 0xf15ers, 0xmatt, 0xsanson, 0xsolstars, 8olidity, AuditsAreUS, Bahurum, Bnke0x0, CRYP70, CertoraInc, Ch_301, Chom, CryptoMartian, Deivitto, DevABDee, Dravee, ElKu, Franfran, Funen, GalloDaSballo, GimelSec, GiveMeTestEther, Green, JC, Jmaxmanblue, JohnSmith, Jujic, Junnon, Kenshin, Krow10, Kumpa, Lambda, MEP, Maxime, MiloTruck, Mohandes, NoamYakov, Picodes, RedOneN, Rohan16, Rolezn, Ruhum, RustyRabbit, Sm4rty, Soosh, StErMi, StyxRave, Tadashi, TomJ, Treasure-Seeker, TrungOre, Waze, _Adam, __141345__, ajtra, ak1, apostle0x01, arcoun, asutorufos, async, benbaessler, berndartmueller, bin2chen, brgltd, c3phas, cRat1st0s, carlitox477, chatch, codetilda, codexploder, cryptonue, cryptphi, csanuragjain, cthulhu_cult, delfin454000, dipp, dirk_y, djxploit, ellahi, exd0tpy, fatherOfBlocks, giovannidisiena, hansfriese, horsefacts, hyh, idkwhatimdoing, indijanc, jayfromthe13th, jayphbee, joestakey, kenzo, kyteg, lucacez, luckypanda, mics, minhquanym, obront, oyc_109, pedr02b2, rajatbeladiya, rbserver, reassor, robee, rokinot, rotcivegaf, sach1r0, saian, saneryee, sashik_eth, scaraven, shenwilly, simon135, sseefried, supernova, teddav, ych18, zuhaibmohd, zzzitron
35.1687 USDC - $35.17
Contracts do not emit relevant events after setting sensitive variables. Consider emitting events after sensitive changes take place, to facilitate tracking and notify off-chain clients following the contractโs activity in following functions:
contracts/core/GolomTrader.sol:454 function executeSetDistributor() external onlyOwner { contracts/governance/GolomToken.sol:65 function executeSetMinter() external onlyOwner { contracts/rewards/RewardDistributor.sol:290 function executeChangeTrader() external onlyOwner { contracts/rewards/RewardDistributor.sol:307 function executeAddVoteEscrow() external onlyOwner {
contracts/rewards/RewardDistributor.sol:151 rewardToken.transfer(addr, reward); contracts/rewards/RewardDistributor.sol:165 rewardToken.transfer(addr, reward); contracts/rewards/RewardDistributor.sol:207 rewardToken.transfer(tokenowner, reward); contracts/rewards/RewardDistributor.sol:208 weth.transfer(tokenowner, rewardEth);
contracts/core/GolomTrader.sol:45 ERC20 WETH = ERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
The contracts should be deployed with the same solidity version with which they have been tested. Locking the pragma ensures that the contracts don't get deployed with an older version which might introduce bugs.
contracts/governance/GolomToken.sol:2 pragma solidity ^0.8.11;
contracts/rewards/RewardDistributor.sol:62 mapping(uint256 => uint256) public rewardExchange; // reward minted each epoc for exhange contracts/rewards/RewardDistributor.sol:107 // this assumes atleast 1 trade is done daily?????? contracts/rewards/RewardDistributor.sol:111 // emissions is decided by epoch begiining locked/circulating , and amount each nft gets also decided at epoch begining //
contracts/rewards/RewardDistributor.sol:88 require(msg.sender == trader); contracts/rewards/RewardDistributor.sol:144 require(epochs[index] < epoch); contracts/rewards/RewardDistributor.sol:158 require(epochs[index] < epoch);
return
contracts/rewards/RewardDistributor.sol:137 return;
contracts/vote-escrow/TokenUriHelper.sol:1 /// [MIT License]
Could cause misleading
contracts/vote-escrow/VoteEscrowDelegation.sol:50 uint256 public MIN_VOTING_POWER_REQUIRED = 0;
contracts/vote-escrow/VoteEscrowDelegation.sol:219 // function removeDelegationByOwner(uint256 delegatedTokenId, uint256 ownerTokenId) external {
Assert
should be used only for testing codecontracts/vote-escrow/VoteEscrowCore.sol:977 assert(_isApprovedOrOwner(msg.sender, _tokenId)); contracts/vote-escrow/VoteEscrowCore.sol:981 assert(_value > 0); contracts/vote-escrow/VoteEscrowCore.sol:991 assert(_isApprovedOrOwner(msg.sender, _tokenId));
๐ Selected for report: JohnSmith
Also found by: 0x1f8b, 0xA5DF, 0xDjango, 0xKitsune, 0xLovesleep, 0xNazgul, 0xSmartContract, 0xmatt, 0xsam, Aymen0909, Bnke0x0, CRYP70, Chandr, Chinmay, CodingNameKiki, Deivitto, Dravee, ElKu, Fitraldys, Funen, GalloDaSballo, Green, IllIllI, JC, Jmaxmanblue, Junnon, Kaiziron, Kenshin, Krow10, Maxime, Migue, MiloTruck, Noah3o6, NoamYakov, Randyyy, RedOneN, ReyAdmirado, Rohan16, Rolezn, Ruhum, Sm4rty, StyxRave, TomJ, Tomio, _Adam, __141345__, ajtra, ak1, apostle0x01, asutorufos, async, benbaessler, brgltd, c3phas, cRat1st0s, carlitox477, delfin454000, djxploit, durianSausage, ellahi, erictee, fatherOfBlocks, gerdusx, gogo, hyh, jayfromthe13th, jayphbee, joestakey, kaden, kenzo, kyteg, ladboy233, lucacez, m_Rassska, mics, minhquanym, oyc_109, pfapostol, rbserver, reassor, rfa, robee, rokinot, sach1r0, saian, samruna, sashik_eth, simon135, supernova, tofunmi, zuhaibmohd
21.3211 USDC - $21.32
contracts/core/GolomTrader.sol:72 address public governance;
Since reading from memory
is much cheaper than reading from storage
, state variables that are called more than 1 SLOAD inside the function should be cached.
contracts/core/GolomTrader.sol:383 WETH.withdraw(o.totalAmt * amount); // WETH 2nd SLOAD contracts/core/GolomTrader.sol:402 distributor.addFee([msg.sender, o.exchange.paymentAddress], protocolfee); // distributor 2nd SLOAD contracts/rewards/RewardDistributor.sol:132 emit NewEpoch(epoch, tokenToEmit, stakerReward, previousEpochFee); // epoch 5 SLOADs during this logic block contracts/rewards/RewardDistributor.sol:136 epochTotalFee[epoch] = epochTotalFee[epoch] + fee; // epoch 6 SLOADs during this logic block contracts/rewards/RewardDistributor.sol:191 ve.totalSupplyAt(epochBeginTime[1]); // 2 SLOADs ve, 2 SLOADs epochBeginTime during this logic block contracts/rewards/RewardDistributor.sol:202 ve.totalSupplyAt(epochBeginTime[epochs[index]]); // 4 SLOADs ve, 4 SLOADs epochBeginTime during this logic block contracts/rewards/RewardDistributor.sol:225 for (uint256 index = 0; index < epoch; index++) { // 2 SLOADs epoch contracts/rewards/RewardDistributor.sol:233 ve.totalSupplyAt(epochBeginTime[1]); // 2 SLOADs ve, 2 SLOADs epochBeginTime during this logic block contracts/rewards/RewardDistributor.sol:244 ve.totalSupplyAt(epochBeginTime[index]); // 4 SLOADs ve, 4 SLOADs epochBeginTime during this logic block contracts/rewards/RewardDistributor.sol:227 if (claimed[tokenid][index] == 0){ // claimed[tokenid][index] 2nd SLOAD
unchecked
block can be used for gas efficiency of the expression that can't overflow/underflowcontracts/vote-escrow/VoteEscrowDelegation.sol:79 Checkpoint storage checkpoint = checkpoints[toTokenId][nCheckpoints - 1]; // could be unchecked due to check on previous line contracts/vote-escrow/VoteEscrowDelegation.sol:119 return nCheckpoints > 0 ? checkpoints[tokenId][nCheckpoints - 1].delegatedTokenIds : myArray; // could be unchecked due to statement logic contracts/vote-escrow/VoteEscrowDelegation.sol:138 if (checkpoints[nftId][nCheckpoints - 1].fromBlock <= blockNumber) { // could be unchecked due to check on line 133 contracts/vote-escrow/VoteEscrowDelegation.sol:150 uint256 center = upper - (upper - lower) / 2; // could be unchecked due to check on previous line contracts/vote-escrow/VoteEscrowDelegation.sol:157 upper = center - 1; // could be unchecked due to function logic contracts/vote-escrow/VoteEscrowCore.sol:1166 uint256 t_i = (last_point.ts / WEEK) * WEEK; // could be unchecked since constant have non-zero value contracts/vote-escrow/VoteEscrowCore.sol:1124 _max = _mid - 1; // could be unchecked due to loop logic contracts/vote-escrow/VoteEscrowCore.sol:1053 _max = _mid - 1; // could be unchecked due to loop logic contracts/vote-escrow/VoteEscrowCore.sol:744 uint256 t_i = (last_checkpoint / WEEK) * WEEK; // could be unchecked since constant have non-zero value contracts/rewards/RewardDistributor.sol:285 traderEnableDate = block.timestamp + 1 days; // could be unchecked since overflow will be in extreme far furute contracts/core/GolomTrader.sol:449 distributorEnableDate = block.timestamp + 1 days; // could be unchecked since overflow will be in extreme far furute contracts/governance/GolomToken.sol:60 minterEnableDate = block.timestamp + 1 days; // could be unchecked since overflow will be in extreme far furute
immutable
since it never changes after setting in constructorcontracts/rewards/RewardDistributor.sol:46 uint256 public startTime; contracts/rewards/RewardDistributor.sol:68 ERC20 public rewardToken; contracts/rewards/RewardDistributor.sol:69 ERC20 public weth;
Shortening revert strings to fit in 32 bytes will decrease deployment time gas and will decrease runtime gas when the revert condition has been met. Also using custom errors could save some gas: https://blog.soliditylang.org/2021/04/21/custom-errors/
contracts/governance/GolomToken.sol:24 require(msg.sender == minter, 'GolomToken: only reward distributor can enable'); contracts/rewards/RewardDistributor.sol:180 require(tokenowner == ve.ownerOf(tokenids[tindex]), 'Can only claim for a single Address together'); contracts/rewards/RewardDistributor.sol:291 require(traderEnableDate <= block.timestamp, 'RewardDistributor: time not over yet'); contracts/rewards/RewardDistributor.sol:308 require(voteEscrowEnableDate <= block.timestamp, 'RewardDistributor: time not over yet'); contracts/vote-escrow/VoteEscrowCore.sol:608 revert('ERC721: transfer to non ERC721Receiver implementer'); contracts/vote-escrow/VoteEscrowCore.sol:929 require(_locked.end > block.timestamp, 'Cannot add to expired lock. Withdraw'); contracts/vote-escrow/VoteEscrowCore.sol:945 require(unlock_time > block.timestamp, 'Can only lock until time in the future'); contracts/vote-escrow/VoteEscrowCore.sol:983 require(_locked.end > block.timestamp, 'Cannot add to expired lock. Withdraw'); contracts/vote-escrow/VoteEscrowDelegation.sol:73 require(this.balanceOfNFT(tokenId) >= MIN_VOTING_POWER_REQUIRED, 'VEDelegation: Need more voting power');
for
loopsLoop could save gas if:
++i
incrementunchecked
block
It would have next view:uint256 length = myStateVarOrArrayLength; for (uint256 i = 0; i < length;) { // ... unchecked { ++i; } }
Next loops could be optimized:
contracts/core/GolomTrader.sol:415 for (uint256 i = 0; i < proof.length; i++) { contracts/rewards/RewardDistributor.sol:143 for (uint256 index = 0; index < epochs.length; index++) { contracts/rewards/RewardDistributor.sol:157 for (uint256 index = 0; index < epochs.length; index++) { contracts/rewards/RewardDistributor.sol:225 for (uint256 index = 0; index < epoch; index++) { contracts/rewards/RewardDistributor.sol:257 for (uint256 index = 0; index < epoch; index++) { contracts/rewards/RewardDistributor.sol:272 for (uint256 index = 0; index < epoch; index++) { contracts/vote-escrow/VoteEscrowCore.sol:745 for (uint256 i = 0; i < 255; ++i) { contracts/vote-escrow/VoteEscrowCore.sol:1044 for (uint256 i = 0; i < 128; ++i) { contracts/vote-escrow/VoteEscrowCore.sol:1115 for (uint256 i = 0; i < 128; ++i) { contracts/vote-escrow/VoteEscrowCore.sol:1167 for (uint256 i = 0; i < 255; ++i) { contracts/vote-escrow/VoteEscrowDelegation.sol:171 for (uint256 index = 0; index < delegated.length; index++) { contracts/vote-escrow/VoteEscrowDelegation.sol:189 for (uint256 index = 0; index < delegatednft.length; index++) { contracts/vote-escrow/VoteEscrowDelegation.sol:199 for (uint256 i; i < _array.length; i++) {
> 0
is less gas efficient than != 0
with uint256
in require
statement with optimizercontracts/vote-escrow/VoteEscrowCore.sol:927 require(_value > 0); contracts/vote-escrow/VoteEscrowCore.sol:928 require(_locked.amount > 0, 'No existing lock found'); contracts/vote-escrow/VoteEscrowCore.sol:944 require(_value > 0); contracts/vote-escrow/VoteEscrowCore.sol:982 require(_locked.amount > 0, 'No existing lock found'); contracts/vote-escrow/VoteEscrowCore.sol:997 require(_locked.amount > 0, 'Nothing is locked');
rewardToken.balanceOf(address(ve))
called 2 times, rewardToken.totalSupply()
called 3 times:
contracts/rewards/RewardDistributor.sol:112 uint256 tokenToEmit = (dailyEmission * (rewardToken.totalSupply() - rewardToken.balanceOf(address(ve)))) / rewardToken.totalSupply(); contracts/rewards/RewardDistributor.sol:114 uint256 stakerReward = (tokenToEmit * rewardToken.balanceOf(address(ve))) / rewardToken.totalSupply();