Lucene search

K
code423n4Code4renaCODE423N4:2023-08-LIVEPEER-FINDINGS-ISSUES-168
HistorySep 06, 2023 - 12:00 a.m.

When user unbonds before transcoder calls reward, then cumulativeRewardFactor for the round is less than it should be

2023-09-0600:00:00
Code4rena
github.com
3
vulnerability
impact
proof of concept
earningspool
total stake
unbonds
mitigation steps
error

Lines of code
<https://github.com/code-423n4/2023-08-livepeer/blob/main/contracts/bonding/libraries/EarningsPoolLIP36.sol#L47-L59&gt;

Vulnerability details

Impact

When user unbonds before transcoder calls reward, then cumulativeRewardFactor for the round is less than it should be. As result other delegators loose rewards.

Proof of Concept

Each round transcoder can call reward function in order to claim reward tokens for himself and his delegators. This function can be called at any time in the round. In order to pay correct amount of rewards, protocol stores EarningsPool.Data for each round. One of it’s variables is cumulativeRewardFactor, which is used to calculate rewards for user. When reward function is called, then cumulativeRewardFactor for current round is updated.

<https://github.com/code-423n4/2023-08-livepeer/blob/main/contracts/bonding/libraries/EarningsPoolLIP36.sol#L47-L59&gt;

        function updateCumulativeRewardFactor(
        EarningsPool.Data storage earningsPool,
        EarningsPool.Data memory _prevEarningsPool,
        uint256 _rewards
    ) internal {
        uint256 prevCumulativeRewardFactor = _prevEarningsPool.cumulativeRewardFactor != 0
            ? _prevEarningsPool.cumulativeRewardFactor
            : PreciseMathUtils.percPoints(1, 1);


        earningsPool.cumulativeRewardFactor = prevCumulativeRewardFactor.add(
            PreciseMathUtils.percOf(prevCumulativeRewardFactor, _rewards, earningsPool.totalStake)
        );
    }

As you can see, this function uses earningsPool.totalStake as total amount of staked funds in the epoch.

Now, let’s see what happens if user unbonds. In this case decreaseTotalStake function is called, which should decrease staked amount. It will do so only for next round, which means that earningsPoolPerRound for current round will be same.
That means that if user unbonds before reward is called, then this unbonded amount is still in the earningsPoolPerRound[currentPeriod].totalStaked, which means that it dilutes cumulativeRewardFactor for all other users.

Example.
1.There is transcoder with delegatedAmount 100 tokens.
2.In round 10 delegator that has 50 tokens unbonds them all.
3.In same round 10 transcoder calls reward and receives 10 more tokens that should be distributed to delegators.
4.cumulativeRewardFactor calculation will use 100 tokens as earningsPool.totalStake.
5.Rest of delegators received and claimed only 5 reward tokens.
6.Unbonded delegator also never received rest 5 tokens. They just stuck in the contract.

Tools Used

VsCode

Recommended Mitigation Steps

In case if user unbonds some amount, before reward call, then update earningsPoolPerRound[currentPeriod].totalStaked for the current round.

Assessed type

Error


The text was updated successfully, but these errors were encountered:

All reactions