# createPromotion() Lack of input validation for _epochDuration can potentially freeze promotion creator's funds

TwabRewards.createPromotion allowed _epochDuration=0, causing claimRewards to divide by zero and cancelPromotion to revert, permanently freezing the creator's deposited tokens.

- Payout: Audit contest
- Severity: high
- Program: PoolTogether
- Found by: WatchPug
- Disclosed: 2022-02-03
- Write-up quality: 6/10
- Original: https://github.com/code-423n4/2021-12-pooltogether-findings/issues/106
- Library page: https://bugbountylibrary.com/r/c4%3A2021-12-pooltogether%3AH-01
- Tags: area:web3 domain:web3 impact:funds-loss kind:contest platform:code4rena sev:high tech:evm tech:solidity technique:source-review vuln:accounting-error vuln:business-logic

**Target:** TwabRewards.createPromotion (Solidity smart contract)
**Root cause:** createPromotion lacks a require(_epochDuration > 0) check, so a zero duration is stored; later getAverageBalanceBetween divides by the epoch duration and reverts, and the promotion is immediately considered ended so cancelPromotion's _requirePromotionActive fails.
**Steps:**
1. Call `createPromotion(_ticket, USDC, 10000e6, startTimestamp, 0, 10)` with `_epochDuration = 0`.
2. Contract pulls `_tokensPerEpoch * _numberOfEpochs = 100,000 USDC` via `safeTransferFrom`.
3. Call `claimRewards()`: reverts in `_ticket.getAverageTotalSuppliesBetween()` -> `TwabLib.getAverageBalanceBetween()` due to division by zero.
4. Call `cancelPromotion()`: reverts at `_requirePromotionActive()` because the promotion already ended.
5. Funds remain stuck in TwabRewards with no recovery path.
**How found:** Code review of createPromotion input validation; noticed _epochDuration accepted 0 and traced downstream division in TwabLib.getAverageBalanceBetween and the ended-promotion check in cancelPromotion.
**Exploitation:** Not an attacker-driven exploit; a user error or frontend bug setting _epochDuration=0 permanently locks the creator's deposited reward tokens since neither claim nor cancel can succeed.
**Insight:** When auditing deposit-then-distribute contracts, check every stored time/interval parameter for zero and trace it into division and 'active/ended' predicates: a zero interval can both break math and flip state to 'ended', removing the refund path.
