Yieldy contest - c3phas's results

A protocol for gaining single side yields on various tokens.

General Information

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

Yieldy

Findings Distribution

Researcher Performance

Rank: 89/99

Findings: 1

Award: $26.58

🌟 Selected for report: 0

πŸš€ Solo Findings: 0

FINDINGS

Using unchecked blocks to save gas

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++ (~5 gas cheaper)

++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;

Splitting require() statements that use && saves gas - 8 gas per &&

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

Comparisons: != is more efficient than > in require (6 gas cheaper)

!= 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");
AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax Β© 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter