Rough Brick Meerkat
High
When the staker changes the governance delegate using function alterDelegatee, it affects the rewards of all stakers.
The function alterDelegatee in GovernanceStaker contract is called to change the address to which governance voting power is assigned. In this function, call is made to function getEarningPower in the file BinaryEligibilityOracleEarningPowerCalculator.sol to get the earning power of _newDelegatee. As the delegateeScores[_newDelegatee] has not been updated, this value will be ‘0’. This value will make the _newDelegatee ineligible (because delegateeScores[_newDelegatee] < delegateeEligibilityThresholdScore).
This affects the deposit.earningPower and the totalEarningPower thereby not only affecting the reward for the concerned deposit but also for all the other stakes.
function alterDelegatee(DepositIdentifier _depositId, address _newDelegatee) external virtual {
Deposit storage deposit = deposits[_depositId];
_revertIfNotDepositOwner(deposit, msg.sender);
_alterDelegatee(deposit, _depositId, _newDelegatee);
}
function _alterDelegatee(
Deposit storage deposit,
DepositIdentifier _depositId,
address _newDelegatee
) internal virtual {
_revertIfAddressZero(_newDelegatee);
_checkpointGlobalReward();
_checkpointReward(deposit);
DelegationSurrogate _oldSurrogate = surrogates(deposit.delegatee);
uint256 _newEarningPower =
earningPowerCalculator.getEarningPower(deposit.balance, deposit.owner, _newDelegatee);
totalEarningPower =
_calculateTotalEarningPower(deposit.earningPower, _newEarningPower, totalEarningPower);
depositorTotalEarningPower[deposit.owner] = _calculateTotalEarningPower(
deposit.earningPower, _newEarningPower, depositorTotalEarningPower[deposit.owner]
);
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;
}
Affects negatively for the staker who is changing the governance delegate and positively for all other stakers.
Manual Review
In the GovernanceStaker contract, a) The function alterDelegatee should be called with one additional parameter _oldDelegatee. b) The internal function _alterDelegatee should be called with one additional parameter _oldDelegatee. c) _newScore = delegateeScores[_oldDelegatee]; d) call the internal function _updateDelegateeScore(address _newDelegatee, uint256 _newScore) prior to _newEarningPower calculation at #L634 in GovernanceStaker contract.
The modified code may look like
function alterDelegatee(DepositIdentifier _depositId, address _oldDelegatee, address _newDelegatee) external virtual { //modified
Deposit storage deposit = deposits[_depositId];
_revertIfNotDepositOwner(deposit, msg.sender);
_alterDelegatee(deposit, _depositId, _newDelegatee);
}
function _alterDelegatee(
Deposit storage deposit,
DepositIdentifier _depositId,
address _oldDelegatee, //added
address _newDelegatee
) internal virtual {
_revertIfAddressZero(_newDelegatee);
_checkpointGlobalReward();
_checkpointReward(deposit);
DelegationSurrogate _oldSurrogate = surrogates(deposit.delegatee);
_newScore = delegateeScores[_oldDelegatee]; //added
_updateDelegateeScore(address _newDelegatee, uint256 _newScore); //added
uint256 _newEarningPower =
earningPowerCalculator.getEarningPower(deposit.balance, deposit.owner, _newDelegatee);
totalEarningPower =
_calculateTotalEarningPower(deposit.earningPower, _newEarningPower, totalEarningPower);
depositorTotalEarningPower[deposit.owner] = _calculateTotalEarningPower(
deposit.earningPower, _newEarningPower, depositorTotalEarningPower[deposit.owner]
);