# DoS in distribute()

Weekly StakingEmissions.distribute() reverts because feeDistributor.checkpointToken() has a 1-day cooldown that anyone can trigger via front-run or normal claim.

- Payout: Contest reward
- Severity: high
- Program: Zerolend
- Credited: bin2chen
- Disclosed: 2024-09-16
- Write-up quality: 6/10
- Original: https://cdn.cantina.xyz/reports/cantina_competition_zerolend_jan2024.pdf#3.1.4
- Library page: https://bugbountylibrary.com/r/cantina%3Azerolend_jan2024%3A3.1.4
- Tags: domain:web3 impact:dos kind:contest platform:cantina sev:high tech:solidity technique:race-window technique:source-review vuln:business-logic vuln:dos vuln:reward-distribution

**Target:** StakingEmissions.distribute() / FeeDistributor.checkpointToken()
**Root cause:** distribute() calls feeDistributor.checkpointToken() which requires block.timestamp > lastTokenTime + TOKEN_CHECKPOINT_DEADLINE; any prior checkpoint within the deadline makes distribute() revert.
**Steps:**
1. Observe distribute() runs weekly and calls feeDistributor.checkpointToken()
2. Attacker front-runs the distribute() tx by calling feeDistributor.checkpointToken() directly
3. distribute()'s internal checkpointToken() reverts on the deadline require
4. Alternatively a user calling FeeDistributor.claim(id) triggers checkpointToken(), same effect
5. Reward distribution is delayed until next epoch or longer
**How found:** Code reading: noticed distribute() calls checkpointToken() which enforces a 1-day deadline, and that the same function is publicly callable and also invoked by claim(), making the weekly distribute() griefable.
**Exploitation:** Front-run the scheduled distribute() transaction with a direct checkpointToken() call (or rely on any claim tx) so the cooldown require fails inside distribute(), blocking the weekly transfer of VestedZeroLend to feeDistributor.
**Insight:** When a privileged periodic function calls a public function that enforces a cooldown/timestamp require, check whether anyone can pre-trigger that cooldown to make the periodic call revert; also check other entrypoints (like claim) that share the same guarded function.
