# AirdropBroker: Airdrops in epoch 4 can participate and exercise options in subsequent epochs

AirdropBroker never clears phase4Users between phase-4 sub-epochs, letting users claim and exercise epoch-4 options again in later epochs, inflating airdrop supply.

- Payout: Contest reward
- Severity: medium
- Program: Tapioca DAO
- Credited: cccz
- Disclosed: 2024-05-23
- Write-up quality: 6/10
- Original: https://github.com/code-423n4/2024-02-tapioca-findings/issues/21
- Library page: https://bugbountylibrary.com/r/c4%3A2024-02-tapioca%3AM-29
- Tags: domain:web3 impact:funds-loss impact:integrity kind:contest platform:code4rena sev:medium tech:evm tech:solidity vuln:business-logic vuln:reward-distribution

**Target:** AirdropBroker.sol option-airdrop contract (Solidity/EVM)
**Root cause:** phase4Users mapping is populated once for phase 4 but never cleared in newEpoch(), so eligibility persists across the five weekly sub-epochs instead of being scoped per epoch.
**Steps:**
1. Owner calls `registerUsersForPhase(4, users, amounts)` populating `phase4Users`.
2. Alice is registered with 1000 aoTAP but does not call `participate` in epoch 4.
3. Epoch advances via `newEpoch()`; `phase4Users` still holds Alice's 1000.
4. In epoch 5 Alice calls `participate`; `_participatePhase4` reads `phase4Users[msg.sender]` = 1000, passes `NotEligible` check, then zeroes it.
5. Alice exercises epoch-4 options in epoch 5, adding 1000 to the epoch-5 distribution (3500 -> 4500).
**How found:** Code reading of `_participatePhase4` and `newEpoch()`: noticed phase4Users is only zeroed per-user on participation, and newEpoch() resets epoch state but not the mapping, contradicting documented per-epoch rollover semantics.
**Exploitation:** Eligible user simply delays participation past the epoch boundary; since newEpoch() is permissionless and does not reset phase4Users, the stale allocation is claimable in any later epoch, diluting/duplicating airdrop supply.
**Insight:** When a mapping gates per-epoch eligibility, check whether the epoch-advance function resets it; per-user zeroing on claim is not equivalent to per-epoch scoping and lets claims slip into later epochs.
