# Fee accounting mismatch

Fee formula, scale, or cap that undercharges or misapplies versus the intended rate

_Area: DeFi accounting and math bugs. Seen in 3 disclosed reports; latest 2024-09-16. Updated 2026-11-02._

## How it works
A fee is computed with the wrong base, scale, or cap, so the amount charged diverges from the intended rate. The broken assumption is that every place using the fee agrees on its units and base. When one site scales by 10000 and another by 1000, or one grosses up by `(1-fee)` while the fee is taken on the net, the numbers still look plausible but the economics are wrong. The result is undercharging (protocol loses revenue) or a vacuous check (invariant passes when it should not).

## Where it shows up
- Magic constants in fee math: a fee multiplier that differs from the constant used in a related check (e.g. `10000` vs `1000**2`) [E1](https://bugbountylibrary.com/r/blog%3Aimmunefi-com-blog-bug-fix-reviews-hack-analysis-uranium-finance-april-2021.md)
- Gross-up formulas dividing by `(1-fee)` paired with a fee computed as `out*fee` instead of `out*fee/(1-fee)` [E2](https://bugbountylibrary.com/r/c4%3A2024-06-size%3AH-01.md)
- Fee caps or reward caps that reference a global minimum rather than the value being acted on [E3](https://bugbountylibrary.com/r/c4%3A2023-07-pooltogether%3AM-09.md)
- Forked contracts where the fee scale was edited but dependent constants were not [E1](https://bugbountylibrary.com/r/blog%3Aimmunefi-com-blog-bug-fix-reviews-hack-analysis-uranium-finance-april-2021.md)
- Documented fee examples that disagree with the code's formula [E2](https://bugbountylibrary.com/r/c4%3A2024-06-size%3AH-01.md)

## How to review
Identify the invariant: every fee computation must use the same base and scale as the value it is charged against, and any constant appearing in a fee must match the constant in every dependent check. Concretely:
- For each fee formula, write the intended rate from docs/spec and recompute a worked example by hand; compare to code output [E2](https://bugbountylibrary.com/r/c4%3A2024-06-size%3AH-01.md)
- For AMM forks, diff the pair's `swap()` constants against canonical source; a changed fee multiplier must be mirrored in the K-check term [E1](https://bugbountylibrary.com/r/blog%3Aimmunefi-com-blog-bug-fix-reviews-hack-analysis-uranium-finance-april-2021.md)
- For incentive caps, confirm the cap scales with the item's value, not a fixed minimum [E3](https://bugbountylibrary.com/r/c4%3A2023-07-pooltogether%3AM-09.md)
- Check that gross-up and fee use the same denominator convention.

## How finders spotted it
- Diffed a forked pair against canonical Uniswap V2 and noticed `10000` in fee scaling but `1000**2` in the K requirement — a mismatch that makes the invariant vacuous [E1](https://bugbountylibrary.com/r/blog%3Aimmunefi-com-blog-bug-fix-reviews-hack-analysis-uranium-finance-april-2021.md)
- Read `getCreditAmountIn` against documented fee examples; the docs' Example 2 showed a fee the formula did not produce [E2](https://bugbountylibrary.com/r/c4%3A2024-06-size%3AH-01.md)
- Read `_computeMaxFee` and saw it referenced the canary tier's prize size instead of the claimed tier, then reasoned about gas-vs-minPrize economics [E3](https://bugbountylibrary.com/r/c4%3A2023-07-pooltogether%3AM-09.md)

## Variants seen
- Scale mismatch between fee multiplier and invariant constant in an AMM fork [E1](https://bugbountylibrary.com/r/blog%3Aimmunefi-com-blog-bug-fix-reviews-hack-analysis-uranium-finance-april-2021.md)
- Base mismatch in a gross-up fee formula (net vs gross) [E2](https://bugbountylibrary.com/r/c4%3A2024-06-size%3AH-01.md)
- Cap referencing the wrong tier/minimum, misaligning solver incentives [E3](https://bugbountylibrary.com/r/c4%3A2023-07-pooltogether%3AM-09.md)

## Impact and severity
E1 was critical: the vacuous K check let attackers drain 26 pools for ~$50m; impact scaled because one broken pair generalized to all pairs sharing the code [E1](https://bugbountylibrary.com/r/blog%3Aimmunefi-com-blog-bug-fix-reviews-hack-analysis-uranium-finance-april-2021.md). E2 is a revenue leak — users pay less fee than documented [E2](https://bugbountylibrary.com/r/c4%3A2024-06-size%3AH-01.md). E3 is a liveness/incentive failure: high prizes become unclaimable when gas exceeds the capped reward [E3](https://bugbountylibrary.com/r/c4%3A2023-07-pooltogether%3AM-09.md). Severity rises with how far the wrong number propagates (all pools, all claims) and whether it enables value extraction versus mere undercharging.

## Seen in real software
Open-source packages and projects, and products, where this pattern was found. If the target runs one, compare the version it runs with the fix.
- **Uranium Finance** (pair contract swap() K invariant check) (version not stated): Hack Analysis: Uranium Finance, April 2021. Report: https://bugbountylibrary.com/r/blog%3Aimmunefi-com-blog-bug-fix-reviews-hack-analysis-uranium-finance-april-2021.md

## Evidence
- E1: https://bugbountylibrary.com/r/blog%3Aimmunefi-com-blog-bug-fix-reviews-hack-analysis-uranium-finance-april-2021.md
- E2: https://bugbountylibrary.com/r/c4%3A2024-06-size%3AH-01.md
- E3: https://bugbountylibrary.com/r/c4%3A2023-07-pooltogether%3AM-09.md
