Closed c4-bot-9 closed 5 months ago
Duplicate of #341
Picodes marked the issue as duplicate of #614
Picodes marked the issue as not a duplicate
Picodes changed the severity to QA (Quality Assurance)
This previously downgraded issue has been upgraded by Picodes
Splitting this report in 3 as it discusses 3 different issues within the contract.
Picodes marked the issue as satisfactory
Picodes marked the issue as duplicate of #614
Picodes changed the severity to 3 (High Risk)
Lines of code
https://github.com/code-423n4/2024-01-salty/blob/main/src/staking/StakingRewards.sol#L57-L92
Vulnerability details
There are multiple issues when calculating virtual rewards in
StakingRewards._increaseUserShare()
:Virtual rewards are not added for the first staker in a pool:
https://github.com/code-423n4/2024-01-salty/blob/main/src/staking/StakingRewards.sol#L78-L85
When the first staker stakes,
existingTotalShares
is 0, so no virtual rewards are added and they get the full reward that has been accumulated so far, giving them a larger reward share than following stakers.According to the comment, this is a measure to avoid a division by zero. However, the relevant calculation to calculate
virtualRewardsToAdd
shouldn't divide byexistingTotalShares
in the first place. By calculatingvirtualRewardsToAdd
astotalRewards[poolID] * increaseShareAmount / existingTotalShares
(rounding up instead of down),virtualRewardsToAdd
can be higher thantotalRewards[poolID]
ifincreaseShareAmount > existingTotalShares
(which especially can be the case when the pool is new and doesn't have many stakers yet.Later,
virtualRewardsToAdd
is added tototalRewards[poolID]
. BecausetotalRewards[poolID]
is only decreased when a user unstakes, it is possible that in subsequent calculationsvirtualRewardsToAdd
becomes larger thantype(uint128).max
, which causes truncation whenvirtualRewardsToAdd
is cast touint128
and also can lead to theuser.virtualRewards += uint128(virtualRewardsToAdd)
operation overflowing.Additionally,
totalRewards[poolID]
can become so large that individual users' reward shares can exceed the contract's SALT balance.Impact
The fairness/correctness of the reward distribution is impacted:
uint128
overflow. However, specific amounts need to be staked for that to happen.Proof of Concept
The following test cases can be added to https://github.com/code-423n4/2024-01-salty/blob/main/src/staking/tests/StakingRewards.t.sol to demonstrate the issues:
Run with:
Run with:
Run with:
Recommended Mitigation Steps
I find the current method of using virtual rewards very hard to reason about. Thus I don't feel confident to suggest specific changes.
Instead I would suggest using an epoch- or cycle-based reward distribution mechanism:
Assessed type
Math