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: 86/99
Findings: 1
Award: $28.15
🌟 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
28.1525 USDC - $28.15
G-01 - Caching storage variables in memory
Reading from storage (SLOAD) is more expensive than reading from memory (MLOAD) or storing in memory (MSTORE) . SLOAD costs 100 gas while MLOAD and MSTORE cost 3gas each. Consequently, each variable being read from storage more than once should be stored in memory and then called from memory.
File : Staking.sol
Function claim() : YIELDY_TOKEN is read twice Staking.sol:471 Staking.sol:473
Function stake() : YIELDY_TOKEN is read 4 times Staking.sol:412 Staking.sol:436 Staking.sol:442 Staking.sol:446
Function claimWithdraw() : YIELDY_TOKEN is read 2 times Staking.sol:487 Staking.sol:503
Function initialize() : CURVE_POOL is read twice although the information is already in memory inside _curvePool Staking.sol:78 Staking.sol:79 TOKE_POOL is read twice although the information is already in memory inside _tokePool Staking.sol:83 Staking.sol:84 STAKING_TOKEN is read once but the information could be read from memory inside _stakingToken Staking.sol:83 YIELDY_TOKEN is read twice although the information is already in memory inside _yieldyToken Staking.sol:84 Staking.sol:88 LIQUIDITY_RESERVE is read twice although the information is already in memory inside _liquidityReserve Staking.sol:85 Staking.sol:89 TOKE_TOKEN is read once but the information could be read from memory inside _tokeToken Staking.sol:92 type(uint256).max is called 5 times. Consider caching and calling the result Staking.sol:79 Staking.sol:83 Staking.sol:86 Staking.sol:90 Staking.sol:92
Function _retrieveBalanceFromUser() : YIELDY_TOKEN is read 5 times Staking.sol:519 Staking.sol:522 Staking.sol:545 Staking.sol:546 Staking.sol:559
Function instantUnstakeReserve() : LIQUIDITY_RESERVE is read 2 times Staking.sol:583 Staking.sol:588
Function instantUnstakeCurve() : curvePoolFrom is read twice Staking.sol:607 Staking.sol:621 curvePoolTo is read twice Staking.sol:607 Staking.sol:622 CURVE_POOL is read twice Staking.sol:606 Staking.sol:620
Function setToAndFromCurve(): CURVE_POOL is read 4 times : Staking.sol:633 Staking.sol:634 Staking.sol:635 Staking.sol:649 STAKING_TOKEN is read twice : Staking.sol:639 Staking.sol:641 TOKE_POOL is read twice : Staking.sol:639 Staking.sol:641 curvePoolTo is read from storage while the value is available in memory within the « to » variable; Staking.sol:649 curvePoolFrom is read from while the value is available in memory within the « from » variable; Staking.sol:649
Function rebase() : YIELDY_TOKEN is read twice : Staking.sol:704 Staking.sol:711 Epoch.endTime is read twice : Staking.sol:706 Staking.sol:703
Function addRewardsForStakers() : STAKING_TOKEN is read twice : Staking.sol:747 Staking.sol:755
File : Yieldy.sol
Function _burn(): creditBalances[_address] is read while the same information is available in memory inside the variable « currentCredits » Yieldy.sol:288
File : Migration.sol
Function _moveFundsToUpgradedContract(): OLD_YIEDLY_TOKEN is read twice : Migration.sol:44 Migration.sol:48
File : BatchRequests.sol
Function canBatchContracts: contracts[I] is read 2 times from storage at each iteration : BatchRequests.sol:37 BatchRequests.sol:38
G-02 - Multiple require instead of multiple && inside one require
Require statement containing multiple && operators can be broken down in multiple require to optimize gas consumption ( 3 gas per &&)
File : Staking.sol Staking.sol:54:63 Staking.sol:574:577 Staking.sol:605:609 Staking.sol:611:614
File : Migration.sol Migration.sol:20:23
File : LiquidityReserve.sol LiquidityReserve.sol:44:47
G-03 - Redundant code Redundant code should be avoided to save gas.
File : Staking.sol The IERC20Upgradeable() function is called twice with exactly the same parameters. Staking.sol:84:87 Staking.sol:88:91
G-04 - Use custom errors rather than revert()/require() strings
Custom errors are ABI encoded. Consequently, they can be decoded using ABI decoders. This makes it more gas efficient than « revert string ».
File : Staking.sol Staking.sol:54 Staking.sol:118 Staking.sol:143 Staking.sol:408 Staking.sol:410 Staking.sol:527 Staking.sol:572 Staking.sol:574 Staking.sol:586 Staking.sol:604 Staking.sol:605 Staking.sol:611 Staking.sol:644 Staking.sol:676
File : Yieldy.sol Yieldy.sol:58 Yieldy.sol:59 Yieldy.sol:83 Yieldy.sol:96 Yieldy.sol:187 Yieldy.sol:190 Yieldy.sol:210 Yieldy.sol:249 Yieldy.sol:257 Yieldy.sol:279 Yieldy.sol:286
File : LiquidityReserve.sol LiquidityReserve:25 LiquidityReserve:44 LiquidityReserve:61 LiquidityReserve:62 LiquidityReserve:68 LiquidityReserve:94 LiquidityReserve:105 LiquidityReserve:163 LiquidityReserve:170 LiquidityReserve:192 LiquidityReserve:215
G-05 - Usage of Uints/Ints smaller than 256bits incurs overhead
EVM operates on a 32bytes basis. If an element is smaller than 32bytes, the EVM has to perform operation in order to get the desired size. As a result, defining elements smaller than 32bytes might result in higher gas cost. It is better to work with 256 bits and downcast where needed.
File : StakingStorage.sol: curvePoolFrom and curvePoolTo could be declared as int256 instead of int128 StakingStorage:41 StakingStorage:42
File : YieldyStorage.sol decimal could be declared as uint256 instead of uint8; YieldyStorage.sol:23
G-06 - Duplicated Require()/Revert() checks should be refactored to a modifier or function
File : Staking.sol Require() related to amount >0 : Staking.sol:118 Staking.sol:410 Staking.sol:572 Staking.sol:604
Require() related to paused staking : Staking.sol:408 Staking.sol:574 Staking.sol:611
LiquidityReserve.sol Require() related to « reserve enabled » : LiquidityReserve.sol:105 LiquidityReserve.sol:192 LiquidityReserve.sol:215
G-07 - Expressions for constant values such as a call to KECCAK256() should use immutable rather than constant
File : YieldyStorage.sol YieldyStorage.sol:10 YieldyStorage.sol:11 YieldyStorage.sol:13
G-08 - Using >0 costs more gas than != when used on a UINT in a require statement != 0 costs 6 less GAS compared to >0 for unsigned integers in require statements with the optimizer enabled.
File : Yieldy.sol Yieldy.sol:83 Yieldy.sol:96
File : Staking.sol Staking.sol:118 Staking.sol:410 Staking.sol:572 Staking.sol:604
G-09 - Non-strict inequalities are cheaper than strict ones In the EVM, there is no opcode for non-strict inequalities (>=, <=) and two operations are performed. Hence, consider replacing >= with > while incrementing the value.
File : LiquidityReserve.sol LiquidityReserve.sol:69 LiquidityReserve.sol:171 LiquidityReserve.sol:94 LiquidityReserve.sol:163
File : Staking.sol Staking.sol:265 Staking.sol:285 Staking.sol:289 Staking.sol:290 Staking.sol:361 Staking.sol:535 Staking.sol:586
File : Yieldy.sol Yieldy.sol:210 Yieldy.sol:286 Yieldy.sol:190
G-10 - Use a more recent version of solidity Use a solidity version of at least 0.8.10 to have external calls skip contract existence checks if the external call has a return value.