# withdraw timelock can be circumvented

Calling withdrawWithTimelockFrom twice, second with amount=0, resets the unlock timestamp to now, letting users bypass the withdrawal timelock entirely.

- Payout: Audit contest
- Severity: high
- Program: PoolTogether
- Found by: cmichel
- Disclosed: 2021-09-16
- Write-up quality: 7/10
- Original: https://github.com/code-423n4/2021-06-pooltogether-findings/issues/91
- Library page: https://bugbountylibrary.com/r/c4%3A2021-06-pooltogether%3AH-04
- Tags: area:web3 domain:web3 impact:funds-loss impact:integrity kind:contest platform:code4rena sev:high technique:source-review vuln:accounting-error vuln:business-logic

**Target:** PrizePool withdrawWithTimelockFrom / timelock accounting
**Root cause:** _mintTimelock resets _unlockTimestamps[user] to blockTime + lockDuration on each call; a second call with amount=0 sets unlockTimestamp to blockTime, overwriting the prior lock.
**Steps:**
1. User calls `withdrawWithTimelockFrom(user, amount=userBalance)`; mints timelock and sets `_unlockTimestamps[user] = blockTime + lockDuration`.
2. User calls `withdrawWithTimelockFrom(user, amount=0)`; lockDuration=0 so unlockTimestamp=blockTime, and `_mintTimelock` resets `_unlockTimestamps[user]` to that.
3. Check `if (timestamp <= _currentTime())` now passes, so `_sweepTimelockBalances` transfers the full balance out immediately.
**How found:** Code review of the timelock accounting: noticed _mintTimelock resets rather than accumulates the unlock timestamp, and that a zero-amount call yields lockDuration=0.
**Exploitation:** Attacker deposits before prize award, then after award calls withdrawWithTimelockFrom with full balance followed by a zero-amount call to reset the unlock timestamp to now, sweeping the full deposit out with no lock penalty.
**Insight:** When a lock/timelock is stored as an absolute timestamp, check whether a second call with zero amount or zero duration can overwrite it; timestamps should accumulate, not reset.

