Rough Brick Meerkat
Medium
The call to function setDelegateeScoreEligibilityThreshold in the BinaryEligibilityOracleEarningPowerCalculator contract should be accompanied by updating each and every Delegatee’s Score, eligibility and the earning power as all these are linked to the threshold. Not doing these causes the contract to keep running with the stale values of these thereby affecting the rewards also.
The call to function setDelegateeScoreEligibilityThreshold in the BinaryEligibilityOracleEarningPowerCalculator contract should be accompanied by updating each and every Delegatee’s Score, eligibility and the earning power as all these are linked to the threshold. Not doing these causes the contract to keep running with the stale values of these thereby affecting the rewards also.
The function setDelegateeScoreEligibilityThreshold can only be called by the contract owner to set a new delegatee score eligibility threshold.
function _setDelegateeScoreEligibilityThreshold(uint256 _newDelegateeScoreEligibilityThreshold)
internal
{
emit DelegateeEligibilityThresholdScoreSet(
delegateeEligibilityThresholdScore, _newDelegateeScoreEligibilityThreshold
);
delegateeEligibilityThresholdScore = _newDelegateeScoreEligibilityThreshold;
}
}
This value is used to determine the eligibility of the delegatee for the earning power. Only when the delegatee score is equal or above this threshold, the delegate will get 100% earning power, otherwise the earning power is ‘0’. i.e.
Earning power = staked amount if delegatee score >= delegateeEligibilityThresholdScore
and
Earning power = 0 if delegatee score < delegateeEligibilityThresholdScore
function getEarningPower(uint256 _amountStaked, address, /* _staker */ address _delegatee)
external
view
returns (uint256)
{
if (_isOracleStale() || isOraclePaused) return _amountStaked;
return _isDelegateeEligible(_delegatee) ? _amountStaked : 0;
}
function _isDelegateeEligible(address _delegatee) internal view returns (bool) {
return delegateeScores[_delegatee] >= delegateeEligibilityThresholdScore;
}
The earning power of the delegate and the totalEarningPower are used in the reward calculations as well. Refer, for example, https://github.com/sherlock-audit/2024-11-tally/blob/b125d1f2b52170a3789b1060a52fc6609e6e2262/staker/src/GovernanceStaker.sol#L303-L308
function rewardPerTokenAccumulated() public view virtual returns (uint256) {
if (totalEarningPower == 0) return rewardPerTokenAccumulatedCheckpoint;
return rewardPerTokenAccumulatedCheckpoint
+ (scaledRewardRate * (lastTimeRewardDistributed() - lastCheckpointTime)) / totalEarningPower;
}
function _scaledUnclaimedReward(Deposit storage deposit) internal view virtual returns (uint256) {
return deposit.scaledUnclaimedRewardCheckpoint
+ (deposit.earningPower * (rewardPerTokenAccumulated() - deposit.rewardPerTokenCheckpoint));
}
Assume the case where delegateeEligibilityThresholdScore = 300 eth; a. Number of stakers with staked amount 70 eth = 500; Total amount 35000 eth b. Number of stakers with staked amount 150 eth = 250; Total amount 37500 eth c. Number of stakers with staked amount 225 eth = 100; Total amount 22500 eth d. Number of stakers with staked amount 350 eth = 600; Total amount 210000 eth e. Number of stakers with staked amount 500 eth = 300; Total amount 150000 eth
Under this condition(not using the scalefactors), eventhough totalStaked = 455000 eth, totalEarningPower = 360000 as only the stakes at ‘d’ and ‘e’ qualify for the earning power. For the stakes at a, b and c, earning power = 0.
If the delegateeEligibilityThresholdScore, which is presently 300 eth is altered to 200 eth or 400 eth, it will affect the earning power of some of these stakes, and consequently the rewards due.
In the cases where the delegateeEligibilityThresholdScore is reduced, Bumper(s) may call function bumpEarningPower in the GovernanceStaker contract and collect fees for it. Alternatively, the stakers themselves have to call the function updateDelegateeScore in the BinaryEligibilityOracleEarningPowerCalculator.sol to update their earning power. The stakers who become ineligible when the delegateeEligibilityThresholdScore is increased, will not take any action as it impacts them negatively.
The process can be easily automated in the contract with fair deal to every staker in the event of either increase or decrease of the delegateeEligibilityThresholdScore.
https://github.com/sherlock-audit/2024-11-tally/blob/b125d1f2b52170a3789b1060a52fc6609e6e2262/staker/src/BinaryEligibilityOracleEarningPowerCalculator.sol#L325-L333 https://github.com/sherlock-audit/2024-11-tally/blob/b125d1f2b52170a3789b1060a52fc6609e6e2262/staker/src/BinaryEligibilityOracleEarningPowerCalculator.sol#L130-L137 https://github.com/sherlock-audit/2024-11-tally/blob/b125d1f2b52170a3789b1060a52fc6609e6e2262/staker/src/BinaryEligibilityOracleEarningPowerCalculator.sol#L282-L284 https://github.com/sherlock-audit/2024-11-tally/blob/b125d1f2b52170a3789b1060a52fc6609e6e2262/staker/src/GovernanceStaker.sol#L303-L308 https://github.com/sherlock-audit/2024-11-tally/blob/b125d1f2b52170a3789b1060a52fc6609e6e2262/staker/src/GovernanceStaker.sol#L522-L525
Manual Review
The call to function setDelegateeScoreEligibilityThreshold should be accompanied by updating each and every Delegatee’s Score, eligibility and the earning power as all these are linked to the threshold.