# twTAP.sol: Reward tokens stored in index 0 can be stolen

User-supplied rewardTokens array in twTAP crosschain claim maps unknown tokens to index 0, letting attacker drain the index-0 reward token repeatedly.

- Payout: Contest reward
- Severity: high
- Program: Tapioca DAO
- Credited: carrotsmuggler
- Disclosed: 2023-11-16
- Write-up quality: 7/10
- Original: https://github.com/code-423n4/2023-07-tapioca-findings/issues/1022
- Library page: https://bugbountylibrary.com/r/c4%3A2023-07-tapioca%3AH-36
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:high tech:evm tech:solidity technique:chained technique:source-review vuln:access-control vuln:accounting-error

**Target:** twTAP.sol claimAndSendRewards / TapOFT crosschain claim
**Root cause:** rewardTokenIndex[unknownToken] returns default 0, so claimableIndex becomes 0 and safeTransfer always pays rewardTokens[0] while amounts[i] comes from claimable() per index i.
**Steps:**
1. Attacker holds a twTAP position on chain B with pending rewards.
2. Call `claimRewards` on chain A passing `rewardTokens` as an array of garbage addresses.
3. LayerZero payload relays to chain B; TapOFT decodes user-supplied `rewardTokens` and calls `twTap.claimAndSendRewards(tokenID, rewardTokens)`.
4. In `_claimRewardsOn`, each garbage token yields `claimableIndex = rewardTokenIndex[garbage] = 0`, so `rewardTokens[0].safeTransfer(_to, amounts[i])` executes for every i.
5. TapOFT forwards collected rewards crosschain back to attacker.
**How found:** Code review of the crosschain claim path: noticed rewardTokens is fully user-controlled from the LayerZero payload, then traced the mapping lookup rewardTokenIndex[_rewardTokens[i]] and realized unknown keys default to 0, aliasing all entries to the first reward token.
**Exploitation:** Attacker with a valid position and pending rewards submits a crosschain claim with a garbage rewardTokens array; the loop transfers the index-0 reward token once per array entry using amounts from claimable(), multiplying payout of the most valuable reward token and draining it from the contract.
**Insight:** When a user-supplied array indexes into a mapping of token->index, check the default-value behavior for unknown keys; a missing existence check aliases arbitrary entries to index 0, turning a per-index payout loop into repeated transfers of one token.
