Contracts: per-podium timer params (extension, initial, hard-reset bands) #271

Closed
opened 2026-05-30 14:06:50 +00:00 by PlasticDigits · 13 comments
PlasticDigits commented 2026-05-30 14:06:50 +00:00 (Migrated from gitlab.com)

Context

GitLab #247 landed independent podiumDeadline[4] / podiumEpoch[4] storage and rollPodiumEpoch, but all four categories still share one set of timer parameters at runtime:

  • Global timerExtensionSec, initialTimerSec, timerCapSec
  • Global hard-reset constants TIMER_RESET_BELOW_REMAINING_SEC (780) / TIMER_RESET_TO_REMAINING_SEC (900)

The product spec already defines per-podium values in docs/product/time-arena.md (see “Onchain implementation note” — currently marked as not enforced). This issue closes that gap immediately so onchain behavior matches the spec table.

Parent epic: #238.


Product target (authoritative)

Podium Cat Initial Extension / buy Hard-reset if remaining below Reset to
Last Buy 0 24h (86_400) +120s 13m (780) 15m (900)
Time Booster 1 12h (43_200) +60s 4m (240) 5m (300)
Defended Streak 2 18h (64_800) +90s 8.5m (510) 10m (600)
WarBow 3 48h (172_800) +300s 55m (3300) 1h (3600)

Timer cap: default 96h (4 × 86_400) for Last Buy today. Recommended default for this issue: timerCapSec[cat] = 4 × initialTimerSec[cat] (preserves ratio; WarBow cap = 192h). Document final choice in PARAMETERS.md.

