31430 - [SC - Insight] QA
Submitted on May 19th 2024 at 01:07:31 UTC by @imsrybr0 for Boost | Alchemix
Report ID: #31430
Report type: Smart Contract
Report severity: Insight
Target: https://immunefi.com
Impacts:
Contract fails to deliver promised returns, but doesn't lose value
Description
VotingEscrow cannot be deployed
VotingEscrow cannot be deployedIssue Description:
The contract size (27031) currently exceeds the maximum allowed code size.
RevenueHandler@claim might leave unused approvals
RevenueHandler@claim might leave unused approvalsIssue Description:
This can happen if :
There are no deposits.
Debt is lower than the approved amount.
Bribe@getRewardForOwner writes the same checkpoint in a loop
Bribe@getRewardForOwner writes the same checkpoint in a loopIssue Description
When multiple tokens are claimed, the same check point (same balance, same timestamp) will be written multiple times (up to the number of claimed tokens).
Voter@claimBribes does not check if the bribe is valid
Voter@claimBribes does not check if the bribe is validIssue Description
_bribes[i] is a user input and can be any contract that implements getRewardForOwner.
staleThreshold is 60 days in RewardDistributor@amountToCompound
staleThreshold is 60 days in RewardDistributor@amountToCompoundIssue Description
It seems that the stale threshold was updated for testing purposes.
Rewards will be lost for a token if it's merged before claiming them
Issue Description
VotingEscrow@merge doesn't claim rewards from RewardsDistributor for the merged token before burning it. This makes it impossible to claim the rewards if it wasn't done before merging since the approval check will fail.
Voter@pokeTokens will fail when the poked token lock is expired
Voter@pokeTokens will fail when the poked token lock is expiredIssue Description
It will fail at the reset step if the token already voted in the current epoch, either normally or by the owner front running the pokeTokens transaction with a reset.
It will fail at the poke (poke -> vote) step because the voting power of the token will be 0.
Boost is not carried over in Voter when poking
Voter when pokingIssue Description
The following comment in the Voter@poke function suggests that boosts is carried over when poking :
// Previous boost will be taken into account with weights being pulled from the votes mapping
However this is not the case.
For example, a user votes on a single pool with a weight of X using a token which has a voting power of Y and uses a boost of Z :
_totalVoteWeightwill be equal to XtotalPowerwill be equal to Y + Z_poolWeightwill be equal to X * (Y + Z) / X = Y + Z
Now the user pokes using that same token :
_totalVoteWeightwill be equal to Y + ZtotalPowerwill be equal to Y + 0 = Y_poolWeightwill be equal to (Y + Z) * (Y) / (Y + Z) = Y
The weights stay proportional to the initial vote weights but the boost is lost.
There is no link between the Minter and Voter epochs
Minter and Voter epochsIssue Description
There is no link between the Minter and Voter epochs even if they share the same duration.
This means that voters can vote as soon as possible even before a distribution.
In this case, total votes will be reset to 0 in bribes discarding previous voters and leading to the rewards being split based on future voters but still be distributed to all of them. Not all of them will able to claim though (fewer voters are accounted for mean more rewards per voter).
Proposals can be created with 0 voting power
Issue Description
veALCX supply starts at 0
veALCX supply is dynamic
The proposer threshold (amount needed to be able to create a proposal) is checked at the proposal creation time based on the veALCX balance of the creator and the veALCX total supply at that time :
The proposal quorum threshold is checked against the total supply at the start of the voting period :
A voter's power is calculated based on the start of the voting period :
This would allow to create a proposal with 0 voting power as the proposal threshold is initially 0 when veALCX total voting power is still 0 (or decays to 0). This may also lead to easily pass a proposal depending on the veALCX voting power created up to the start of the voting period.
Reentrancy guard modifier is named differently across different contracts
Issue Description
VotingEscrow
nonreentrant
Yes
Voter
lock
Yes
RewardsDistributor
nonReentrant
No (OpenZeppelin ReentrancyGuard)
Bribe
lock
Yes
BaseGauge
lock
Yes
Proof of Concept
QA report
Last updated
Was this helpful?