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: 83/179
Findings: 2
Award: $129.83
🌟 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
Context:
ERC20Votes.sol#L14 GolomToken.sol#L36-L38
Description:
@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol
contract supports token supply up to 2^224^ - 1
but GolomToken can mint 2^226^ - 1
. Naturally, it does not seem possible to reach this number, but it is mathematically possible, it can be a problem if the project team has a security vulnerability such as privatekey theft.
Context:
RewardDistributor.sol#L74-L85) GolomToken.sol#L36-L38 GolomToken.sol#L28-L30
Description: Also check the length of the address to protect the code from short address problem just in case.This is best practice
Recommendation:
use this ; require(_governance =! address(0x0));
Context:
VoteEscrowDelegation.sol#L51 RewardDistributor.sol#L48 RewardDistributor.sol#L57 VoteEscrowCore.sol#L295-L298
Description - Recommendation: https://docs.soliditylang.org/en/v0.8.15/style-guide.html Must be written according to Style Guide Format rules as stated in Solidity documentation
MAX_BLOCKS, TOKEN_NAME, TOKEN_TICKER, CONTRACT_VERSION
)Context: RewardDistributor.sol#L66-L67
Description For code readability, the explanations should be clear and distinct, the explanations in these two variables are the same, but they should have different functions
🌟 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
94.6571 USDC - $94.66
Context:
Description:
You can cut out 10 opcodes in the creation-time EVM bytecode if you declare a constructor payable. Making the constructor payable eliminates the need for an initial check of msg.value == 0
and saves 21
💰 gas on deployment with no security risks.
Proof of Concept: https://forum.openzeppelin.com/t/a-collection-of-gas-optimisation-tricks/19966/5?u=pcaversaccio
Recommendation: Set the constructor to payable.
Context:
RewardDistributor.sol#L9 VoteEscrowDelegation.sol#L6
Description: It's used to print the values of variables while running tests to help debug and see what's happening inside your contracts But since it's a development tool, it serves no purpose on mainnet. This will save some gas💰
Recommendation:
Use to import 'hardhat/console.sol';
only for tests, remove to mainnet deploy
Context:
VoteEscrowDelegation.sol#L51 RewardDistributor.sol#L257 RewardDistributor.sol#L222-L223 RewardDistributor.sol#L175-L176 RewardDistributor.sol#L156 RewardDistributor.sol#L142 RewardDistributor.sol#L45
Description: When a variable is declared solidity assigns the default value. In case the contract assigns the value again, it costs extra gas.
Recommendation:
uint x = 0
costs more gas than uint x
without having any different functionality.
Context:
RewardDistributor.sol#L269 RewardDistributor.sol#L254 RewardDistributor.sol#L215 RewardDistributor.sol#L172 RewardDistributor.sol#L155 RewardDistributor.sol#L141
Description: Several functions across multiple contracts have a public visibility and can be marked with external visibility to save gas. This function is never called from within the contract
Recommendation: Change the functions visibility to external to save gas.
Context:
VoteEscrowDelegation.sol#L200 VoteEscrowDelegation.sol#L190 VoteEscrowDelegation.sol#L172 RewardDistributor.sol#L157 RewardDistributor.sol#L183 RewardDistributor.sol#L180 RewardDistributor.sol#L143 GolomTrader.sol#L415
Description: One can save gas by caching the array length (in stack) and using that set variable in the loop. Replace state variable reads and writes within loops with local variable reads and writes. This is done by assigning state variable values to new local variables, reading and/or writing the local variables in a loop, then after the loop assigning any changed local variables to their equivalent state variables.
Recommendation:
Simply do something like so before the for loop: uint length = variable.length
Then add length in place of variable.length
in the for loop.
Context:
VoteEscrowDelegation.sol#L200 VoteEscrowDelegation.sol#L172 RewardDistributor.sol#L258 RewardDistributor.sol#L226 RewardDistributor.sol#L183 RewardDistributor.sol#L180 RewardDistributor.sol#L157 RewardDistributor.sol#L143 GolomTrader.sol#L415
Description:
Due to reduced stack operations, using ++index
saves 5
💰 gas per iteration. Use ++index instead of index++ to increment a loop counter
Recommendation:
Use ++index
to increment a loop counter.
uint256
is Cheaper Than uint8
Context: VoteEscrowCore.sol#L320
Description:
The EVM reads in 32 byte words if your data is smaller, further operations are needed to downscale from 256 bits to 8 bit. Since these uint8
s are not packed with others to be read from the same slot it's cheaper to just use uint256
from them.
Recommendation:
use uint256
instead of uint8
uint256
is Cheaper Than uint8
for ReentrancyGuardContext:
Description:
The EVM reads in 32 byte words if your data is smaller, further operations are needed to downscale from 256 bits to 8 bit. Since these uint8
s are not packed with others to be read from the same slot it's cheaper to just use uint256 from them.
Proof of Concept:
ReentrancyGuard.sol#L34-L37
OpenZeppelin Reentrancy Guard use uint256
but VoteEscrowCore.sol use uint8
Recommendation:
use uint256
instead of uint8
Context: VoteEscrowCore.sol#L356-L364
Description:
Project Reentrancy Guard Pattern use 23888 gas , OpenZeppeling Reentrancy Guard use 23.537 gas
It saves 351
💰 gas in each operation, modifiers are designs with high usage, so gas saving is important
Proof of Concept: Project Reentrancy Code Pattern (23888 💰Gas);
pragma solidity ^0.8.10; contract fooContract { uint8 internal constant _not_entered = 1; uint8 internal constant _entered = 2; uint8 internal _entered_state = 1; modifier nonreentrant() { require(_entered_state == _not_entered); _entered_state = _entered; _; _entered_state = _not_entered; } // 23888 Gas function foo() public nonreentrant { } }
Openzeppelin Reentrancy Code Pattern (23537 💰Gas);
pragma solidity ^0.8.10; contract fooContract { uint256 private constant _not_entered = 1; uint256 private constant _entered = 2; uint256 private _entered_state = 1; modifier nonreentrant() { require(_entered_state == _not_entered); _entered_state = _entered; _; _entered_state = _not_entered; } // 23537 Gas function foo() public nonreentrant { } }
Recommendation: Use this OZ pattern ; ReentrancyGuard.sol#L34-L37
Context: All Contracts
Description:
Contracts most called functions could simply save gas by function ordering via Method ID. Calling a function at runtime will be cheaper if the function is positioned earlier in the order (has a relatively lower Method ID) because 22 gas
are added to the cost of a function for every position that came before it. The caller can save on gas if you prioritize most called functions.
Recommendation:
Find a lower method ID name for the most called functions for example Call()
vs. Call1()
is cheaper by 22 gas
Proof of Concept: https://coinsbench.com/advanced-gas-optimizations-tips-for-solidity-85c47f413dc5
!= 0
instead of > 0
Context:
GolomTrader.sol#L152 GolomTrader.sol#L250 GolomTrader.sol#L387
Description:
When dealing with unsigned integer types, comparisons with != 0
are cheaper then with > 0
Proof of Consept: https://aws1.discourse-cdn.com/business6/uploads/zeppelin/original/2X/3/363a367d6d68851f27d2679d10706cd16d788b96.png
&&
used more gasContext:
VoteEscrowCore.sol#L894 VoteEscrowCore.sol#L538
Description:
Using double require instead of operator &&
can save more gas. (8
gas💰 cheaper)
Proof of Consept: Example:
// gas cost 21630 function check(uint x)public view{ require(x == 0 && x < 1 ); } // using double require: // gas cost 21622 require(x == 0 ); require( x < 1); } }
Context:
RewardDistributor.sol#L181 GolomToken.sol#L24
Description:
require(msg.sender == minter, 'GolomToken: only reward distributor can enable’);
( require error string length 32>)
Shortening revert strings to fit in 32 bytes will decrease deployment time gas and will decrease runtime gas when the revert condition is met. Revert strings that are longer than 32 bytes require at least one additional mstore, along with additional overhead for computing memory offset, etc.
Recommendation: Revert strings > 32 bytes:
Context:
VoteEscrowCore.sol#L296-L297 RewardDistributor.sol#L57 RewardDistributor.sol#L48
Description: Due to how constant variables are implemented (replacements at compile-time), an expression assigned to a constant variable is recomputed each time that the variable is used, which wastes some gas. If the developer wants to show the calculation in the Constant expression, he can annotate it with ‘//’
Proof Of Concept:
RewardDistributor.sol#L48 :
uint256 constant dailyEmission = 600000 * 10**18;
Recommendation:
uint256 constant dailyEmission = 108000000; //(600000 * 10**18)
Context:
Description: SSTORE from 0 to 1 (or any non-zero value), the cost is 20000;SSTORE from 1 to 2 (or any other non-zero value), the cost is 5000. Therefore, switching between 1, 2 instead of 0, 1 will be more gas efficient.
Proof Of Concept: GolomTrader.sol#L158-L162 :
/// OrderStatus = 0 , if signature is invalid. (This is costly operator) /// OrderStatus = 1 , if deadline has been /// OrderStatus = 2 , order is filled or cancelled /// OrderStatus = 3 , valid order
Recommendation:
/// OrderStatus = 1 , if signature is invalid /// OrderStatus = 2 , if deadline has been /// OrderStatus = 3 , order is filled or cancelled /// OrderStatus = 4 , valid order
Context:
Description:
This state variable is not used anywhere in Contracts:mapping(uint256 => uint256) public rewardLP;
Recommendation: Remove this line from contract
mapping(uint256 => uint256) public rewardLP;
Context: RewardDistributor.sol#L69 RewardDistributor.sol#L46
Description:
Define the data to be assigned value as Immutable in Constructor
Immutable state variables can be declared using the immutable
keyword. They cannot be read during contract creation, but either have to be written to exactly once in the constructor or initialized directly in their declaration. Runtime code can only read immutable
, but not write to them
Recommendation: Change to code like this for save gas ;
ERC20 public weth;
===> ERC20 public immutable weth;
uint256 public startTime;
===> uint256 public immutable startTime;