# timeToEpochEnd calculates backwards, breaking protocol math

Astaria's timeToEpochEnd() inverted its comparison, returning 0 for active epochs, so every liquidation was misrouted into future-epoch accounting, corrupting lien counts and payout ratios.

- Payout: Contest reward
- Severity: medium
- Program: Astaria
- Credited: hansfriese
- Disclosed: 2022-11-07
- Write-up quality: 6/10
- Original: https://github.com/sherlock-audit/2022-10-astaria-judging/issues/50
- Library page: https://bugbountylibrary.com/r/sherlock%3A2022-10-astaria%3AM-20
- Tags: domain:web3 impact:integrity kind:contest platform:sherlock sev:medium tech:evm tech:solidity technique:source-review vuln:accounting-error

**Target:** PublicVault.sol timeToEpochEnd() / AstariaRouter liquidate()
**Root cause:** timeToEpochEnd() returns 0 when epochEnd >= block.timestamp (epoch still active) and returns block.timestamp - epochEnd when the epoch is over, inverting the intended remaining-time semantics.
**Steps:**
1. Read PublicVault.timeToEpochEnd(): `if (epochEnd >= block.timestamp) return 0; return block.timestamp - epochEnd;`
2. Note liquidate() checks `if (PublicVault(owner).timeToEpochEnd() <= COLLATERAL_TOKEN.auctionWindow())` to decide future-epoch accounting.
3. Observe timeToEpochEnd() returns 0 for any active epoch, so 0 <= auctionWindow() is always true.
4. Trigger a liquidation in the current epoch; it incorrectly deploys a liquidation accountant and routes payout to a future epoch.
5. Result: decreaseEpochLienCount() lowers lien count, payee set to liquidation accountant paying by withdrawal ratio, increaseLiquidationsExpectedAtBoundary() skews epoch math.
**How found:** Manual code review of the timeToEpochEnd() implementation against its call site in liquidate(), spotting the inverted comparison and return-value semantics.
**Exploitation:** Any liquidator calling liquidate() during an active epoch triggers the misrouted future-epoch path; no special capital needed, but impact is accounting corruption rather than direct theft.
**Insight:** When a helper returns a duration, verify the comparison direction and subtraction order against every call site; an inverted `>=` returning 0 silently makes threshold checks always pass.
