Platform: Code4rena
Start Date: 21/04/2022
Pot Size: $75,000 USDC
Total HM: 7
Participants: 45
Period: 7 days
Judge: 0xean
Total Solo HM: 5
Id: 111
League: ETH
Rank: 7/45
Findings: 3
Award: $4,631.72
🌟 Selected for report: 0
🚀 Solo Findings: 0
Though setFlywheelRewards
has requiresAuth
, it still has rug risk that a privileged user can move all rewardToken
of flywheelRewards
to new (malicious) newFlywheelRewards
unconditionally.
A malicious user or a compromised admin can call setFlywheelRewards
in flywheel-v2/src/FlywheelCore.sol, which can transfer all rewardToken
to newFlywheelRewards
directly.
vim, ethers.js
Use timelock on setFlywheelRewards
function before transfering all rewardToken
to new FlywheelRewards
address.
Or separate into two functions with different auth:
FlywheelRewards
(but doesn't transfer reward), requireAuth A admin role.rewardToken
, requireAuth with B admin role.#0 - Joeysantoro
2022-05-13T00:41:48Z
This is a configuration issue, users of flywheel can have immutable rewards modules or timelocks as needed.
#1 - 0xean
2022-05-19T12:11:19Z
Going to mark this as a duplicate of #40
🌟 Selected for report: IllIllI
Also found by: 0v3rf10w, 0xDjango, 0xmint, CertoraInc, Dravee, MaratCerby, Ruhum, VAD37, catchup, csanuragjain, defsec, delfin454000, dipp, fatima_naz, gzeon, hake, hyh, joestakey, kebabsec, oyc_109, rayn, robee, samruna, simon135, sorrynotsorry, teryanarmen
347.024 USDC - $347.02
We list 2 low-critical findings and 2 non-critical findings:
newFlywheelRewards
is valid in setFlywheelRewards()
setRewardsStream
newFlywheelRewards
is valid in setFlywheelRewards()
Doesn’t check whether newFlywheelRewards
is valid in setFlywheelRewards()
. It may be a invalid or malicious. it should add some kind of check
function setFlywheelRewards(IFlywheelRewards newFlywheelRewards) external requiresAuth { uint256 oldRewardBalance = rewardToken.balanceOf(address(flywheelRewards)); if (oldRewardBalance > 0) { rewardToken.safeTransferFrom(address(flywheelRewards), address(newFlywheelRewards), oldRewardBalance); } flywheelRewards = newFlywheelRewards; emit FlywheelRewardsUpdate(address(newFlywheelRewards)); }
vim
Check whether newFlywheelRewards
is valid
Floating pragma may cause unexpected compilation time behaviour and introduce unintended bugs.
Contracts have floating pragma problems.
vim
Don't use ^
, lock pragma to ensure compiler version. e.g. pragma solidity 0.8.0;
Using ecrecover is against best practice. Preferably use ECDSA.recover instead. EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature unique. However it should be impossible to be a threat by now.
vim
Take these implementation into consideration
setRewardsStream
In the function setRewardsStream
in flywheel-v2/src/rewards/FlywheelGaugeRewards.sol, it’s better to emit an event.
vim, ethers.js
Emit an event in set function:
function setRewardsStream(IRewardsStream newRewardsStream) external requiresAuth { rewardsStream = newRewardsStream; + emit RewardsStreamUpdate(address(newRewardsStream)); }
#0 - Joeysantoro
2022-05-13T00:38:49Z
We should emit the event, the others I think are fine to leave as is
🌟 Selected for report: 0xkatana
Also found by: 0v3rf10w, 0x1f8b, 0xNazgul, 0xmint, CertoraInc, Dravee, Fitraldys, Funen, IllIllI, NoamYakov, Scocco, Tomio, catchup, csanuragjain, defsec, delfin454000, djxploit, fatima_naz, gzeon, joestakey, joshie, kebabsec, nahnah, oyc_109, rayn, robee, rotcivegaf, saian, samruna, sorrynotsorry, teryanarmen, z3s
65.9531 USDC - $65.95
The for loop has no overflow risk of i
. Use an unchecked block to save gas.
flywheel-v2/src/token/ERC20MultiVotes.sol 346: for (uint256 i = 0; i < size && (userFreeVotes + totalFreed) < votes; i++) { flywheel-v2/src/rewards/FlywheelGaugeRewards.sol 189: for (uint256 i = 0; i < size; i++) {
Use unchecked
blocks to avoid overflow checks, or use ++i
rather than i++
if you don't use unchecked blocks.
for (uint256 i = 0; i < length; ) { ... unchecked { ++i; } }