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: 1/99
Findings: 5
Award: $5,457.45
🌟 Selected for report: 2
🚀 Solo Findings: 2
🌟 Selected for report: Lambda
4039.0029 USDC - $4,039.00
The withdraw
function of the ETH Tokemak pool has an additional parameter asEth
. This can be seen in the Tokemak Github repository or also when looking at the deployed code of the ETH pool. Compare that to e.g. the USDC pool, which does not have this parameter.
This means that the call to withdraw
will when the staking token is ETH / WETH and no withdrawals would be possible.
A new Staking
contract with ETH / WETH as the staking token is deployed. Deposits in Tokemak work fine, so users stake their tokens. However, because of the previously described issue, no withdrawal is possible, leaving the funds locked.
Handle the case where the underlying asset is WETH / ETH separately and pass this boolean in that case.
🌟 Selected for report: 0x1f8b
Also found by: BowTiedWardens, Lambda, StErMi, berndartmueller, csanuragjain, neumo, rfa
https://github.com/code-423n4/2022-06-yieldy/blob/34774d3f5e9275978621fd20af4fe466d195a88b/src/contracts/BatchRequests.sol#L37 https://github.com/code-423n4/2022-06-yieldy/blob/34774d3f5e9275978621fd20af4fe466d195a88b/src/contracts/BatchRequests.sol#L93
When contracts[i]
is the 0 address (which happens after removeAddress
was called), IStaking(contracts[i]).canBatchTransactions()
will fail for this index, meaning that the whole function will always fail.
Check if the address is equal to 0.
#0 - toshiSat
2022-06-27T23:50:33Z
duplicate #241
🌟 Selected for report: Lambda
1211.7009 USDC - $1,211.70
In the constructor of Staking.sol
, it is not enforced that the _firstEpochEndTime
is larger than the current block.timestamp
. If a low value is accidentally passed (or even 0), rebase
can be called multiple times in sucession, causing the epoch.number
to increase. Therefore, the coolDown & warmUp period can be circumvented in such a scenario, as epoch.number >= info.expiry
(in _isClaimAvailable
and _isClaimWithdrawAvailable
) will return true after rebase
caused several increases of epoch.number
.
Either require that _firstEpochEndTime
is larger than block.timestamp
or set the expiry of the first epoch to block.timestamp + _epochDuration
.
#0 - toshiSat
2022-06-27T23:48:37Z
This is something we thought about and will most likely set the period temporarily 1 higher when launching with low initial epoch durations
🌟 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
60.7879 USDC - $60.79
Staking.sol
is wrong, which can be confusing. In the case of unstakeAllFromTokemak
, we have per definition balance == _amount
(as the balance of the pool is passed from unstakeAllFromTokemak
).LiquidityReserve.sol
still refers to the old project (FOX).Staking.sol
goes against best practices.Staking.sol
, the code from line 84 is duplicated, i.e. the yieldy token approval for the liquidity reserve is executed two times.Staking.sol
, the function _getTokemakBalance
could be used.amountToRequest
should be passed to requestWithdrawal
in Line 326 of Staking.sol
, otherwise the wrong value is used when balance < _amount
.tokePoolContract
was already instantiated with ITokePool
, there is no reason to call ITokePool(tokePoolContract)
instead of tokePoolContract.balanceOf
Yieldy.sol
, the new supply is passed as _previousCirculating
to _storeRebase
. In Line 121, this leads to totalStakedBefore
and totalStakedAfter
always being equal (and a wrong rebasePercent
, which is also calculated with the _previousCirculating
.creditAmount
calculation in Yieldy.sol
is inconsistent. In transferFrom
, creditsForTokenBalance
is called, whereas the calculation is done directly in transfer
, _mint
, and _burn
. Consider using one consistent method for that.rebase
, it should be require(_totalSupply <= MAX_SUPPLY, ...);
in Line 257 of Yieldy.sol
, i.e. _totalSupply == MAX_SUPPLY
should also be allowed.🌟 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.7051 USDC - $26.71
IERC20Upgradeable(TOKE_TOKEN)
can be saved in a variable to reduce the number of memory reads by one in transferToke._storeRebase
within Yieldy.sol
, _totalSupply
is used two times (line 122 and line 129) and can therefore be stored to a variable to save one read.BatchRequests.sol
, the array size is never decreased when an item is deleted. Therefore, the other functions of the contract also need to iterate over deleted entries, which can incur a lot of additional gas. Consider decreasing the array size (combined with a swap of the last array entry).tokePoolContract
can be used instead of reading TOKE_POOL
again._isClaimAvailable
check in Line 428 is also performed within claim
, i.e. it is unnecessary performed two times, which costs gas.Yieldy.sol
, currentCredits
can be used instead of reading it again.Yieldy.sol
, there is an unnecessary storage read, currentTotalSupply
could be used instead.