WarBow steal daily-cap UX: UTC reset in summary and inline cap warning #361

Closed
opened 2026-07-07 02:27:59 +00:00 by PlasticDigits · 10 comments
PlasticDigits commented 2026-07-07 02:27:59 +00:00 (Migrated from gitlab.com)

Parent

GitLab #360 — User bug report (ui/ux)

Bundled approved items:

  • Show when eligibility for another 3 steals per UTC day resets next to the BP / guard viewer summary.
  • Move the “3 steals already done today” warning closer to the Steal button (not only after a failed submit).

Current codebase

Primary play surface is / (ArenaSimplePage.tsx), which renders ArenaWarbowHeroPanel for WarBow PvP.

Viewer summary (warbow-hero-card--viewer-summary in ArenaWarbowHeroPanel.tsx) currently shows only:

  • YOUR BP: …
  • GUARD: … (countdown when active, else INACTIVE)

Steal daily caps (onchain):

  • TimeArena.WARBOW_MAX_STEALS_PER_DAY = 3
  • UTC day id: block.timestamp / SECONDS_PER_DAY (contracts/src/arena/TimeArena.sol, warbowSteal)
  • Per-victim cap: stealsReceivedOnDay[victim][day]
  • Per-attacker cap (across all victims): stealsCommittedByAttackerOnDay[attacker][day]
  • Bypass: extra WARBOW_STEAL_LIMIT_BYPASS_DOUB when either cap is hit

Frontend steal UX gap:

  • useArenaWarbowHero.ts computes utcDayId from chainNowSec and SECONDS_PER_DAY, but does not expose live steal-cap reads or inline preflight to the panel.
  • On submit, readFreshWarbowStealPreflight reads victim BP / victim steals / victim guard, but passes attackerStealsToday: undefined into describeStealPreflight (timeArenaUx.ts).
  • Daily-limit feedback in the hero panel appears only as a bottom pvpErr after a blocked submit — not inline near the Steal button.
  • By contrast, the protocol/audit WarBow section (ArenaSections.tsx) already renders describeStealPreflight in a StatusMessage directly above the steal action row.

Indexer-first display (#301): When VITE_INDEXER_URL is set, chainNowSec and guard reads come from indexer head (indexerWarbowHead); steal-cap reads should use the same clock source and avoid duplicating browser RPC mirrors for display-only fields where indexer wallet stats can supply them.

Why needed

Players cannot see when their free steal quota resets without mental UTC math, and the daily-cap warning is easy to miss because it surfaces only after a failed transaction at the bottom of the WarBow section. This was reported as a UX bug in #360 and approved for implementation.

Constraints / guardrails

  • Frontend-only — no contract or indexer schema changes unless a gap is discovered; caps are authoritative onchain.
  • Onchain authority: UTC-day semantics must match TimeArena (SECONDS_PER_DAY, stealsReceivedOnDay, stealsCommittedByAttackerOnDay).
  • INV-FRONTEND-301-INDEXER-FIRST-DISPLAY: Prefer indexer head time + existing wallet stats paths when VITE_INDEXER_URL is set; do not add parallel browser RPC polling for fields the indexer already serves.
  • No new business logic in the UI — reuse describeStealPreflight / existing constants (arenaWarbowConstants.ts, abis.ts).
  • Keep copy concise and consistent with warbowHeroSubcardHelpCopy.ts (“per UTC day”, bypass spend label).
  • AGPL; match existing warbow-hero-viewer-summary__line styling in index.css.

Relevant files

Area Path
Hero panel UI frontend/src/pages/arena/ArenaWarbowHeroPanel.tsx
WarBow hook frontend/src/pages/arena/useArenaWarbowHero.ts
Steal preflight narrative frontend/src/lib/timeArenaUx.ts (describeStealPreflight)
Fresh victim reads frontend/src/lib/timeArenaWarbowStealSubmitPreflight.ts
Constants / ABI frontend/src/lib/arenaWarbowConstants.ts, frontend/src/lib/abis.ts
Protocol reference UX frontend/src/pages/arena/ArenaSections.tsx
Play page wiring frontend/src/pages/arena/ArenaSimplePage.tsx
Styles frontend/src/index.css (.warbow-hero-card--viewer-summary)
Tests frontend/src/pages/arena/ArenaWarbowHeroPanel.test.tsx, frontend/src/lib/timeArenaUx.test.ts
  1. UTC-day reset in viewer summary

    • Add a third summary line, e.g. STEAL QUOTA: or NEXT FREE STEALS:, showing:
      • Attacker steals used today: X / 3 (from stealsCommittedByAttackerOnDay when connected).
      • Countdown until next UTC day: formatCountdown(secondsUntilNextUtcDay) derived from chainNowSec % SECONDS_PER_DAY.
      • When at cap: short hint that bypass is required until reset (link mentally to bypass checkbox).
    • Extract a small pure helper (e.g. warbowUtcDayResetSec(chainNowSec, secondsPerDay)) next to existing timer helpers; unit-test it.
  2. Inline steal-cap warning near Steal button

    • Mirror ArenaSections.tsx: when a victim is selected, render describeStealPreflight (warning/error tones) immediately above the Steal button inside the steal card — not only in the global pvpErr footer.
    • Extend useArenaWarbowHero to read stealsCommittedByAttackerOnDay(viewer, utcDayId) and victim steals when a victim is selected (wagmi useReadContract when indexer off; optional indexer wallet stats if available).
    • Pass real attackerStealsToday into describeStealPreflight (fix the current undefined gap).
    • Keep submit-time readFreshWarbowStealPreflight as the final gate before warbowSteal (#216 / #101).
  3. Optional polish: disable Steal button when preflight tone is error (protocol page already does this); keep bypass checkbox behavior unchanged.

Acceptance criteria

  • Connected viewer summary shows attacker steal usage X/3 and a live countdown to the next UTC-day reset.
  • When a steal victim is selected and either victim or attacker is at the daily cap (bypass off), a visible warning appears in the steal card, directly above the Steal button, using describeStealPreflight copy.
  • Warning updates when victim, bypass checkbox, or chain/indexer clock changes — without requiring a failed tx.
  • Indexer-first stack (VITE_INDEXER_URL set) still renders correct countdown and cap state (no extra RPC mirror violations).
  • No contract changes.

Test plan

Layer Command Expect
Unit cd frontend && npm test -- ArenaWarbowHeroPanel.test.ts timeArenaUx.test.ts PASS — new summary line + inline preflight cases
Typecheck cd frontend && npm run typecheck PASS
Lint cd frontend && npm run lint PASS
Manual (Anvil stack) bash scripts/start-qa-local-full-stack.sh --live-sale --no-swarm --no-frontend then cd frontend && npm run dev At /, connect wallet, open WarBow panel: summary shows quota + reset; select victim at cap → warning adjacent to Steal
Manual (Rabby, optional) bash scripts/qa/build-frontend-for-rabby.sh + preview + Rabby on chain 31337 Real signing path unchanged; warnings visible before submit
Regression cd frontend && npm test PASS

Attack / abuse test plan

  • Misleading reset time: Countdown must use the same SECONDS_PER_DAY and floor division as TimeArena — verify helper against known timestamps (not local timezone).
  • Stale cap reads: After a successful steal, cap display must refresh (existing BP-moving event invalidation / refetch paths).
  • Bypass bypass: UI must not imply free steals remain when onchain would require bypass — preflight warning tone warning until bypass checked.
  • Self-target / band errors: Inline preflight must not leak victim addresses beyond existing PlayerIdentity patterns.

Verification criteria

  • npm run typecheck && npm run lint && npm test in frontend/ — PASS
  • Manual check on Anvil play route / with connected wallet — quota line + inline cap warning visible per acceptance criteria
  • No new browser RPC mirrors for indexer-first display paths (#301)
## Parent [GitLab #360 — User bug report (ui/ux)](https://gitlab.com/PlasticDigits/yieldomega/-/issues/360) Bundled approved items: - Show when eligibility for another 3 steals per UTC day resets next to the BP / guard viewer summary. - Move the “3 steals already done today” warning closer to the Steal button (not only after a failed submit). ## Current codebase Primary play surface is `/` (`ArenaSimplePage.tsx`), which renders `ArenaWarbowHeroPanel` for WarBow PvP. **Viewer summary** (`warbow-hero-card--viewer-summary` in `ArenaWarbowHeroPanel.tsx`) currently shows only: - `YOUR BP: …` - `GUARD: …` (countdown when active, else `INACTIVE`) **Steal daily caps (onchain):** - `TimeArena.WARBOW_MAX_STEALS_PER_DAY = 3` - UTC day id: `block.timestamp / SECONDS_PER_DAY` (`contracts/src/arena/TimeArena.sol`, `warbowSteal`) - Per-victim cap: `stealsReceivedOnDay[victim][day]` - Per-attacker cap (across all victims): `stealsCommittedByAttackerOnDay[attacker][day]` - Bypass: extra `WARBOW_STEAL_LIMIT_BYPASS_DOUB` when either cap is hit **Frontend steal UX gap:** - `useArenaWarbowHero.ts` computes `utcDayId` from `chainNowSec` and `SECONDS_PER_DAY`, but does **not** expose live steal-cap reads or inline preflight to the panel. - On submit, `readFreshWarbowStealPreflight` reads victim BP / victim steals / victim guard, but passes `attackerStealsToday: undefined` into `describeStealPreflight` (`timeArenaUx.ts`). - Daily-limit feedback in the hero panel appears only as a bottom `pvpErr` after a blocked submit — not inline near the Steal button. - By contrast, the protocol/audit WarBow section (`ArenaSections.tsx`) already renders `describeStealPreflight` in a `StatusMessage` directly above the steal action row. **Indexer-first display (#301):** When `VITE_INDEXER_URL` is set, `chainNowSec` and guard reads come from indexer head (`indexerWarbowHead`); steal-cap reads should use the same clock source and avoid duplicating browser RPC mirrors for display-only fields where indexer wallet stats can supply them. ## Why needed Players cannot see when their free steal quota resets without mental UTC math, and the daily-cap warning is easy to miss because it surfaces only after a failed transaction at the bottom of the WarBow section. This was reported as a UX bug in #360 and approved for implementation. ## Constraints / guardrails - **Frontend-only** — no contract or indexer schema changes unless a gap is discovered; caps are authoritative onchain. - **Onchain authority:** UTC-day semantics must match `TimeArena` (`SECONDS_PER_DAY`, `stealsReceivedOnDay`, `stealsCommittedByAttackerOnDay`). - **INV-FRONTEND-301-INDEXER-FIRST-DISPLAY:** Prefer indexer head time + existing wallet stats paths when `VITE_INDEXER_URL` is set; do not add parallel browser RPC polling for fields the indexer already serves. - **No new business logic in the UI** — reuse `describeStealPreflight` / existing constants (`arenaWarbowConstants.ts`, `abis.ts`). - Keep copy concise and consistent with `warbowHeroSubcardHelpCopy.ts` (“per UTC day”, bypass spend label). - AGPL; match existing `warbow-hero-viewer-summary__line` styling in `index.css`. ## Relevant files | Area | Path | |------|------| | Hero panel UI | `frontend/src/pages/arena/ArenaWarbowHeroPanel.tsx` | | WarBow hook | `frontend/src/pages/arena/useArenaWarbowHero.ts` | | Steal preflight narrative | `frontend/src/lib/timeArenaUx.ts` (`describeStealPreflight`) | | Fresh victim reads | `frontend/src/lib/timeArenaWarbowStealSubmitPreflight.ts` | | Constants / ABI | `frontend/src/lib/arenaWarbowConstants.ts`, `frontend/src/lib/abis.ts` | | Protocol reference UX | `frontend/src/pages/arena/ArenaSections.tsx` | | Play page wiring | `frontend/src/pages/arena/ArenaSimplePage.tsx` | | Styles | `frontend/src/index.css` (`.warbow-hero-card--viewer-summary`) | | Tests | `frontend/src/pages/arena/ArenaWarbowHeroPanel.test.tsx`, `frontend/src/lib/timeArenaUx.test.ts` | ## Recommended direction 1. **UTC-day reset in viewer summary** - Add a third summary line, e.g. `STEAL QUOTA:` or `NEXT FREE STEALS:`, showing: - Attacker steals used today: `X / 3` (from `stealsCommittedByAttackerOnDay` when connected). - Countdown until next UTC day: `formatCountdown(secondsUntilNextUtcDay)` derived from `chainNowSec % SECONDS_PER_DAY`. - When at cap: short hint that bypass is required until reset (link mentally to bypass checkbox). - Extract a small pure helper (e.g. `warbowUtcDayResetSec(chainNowSec, secondsPerDay)`) next to existing timer helpers; unit-test it. 2. **Inline steal-cap warning near Steal button** - Mirror `ArenaSections.tsx`: when a victim is selected, render `describeStealPreflight` (warning/error tones) **immediately above** the Steal button inside the steal card — not only in the global `pvpErr` footer. - Extend `useArenaWarbowHero` to read `stealsCommittedByAttackerOnDay(viewer, utcDayId)` and victim steals when a victim is selected (wagmi `useReadContract` when indexer off; optional indexer wallet stats if available). - Pass real `attackerStealsToday` into `describeStealPreflight` (fix the current `undefined` gap). - Keep submit-time `readFreshWarbowStealPreflight` as the final gate before `warbowSteal` (#216 / #101). 3. **Optional polish:** disable Steal button when preflight tone is `error` (protocol page already does this); keep bypass checkbox behavior unchanged. ## Acceptance criteria - [ ] Connected viewer summary shows attacker steal usage `X/3` and a live countdown to the next UTC-day reset. - [ ] When a steal victim is selected and either victim or attacker is at the daily cap (bypass off), a visible warning appears **in the steal card**, directly above the Steal button, using `describeStealPreflight` copy. - [ ] Warning updates when victim, bypass checkbox, or chain/indexer clock changes — without requiring a failed tx. - [ ] Indexer-first stack (`VITE_INDEXER_URL` set) still renders correct countdown and cap state (no extra RPC mirror violations). - [ ] No contract changes. ## Test plan | Layer | Command | Expect | |-------|---------|--------| | Unit | `cd frontend && npm test -- ArenaWarbowHeroPanel.test.ts timeArenaUx.test.ts` | PASS — new summary line + inline preflight cases | | Typecheck | `cd frontend && npm run typecheck` | PASS | | Lint | `cd frontend && npm run lint` | PASS | | Manual (Anvil stack) | `bash scripts/start-qa-local-full-stack.sh --live-sale --no-swarm --no-frontend` then `cd frontend && npm run dev` | At `/`, connect wallet, open WarBow panel: summary shows quota + reset; select victim at cap → warning adjacent to Steal | | Manual (Rabby, optional) | `bash scripts/qa/build-frontend-for-rabby.sh` + preview + Rabby on chain 31337 | Real signing path unchanged; warnings visible before submit | | Regression | `cd frontend && npm test` | PASS | ## Attack / abuse test plan - **Misleading reset time:** Countdown must use the same `SECONDS_PER_DAY` and floor division as `TimeArena` — verify helper against known timestamps (not local timezone). - **Stale cap reads:** After a successful steal, cap display must refresh (existing BP-moving event invalidation / refetch paths). - **Bypass bypass:** UI must not imply free steals remain when onchain would require bypass — preflight warning tone `warning` until bypass checked. - **Self-target / band errors:** Inline preflight must not leak victim addresses beyond existing `PlayerIdentity` patterns. ## Verification criteria - [ ] `npm run typecheck && npm run lint && npm test` in `frontend/` — PASS - [ ] Manual check on Anvil play route `/` with connected wallet — quota line + inline cap warning visible per acceptance criteria - [ ] No new browser RPC mirrors for indexer-first display paths (#301)
PlasticDigits commented 2026-07-07 02:28:10 +00:00 (Migrated from gitlab.com)

mentioned in issue #360

mentioned in issue #360
PlasticDigits commented 2026-07-07 02:39:05 +00:00 (Migrated from gitlab.com)

mentioned in commit 78f57e5fcc

mentioned in commit 78f57e5fccc3c80496075f1dded250ab1f2c083d
PlasticDigits commented 2026-07-07 02:39:27 +00:00 (Migrated from gitlab.com)

mentioned in merge request !533

mentioned in merge request !533
PlasticDigits commented 2026-07-07 02:54:37 +00:00 (Migrated from gitlab.com)

mentioned in commit 824be4bf42

mentioned in commit 824be4bf42221c9ce87e8e745c8a48cbca9989d0
PlasticDigits commented 2026-07-07 02:59:06 +00:00 (Migrated from gitlab.com)

mentioned in commit d363d40ff3

mentioned in commit d363d40ff38b20afce23b4042c601ab363297abb
PlasticDigits commented 2026-07-07 02:59:31 +00:00 (Migrated from gitlab.com)

mentioned in merge request !534

mentioned in merge request !534
PlasticDigits commented 2026-07-07 03:32:47 +00:00 (Migrated from gitlab.com)

mentioned in commit 6550fe97a0

mentioned in commit 6550fe97a010c59e02b6a8e02856e75e71ab5f84
PlasticDigits commented 2026-07-07 03:37:49 +00:00 (Migrated from gitlab.com)

mentioned in commit 1459ead164

mentioned in commit 1459ead164787976f9962610be3d4d156072efd3
PlasticDigits commented 2026-07-07 03:53:51 +00:00 (Migrated from gitlab.com)

mentioned in commit c4de872e05

mentioned in commit c4de872e05f7a9253596323ff893c1614a4e1a58
PlasticDigits (Migrated from gitlab.com) closed this issue 2026-07-07 03:53:51 +00:00
PlasticDigits commented 2026-07-07 12:41:28 +00:00 (Migrated from gitlab.com)

mentioned in issue #366

mentioned in issue #366
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#361
No description provided.