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: 42/99
Findings: 2
Award: $119.21
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: IllIllI
Also found by: 0x1337, 0x1f8b, 0x29A, 0x52, 0xDjango, 0xNazgul, 0xNineDec, 0xc0ffEE, 0xf15ers, 0xmint, Bnke0x0, BowTiedWardens, Chom, ElKu, FudgyDRS, Funen, GalloDaSballo, GimelSec, JC, Kaiziron, Lambda, Limbooo, Metatron, MiloTruck, Noah3o6, Picodes, PumpkingWok, PwnedNoMore, Sm4rty, StErMi, TomJ, TrungOre, UnusualTurtle, Waze, _Adam, aga7hokakological, ak1, antonttc, berndartmueller, cccz, cryptphi, csanuragjain, defsec, delfin454000, dipp, elprofesor, exd0tpy, fatherOfBlocks, hake, hansfriese, hubble, joestakey, kenta, ladboy233, mics, oyc_109, pashov, pedr02b2, reassor, robee, samruna, scaraven, shung, sikorico, simon135, sseefried, tchkvsky, unforgiven, zzzitron
84.4957 USDC - $84.50
Contract Migration
implements function moveFundsToUpgradedContract
that is expected to move funds from OLD_CONTRACT
to NEW_CONTRACT
. The issue is that for withdrawing funds it uses instantUnstake
that does not exist in Staking
contract. Contract Staking
implements instantUnstakeReserve
and instantUnstakeCurve
, while instantUnstake
is being implemented by LiquidityReserve
.
Migration.sol
:
Manual Review / VSCode
It is recommended to use ILiquidityReserve
interface for OLD_CONTRACT
and make sure that OLD_CONTRACT
is a LiquidityReserve
contract.
Low
Contract Staking
implements multiple functions for setting staking parameters. The issue is that these functions are missing basic threshold checks of received arguments which makes it risky that, either by accident or intentionally, the parameters will be set to values that will completely break Staking
contract logic.
Staking.sol
:
affiliateFee
for maximum value - https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/Staking.sol#L167-L170duration
for minimum and maximum value - https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/Staking.sol#L217-L220_vestingPeriod
for minimum and maximum warmUpPeriod
value - https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/Staking.sol#L226-L229_vestingPeriod
for minimum and maximum coolDownPeriod
value - https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/Staking.sol#L235-L238_timestamp
for minimum and maximum timemLeftToRequestWithdrawal
value - https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/Staking.sol#L246-L251Manual Review / VSCode
It is recommended to add threshold checks for listed parameters.
Low
Some tokens (like USDT L199) do not work when changing the allowance from an existing non-zero allowance value. They must first be approved by zero and then the actual allowance must be approved.
IERC20(token).approve(address(operator), 0); IERC20(token).approve(address(operator), amount);
Staking.sol
:
LiquidityReserver.sol
:
Migration.sol
:
Manual Review / VSCode
Approve with a zero amount first before setting the actual amount.
Low
Multiple contracts are not implementing events for critical functions. Lack of events makes it difficult for off-chain applications to monitor the protocol.
Staking.sol
:
claimFromTokemak
function event - https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/Staking.sol#L111transferToke
function event - https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/Staking.sol#L141setTimeLeftToRequestWithdrawal
function event - https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/Staking.sol#L246unstakeAllFromTokemak
function event - https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/Staking.sol#L370sendWithdrawalRequests
function event - https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/Staking.sol#L384stake
function event - https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/Staking.sol#L406claim
function event - https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/Staking.sol#L465claimWithdraw
function event - https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/Staking.sol#L485instantUnstakeReserve
function event - https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/Staking.sol#L571instantUnstakeCurve
function event - https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/Staking.sol#L600unstake
function event - https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/Staking.sol#L674rebase
function event - https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/Staking.sol#L701addRewardsForStakers
function event - https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/Staking.sol#L741preSign
function event - https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/Staking.sol#L769LiquidityReserve.sol
:
enableLiquidityReserve
function event - https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/LiquidityReserve.sol#L57addLiquidity
function event - https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/LiquidityReserve.sol#L104removeLiquidity
function event - https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/LiquidityReserve.sol#L161instantUnstake
function event - https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/LiquidityReserve.sol#L188unstakeAllRewardTokens
function event - https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/LiquidityReserve.sol#L214Migration.sol
:
moveFundsToUpgradeContract
function event - https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/Migration.sol#L43BatchRequests.sol
:
addAddress
function event - https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/BatchRequests.sol#L81-L83removeAddress
function event - https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/BatchRequests.sol#L89-L99Manual Review / VSCode
It is recommended to add missing events to listed functions.
Low
Multiple contracts do not check for zero addresses which might lead to loss of funds, failed transactions and can break the protocol functionality.
Staking.sol
:
stake
for _recipient
- https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/Staking.sol#L406LiquidityReserve.sol
:
instantUnstake
for _recipient
- https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/LiquidityReserve.sol#L188BatchRequests.sol
:
addAddress
for _address
- https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/BatchRequests.sol#L81removeAddress
for _address
- https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/BatchRequests.sol#L89Manual Review / VSCode
It is recommended to add zero address checks for listed parameters.
Low
Changing critical addresses such as ownership should be a two-step process where the first transaction (from the old/current address) registers the new address (i.e. grants ownership) and the second transaction (from the new address) replaces the old address with the new one. This gives an opportunity to recover from incorrect addresses mistakenly used in the first step. If not, contract functionality might become inaccessible.
Staking.sol
:
owner
through OwnableUpgradeable
- https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/Staking.sol#L18LiquidityReserve.sol
:
owner
through OwnableUpgradeable
- https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/LiquidityReserve.sol#L16BatchRequests.sol
:
owner
through Ownable
- https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/BatchRequests.sol#L8Manual Review / VSCode
It is recommended to implement two-step process for changing ownership.
Non-Critical
Contract YieldStorage.sol
uses bit negation to retrieve MAX_UINT256
and MAX_SUPPLY
(max uint128
), these values can be easily retrieved from type
object.
(..) uint256 internal constant MAX_UINT256 = ~uint256(0); uint256 internal constant MAX_SUPPLY = ~uint128(0); // (2^128) - 1 (..)
YieldyStorage.sol
:
Manual Review / VSCode
It is recommended to use type(uint256).max
and type(uint128).max
respectively.
Non-Critical
uint256 public constant BASIS_POINTS = 10000; // 100% in basis points
StakingStorage.sol
:
LiquidityReserveStorage.sol
:
Manual Review / VSCode
It is recommended to use scientific notation such as 1e4
.
Non-Critical
Contracts are missing natspec comments which makes code more difficult to read and prone to errors.
Staking.sol
:
@param _curvePool
description - https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/Staking.sol#L155@param _affiliateFee
description - https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/Staking.sol#L163-L167@param _affiliateAddress
description - https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/Staking.sol#L175@param _shouldPause
description - https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/Staking.sol#L183-L187@param _shouldPause
description - https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/Staking.sol#L193-L197@param _shouldPause
description - https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/Staking.sol#L203-L207@param duration
description - https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/Staking.sol#L213-L217@param _vestingPeriod
description - https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/Staking.sol#L223-L226@param _vestingPeriod
description - https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/Staking.sol#L232-L235@param _amount
and @param _recipient
descriptions - https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/Staking.sol#L402-L406@param stake
description - https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/Staking.sol#L453-L456@param _recipient
description - https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/Staking.sol#L461-L465@param _amount
description - https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/Staking.sol#L511-L517Yieldy.sol
:
@param _previsionCirculating
, @param _profit
and @param _epoch
descriptions - https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/Yieldy.sol#L105-L110@param _wallet
and @return
descriptions - https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/Yieldy.sol#L133-L138@param _amount
and @return
descriptions - https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/Yieldy.sol#L143-L147@param _credits
and @return
descriptions - https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/Yieldy.sol#L156-L160@param _to
, @param _value
descriptions - https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/Yieldy.sol#L177-L182@param _from
, @param _to
, @param _value
, @return
descriptions - https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/Yieldy.sol#L198-L205@return
- https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/Yieldy.sol#L224-L227Migration.sol
:
BatchRequests.sol
:
@return
description - https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/BatchRequests.sol#L29-L33@param _index
and @return
description - https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/BatchRequests.sol#L46-L50@param _index
and @return
description - https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/BatchRequests.sol#L61-L65@return
description - https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/BatchRequests.sol#L69-L73Manual Review / VSCode
It is recommended to add missing natspec comments.
🌟 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
34.7125 USDC - $34.71
Contract LiquidityReserveStorage
is using math exponent calculation to express big numbers. This consumes additional gas and it is better to use scientific notation.
LiquidityReserveStorage.sol:9: uint256 public constant MINIMUM_LIQUIDITY = 10**3; // lock minimum stakingTokens for initial liquidity
Manual Review / VSCode
It is recommended to use scientific notation, for example: 1e3
.
Packing integer variables into storage slots saves gas.
struct 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 }
In order to save gas following variables can be packed:
uint256 duration -> uint32 duration (alternatively uint64) uint256 number -> uint32 number (alternatively uint64) uint256 timestamp -> uint32 timestamp (alternatively uint64) uint256 endTime -> uint32 endTime (alternatively uint64)
This will allow pack struct's 5 slots into 2 slots.
structs/Epoch.sol
:
Manual Review / VSCode
It is recommended to pack listed integer variables.
Usage of custom errors reduces the gas cost.
Contracts that should be using custom errors:
LiquidityReserve.sol
Migration.sol
Staking.sol
Yieldy.sol
Manual Review / VSCode
It is recommended to add custom errors to listed contracts.
++i
or --i
costs less gas compared to i++
, i += 1
, i--
or i -= 1
for unsigned integer as pre-increment/pre-decrement is cheaper (about 5 gas per iteration).
Staking.sol:708: epoch.number++;
Manual Review / VSCode
It is recommended to use ++i
or --i
instead of i++
, i += 1
, i--
or i -= 1
to increment value of an unsigned integer variable.
Starting from solidity 0.8.0
there is built-in check for overflows/underflows. This mechanism automatically checks if the variable overflows or underflows and throws an error. Multiple contracts use increments that cannot overflow but consume additional gas for checks.
Staking.sol:708: epoch.number++;
Manual Review / VSCode
It is recommended to wrap incrementing with unchecked
block, for example: unchecked { ++i }
or unchecked { --i }
If a variable is not set/initialized, it is assumed to have the default value (0
for uint, false
for bool, address(0)
for addresses). Explicitly initializing it with its default value is an anti-pattern and waste of gas.
Staking.sol:636: int128 from = 0; Staking.sol:637: int128 to = 0;
Manual Review / VSCode
It is recommended to remove explicit initializations with default values.
When dealing with unsigned integer types, comparisons with != 0
are cheaper than with > 0
.
LiquidityReserve.sol:223: if (amount > 0) IStaking(stakingContract).unstake(amount, false); Staking.sol:118: require(_recipient.amount > 0, "Must enter valid amount"); Staking.sol:305: requestedWithdrawals.amount > 0 && Staking.sol:326: if (amountToRequest > 0) tokePoolContract.requestWithdrawal(_amount); Staking.sol:363: requestWithdrawalAmount > 0; Staking.sol:392: if (requestWithdrawalAmount > 0) { Staking.sol:410: require(_amount > 0, "Must have valid amount"); Staking.sol:415: if (yieldyTotalSupply > 0) { Staking.sol:470: if (info.credits > 0) { Staking.sol:533: if (warmUpBalance > 0) { Staking.sol:572: require(_amount > 0, "Invalid amount"); Staking.sol:604: require(_amount > 0, "Invalid amount"); Yieldy.sol:83: require(_totalSupply > 0, "Can't rebase if not circulating"); Yieldy.sol:96: require(rebasingCreditsPerToken > 0, "Invalid change in supply");
Manual Review / VSCode
It is recommended to use != 0
instead of > 0
.