-
Notifications
You must be signed in to change notification settings - Fork 0
Open
Description
Mammoth Ocean Donkey
High
Users will loose rewards if they increase their stake before claiming reward in the UsualSP
contract
Summary
UsualS staker reward index is updated whenever they interact with staking and unstaking
File: pegasus/packages/solidity/src/token/UsualSP.sol
282: function stake(uint256 amount) public nonReentrant whenNotPaused {
//////
286:
287: @> _updateReward(msg.sender);
This is done in the _updateReward()
function
Root Cause
The problem is that for every time the _updateReward()
is called on the msg.sender
the
rewardPerTokenStored
is updatedlastRewardPerTokenUsed
of the staker is updated to the currentrewardPerTokenStored
- rewards earned are overwritten
function _updateReward(address account) internal virtual {
RewardAccrualBaseStorageV0 storage $ = _getRewardAccrualBaseDataStorage();
if (block.timestamp > $.lastUpdateTime) {
@> $.rewardPerTokenStored = _rewardPerToken();
$.lastUpdateTime = block.timestamp;
}
@> $.rewards[account] = _earned(account);
$.lastRewardPerTokenUsed[account] = $.rewardPerTokenStored;
}
Problem arises when the user increases their stake and _updateReward()
is called, the _earned()
function will calculate the rewards based on the differenc between the rewardPerTokenStored
and the last lastRewardPerTokenUsed
and then overwrite the rewards[account]
previously updated
Internal Pre-conditions
NIL
External Pre-conditions
NIL
Attack Path
See root cause
Impact
Loss of rewards
PoC
No response
Mitigation
Modify as shown below
function _updateReward(address account) internal virtual {
RewardAccrualBaseStorageV0 storage $ = _getRewardAccrualBaseDataStorage();
if (block.timestamp > $.lastUpdateTime) {
@> $.rewardPerTokenStored = _rewardPerToken();
$.lastUpdateTime = block.timestamp;
}
- $.rewards[account] = _earned(account);
+ $.rewards[account] += _earned(account);
Metadata
Metadata
Assignees
Labels
No labels