TempleGold

TempleDAO
Foundry
25,000 USDC
View results
Submission Details
Severity: high
Invalid

Later stakers may earn more rewards than expected

Summary

If some stakers don't stake in the start of one staking period, they may get more rewards than expected because of the improper calculation in _rewardPerToken.

Vulnerability Details

In Staking contract, when users want to stake, withdraw, getReward, function _rewardPerToken() will be triggered to update reward per token. We will get the latest reward per token based on the previous reward per token and delta reward per token.
The vulnerability is that if some stakers stake in the middle of the whole staking period, they should not have some previous rewards. They may get more rewards than expected.

function _rewardPerToken() internal view returns (uint256) {
if (totalSupply == 0) {
return rewardData.rewardPerTokenStored;
}
return
rewardData.rewardPerTokenStored +
(((_lastTimeRewardApplicable(rewardData.periodFinish) -
rewardData.lastUpdateTime) *
rewardData.rewardRate * 1e18)
/ totalSupply);
}

Poc

Add this test case into TempleGoldStaking.t.sol.
In this case, Alice stakes 100 ether from start to the staking end, and Bob stakes 100 ether two weeks later.

function test_Poc2_tgldStaking_single_stake_single_account() public {
// for distribution
skip(4 weeks);
uint32 _rewardDuration = 4 weeks;
_setVestingPeriod(_rewardDuration);
_setRewardDuration(_rewardDuration);
_setVestingFactor(templeGold);
//skip(4 weeks);
vm.startPrank(alice);
deal(address(templeToken), alice, 1000 ether, true);
deal(address(templeToken), bob, 1000 ether, true);
_approve(address(templeToken), address(staking), type(uint).max);
uint256 stakeAmount = 100 ether;
staking.stake(stakeAmount);
uint256 stakeTime = block.timestamp;
uint256 goldBalanceBefore = templeGold.balanceOf(address(staking));
staking.distributeRewards();
uint256 goldRewardsAmount = templeGold.balanceOf(address(staking)) - goldBalanceBefore;
skip(2 weeks);
vm.stopPrank();
vm.startPrank(bob);
_approve(address(templeToken), address(staking), type(uint).max);
staking.stake(stakeAmount);
vm.stopPrank();
skip(2 weeks);
ITempleGoldStaking.Reward memory rewardData = staking.getRewardData();
uint256 earned = staking.earned(alice, 1);
console.log("Alice reward", earned);
//staking.getReward(alice, 1);
earned = staking.earned(bob, 1);
console.log("Bob reward: ", earned);
}
Logs:
Whole reward amount: 84000000000000000000000000
Alice reward 62999999999999999999596800
Bob reward: 31499999999999999999798400

From the output, the total amount for Alice and Bob is larger than the total reward amount.

Impact

The protocol will distribute more reward than expected. The protocol will lose funds. If the owner changes the distribution ratio to staking part, stakers may fail to get the rewards.

Tools Used

Manual

Recommendations

Updates

Lead Judging Commences

inallhonesty Lead Judge 11 months ago
Submission Judgement Published
Invalidated
Reason: Too generic

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.