Livepeer Onchain Treasury Upgrade - ether_sky's results

Decentralized video infrastructure protocol powering video in web3's leading social and media applications.

General Information

Platform: Code4rena

Start Date: 31/08/2023

Pot Size: $55,000 USDC

Total HM: 5

Participants: 30

Period: 6 days

Judge: hickuphh3

Total Solo HM: 2

Id: 282

League: ETH

Livepeer

Findings Distribution

Researcher Performance

Rank: 6/30

Findings: 1

Award: $2,318.70

🌟 Selected for report: 0

🚀 Solo Findings: 0

Findings Information

🌟 Selected for report: bronze_pickaxe

Also found by: Krace, VAD37, ether_sky

Labels

bug
3 (High Risk)
satisfactory
upgraded by judge
duplicate-165

Awards

2318.7003 USDC - $2,318.70

External Links

Lines of code

https://github.com/code-423n4/2023-08-livepeer/blob/a3d801fa4690119b6f96aeb5508e58d752bda5bc/contracts/bonding/BondingManager.sol#L348-L352

Vulnerability details

Impact

When we updated a transcoder with rewards and then try to update a transcoder with fees, it incorrectly calculates the reward generated in the current round for that transcoder, which also incorrectly calculates the previous pool's cumulativeRewardFactor, then the current pool's incorrect cumulativeRewardFactor.

Proof of Concept

If the transcoder has already been updated with a reward, lastRewardRound is equivalent to currentRound. And let's assume the previous pool's cumulativeRewardFactor is zero. In this case, we calculate the previous pool's cumulativeRewardFactor value from the current pool's ``cumulativeRewardFactor```. That is, it falls to the below part.

if (prevEarningsPool.cumulativeRewardFactor == 0 && lastRewardRound == currentRound) { IMinter mtr = minter(); uint256 rewards = PreciseMathUtils.percOf( mtr.currentMintableTokens().add(mtr.currentMintedTokens()), totalStake, currentRoundTotalActiveStake ); ... }

The logic behind this is below.

rewards = mtr.currentMintableTokens() * totalStake / currentRoundTotalActiveStake; delegatorsRewards = rewards - treasuryRewards - transcoderCommissionRewards; prevEarningsPool.cumulativeRewardFactor * (1 + delegatorsRewards / totalStake) = earningsPool.cumulativeRewardFactor;

But in Minter, we calculate the rewards for the transcoder like below.

uint256 mintAmount = MathUtils.percOf(currentMintableTokens, _fracNum, _fracDenom);

As you can see, it doesn't involve currentMintedTokens because currentMintableTokens is constant during the round.

And also in Minter, we used MathUtils but PreciseMathUtils in BondingManager. This is also bug I think.

Tools Used

Manual audit

Update like below.

uint256 rewards = MathUtils.percOf( mtr.currentMintableTokens(), totalStake, currentRoundTotalActiveStake );

Assessed type

Error

#0 - c4-pre-sort

2023-09-08T15:25:50Z

141345 marked the issue as duplicate of #165

#1 - c4-judge

2023-09-18T02:45:06Z

HickupHH3 changed the severity to 3 (High Risk)

#2 - c4-judge

2023-09-18T02:45:11Z

HickupHH3 marked the issue as satisfactory

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter