# Time-boundary and phase-gate consistency

Inclusive or missing timestamp bounds letting phases overlap, lock funds, or skip a grace period

_Area: Smart contract access control and control flow. Seen in 3 disclosed reports; latest 2025-04-10. Updated 2026-10-23._

## How it works
Contracts gate behavior by comparing `block.timestamp` to phase boundaries (bid end, claim end, unlock end, grace period). The invariant that must hold is: each timestamp belongs to exactly one phase, and every mutation path enforces the same bound. Three ways this breaks: (1) inclusive comparisons on both sides make the closing instant of one phase also the opening instant of the next, so two phases are live at the boundary; (2) a window closes exactly when funds become fully vested, so the last withdrawal is never allowed; (3) one path (extend/renew) omits the bound its sibling (claim) enforces, so a phase that should be closed stays open. In each case the code assumes a boundary or bound that the comparison does not actually provide.

## Where it shows up
- Phase gates using `>=`/`<=` against a shared `endTime`/`startTime`.
- A time-bounded modifier whose window end coincides with full vesting/unlock.
- Renew/extend/transfer functions lacking the grace-period bound enforced on the claim path.
- Any timestamp comparison where the boundary instant is not explicitly assigned to one side.

## How to review
Enumerate every timestamp comparison, the phase it defines, and which side owns the boundary instant. Check: for any timestamp at most one phase is active (strict `>` on the closing side, or an explicit buffer). For grace periods, confirm every mutation path enforces the same bound. For vesting, confirm the withdrawal window closes strictly after full unlock. These are pure boundary checks; no cross-transaction atomicity is needed to trigger them.

## Variants seen
- Claim overlaps bidding at `auctionEndTime`; the same-block refund path also lacks the status flag its sibling sets, amplifying the drain [E1](https://bugbountylibrary.com/r/c4%3A2023-10-nextgen%3AH-02.md).
- Withdrawal modifier reverts once `claimEnd + window < now`, disabling withdrawal exactly at full unlock [E2](https://bugbountylibrary.com/r/c4%3A2024-03-taiko%3AH-03.md).
- Adjacent (missing bound, not overlap): `extend_expiration()` omits the grace-period check, letting the prior owner renew expired domains and block buyers [E3](https://bugbountylibrary.com/r/c4%3A2025-01-initia-move%3AH-04.md).

## Why fixes fail
E1's sibling `cancelBid` sets status but `claimAuction` does not; fixing the comparison alone leaves the refund path exploitable, and fixing the flag alone leaves the overlap. Both must be corrected.

## Impact and severity
E1 drained all ETH from the auction contract (critical). E2 permanently locked fully vested funds (high). E3 enabled frontrunning to block registrations (high). Severity rises when the boundary path moves funds without a paid-status guard, when no buffer exists between phases, and when a missing bound lets one actor block another's claim.

## Evidence
- E1: https://bugbountylibrary.com/r/c4%3A2023-10-nextgen%3AH-02.md
- E2: https://bugbountylibrary.com/r/c4%3A2024-03-taiko%3AH-03.md
- E3: https://bugbountylibrary.com/r/c4%3A2025-01-initia-move%3AH-04.md
