# Underflow in updateTranscoderWithFees can cause corrupted data and loss of winning tickets

BondingManager.updateTranscoderWithFees used MathUtils (1e6 divisor) instead of PreciseMathUtils (1e27) for treasuryRewards, causing underflow and loss of redeemable winning tickets.

- Payout: Contest reward
- Severity: high
- Program: Livepeer
- Credited: bronze\_pickaxe
- Disclosed: 2023-10-17
- Write-up quality: 7/10
- Original: https://github.com/code-423n4/2023-08-livepeer-findings/issues/165
- Library page: https://bugbountylibrary.com/r/c4%3A2023-08-livepeer%3AH-01
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:high tech:evm tech:solidity technique:source-review vuln:accounting-error vuln:precision-loss

**Target:** BondingManager.updateTranscoderWithFees (Solidity smart contract)
**Root cause:** treasuryRewardCutRate is stored with 27-digit precision (PreciseMathUtils, PERC_DIVISOR 1e27) but treasuryRewards is computed with MathUtils.percOf using PERC_DIVISOR 1e6, inflating treasuryRewards beyond rewards and underflowing the subsequent subtraction.
**Steps:**
1. Note treasuryRewardCutRate set via _setTreasuryRewardCutRate validated against PreciseMathUtils.validPerc (<=1e27), initial 10% = 1e26.
2. Observe updateTranscoderWithFees computes `treasuryRewards = MathUtils.percOf(rewards, treasuryRewardCutRate)` with PERC_DIVISOR 1e6.
3. With cutRate 1e26, percOf yields rewards*1e26/1e6 >> rewards, so `rewards.sub(treasuryRewards)` underflows and reverts.
4. Trigger by redeeming a winning ticket via TicketBroker when transcoder skipped previous round reward call, forcing recalculation path.
5. Run provided gist PoC to confirm revert on every such redemption.
**How found:** Code-reading: noticed treasuryRewardCutRate validated with PreciseMathUtils (1e27) but consumed by MathUtils (1e6) in updateTranscoderWithFees, a precision mismatch hypothesis confirmed by PoC.
**Escalation:** Ticket valid only two rounds; failed redemption in currentRound plus expiry after currentRound+1 means winning tickets become unredeemable, and cumulativeRewardFactor for the skipped round is never updated.
**Insight:** When a value is validated/stored with one fixed-point precision library, grep every consumer of that value for use of a different PERC_DIVISOR; mismatched precision between setter and getter is a reliable underflow/overflow source.
