# Logic error in AVM original owner resolution

claim_many() in Blackhole RewardsDistributor omits the AVM original-owner check present in claim(), sending expired-lock rewards to the AVM contract instead of the user.

- Payout: Contest reward
- Severity: medium
- Program: Blackhole
- Credited: maze
- Disclosed: 2025-07-03
- Write-up quality: 6/10
- Original: https://code4rena.com/reports/2025-05-blackhole#m-04
- Library page: https://bugbountylibrary.com/r/c4%3A2025-05-blackhole%3AM-04
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:medium tech:solidity vuln:accounting-error vuln:business-logic

**Target:** RewardsDistributor.sol claim/claim_many reward distribution
**Root cause:** claim_many() resolves the reward recipient via ownerOf() only, omitting the avm.tokenIdToAVMId/getOriginalOwner branch that claim() uses, so AVM-managed tokens pay the AVM contract.
**Steps:**
1. User A delegates tokenId #123 to AVM; lock expires.
2. Call `claim(123)`: AVM check triggers, rewards go to User A.
3. Call `claim_many([123])`: no AVM check, `ownerOf(123)` returns AVM contract, rewards transferred to AVM.
4. AVM has no forwarding mechanism; User A's rewards are permanently stuck.
**How found:** Code-reading comparison of the two reward-claim paths revealed claim() had an AVM original-owner branch that claim_many() lacked.
**Exploitation:** Triggered by any caller invoking the gas-efficient claim_many() on an expired AVM-managed tokenId; rewards are transferred to the AVM contract which cannot forward them, causing permanent loss to the original owner.
**Insight:** When a contract has parallel entry points (single vs batch), diff their recipient-resolution logic; batch variants frequently omit special-case owner remapping like delegation/escrow overrides.
