# Unsafe casting from uint256 to uint128 in RewardsManager

RewardsManager downcasts uint256 LP and bucket exchange rate to uint128 without checks, silently truncating stake snapshots and corrupting or zeroing reward accrual.

- Payout: Contest reward
- Severity: medium
- Program: Ajna Protocol
- Credited: Haipls
- Disclosed: 2023-06-29
- Write-up quality: 6/10
- Original: https://github.com/code-423n4/2023-05-ajna-findings/issues/227
- Library page: https://bugbountylibrary.com/r/c4%3A2023-05-ajna%3AM-10
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:medium tech:solidity technique:source-review vuln:accounting-error vuln:precision-loss

**Target:** RewardsManager.sol stake()/moveStakedLiquidity() reward accounting
**Root cause:** stake() and moveStakedLiquidity() cast positionManager.getLP() and bucketExchangeRate() from uint256 to uint128 without bounds checks, so values above type(uint128).max wrap and are stored truncated in BucketState.
**Steps:**
1. Mint a position NFT with mintAmount = type(uint128).max + 1 into bucket index 9 via _mintAndMemorializePositionNFT
2. Stake the token via _stakeToken so stake() executes `bucketState.lpsAtStakeTime = uint128(positionManager.getLP(tokenId_, bucketId))`
3. Read getBucketStateStakeInfo(tokenId, 9) and observe lpsAtStakeTime truncated (wrapped) rather than the true LP
4. Reward calc at line 504 `interestEarned_ = Maths.wmul(nextExchangeRate - exchangeRate_, bucketLP_)` then yields 0 or wrong value
**How found:** Code review of RewardsManager casting sites flagged unchecked uint256→uint128 downcasts; Foundry test with mintAmount = type(uint128).max + 1 confirmed the stored lpsAtStakeTime wraps.
**Exploitation:** No attacker profit path demonstrated; a staker with LP or exchange rate exceeding uint128 max gets truncated snapshots, causing lost rewards for an epoch or, if rateAtStakeTime wraps below the next epoch rate, inflated rewards.
**Insight:** When auditing reward/staking accounting, grep every uint128(...) cast of balances or exchange rates and test with values just above type(uint128).max; truncation of a snapshot silently skews later delta-based reward math rather than reverting.
