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: 74/99
Findings: 1
Award: $59.96
🌟 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
59.9591 USDC - $59.96
Gas:
In LiquidityReserve.sol
unstakeAllRewardTokens
you can save stakingContract
as a local variable to save a (warm) SLOAD.removeLiquidity
you can avoid the require(_amount <= balanceOf(msg.sender), "Not enough lr tokens")
as that check is already performed during _burn(msg.sender, _amount)
_calculateReserveTokenValue
you can declare a named returns variable as returns (uint256 convertedAmount)
to save some gas for declaring another variable.enableLiquidityReserve
you can save stakingToken
as a local variable to save a storage read.In BatchRequests.sol
canBatchContracts
you can add a local variable address currContract
outside the for
loop and in the loop do currContract = contracts[i]
to save a (warm) SLOAD. Here you can also use a named returns statement like returns (Batch[] memory batch)
to avoid declaring the local batch
variable and save gas.sendWithdrawalRequests
you can do the same for the currContract = contracts[i]
In Yieldy.sol
initialize
you can rewrite the linked code to thisdecimal = _decimal; uint256 oneToken = 10**_decimal; WAD = oneToken; rebasingCreditsPerToken = oneToken; _setIndex(oneToken);
to save potentially 3 SLOADs
rebase
you can use directly currentTotalSupply
instead of _totalSupply
_storeRebase
you can save _totalSupply
in a local var as it's read 2 times from storage_burn
here you can use currentCredits
directly instead of reading creditBalances[_address]
from storage againIn Staking.sol
:
initialize
you can always use function arguments instead of reading from the storage eg instead of if (CURVE_POOL != address(0)) {
you should do if (_curvePool != address(0)) {
so to save some gas_sendAffiliateFee
you can read both affiliateFee
and FEE_ADDRESS
into local vars to save 2 SLOAD in totaltransferToke
you can read FEE_ADDRESS
into local var to save some gas_isClaimAvailable
you can change info
from memory
to storage
to avoid creating a copy in memory and read alwyas directly from storage
_isClaimWithdrawAvailable
you can change memory
to storage
for info
like said before. Here you can also create a local variable for withdrawalAmount
to avoid reading it twice._requestWithdrawalFromTokemak
you can also create a local variable for TOKE_POOL
to avoid reading it twice.sendWithdrawalRequests
you can read requestWithdrawalAmount
into a local var.
Same in
claim
for YIELDY_TOKEN
claimWithdraw
for YIELDY_TOKEN
_retrieveBalanceFromUser
for YIELDY_TOKEN
instantUnstakeReserve
for LIQUIDITY_RESERVE
instantUnstakeCurve
for CURVE_POOL
, curvePoolFrom
and curvePoolTo
stake
you can read YIELDY_TOKEN
into a local var to save potentially up to 3 SLOADs. Similarly also for warmUpPeriod
.General
Rebase
, Claim
and Epoch
some values can be safely reduced in precision, so to pack values into 32 bytes. eg
instead of thisstruct Epoch { uint256 duration; // length of the epoch (in seconds) uint256 number; // epoch number (starting 1) uint256 timestamp; // epoch start time uint256 endTime; // time that current epoch ends on uint256 distribute; // amount of rewards to distribute this epoch }
you can do this
struct Epoch { uint32 duration; // length of the epoch (in seconds) uint32 number; // epoch number (starting 1) uint32 timestamp; // epoch start time uint32 endTime; // time that current epoch ends on uint128 distribute; // amount of rewards to distribute this epoch }
in this way you can use a single storage slot instead of 5 slots which is a pretty significant saving both for reads and writes.
Staking.sol
you can replace instances like this (one)[https://github.com/code-423n4/2022-06-yieldy/blob/main/src/contracts/Staking.sol#L118]require(_recipient.amount > 0, "Must enter valid amount");
to
error InvalidAmount(); if (_recipient.amount == 0) revert InvalidAmount();
same for all errors.
#0 - moose-code
2022-07-09T13:44:44Z
Good stuff