Platform: Code4rena
Start Date: 21/06/2022
Pot Size: $50,000 USDC
Total HM: 31
Participants: 99
Period: 5 days
Judges: moose-code, JasoonS, denhampreen
Total Solo HM: 17
Id: 139
League: ETH
Rank: 89/99
Findings: 1
Award: $26.58
π Selected for report: 0
π Solo Findings: 0
π Selected for report: BowTiedWardens
Also found by: 0v3rf10w, 0x1f8b, 0x29A, 0xKitsune, 0xNazgul, 0xf15ers, 0xkatana, 0xmint, 8olidity, ACai, Bnke0x0, Chom, ElKu, Fabble, Fitraldys, FudgyDRS, Funen, GalloDaSballo, GimelSec, IllIllI, JC, Kaiziron, Lambda, Limbooo, MiloTruck, Noah3o6, Nyamcil, Picodes, PwnedNoMore, Randyyy, RedOneN, Sm4rty, StErMi, TomJ, Tomio, TrungOre, UnusualTurtle, Waze, _Adam, aga7hokakological, ajtra, antonttc, asutorufos, bardamu, c3phas, defsec, delfin454000, exd0tpy, fatherOfBlocks, hansfriese, ignacio, joestakey, kenta, ladboy233, m_Rassska, mics, minhquanym, oyc_109, pashov, reassor, robee, s3cunda, sach1r0, saian, sashik_eth, scaraven, sikorico, simon135, slywaters
26.5777 USDC - $26.58
Solidity version 0.8+ comes with implicit overflow and underflow checks on unsigned integers. When an overflow or an underflow isnβt possible (as an example, when a comparison is made before the arithmetic operation), some gas can be saved by using an unchecked block
File: Staking.sol line 716
epoch.distribute = balance - staked;
The above operation cannot underflow due to the check on line 716 which ensures that the arithmetic operation balance - staked
would only be performed if balance is greater than staked.
We can therefore modify the above arithmetic operation as follows
unchecked{ epoch.distribute = balance - staked; }
File: Yieldy.sol line 192
creditBalances[msg.sender] = creditBalances[msg.sender] - creditAmount;
The above line cannot underflow due to the check on line 190 which ensures that the arithmetic operation would only be performed if creditBalances[msg.sender]
is greater than creditAmount
File: Yieldy.sol line 212
uint256 newValue = _allowances[_from][msg.sender] - _value;
The above line cannot underflow due to the check on line 210 which ensures that the arithmetic operation would only be performed if _allowances[_from][msg.sender]
is greater than value
Something similar to my proposal has already been implemented on line 537
++i costs less gas compared to i++ or i += 1 for unsigned integer, as pre-increment is cheaper. This statement is true even with the optimizer enabled.
i++ increments i and returns the initial value of i. Which means:
uint i = 1; i++; // == 1 but i == 2
But ++i returns the actual incremented value:
uint i = 1; ++i; // == 2 and i == 2 too, so no need for a temporary variable
In the first case, the compiler has to create a temporary variable (when used) for returning 1 instead of 2
Instances include:
File: Staking.sol line 708
epoch.number++;
The above should be modified to
++epoch.number;
Instead of using the && operator in a single require statement to check multiple conditions,using multiple require statements with 1 condition per require statement will save 8 GAS per && The gas difference would only be realized if the revert condition is realized(met).
File: Staking.sol line 574
require(!isUnstakingPaused && !isInstantUnstakingPaused,"Unstaking is paused" );
The above should be modified to:
require(!isUnstakingPaused, "Unstaking is paused"); require(!isInstantUnstakingPaused,"Unstaking is paused" );
Other instances
File: Staking.sol line 54
require( _stakingToken != address(0) && _yieldyToken != address(0) && _tokeToken != address(0) && _tokePool != address(0) && _tokeManager != address(0) && _tokeReward != address(0) && _liquidityReserve != address(0),"Invalid address" );
File: Staking.sol line 605
require(CURVE_POOL != address(0) && (curvePoolFrom == 1 || curvePoolTo == 1),"Invalid Curve Pool" );
File: Staking.sol line 611
require(!isUnstakingPaused && !isInstantUnstakingPaused,"Unstaking is paused");
File:Migration.sol line 20
require( _oldContract != address(0) && _newContract != address(0),"Invalid address");
File:Migration.sol line 44
require( _stakingToken != address(0) && _rewardToken != address(0), "Invalid address");
File: LiquidityReserve.sol line 44
require( _stakingToken != address(0) && _rewardToken != address(0), "Invalid address" );
Proof The following tests were carried out in remix with both optimization turned on and off
require ( a > 1 && a < 5, "Initialized"); return a + 2; }
Execution cost 21617 with optimization and using && 21976 without optimization and using &&
After splitting the require statement
require (a > 1 ,"Initialized"); require (a < 5 , "Initialized"); return a + 2; }
Execution cost 21609 with optimization and split require 21968 without optimization and using split require
!= 0 costs less gas compared to > 0 for unsigned integers in require statements with the optimizer enabled (6 gas)
For uints the minimum value would be 0 and never a negative value. Since it cannot be a negative value, then the check > 0 is essentially checking that the value is not equal to 0 therefore >0 can be replaced with !=0 which saves gas.
Proof: While it may seem that > 0 is cheaper than !=, this is only true without the optimizer enabled and outside a require statement. If you enable the optimizer at 10k AND you're in a require statement, this will save gas.
I suggest changing > 0 with != 0 here:
File: Staking.sol line 410
require(_amount > 0, "Must have valid amount");
The above should be modified to:
require(_amount != 0, "Must have valid amount");
Other instances File: Staking.sol line 572
require(_amount > 0, "Invalid amount");
File: Staking.sol line 604
require(_amount > 0, "Invalid amount");