# users still forced to follow previously set cooldownDuration even when cooldown is off (set to zero) before unstaking

StakedUSDeV2.unstake ignores global cooldownDuration=0, so users with pending silo withdrawals stay locked for their original 90-day cooldown after admin disables cooldowns.

- Payout: Audit contest
- Severity: medium
- Program: Ethena Labs
- Found by: adeolu
- Disclosed: 2023-12-21
- Write-up quality: 6/10
- Original: https://code4rena.com/reports/2023-10-ethena#m-03
- Library page: https://bugbountylibrary.com/r/c4%3A2023-10-ethena%3AM-03
- Tags: area:web3 domain:web3 impact:dos kind:contest platform:code4rena sev:medium tech:evm tech:solidity technique:source-review vuln:business-logic

**Target:** StakedUSDeV2 ERC4626 staking vault (unstake/silo withdrawal)
**Root cause:** unstake() only checks block.timestamp >= userCooldown.cooldownEnd and never consults the current cooldownDuration, so a cooldownEnd set while cooldown was 90 days persists after setCooldownDuration(0).
**Steps:**
1. Deploy StakedUSDeV2 with cooldownDuration = 90 days
2. As staker: `usdeToken.approve(stakedUSDeV2, amt)` then `stakedUSDeV2.deposit(amt/2, staker)`
3. `stakedUSDeV2.cooldownAssets(maxWithdraw(staker), staker)` — shares burned, assets sent to USDeSilo, cooldowns[staker].cooldownEnd = now + 90 days
4. As owner: `stakedUSDeV2.setCooldownDuration(0)`
5. As staker: `stakedUSDeV2.unstake(staker)` reverts with `InvalidCooldown` despite cooldown being off
**How found:** Code reading of the ensureCooldownOff/ensureCooldownOn modifiers and unstake() revealed that unstake never re-checks the global cooldownDuration, only the per-user stored cooldownEnd; confirmed with a Foundry test toggling cooldown off mid-cooldown.
**Exploitation:** No attacker profit path; the affected user simply cannot retrieve silo-held assets until the original cooldownEnd elapses, exposing them to asset volatility for up to 90 days after cooldowns were globally disabled.
**Insight:** When a protocol has both a global config flag and per-user snapshotted state derived from it, check every read path for stale snapshots — here unstake() trusts cooldowns[user].cooldownEnd instead of re-deriving from cooldownDuration, so config changes don't propagate to in-flight state.