Unchanged: lastBuyEpoch / CHARM / CRED accrual still roll only on Last Buy (cat 0) hard reset during _finishBuy, not on other podium rolls (#247).


Relevant files

Contracts (primary)

File Why
contracts/src/arena/TimeArena.sol Replace shared timer scalars/constants; per-category extend in _finishBuy / _extendOtherPodiumTimers; per-category roll reset in rollPodiumEpoch; fix scoring hooks that currently use Last Buy timer deltas
contracts/src/libraries/TimeMath.sol No API change expected (extendDeadlineOrResetBelowThreshold already parameterized)
New: contracts/src/arena/libraries/ArenaPodiumTimerConfig.sol (recommended) Canonical default table + validation helper (keeps TimeArena readable)
contracts/script/UUPSDeployLib.sol Pass per-category timer arrays into initialize
contracts/script/DeployProduction.s.sol Production defaults + env overrides per category
contracts/script/DeployDev.s.sol Anvil/E2E defaults (may keep compressed timers for CI — document if so)
contracts/test/TimeArena.t.sol Update / replace tests assuming uniform +120s / 24h initial
contracts/PARAMETERS.md Per-podium table replaces single-row timer section

Docs & invariants

File Why
docs/product/time-arena.md Remove or rewrite “Onchain implementation note” once enforced
docs/product/arena-v2.md Timer table currently says “Same params” for cats 1–3 — update to spec values
docs/testing/invariants-and-business-logic.md New invariants for per-category extension / initial divergence
.cursor/skills/yieldomega-guardrails/SKILL.md Guardrail bullet for per-podium params
skills/play-time-arena-doub/SKILL.md Play-agent timer reference

Derived layers (follow-on in same issue or sub-issues — list for visibility)

File Why
frontend/src/lib/abis.ts ABI for new getters (podiumTimerExtensionSec(uint8), etc.)
frontend/src/pages/arena/useArenaSaleSession.ts Buy preview currently uses single timerExtensionSec
frontend/src/lib/timeArenaBuyPreview.ts Hardcoded 120 default
frontend/src/pages/arena/ArenaTimerChips.tsx Already reads four deadlines; may need per-chip extension labels
indexer/src/chain_timer.rs Optional: expose per-category params on GET /v1/arena/timers for UI preview
bots/timearena/ Bot buy timing heuristics if they assume uniform extension

1. Storage & initialization (UUPS-safe)

Append per-category arrays (do not repurpose existing scalar slots on upgraded proxies):

uint256[4] public podiumTimerExtensionSec;
uint256[4] public podiumInitialTimerSec;
uint256[4] public podiumTimerCapSec;
uint256[4] public podiumResetBelowRemainingSec;
uint256[4] public podiumResetToRemainingSec;
  • initialize: accept arrays (or struct[4]) validated by ArenaPodiumTimerConfig.validate(...).
  • startArena: set podiumDeadline[i] = block.timestamp + podiumInitialTimerSec[i] (categories no longer share the same initial deadline).
  • rollPodiumEpoch(cat): reset with podiumInitialTimerSec[cat], not global initialTimerSec.
  • Deprecate / stop writing global timerExtensionSec & initialTimerSec for new logic; keep getters shimmed to cat 0 for one release if needed for ABI compat.

2. Buy path — per-category TimeMath

Refactor _finishBuy:

  1. Last Buy (cat 0): existing flow; hardReset on cat 0 only drives lastBuyEpoch.
  2. Cats 1–3: replace _extendOtherPodiumTimers loop body to use podium*Sec[c] for each category independently (each may hard-reset on its own remaining band).

Return per-category (actualSecondsAdded[c], hardReset[c]) for downstream scoring.

3. Fix scoring hooks (currently Last Buy–coupled)

Hook Today Should use
Time Booster totalEffectiveTimerSecAdded Last Buy actualSecondsAdded Cat 1 seconds added
Defended Streak _processDefendedStreak Last Buy remainingBefore / secondsAdded Cat 2 remaining / seconds added
WarBow _applyBuyWarBowBp clutch (remainingBefore < 30) Last Buy remaining Cat 3 remaining
WarBow timer-reset bonus BP Last Buy hardReset Cat 3 hard reset

4. Deploy defaults

Wire product table into DeployProduction / DeployDev (env overrides e.g. ARENA_PODIUM_1_TIMER_EXTENSION_SEC). Anvil may use compressed timers only behind DeployDev guard with explicit comment — production must use spec table.

5. Upgrade path

If a TimeArena proxy is already live with shared params: add reinitializePodiumTimers(...) (owner-only, once) or document fresh redeploy only per Arena v2 “no backwards compatibility” policy (#238). Pick one approach in implementation PR and document in PARAMETERS.md.


Acceptance criteria

  • Each of the four podium categories has independent extension, initial, cap, resetBelow, resetTo values onchain matching the product table (or documented dev-compressed Anvil subset).
  • startArena seeds different initial podiumDeadline[i] per category.
  • A single buy extends each category by its own extension / hard-reset rules (not shared +120s).
  • rollPodiumEpoch(cat) resets deadline to that category’s initial timer.
  • lastBuyEpoch still increments only on Last Buy (cat 0) hard reset; unaffected by other categories’ hard resets.
  • Time Booster, Defended Streak, and WarBow scoring hooks use their category’s timer deltas / remaining / hard-reset flags.
  • Deploy scripts (DeployProduction, DeployDev) and PARAMETERS.md updated.
  • Product docs no longer claim shared params are intentional (time-arena.md implementation note removed/updated; arena-v2.md table aligned).

Verification checklist

Forge

  • FOUNDRY_PROFILE=ci forge test --match-contract TimeArenaTest
  • New: test_start_arena_initial_deadlines_differ_by_category
  • Update: test_multi_podium_deadline_extend — assert cat-specific deltas (+120 / +60 / +90 / +300), not uniform +120
  • New: test_time_booster_hard_reset_band_240_to_300 (warp near cat 1 deadline)
  • New: test_warbow_bp_bonus_uses_warbow_hard_reset_not_last_buy (Last Buy extend without WarBow hard reset → no WarBow reset bonus)
  • New: test_defended_streak_uses_streak_timer_not_last_buy
  • Existing #247 divergence tests still pass (may need adjusted warp targets)

Integration / UI (manual or automated)

  • Anvil deploy + GET /v1/arena/timers shows four different initial deadlines after startArena
  • /arena timer chips show distinct countdowns at arena start
  • Buy preview / projected effects document which timer(s) a buy affects (at minimum Last Buy + note for podiums)

Docs / agents

  • Invariants table lists per-podium timer IDs with test names
  • skills/play-time-arena-doub/SKILL.md references per-podium table
  • Guardrails skill updated

Out of scope (unless blocking)

  • Governance setter to change timer params post-deploy (owner setPodiumTimerConfig — nice-to-have, not required for “immediate” spec alignment)
  • Indexer historical backfill of timer config (head reads sufficient for v1)
## Context GitLab [#247](https://gitlab.com/PlasticDigits/yieldomega/-/issues/247) landed **independent** `podiumDeadline[4]` / `podiumEpoch[4]` storage and `rollPodiumEpoch`, but all four categories still share **one** set of timer parameters at runtime: - Global `timerExtensionSec`, `initialTimerSec`, `timerCapSec` - Global hard-reset constants `TIMER_RESET_BELOW_REMAINING_SEC` (780) / `TIMER_RESET_TO_REMAINING_SEC` (900) The **product spec** already defines per-podium values in [`docs/product/time-arena.md`](docs/product/time-arena.md) (see “Onchain implementation note” — currently marked as not enforced). This issue closes that gap **immediately** so onchain behavior matches the spec table. Parent epic: [#238](https://gitlab.com/PlasticDigits/yieldomega/-/issues/238). --- ## Product target (authoritative) | Podium | Cat | Initial | Extension / buy | Hard-reset if remaining below | Reset to | |--------|-----|---------|-----------------|-------------------------------|----------| | **Last Buy** | 0 | 24h (`86_400`) | +120s | 13m (`780`) | 15m (`900`) | | **Time Booster** | 1 | 12h (`43_200`) | +60s | 4m (`240`) | 5m (`300`) | | **Defended Streak** | 2 | 18h (`64_800`) | +90s | 8.5m (`510`) | 10m (`600`) | | **WarBow** | 3 | 48h (`172_800`) | +300s | 55m (`3300`) | 1h (`3600`) | **Timer cap:** default **96h** (`4 × 86_400`) for Last Buy today. Recommended default for this issue: **`timerCapSec[cat] = 4 × initialTimerSec[cat]`** (preserves ratio; WarBow cap = 192h). Document final choice in `PARAMETERS.md`. **Unchanged:** `lastBuyEpoch` / CHARM / CRED accrual still roll **only** on Last Buy (cat 0) **hard reset** during `_finishBuy`, not on other podium rolls ([#247](https://gitlab.com/PlasticDigits/yieldomega/-/issues/247)). --- ## Relevant files ### Contracts (primary) | File | Why | |------|-----| | [`contracts/src/arena/TimeArena.sol`](contracts/src/arena/TimeArena.sol) | Replace shared timer scalars/constants; per-category extend in `_finishBuy` / `_extendOtherPodiumTimers`; per-category roll reset in `rollPodiumEpoch`; fix scoring hooks that currently use Last Buy timer deltas | | [`contracts/src/libraries/TimeMath.sol`](contracts/src/libraries/TimeMath.sol) | No API change expected (`extendDeadlineOrResetBelowThreshold` already parameterized) | | New: `contracts/src/arena/libraries/ArenaPodiumTimerConfig.sol` (recommended) | Canonical default table + validation helper (keeps `TimeArena` readable) | | [`contracts/script/UUPSDeployLib.sol`](contracts/script/UUPSDeployLib.sol) | Pass per-category timer arrays into `initialize` | | [`contracts/script/DeployProduction.s.sol`](contracts/script/DeployProduction.s.sol) | Production defaults + env overrides per category | | [`contracts/script/DeployDev.s.sol`](contracts/script/DeployDev.s.sol) | Anvil/E2E defaults (may keep compressed timers for CI — document if so) | | [`contracts/test/TimeArena.t.sol`](contracts/test/TimeArena.t.sol) | Update / replace tests assuming uniform +120s / 24h initial | | [`contracts/PARAMETERS.md`](contracts/PARAMETERS.md) | Per-podium table replaces single-row timer section | ### Docs & invariants | File | Why | |------|-----| | [`docs/product/time-arena.md`](docs/product/time-arena.md) | Remove or rewrite “Onchain implementation note” once enforced | | [`docs/product/arena-v2.md`](docs/product/arena-v2.md) | Timer table currently says “Same params” for cats 1–3 — update to spec values | | [`docs/testing/invariants-and-business-logic.md`](docs/testing/invariants-and-business-logic.md) | New invariants for per-category extension / initial divergence | | [`.cursor/skills/yieldomega-guardrails/SKILL.md`](.cursor/skills/yieldomega-guardrails/SKILL.md) | Guardrail bullet for per-podium params | | [`skills/play-time-arena-doub/SKILL.md`](skills/play-time-arena-doub/SKILL.md) | Play-agent timer reference | ### Derived layers (follow-on in same issue or sub-issues — list for visibility) | File | Why | |------|-----| | [`frontend/src/lib/abis.ts`](frontend/src/lib/abis.ts) | ABI for new getters (`podiumTimerExtensionSec(uint8)`, etc.) | | [`frontend/src/pages/arena/useArenaSaleSession.ts`](frontend/src/pages/arena/useArenaSaleSession.ts) | Buy preview currently uses single `timerExtensionSec` | | [`frontend/src/lib/timeArenaBuyPreview.ts`](frontend/src/lib/timeArenaBuyPreview.ts) | Hardcoded `120` default | | [`frontend/src/pages/arena/ArenaTimerChips.tsx`](frontend/src/pages/arena/ArenaTimerChips.tsx) | Already reads four deadlines; may need per-chip extension labels | | [`indexer/src/chain_timer.rs`](indexer/src/chain_timer.rs) | Optional: expose per-category params on `GET /v1/arena/timers` for UI preview | | [`bots/timearena/`](bots/timearena/README.md) | Bot buy timing heuristics if they assume uniform extension | --- ## Recommended changes ### 1. Storage & initialization (UUPS-safe) Append **per-category arrays** (do not repurpose existing scalar slots on upgraded proxies): ```solidity uint256[4] public podiumTimerExtensionSec; uint256[4] public podiumInitialTimerSec; uint256[4] public podiumTimerCapSec; uint256[4] public podiumResetBelowRemainingSec; uint256[4] public podiumResetToRemainingSec; ``` - **`initialize`**: accept arrays (or struct[4]) validated by `ArenaPodiumTimerConfig.validate(...)`. - **`startArena`**: set `podiumDeadline[i] = block.timestamp + podiumInitialTimerSec[i]` (categories **no longer share** the same initial deadline). - **`rollPodiumEpoch(cat)`**: reset with `podiumInitialTimerSec[cat]`, not global `initialTimerSec`. - Deprecate / stop writing global `timerExtensionSec` & `initialTimerSec` for new logic; keep getters shimmed to cat 0 for one release if needed for ABI compat. ### 2. Buy path — per-category `TimeMath` Refactor `_finishBuy`: 1. **Last Buy (cat 0):** existing flow; `hardReset` on cat 0 only drives `lastBuyEpoch`. 2. **Cats 1–3:** replace `_extendOtherPodiumTimers` loop body to use `podium*Sec[c]` for each category independently (each may hard-reset on its **own** remaining band). Return per-category `(actualSecondsAdded[c], hardReset[c])` for downstream scoring. ### 3. Fix scoring hooks (currently Last Buy–coupled) | Hook | Today | Should use | |------|-------|------------| | Time Booster `totalEffectiveTimerSecAdded` | Last Buy `actualSecondsAdded` | Cat 1 seconds added | | Defended Streak `_processDefendedStreak` | Last Buy `remainingBefore` / `secondsAdded` | Cat 2 remaining / seconds added | | WarBow `_applyBuyWarBowBp` clutch (`remainingBefore < 30`) | Last Buy remaining | Cat 3 remaining | | WarBow timer-reset bonus BP | Last Buy `hardReset` | Cat 3 hard reset | ### 4. Deploy defaults Wire product table into `DeployProduction` / `DeployDev` (env overrides e.g. `ARENA_PODIUM_1_TIMER_EXTENSION_SEC`). Anvil may use compressed timers **only** behind `DeployDev` guard with explicit comment — production must use spec table. ### 5. Upgrade path If a `TimeArena` proxy is already live with shared params: add **`reinitializePodiumTimers(...)`** (owner-only, once) or document **fresh redeploy only** per Arena v2 “no backwards compatibility” policy ([#238](https://gitlab.com/PlasticDigits/yieldomega/-/issues/238)). Pick one approach in implementation PR and document in `PARAMETERS.md`. --- ## Acceptance criteria - [ ] Each of the four podium categories has **independent** `extension`, `initial`, `cap`, `resetBelow`, `resetTo` values onchain matching the product table (or documented dev-compressed Anvil subset). - [ ] `startArena` seeds **different** initial `podiumDeadline[i]` per category. - [ ] A single buy extends each category by **its own** extension / hard-reset rules (not shared +120s). - [ ] `rollPodiumEpoch(cat)` resets deadline to **that category’s** initial timer. - [ ] `lastBuyEpoch` still increments **only** on Last Buy (cat 0) hard reset; unaffected by other categories’ hard resets. - [ ] Time Booster, Defended Streak, and WarBow scoring hooks use **their category’s** timer deltas / remaining / hard-reset flags. - [ ] Deploy scripts (`DeployProduction`, `DeployDev`) and `PARAMETERS.md` updated. - [ ] Product docs no longer claim shared params are intentional (`time-arena.md` implementation note removed/updated; `arena-v2.md` table aligned). --- ## Verification checklist ### Forge - [ ] `FOUNDRY_PROFILE=ci forge test --match-contract TimeArenaTest` - [ ] New: `test_start_arena_initial_deadlines_differ_by_category` - [ ] Update: `test_multi_podium_deadline_extend` — assert cat-specific deltas (+120 / +60 / +90 / +300), not uniform +120 - [ ] New: `test_time_booster_hard_reset_band_240_to_300` (warp near cat 1 deadline) - [ ] New: `test_warbow_bp_bonus_uses_warbow_hard_reset_not_last_buy` (Last Buy extend without WarBow hard reset → no WarBow reset bonus) - [ ] New: `test_defended_streak_uses_streak_timer_not_last_buy` - [ ] Existing #247 divergence tests still pass (may need adjusted warp targets) ### Integration / UI (manual or automated) - [ ] Anvil deploy + `GET /v1/arena/timers` shows four **different** initial deadlines after `startArena` - [ ] `/arena` timer chips show distinct countdowns at arena start - [ ] Buy preview / projected effects document which timer(s) a buy affects (at minimum Last Buy + note for podiums) ### Docs / agents - [ ] Invariants table lists per-podium timer IDs with test names - [ ] `skills/play-time-arena-doub/SKILL.md` references per-podium table - [ ] Guardrails skill updated --- ## Out of scope (unless blocking) - Governance setter to change timer params post-deploy (owner `setPodiumTimerConfig` — nice-to-have, not required for “immediate” spec alignment) - Indexer historical backfill of timer config (head reads sufficient for v1)
PlasticDigits commented 2026-05-30 14:06:52 +00:00 (Migrated from gitlab.com)

marked as related to #238

marked as related to #238
PlasticDigits commented 2026-05-30 14:06:52 +00:00 (Migrated from gitlab.com)

marked as related to #247

marked as related to #247
PlasticDigits commented 2026-05-30 14:06:53 +00:00 (Migrated from gitlab.com)

marked as related to #240

marked as related to #240
PlasticDigits commented 2026-05-30 14:13:44 +00:00 (Migrated from gitlab.com)

Time booster, defended streak, and warbow clutch/bonus all should be based on last buy category timer. The podium specific timers are for making prizes distribution only not scoring

Time booster, defended streak, and warbow clutch/bonus all should be based on last buy category timer. The podium specific timers are for making prizes distribution only not scoring
PlasticDigits commented 2026-05-30 14:14:31 +00:00 (Migrated from gitlab.com)

We do not need to upgrade contracts, this is part of a backwards compatibiltiy breaking full reimplementation.

We do not need to upgrade contracts, this is part of a backwards compatibiltiy breaking full reimplementation.
PlasticDigits commented 2026-05-30 14:20:39 +00:00 (Migrated from gitlab.com)

mentioned in commit 9326963277

mentioned in commit 9326963277b836d8539ebca4d7e819c7db5e5f83
PlasticDigits commented 2026-05-30 14:21:32 +00:00 (Migrated from gitlab.com)

Implementation landed on main (9326963)

Per-podium timer params are now enforced onchain via ArenaPodiumTimerConfig + per-category storage arrays on TimeArena. Product table matches time-arena.md.

What changed

  • New library: contracts/src/arena/libraries/ArenaPodiumTimerConfig.sol — canonical defaults + validation
  • Storage: podiumTimerExtensionSec[4], podiumInitialTimerSec[4], podiumTimerCapSec[4], podiumResetBelowRemainingSec[4], podiumResetToRemainingSec[4] (cat-0 shims kept for timerExtensionSec / initialTimerSec / timerCapSec)
  • startArena: seeds different initial podiumDeadline[i] per category
  • _finishBuy / _extendOtherPodiumTimers: per-category TimeMath extension/hard-reset
  • rollPodiumEpoch(cat): resets to that category's initial timer
  • Scoring unchanged (per issue comment): Time Booster, Defended Streak, WarBow BP still use Last Buy (cat 0) timer only
  • Deploy: DeployDev / DeployProduction + UUPSDeployLib.deployTimeArenaProductionDefaults
  • Docs/invariants/skills: updated + cross-linked (#271)

Verification checklist

  • FOUNDRY_PROFILE=ci forge test --match-contract TimeArenaTest — 54 tests pass
  • After startArena, four podiumDeadline[i] differ (24h / 12h / 18h / 48h offsets)
  • One buy extends cats by +120 / +60 / +90 / +300 respectively
  • Time Booster hard-reset band: remaining < 240s → snap to 300s (test_time_booster_hard_reset_band_240_to_300)
  • WarBow BP reset bonus requires Last Buy hard reset, not WarBow timer band alone
  • Defended streak uses Last Buy remaining, not other podium timers
  • lastBuyEpoch still bumps only on Last Buy hard reset
  • Fresh deploy via DeployProduction / Anvil DeployDev wires production table
  • /arena timer chips show distinct countdowns at arena start (manual/E2E)
  • Invariants: INV-TIME-ARENA-PODIUM-TIMER-PARAMS, INV-TIME-ARENA-SCORING-LAST-BUY-TIMER

@brouie — please verify on your side when convenient. Leaving issue open until sign-off.

## Implementation landed on `main` (9326963) Per-podium timer params are now enforced onchain via `ArenaPodiumTimerConfig` + per-category storage arrays on `TimeArena`. Product table matches [time-arena.md](docs/product/time-arena.md). ### What changed - **New library:** `contracts/src/arena/libraries/ArenaPodiumTimerConfig.sol` — canonical defaults + validation - **Storage:** `podiumTimerExtensionSec[4]`, `podiumInitialTimerSec[4]`, `podiumTimerCapSec[4]`, `podiumResetBelowRemainingSec[4]`, `podiumResetToRemainingSec[4]` (cat-0 shims kept for `timerExtensionSec` / `initialTimerSec` / `timerCapSec`) - **`startArena`:** seeds **different** initial `podiumDeadline[i]` per category - **`_finishBuy` / `_extendOtherPodiumTimers`:** per-category `TimeMath` extension/hard-reset - **`rollPodiumEpoch(cat)`:** resets to that category's initial timer - **Scoring unchanged (per issue comment):** Time Booster, Defended Streak, WarBow BP still use **Last Buy (cat 0)** timer only - **Deploy:** `DeployDev` / `DeployProduction` + `UUPSDeployLib.deployTimeArenaProductionDefaults` - **Docs/invariants/skills:** updated + cross-linked ([#271](https://gitlab.com/PlasticDigits/yieldomega/-/issues/271)) ### Verification checklist - [ ] `FOUNDRY_PROFILE=ci forge test --match-contract TimeArenaTest` — 54 tests pass - [ ] After `startArena`, four `podiumDeadline[i]` differ (24h / 12h / 18h / 48h offsets) - [ ] One buy extends cats by +120 / +60 / +90 / +300 respectively - [ ] Time Booster hard-reset band: remaining < 240s → snap to 300s (`test_time_booster_hard_reset_band_240_to_300`) - [ ] WarBow BP reset bonus requires **Last Buy** hard reset, not WarBow timer band alone - [ ] Defended streak uses Last Buy remaining, not other podium timers - [ ] `lastBuyEpoch` still bumps only on Last Buy hard reset - [ ] Fresh deploy via `DeployProduction` / Anvil `DeployDev` wires production table - [ ] `/arena` timer chips show distinct countdowns at arena start (manual/E2E) - [ ] Invariants: `INV-TIME-ARENA-PODIUM-TIMER-PARAMS`, `INV-TIME-ARENA-SCORING-LAST-BUY-TIMER` @brouie — please verify on your side when convenient. Leaving issue **open** until sign-off.
PlasticDigits commented 2026-05-30 14:40:51 +00:00 (Migrated from gitlab.com)

mentioned in issue #252

mentioned in issue #252
PlasticDigits commented 2026-06-01 11:51:18 +00:00 (Migrated from gitlab.com)

mentioned in commit 479448d90a

mentioned in commit 479448d90a1eee5604d03bebf45edfaf910cd55c
PlasticDigits commented 2026-06-01 11:51:25 +00:00 (Migrated from gitlab.com)

QA verification complete (479448d on main)

Verified GitLab #271 per-podium timer params against the issue body, implementation comment (scoring uses Last Buy timer only; per-category timers govern prize settlement), and the verification checklist in the implementation note.

What was verified

Contracts (Forge)

  • FOUNDRY_PROFILE=ci forge test --match-contract TimeArenaTest — 64/64 pass, including:
    • test_start_arena_initial_deadlines_differ_by_category
    • test_multi_podium_deadline_extend (+120 / +60 / +90 / +300)
    • test_time_booster_hard_reset_band_240_to_300
    • test_warbow_bp_bonus_uses_last_buy_hard_reset_not_warbow_timer
    • test_defended_streak_uses_last_buy_timer_not_other_podium
    • test_last_buy_epoch_on_hard_reset_not_on_other_podium_roll
  • #247 divergence tests still pass.

Anvil smoke (new)

  • bash scripts/verify-podium-timers-anvil.sh — fresh DeployDev: distinct initial podiumDeadline[i], product table getters, per-category buy extensions, Time Booster 240→300s hard-reset band.

Docs / agents

  • Added invariants §271, manual QA §271, guardrails + play-time-arena-doub crosslinks.
  • Buy checkout JSDoc clarifies Last Buy preview vs all-four settlement extensions.

Frontend

  • ArenaTimerChips.test.tsx, arenaBuyProjectedEffects.test.ts pass.
  • ArenaTimerChips reads per-category podiumDeadline (indexer podium_deadlines_sec or RPC).

Sign-off checklist (for re-run)

  • FOUNDRY_PROFILE=ci forge test --match-contract TimeArenaTest
  • bash scripts/verify-podium-timers-anvil.sh
  • After startArena, four podiumDeadline[i] differ (24h / 12h / 18h / 48h)
  • One buy extends +120 / +60 / +90 / +300
  • Time Booster hard-reset: remaining < 240s → 300s from block.timestamp
  • WarBow BP reset bonus requires Last Buy hard reset (not WarBow band alone)
  • Defended streak uses Last Buy remaining
  • lastBuyEpoch bumps only on Last Buy hard reset
  • /arena timer chips show distinct secondary countdowns
  • Invariants: INV-TIME-ARENA-PODIUM-TIMER-PARAMS, INV-TIME-ARENA-SCORING-LAST-BUY-TIMER

Implementation commit: 9326963. QA automation/docs: 479448d.

Closing — all acceptance criteria and verification items pass.

## QA verification complete (479448d on `main`) Verified GitLab **#271** per-podium timer params against the issue body, implementation comment (scoring uses **Last Buy** timer only; per-category timers govern prize settlement), and the verification checklist in the implementation note. ### What was verified **Contracts (Forge)** - `FOUNDRY_PROFILE=ci forge test --match-contract TimeArenaTest` — **64/64 pass**, including: - `test_start_arena_initial_deadlines_differ_by_category` - `test_multi_podium_deadline_extend` (+120 / +60 / +90 / +300) - `test_time_booster_hard_reset_band_240_to_300` - `test_warbow_bp_bonus_uses_last_buy_hard_reset_not_warbow_timer` - `test_defended_streak_uses_last_buy_timer_not_other_podium` - `test_last_buy_epoch_on_hard_reset_not_on_other_podium_roll` - `#247` divergence tests still pass. **Anvil smoke (new)** - `bash scripts/verify-podium-timers-anvil.sh` — fresh `DeployDev`: distinct initial `podiumDeadline[i]`, product table getters, per-category buy extensions, Time Booster 240→300s hard-reset band. **Docs / agents** - Added [invariants §271](docs/testing/invariants-and-business-logic.md#timearena-podium-timers-gitlab-271), [manual QA §271](docs/testing/manual-qa-checklists.md#manual-qa-issue-271), guardrails + `play-time-arena-doub` crosslinks. - Buy checkout JSDoc clarifies Last Buy preview vs all-four settlement extensions. **Frontend** - `ArenaTimerChips.test.tsx`, `arenaBuyProjectedEffects.test.ts` pass. - `ArenaTimerChips` reads per-category `podiumDeadline` (indexer `podium_deadlines_sec` or RPC). ### Sign-off checklist (for re-run) - [ ] `FOUNDRY_PROFILE=ci forge test --match-contract TimeArenaTest` - [ ] `bash scripts/verify-podium-timers-anvil.sh` - [ ] After `startArena`, four `podiumDeadline[i]` differ (24h / 12h / 18h / 48h) - [ ] One buy extends +120 / +60 / +90 / +300 - [ ] Time Booster hard-reset: remaining < 240s → 300s from `block.timestamp` - [ ] WarBow BP reset bonus requires Last Buy hard reset (not WarBow band alone) - [ ] Defended streak uses Last Buy remaining - [ ] `lastBuyEpoch` bumps only on Last Buy hard reset - [ ] `/arena` timer chips show distinct secondary countdowns - [ ] Invariants: `INV-TIME-ARENA-PODIUM-TIMER-PARAMS`, `INV-TIME-ARENA-SCORING-LAST-BUY-TIMER` Implementation commit: `9326963`. QA automation/docs: `479448d`. Closing — all acceptance criteria and verification items pass.
PlasticDigits (Migrated from gitlab.com) closed this issue 2026-06-01 11:51:30 +00:00
PlasticDigits commented 2026-06-13 07:02:54 +00:00 (Migrated from gitlab.com)

mentioned in issue #309

mentioned in issue #309
PlasticDigits commented 2026-06-16 05:49:43 +00:00 (Migrated from gitlab.com)

mentioned in issue #330

mentioned in issue #330
PlasticDigits commented 2026-07-03 07:32:48 +00:00 (Migrated from gitlab.com)

mentioned in commit 980e96f814

mentioned in commit 980e96f8143073a2ec82c5d1b03c21bd2f223f51
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#271
No description provided.