#42462 [SC-Low] Potential loss of unclaimed rewards due to updating setting `MAX_CAP_PER_WALLET_PER_EPOCH_FACTOR`
Submitted on Mar 24th 2025 at 06:10:31 UTC by @trtrth for Audit Comp | Yeet
Report ID: #42462
Report Type: Smart Contract
Report severity: Low
Target: https://github.com/immunefi-team/audit-comp-yeet/blob/main/src/Reward.sol
Impacts:
Permanent freezing of unclaimed yield
Description
Brief/Intro
The claimable reward amount of an user is dependant on the global setting MAX_CAP_PER_WALLET_PER_EPOCH_FACTOR
, which is not the max cap at the specified epoch. This can cause user's unclaimed reward unexpectedly changed when the global setting MAX_CAP_PER_WALLET_PER_EPOCH_FACTOR
is updated.
Vulnerability Details
The function Reward::getClaimableAmount()
calculates the amount of tokens that a user can claim, such that it calculates all rewards from user's last claimed epoch to current epoch considering the max cap per wallet per epoch.
function getClaimableAmount(address user) public view returns (uint256) {
uint256 totalClaimable;
// Fixed-point arithmetic for more precision
uint256 scalingFactor = 1e18;
for (uint256 epoch = lastClaimedForEpoch[user] + 1; epoch < currentEpoch; epoch++) {
if (totalYeetVolume[epoch] == 0) continue; // Avoid division by zero
uint256 userVolume = userYeetVolume[epoch][user];
uint256 totalVolume = totalYeetVolume[epoch];
uint256 userShare = (userVolume * scalingFactor) / totalVolume;
>> consider max cap >> uint256 maxClaimable = (epochRewards[epoch] / rewardsSettings.MAX_CAP_PER_WALLET_PER_EPOCH_FACTOR());
uint256 claimable = (userShare * epochRewards[epoch]) / scalingFactor;
if (claimable > maxClaimable) {
claimable = maxClaimable;
}
totalClaimable += claimable;
}
return totalClaimable;
}
Here, there exists scenario that user can lose rewards when the setting MAX_CAP_PER_WALLET_PER_EPOCH_FACTOR
is updated.
For example:
The current
MAX_CAP_PER_WALLET_PER_EPOCH_FACTOR = 5
, meaning the wallet cap is 20%At epoch 10,
Alice
has 25% volume shares and with the current max cap setting,Alice
can claim up to 20% epoch rewardAt epoch 20, the Yeet governance decides to update the setting
MAX_CAP_PER_WALLET_PER_EPOCH_FACTOR
to new value10
, meaning the wallet cap is 10%.Assume that
Alice
has not claimed rewards since the epoch 10. Now at epoch 30,Alice
decides to claim rewards. Now, the claimable reward amount at epoch 10 is only 10% of the epoch reward. In other words,Alice
loses 10% of the epoch reward at epoch 10.
Indeed, updating the max cap setting should have effects for the epochs in the future, not the epochs in the past.
Impact Details
Users can lose unclaimed rewards in past epochs when
MAX_CAP_PER_WALLET_PER_EPOCH_FACTOR
is updated to a higher value (decreasing max cap per wallet per epoch)It can be unfair for users who claims rewards before the setting is updated and the ones who claims rewards after the setting is updated.
References
https://github.com/immunefi-team/audit-comp-yeet/blob/da15231cdefd8f385fcdb85c27258b5f0d0cc270/src/Reward.sol#L173C1-L198C6
Proof of Concept
Proof of Concept
Add the test function below to file
test/Reward.Test.sol
, and within the test contractReward_runFor208weeks
.
function test_lossOfUnclaimRewards() public {
// max cap 20%
reward.rewardsSettings().setYeetRewardsSettings(5);
address alice = makeAddr('alice');
// Alice takes 100% volume shares
reward.addYeetVolume(alice, 1000);
// this is to end epoch and transition to next epoch
skip(1 days + 1);
reward.addYeetVolume(alice, 1000);
// claimable amount before updating setting
uint claimableBefore = reward.getClaimableAmount(alice);
skip(1 days + 1);
// update setting -> cap is 10%
reward.rewardsSettings().setYeetRewardsSettings(10);
// claimable amount after updating setting
uint claimableAfter = reward.getClaimableAmount(alice);
assertEq(claimableAfter, claimableBefore);
}
Run the test and console shows
Failing tests:
Encountered 1 failing test in test/Reward.Test.sol:Reward_runFor208weeks
[FAIL: assertion failed: 18754428571428571428571 != 37508857142857142857142] test_lossOfUnclaimRewards() (gas: 202467)
It means that the claimable is reduced after updating the global setting MAX_CAP_PER_WALLET_PER_EPOCH_FACTOR
to a lower value
Was this helpful?