# Comptroller rewards can be artificially inflated and drained by manipulating [totalStaked - totalFrozen] (or: wrong rewards calculation)

Union Comptroller computes rewards from totalStaked minus totalFrozen; staking a tiny unborrowed amount shrinks the divisor and inflates inflation index, draining rewards.

- Payout: Audit contest
- Severity: medium
- Program: Union Finance
- Found by: kenzo
- Disclosed: 2021-12-08
- Write-up quality: 7/10
- Original: https://github.com/code-423n4/2021-10-union-findings/issues/78
- Library page: https://bugbountylibrary.com/r/c4%3A2021-10-union%3AM-05
- Tags: area:web3 domain:web3 impact:funds-loss kind:contest platform:code4rena sev:medium tech:evm tech:solidity technique:source-review vuln:accounting-error vuln:reward-distribution

**Target:** Comptroller rewards calculation (Solidity smart contract)
**Root cause:** calculateRewardsByBlocks sets effectiveAmount = totalStaked() - totalFrozen; a staker can keep a tiny unborrowed stake so this value is small, and getInflationIndex divides inflationPerBlock(effectiveAmount) by effectiveAmount, exploding the inflation index.
**Steps:**
1. Stakers A,B,C stake 100, 65, 20 DAI and approve for borrower Z.
2. Staker B stakes an additional 0.07 DAI not approved for any loan, so totalStaked - totalFrozen stays tiny.
3. Borrower Z borrows 185; wait 11 blocks for overdue, call `updateOverdueInfo` to update totalFrozen.
4. Staker B calls `withdrawRewards`; curInflationIndex computes to ~215 WAD vs startInflationIndex 1 WAD.
5. B receives 873 of 1000 seeded unionTokens; tune the small stake to enlarge the drain.
**How found:** Code reading of calculateRewardsByBlocks noticed totalStaked is reduced by totalFrozen, making the divisor user-controllable; traced the value through calculateRewards, getInflationIndexNew, getInflationIndex to the division at L315, then built a hardhat PoC based on testUserManager.
**Exploitation:** Attacker stakes a small unborrowed amount alongside normal staking, lets a loan go overdue so totalFrozen updates, then calls withdrawRewards; the inflated (curInflationIndex - startInflationIndex) multiplied by effectiveStakeAmount yields disproportionate unionTokens, draining the Comptroller's seeded rewards.
**Escalation:** Advanced attacker could initiate fake self-loans and default to neutralize existing protocol totals and amplify the effect.
**Insight:** When a reward formula divides by a user-influenced aggregate (totalStaked - totalFrozen), check whether a user can keep that aggregate near the lookup-table floor (10**18) to max out inflationPerBlock while the divisor stays tiny; test by adding a small unborrowed stake before a borrow.
