Late Snowy Mockingbird
Medium
https://github.com/sherlock-audit/2024-11-tally/blob/main/staker/src/GovernanceStaker.sol#L471
After call bumpEarningPower function, even though depositor stakes more or withdraws, the earningPower of the deposit is not changed.
- The depositor might not believe this system because the earningPower is not increased even though stake more.
- The depositor can get reward with old earningpower because the earningPower is not decreased even though withdraw.
function testFuzz_EarningPowerAfterBumpsEarning(
address _depositor,
address _delegatee,
uint256 _stakeAmount,
uint256 _rewardAmount,
address _bumpCaller,
address _tipReceiver,
uint256 _requestedTip,
uint96 _earningPowerIncrease
) public {
vm.assume(_tipReceiver != address(0));
_stakeAmount = _boundToRealisticStake(_stakeAmount);
_rewardAmount = _boundToRealisticReward(_rewardAmount);
_earningPowerIncrease = uint96(bound(_earningPowerIncrease, 1, type(uint48).max));
// A user deposits staking tokens
(, GovernanceStaker.DepositIdentifier _depositId) =
_boundMintAndStake(_depositor, _stakeAmount, _delegatee);
_mintGovToken(_depositor, _boundMintAmount(_stakeAmount));
// The contract is notified of a reward
_mintTransferAndNotifyReward(_rewardAmount);
// The full duration passes
_jumpAheadByPercentOfRewardDuration(101);
// Tip must be less than the max bump, but also less than rewards for the sake of this test
_requestedTip = bound(_requestedTip, 0, _min(maxBumpTip, govStaker.unclaimedReward(_depositId)));
// The staker's earning power increases
earningPowerCalculator.__setEarningPowerForDelegatee(
_delegatee, _stakeAmount + _earningPowerIncrease
);
// Bump earning power is called
vm.startPrank(_bumpCaller);
govStaker.bumpEarningPower(_depositId, _tipReceiver, _requestedTip);
(uint96 _newBalanceAfterBump,, uint96 _newEarningPowerAfterBump,,,,) = govStaker.deposits(_depositId);
assertEq(_newEarningPowerAfterBump, _stakeAmount + _earningPowerIncrease);
vm.stopPrank();
vm.startPrank(_depositor);
govToken.approve(address(govStaker), _boundMintAmount(_stakeAmount));
govStaker.stakeMore( _depositId, _boundMintAmount(_stakeAmount/2));
(uint96 _newBalanceAfterMore,, uint96 _newEarningPowerAfterMore,,,,) = govStaker.deposits(_depositId);
govStaker.withdraw( _depositId, _boundMintAmount(_stakeAmount * 3/2));
(uint96 _newBalanceAfterWithdraw,, uint96 _newEarningPowerAfterWithdraw,,,,) = govStaker.deposits(_depositId);
vm.stopPrank();
console2.log("----Bump earning power-----");
console2.log("new Balance after bump");
console2.log(_newBalanceAfterBump);
console2.log("new Earning Power after bump");
console2.log(_newEarningPowerAfterBump);
console2.log("----After stake more-----");
console2.log("new Balance after stake more");
console2.log(_newBalanceAfterMore);
console2.log("new Earning Power after stake more");
console2.log(_newEarningPowerAfterMore);
console2.log("----After withdraw-----");
console2.log("new Balance after withdraw");
console2.log(_newBalanceAfterWithdraw);
console2.log("new Earning Power after withdraw");
console2.log(_newEarningPowerAfterWithdraw);
assertGt(_newEarningPowerAfterBump, _newEarningPowerAfterMore);
}
This is test code. The result is following.
----Bump earning power----- new Balance after bump 24999999900000000000011555 new Earning Power after bump 24999999900000000000021462 ----After stake more----- new Balance after stake more 37499999850000000000017332 new Earning Power after stake more 24999999900000000000021462 ----After withdraw----- new Balance after withdraw 0 new Earning Power after withdraw 24999999900000000000021462
Encountered 1 failing test in test/GovernanceStaker.t.sol:BumpEarningPower [FAIL: assertion failed: 24999999900000000000021462 <= 24999999900000000000021462;
Manual Review
As the test code had used MockFullEarningPowerCalculator as EarningPowerCalculator, it causes this issue. But if use BinaryEligibilityOracleEarningPowerCalculator as EarningPowerCalculation, the bump will not work. So you need to make proper calculation for bump and need some code for processing after bump.