# Possible underflow of uint128

Zerolend's FeeDistributor casts signed bias-slope*dt to uint128 before Math.max, so negative values underflow to huge numbers, corrupting veSupply and claim balances.

- Payout: Contest reward
- Severity: medium
- Program: Zerolend
- Credited: giraffe0x
- Disclosed: 2024-09-16
- Write-up quality: 6/10
- Original: https://cdn.cantina.xyz/reports/cantina_competition_zerolend_jan2024.pdf#3.2.2
- Library page: https://bugbountylibrary.com/r/cantina%3Azerolend_jan2024%3A3.2.2
- Tags: domain:web3 impact:integrity kind:contest platform:cantina sev:medium tech:solidity technique:regression technique:source-review vuln:precision-loss

**Target:** FeeDistributor.sol (fork of Curve FeeDistributor.vy)
**Root cause:** Conversion of signed (bias - slope*dt) to uint128 happens before Math.max clamping to 0, so negative results wrap to huge uint128 values instead of being floored at zero.
**Steps:**
1. Compare Zerolend's `Math.max(uint128(pt.bias - pt.slope * dt), 0)` against Curve's `convert(max(pt.bias - pt.slope * dt, 0), uint256)`.
2. Note bias, slope, dt are signed ints; when `bias - slope*dt < 0`, uint128 cast wraps to ~3.4e38.
3. Math.max then returns the huge value, writing it into `veSupply[t]` and `balanceOf`.
4. Fix: apply Math.max on signed ints first, then cast: `uint128(int128(Math.maxInt(pt.bias - pt.slope*dt, 0)))`.
**How found:** Code review comparing the Zerolend fork against upstream Curve FeeDistributor.vy; spotted the reordered cast vs max operation and reasoned about signed-to-unsigned underflow.
**Insight:** When auditing forks of well-known contracts, diff the exact ordering of casts and clamps against upstream; signed-to-unsigned conversion before a max/floor is a classic underflow that silently inflates accounting values.
