# Incorrect Unwinding Timestamp Calculation in Farm Bucket Validation

AllocationVoting computes a voter's unwinding timestamp from current time plus lock epochs instead of actual lock start, letting users vote for farms maturing after their real unlock.

- Payout: Contest reward
- Severity: medium
- Program: infiniFi
- Credited: 0xBeastBoy
- Disclosed: 2025-05-23
- Write-up quality: 6/10
- Original: https://cdn.cantina.xyz/reports/cantina_competition_infinifi_april2025.pdf#3.2.8
- Library page: https://bugbountylibrary.com/r/cantina%3Ainfinifi_april2025%3A3.2.8
- Tags: domain:web3 impact:integrity kind:contest platform:cantina sev:medium tech:solidity technique:source-review vuln:business-logic vuln:governance

**Target:** AllocationVoting._validateFarmBucket (Solidity smart contract)
**Root cause:** userUnwindingTimestamp is computed as block.timestamp.epoch() + _unwindingEpochs rather than the position's actual creation epoch plus lock duration, so the value drifts forward as time passes.
**Steps:**
1. Create a lock position with 10 epochs unwinding at epoch 100; true unlock is epoch 110.
2. Set an illiquid farm maturity to unlock + 5 days; initial vote reverts with InvalidTargetBucket.
3. Advance 5 epochs via advanceEpoch(5).
4. Recompute: current epoch 105 + 10 = epoch 115, now past farm maturity.
5. Call `gateway.vote(usdc, lockEpochs, liquidVotes, illiquidVotes)` again; it succeeds.
**How found:** Code review of the maturity validation in _validateFarmBucket; noticed the timestamp was derived from block.timestamp instead of the position's creation epoch, then wrote a Foundry test advancing epochs to confirm the check flips.
**Exploitation:** A user with an active lock waits until enough epochs elapse that currentEpoch + lockEpochs exceeds a target farm's maturity, then votes weight into that long-dated illiquid farm despite unlocking before it matures.
**Insight:** When a validation compares a maturity against a user's lock end, verify the lock end is derived from stored position creation time, not recomputed from block.timestamp — any 'current time + duration' formula drifts and can be gamed by waiting.
