Skip to content

Latest commit

 

History

History
72 lines (48 loc) · 2.34 KB

File metadata and controls

72 lines (48 loc) · 2.34 KB

Radiant Tangerine Nightingale

High

User loses earned rewards after original allocation is removed in UsualSP

Summary

When the original allocation of a user is removed, they lose rewards they have already earned.

Root Cause

In UsualSP.sol:367 The user's rewards aren't updated before their original allocation is removed. Causing already earned, but not realized, rewards to be lost.

The next time, the user's rewards are updated, it will calculate the rewards from the last update timestamp to the current timestamp with the new reduced balance causing the earned rewards to be lower than they should be.

Internal pre-conditions

  1. Admin needs to assign user an original allocation
  2. Admin needs to remove the original allocation at some point

External pre-conditions

none

Attack Path

none

Impact

User loses rewards they've earned but which weren't realized yet.

PoC

// UsualSP.t.sol
    function testRemoveAllocationBug() public {
        // Both Alice and Bob have an original allocation
        setupVestingWithOneYearCliff(1e18);

        deal(address(usualToken), address(distributionModule), 1e18);
        vm.startPrank(address(distributionModule));
        usualToken.approve(address(usualSP), 1e18);
        usualSP.startRewardDistribution(1e18, block.timestamp, block.timestamp + 1 days);
        vm.stopPrank();

        // 33 days because of startDate + ONE_MONTH check in `claimReward()`
        vm.warp(block.timestamp + 33 days);

        address[] memory recipientToRemove = new address[](1);
        recipientToRemove[0] = alice;
        vm.prank(usualSPOperator);
        usualSP.removeOriginalAllocation(recipientToRemove);

        // Alice's original allocation was removed but she still farmed the reward distibution
        // with her full balance. So she should earn the same amount of rewards as Bob
        // But, that's not the case

        vm.prank(alice);
        usualSP.claimReward();
        vm.prank(bob);
        usualSP.claimReward();
        // this will fail
        assertGt(usualToken.balanceOf(bob), 0);
        assertEq(usualToken.balanceOf(alice), usualToken.balanceOf(bob));
    }

Mitigation

Call _updateReward() before removing the user's orignial allocation