# Redemption weight of tiered NFTs miscalculates, making users redeem incorrect amounts - Bug #1

Juicebox DelegateStore passes tier index i instead of i+1 to _numberOfReservedTokensOutstandingFor, skewing total redemption weight so NFT redeemers get wrong payout amounts.

- Payout: Contest reward
- Severity: high
- Program: Juicebox
- Credited: Trust
- Disclosed: 2023-01-09
- Write-up quality: 7/10
- Original: https://github.com/code-423n4/2022-10-juicebox-findings/issues/193
- Library page: https://bugbountylibrary.com/r/c4%3A2022-10-juicebox%3AH-05
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:high tech:solidity technique:source-review vuln:accounting-error vuln:business-logic

**Target:** JB721Delegate / DelegateStore redemption weight calculation
**Root cause:** In _totalRedemptionWeight the loop reads _storedTierOf[_i+1] but calls _numberOfReservedTokensOutstandingFor with tierId _i, so reserved-token counts come from the previous tier while quantities come from the correct tier.
**Steps:**
1. Read _totalRedemptionWeight loop in DelegateStore: `_storedTier = _storedTierOf[_nft][_i + 1]` then `_numberOfReservedTokensOutstandingFor(_nft, _i, _storedTier)`.
2. Compare with recordMintReservesFor which passes the matching _tierId and _storedTier.
3. Construct project with 2 tiers, reserve ratio 50%, redemption ratio 100%: Tier1 contribution 50, initial 10, remaining 3, reserves minted 1; Tier2 contribution 100, initial 30, remaining 2, reserves minted 8.
4. Compute correct weight 50*(10-3)+2+100*(30-2)+2=3154 vs buggy 50*(10-3)+4+100*(30-2)+13=3167.
5. Call redeemParams and observe payout computed from skewed _base via PRBMath.mulDiv(_data.overflow, _redemptionWeight, _total).
**How found:** Code review of the redemption weight loop, noticing the tierId argument (_i) mismatched the stored tier index (_i+1), then cross-checking against recordMintReservesFor which uses them consistently.
**Exploitation:** No direct theft: attacker mint/burn loop is unprofitable since reserve tokens are minted from nothing and share never exceeds deposit; impact is mispriced redemptions (users receive less or more than eligible).
**Insight:** When a loop indexes storage with i+1 but passes i to a helper, cross-check every sibling function that uses the same helper to confirm the intended index pairing.
