Frontend: finish CL8Y unlimited-approval localStorage rename (drop TimeCurve doc token)

Summary

Finish the Arena v2 rename of the opt-in unlimited CL8Y → TimeArena localStorage preference. Canonical storage already uses yieldomega.erc20.cl8yArenaUnlimited.v1, but docs and UI copy still cite the retired cl8yTimeCurveUnlimited.v1 key — the last TimeCurve token in docs/ called out in #276 (closed) follow-up.

Parent: #276 (closed) · Policy: #274 (closed) · Original sizing: #143 (closed) · Shared approve gate: #224 (closed).


Current codebase

Storage + sizing (already partially migrated)

frontend/src/lib/arenaDoubApprovalPreference.ts:

Constant Value Role
CL8Y_ARENA_UNLIMITED_APPROVAL_STORAGE_KEY yieldomega.erc20.cl8yArenaUnlimited.v1 Canonical write target
CL8Y_ARENA_UNLIMITED_APPROVAL_LEGACY_KEY yieldomega.erc20.cl8yTimeCurveUnlimited.v1 Read-only fallback; removed on next enable write
  • readArenaDoubUnlimitedApproval() returns true if either key is "1".
  • writeCl8yArenaUnlimitedApproval(true) sets canonical key and removes legacy key.
  • arenaDoubApprovalAmountWei(need, unlimited) returns type(uint256).max when opted in, else need + 50 bps headroom (#143 (closed)).

Consumers

Tests today

frontend/src/lib/arenaDoubApprovalPreference.test.ts — headroom math + canonical key roundtrip; no legacy-key migration test.

Stale surfaces (this issue)

Location Problem
docs/frontend/wallet-connection.md §143 Documents cl8yTimeCurveUnlimited.v1 as active key
ArenaDoubUnlimitedApprovalFieldset.tsx Label: “Remember unlimited CL8Y allowance for TimeCurve”; file comment still says TimeCurve
ensureCl8yKumbayaAllowance.ts Comment/type names EnsureCl8yTimeCurveAllowanceParams, “allowance for TimeCurve” (spender is already timeArenaAddress)

Doc gate impact

After #276 (closed), bash scripts/check-doc-satellite-retired-count.sh allows ≤25 mentions / ≤15 files. wallet-connection.md is the only non-invariant doc file still containing TimeCurve (embedded in the legacy key string).


Why this is needed

  1. Doc accuracy for agents/QA — Operators and third-party agents read wallet-connection.md; citing a retired key misleads debugging and violates the #276 (closed) intent.
  2. UX consistency/arena participants should see Time Arena / TimeArena, not retired v1 product naming (#256 (closed)).
  3. Complete the in-code migration — Canonical key exists; finish docs, UI copy, tests, and optional identifier cleanup without breaking existing wallets that still have the legacy key set.

Constraints and guardrails

Read .cursor/skills/yieldomega-guardrails/SKILL.md before implementation.

  • Do not change approval economics — same 50 bps headroom, same maxUint256 when opted in (#143 (closed)).
  • Preserve legacy read path until users re-toggle or clear storage — do not drop CL8Y_ARENA_UNLIMITED_APPROVAL_LEGACY_KEY read without a migration test.
  • Spender remains TimeArena proxy — preference only affects CL8Y approve sizing to the configured arena address; no new unlimited paths for DOUB, WETH, USDM, or routers.
  • Toggling off does not revoke onchain allowance — document-only behavior; unchanged.
  • No new env varsVITE_TIME_ARENA_ADDRESS only for arena writes (#266 (closed)).
  • Docs-only gate — updating wallet-connection.md should drop docs/ mention count by 1; re-run check-doc-satellite-retired-count.sh.
  • Minimal diff — rename/comments/UI copy + tests; avoid unrelated TimeCurve comment cleanup elsewhere in this issue.

Relevant files

Path Action
frontend/src/lib/arenaDoubApprovalPreference.ts Export legacy key constant for tests or document inline; no behavior change unless adding explicit one-shot migration helper
frontend/src/lib/arenaDoubApprovalPreference.test.ts Add legacy read + migrate-on-write tests
frontend/src/components/ArenaDoubUnlimitedApprovalFieldset.tsx Arena-neutral label + comment
frontend/src/lib/ensureCl8yKumbayaAllowance.ts Optional: rename EnsureCl8yTimeCurveAllowanceParams → Arena-neutral alias (keep export alias if widely imported)
frontend/src/lib/ensureCl8yKumbayaAllowance.test.ts Update if type renamed
docs/frontend/wallet-connection.md Document cl8yArenaUnlimited.v1; one-line note that legacy key is read once then removed
docs/testing/invariants-and-business-logic.md Optional: add INV-FRONTEND-* row under #276 (closed) satellite section if doc gate counts change

  1. Docs — Replace cl8yTimeCurveUnlimited.v1 with cl8yArenaUnlimited.v1 in wallet-connection.md; add ≤1 line: “Legacy …cl8yTimeCurveUnlimited.v1 is honored on read and removed when the user re-enables the checkbox.”
  2. UI — Change checkbox text to e.g. “Remember unlimited CL8Y allowance for Time Arena (optional)” (match arena-views.md tone).
  3. TestsreadArenaDoubUnlimitedApproval() true when only legacy key set; after writeCl8yArenaUnlimitedApproval(true), legacy absent and canonical present.
  4. Optional cleanup — Rename EnsureCl8yTimeCurveAllowanceParams with a type alias for backward compatibility; fix file header comments to say TimeArena.
  5. Verificationcd frontend && npm test; bash scripts/check-doc-satellite-retired-count.sh; manual /arena checkbox smoke.

Acceptance criteria

  • docs/frontend/wallet-connection.md documents yieldomega.erc20.cl8yArenaUnlimited.v1 as the active key (legacy key mentioned at most once as retired read-only).
  • rg 'cl8yTimeCurveUnlimited' docs/zero matches (or ≤1 in a single “Retired” sentence if team prefers explicit migration note without TimeCurve token — prefer zero).
  • bash scripts/check-doc-satellite-retired-count.sh — exit 0; wallet-connection.md no longer matches TimeCurve|FeeRouter.
  • /arena unlimited-approval checkbox label and component comment use Time Arena / TimeArena, not TimeCurve.
  • Legacy localStorage preference still works for wallets that only have the old key (automated test).
  • Enabling preference migrates legacy → canonical key (automated test).
  • Approval behavior unchanged: arenaDoubApprovalPreference.test.ts + ensureCl8yKumbayaAllowance.test.ts green.
  • cd frontend && npm test — all green.

Test plan — functional paths

# Path Steps Expected
1 Fresh user, unlimited off Clear storage → load /arena → buy CL8Y path Exact (+ headroom) approve when needed; checkbox unchecked
2 Opt in unlimited Check box → buy approve(TimeArena, max) once; subsequent buys skip approve if allowance sufficient
3 Opt out Uncheck box → buy Sized approve again; onchain max allowance may remain (user revokes in wallet if desired)
4 Legacy key only Seed cl8yTimeCurveUnlimited.v1=1 only → load page Checkbox shows checked; buy uses unlimited sizing
5 Legacy migration Legacy only → toggle off/on or enable write Canonical key set; legacy key removed
6 Private mode / quota Simulate localStorage throw Preference false; buys still work with per-tx exact approve
7 Wrong network #95 (closed) gate No approve/submit on wrong chain regardless of preference

Automated: extend arenaDoubApprovalPreference.test.ts; run full Vitest suite.


Test plan — attack, hack, and abuse vectors

Vector Risk Mitigation / test
Unlimited CL8Y approve to wrong spender High if UI pointed approve at non-arena address Assert ensureCl8yKumbayaAllowance always uses timeArenaAddress from env/config; wrong-network gate #95 (closed)
XSS exfiltrating localStorage Low (boolean pref only; no secrets) No change; preference is non-sensitive — do not store keys or mnemonics
Cross-site localStorage injection N/A (same-origin) No change
User tricked into unlimited approve Medium (UX/social) Keep opt-in checkbox + disclosure text; label must name Time Arena spender context clearly
Stale unlimited allowance after arena redeploy Medium (old proxy still approved) Out of scope for key rename — document that users revoke in wallet when addresses change; optional follow-up
Legacy key forever dual-read Low (doc/tech debt) Migration-on-write removes legacy key; test ensures no duplicate writes
Malicious extension rewriting localStorage Low Same as XSS — cannot force onchain approve without wallet signature

Verification criteria

# Doc gates (#274, #276)
bash scripts/check-doc-retired-terms.sh
bash scripts/check-doc-anchors.sh
bash scripts/check-doc-satellite-retired-count.sh

# Doc token spot-check
rg 'TimeCurve|FeeRouter' docs/frontend/wallet-connection.md   # expect: no matches
rg 'cl8yTimeCurveUnlimited' docs/                           # expect: no matches

# Frontend
cd frontend && npm test

# Manual (Anvil)
bash scripts/start-local-anvil-stack.sh
# open /arena, exercise checkbox + one CL8Y buy with pref on/off

Manual QA cross-links: wallet-connection §143 · manual QA §260 · invariants §276.