TimeCurve: WarBow CL8Y paths must respect sale deadline (< deadline vs post-deadline pre-endSale) (Finding 6 from #131) #136

Closed
opened 2026-05-04 08:36:24 +00:00 by PlasticDigits · 4 comments
PlasticDigits commented 2026-05-04 08:36:24 +00:00 (Migrated from gitlab.com)

Parent

Split from #131 (Finding 6).

Problem

These functions enforce !ended but not block.timestamp < deadline (and they already cap some paths with saleStart + MAX_SALE_ELAPSED_SEC separately):

  • claimWarBowFlag
  • warbowSteal
  • warbowRevenge
  • warbowActivateGuard

endSale is permissionless but may be delayed. In post-deadline, pre-endSale window ended is still false, so Battle Points and flag claims can still mutate while “timer expired” from end-user perspective — enabling last-block / delay games that shift WarBow podium snapshots until endSale runs.

Primary fix (recommended): require(block.timestamp < deadline) (or equivalently block.timestamp <= deadline - 1 per strictness preference) on all WarBow mutations above in addition to existing phase checks — align with buy / sale UX expectations (#114/#124 window semantics).

Alternative: Automatically finalize (ended = true, _finalizeLastBuyers, etc.) on first touch after deadline inside these functions — heavier and riskier interaction with buy path; prefer explicit deadline guards unless ops want auto-finalization.

Also confirm interaction with WARBOW_FLAG_SILENCE and MAX_SALE_ELAPSED so no contradictory reverts mid-extension edge cases.

Acceptance criteria

  • No WarBow BP mutation or flag claim succeeds when block.timestamp >= deadline and sale not yet endSale-finalized (unless product explicitly chooses auto-finalization).
  • Podium snapshots used for distributePrizes cannot be manipulated in the dead zone described in Finding 6.

Verification checklist

  • Forge: scenario warp to deadline, withhold endSale, assert WarBow txs revert or finalize per chosen design.
  • Regression: endSale then revenge/steal still correctly blocked by ended.
  • docs/frontend/timecurve-views.md, invariants-and-business-logic.md — document “sale timer ended ⇒ WarBow off” invariant.
  • Indexer/UI only if timestamps or phases displayed need copy updates.

Repo touch list

Area Change
Code contracts/src/TimeCurve.sol (four entrypoints + NatSpec)
Tests contracts/test/TimeCurve.t.sol WarBow timing section
Docs / invariants Frontend + testing invariant map
Indexer Typically none
Frontend Preflight/disable WarBow CTAs when deadline passed (hero timer #48 clock source)
Skills skills/play-timecurve-warbow/SKILL.md Phase expectations
## Parent Split from **#131** (Finding 6). ## Problem These functions enforce `!ended` but **not** `block.timestamp < deadline` (and they already cap some paths with **`saleStart + MAX_SALE_ELAPSED_SEC`** separately): - `claimWarBowFlag` - `warbowSteal` - `warbowRevenge` - `warbowActivateGuard` [`endSale`](contracts/src/TimeCurve.sol) is **permissionless** but may be delayed. In **post-deadline, pre-endSale** window **`ended` is still false**, so Battle Points and flag claims can still mutate while “timer expired” from end-user perspective — enabling **last-block / delay** games that shift WarBow podium snapshots until `endSale` runs. ## Recommended fix **Primary fix (recommended):** `require(block.timestamp < deadline)` (or equivalently **`block.timestamp <= deadline - 1`** per strictness preference) on all WarBow mutations above **in addition** to existing phase checks — align with **`buy`** / sale UX expectations (**#114/#124** window semantics). **Alternative:** Automatically finalize (`ended = true`, `_finalizeLastBuyers`, etc.) on first touch after deadline inside these functions — heavier and riskier interaction with `buy` path; prefer explicit deadline guards unless ops want auto-finalization. Also confirm interaction with **`WARBOW_FLAG_SILENCE`** and **`MAX_SALE_ELAPSED`** so no contradictory reverts mid-extension edge cases. ## Acceptance criteria - No WarBow BP mutation or flag claim succeeds when **`block.timestamp >= deadline`** and sale not yet **`endSale`**-finalized (unless product explicitly chooses auto-finalization). - Podium snapshots used for **`distributePrizes`** cannot be manipulated in the dead zone described in Finding 6. ## Verification checklist - [ ] Forge: scenario **warp to `deadline`**, withhold `endSale`, assert WarBow txs revert **or** finalize per chosen design. - [ ] Regression: **`endSale` then revenge/steal** still correctly blocked by `ended`. - [ ] **`docs/frontend/timecurve-views.md`**, **`invariants-and-business-logic.md`** — document “sale timer ended ⇒ WarBow off” invariant. - [ ] Indexer/UI only if timestamps or phases displayed need copy updates. ## Repo touch list | Area | Change | |------|--------| | **Code** | `contracts/src/TimeCurve.sol` (four entrypoints + NatSpec) | | **Tests** | `contracts/test/TimeCurve.t.sol` WarBow timing section | | **Docs / invariants** | Frontend + testing invariant map | | **Indexer** | Typically none | | **Frontend** | Preflight/disable WarBow CTAs when `deadline` passed (**hero timer `#48`** clock source) | | **Skills** | `skills/play-timecurve-warbow/SKILL.md` Phase expectations |
PlasticDigits commented 2026-05-04 08:36:45 +00:00 (Migrated from gitlab.com)

mentioned in issue #131

mentioned in issue #131
PlasticDigits commented 2026-05-04 09:22:06 +00:00 (Migrated from gitlab.com)

mentioned in commit 6a4f054fe8

mentioned in commit 6a4f054fe84b16fd07493cf316cb8112b58c2625
PlasticDigits commented 2026-05-04 09:22:13 +00:00 (Migrated from gitlab.com)

Merged to `main` (commit 6a4f054).

Summary: Inclusive boundary for buys and WarBow: interactions allowed while `block.timestamp <= deadline()`. `endSale` waits until `block.timestamp > deadline()` so the last second stays usable and settlement cannot race the same timestamp. Router `BadSalePhase` and frontend `saleExpiredAwaitingEnd` use the matching strict past-deadline rule.

Docs / skills: `docs/testing/invariants-and-business-logic.md`, `docs/frontend/timecurve-views.md` (#136 anchors), WarBow/Doubloon skills + README, yieldomega guardrails skill.

@brouie please verify:

  • `forge test --match-contract TimeCurveTest` — includes `test_buy_succeeds_at_deadline_second`, `test_end_sale_reverts_until_strictly_after_deadline`, `test_gitlab136_warbow_steal_reverts_when_past_deadline_before_endsale`
  • On a live indexer/fork: buy at ledger second == deadline succeeds; router rejects only when past deadline (`>`)
  • UI: phase stays “live” through deadline second; switches to awaiting `endSale` only when strictly past deadline
  • `npm test` / Vitest — `timeCurveSimplePhase.test.ts`

Leaving this issue open for your sign-off.

Merged to \`main\` (commit 6a4f054). **Summary:** Inclusive boundary for buys and WarBow: interactions allowed while \`block.timestamp <= deadline()\`. \`endSale\` waits until \`block.timestamp > deadline()\` so the last second stays usable and settlement cannot race the same timestamp. Router \`BadSalePhase\` and frontend \`saleExpiredAwaitingEnd\` use the matching strict past-deadline rule. **Docs / skills:** \`docs/testing/invariants-and-business-logic.md\`, \`docs/frontend/timecurve-views.md\` (#136 anchors), WarBow/Doubloon skills + README, yieldomega guardrails skill. \@brouie please verify: - [ ] \`forge test --match-contract TimeCurveTest\` — includes \`test_buy_succeeds_at_deadline_second\`, \`test_end_sale_reverts_until_strictly_after_deadline\`, \`test_gitlab136_warbow_steal_reverts_when_past_deadline_before_endsale\` - [ ] On a live indexer/fork: buy at ledger second == deadline succeeds; router rejects only when past deadline (\`>\`) - [ ] UI: phase stays “live” through deadline second; switches to awaiting \`endSale\` only when strictly past deadline - [ ] \`npm test\` / Vitest — \`timeCurveSimplePhase.test.ts\` Leaving this issue **open** for your sign-off.
Brouie commented 2026-05-05 02:03:01 +00:00 (Migrated from gitlab.com)

@PlasticDigits — verification PASS on 6a4f054. walked your checklist:

  • forge test --match-contract TimeCurveTest green for the three named tests: test_buy_succeeds_at_deadline_second ✓, test_end_sale_reverts_until_strictly_after_deadline ✓, test_gitlab136_warbow_steal_reverts_when_past_deadline_before_endsale ✓.
  • npx vitest run timeCurveSimplePhase green: 21/21 PASS.
  • contract guards verified at source: TimeCurve.sol L371-379 defines _requireSaleRoundTimerStillLive (<= inclusive) and _requireSaleRoundTimerOverForEndSale (strict >). all four WarBow paths + _buy call the still-live form: _buy (L461), claimWarBowFlag (L607), warbowSteal (L622), warbowRevenge (L693), warbowActivateGuard (L722). endSale (L779-787) calls the strict-over form. asymmetry locked: inclusive last-second buy/WarBow vs strict-past endSale.
  • §136 anchor in docs/testing/invariants-and-business-logic.md reads cleanly. docs/frontend/timecurve-views.md matches the strict-past-deadline phase rule.
    items 2 and 3 (live indexer/fork + UI phase walk) skipped this round — no localnet stack up. logic verified via source + Vitest unit coverage on timeCurveSimplePhase. happy to walk the live UX next session if you want it pinned.
    ready for close on your side.
@PlasticDigits — verification PASS on `6a4f054`. walked your checklist: - `forge test --match-contract TimeCurveTest` green for the three named tests: `test_buy_succeeds_at_deadline_second` ✓, `test_end_sale_reverts_until_strictly_after_deadline` ✓, `test_gitlab136_warbow_steal_reverts_when_past_deadline_before_endsale` ✓. - `npx vitest run timeCurveSimplePhase` green: 21/21 PASS. - contract guards verified at source: TimeCurve.sol L371-379 defines `_requireSaleRoundTimerStillLive` (`<=` inclusive) and `_requireSaleRoundTimerOverForEndSale` (strict `>`). all four WarBow paths + `_buy` call the still-live form: `_buy` (L461), `claimWarBowFlag` (L607), `warbowSteal` (L622), `warbowRevenge` (L693), `warbowActivateGuard` (L722). `endSale` (L779-787) calls the strict-over form. asymmetry locked: inclusive last-second buy/WarBow vs strict-past endSale. - §136 anchor in `docs/testing/invariants-and-business-logic.md` reads cleanly. `docs/frontend/timecurve-views.md` matches the strict-past-deadline phase rule. items 2 and 3 (live indexer/fork + UI phase walk) skipped this round — no localnet stack up. logic verified via source + Vitest unit coverage on `timeCurveSimplePhase`. happy to walk the live UX next session if you want it pinned. ready for close on your side.
PlasticDigits (Migrated from gitlab.com) closed this issue 2026-05-05 02:34:53 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
code/yieldomega#136
No description provided.