Harden claimCred post-transaction lifecycle #347

Closed
opened 2026-06-25 20:07:18 +00:00 by PlasticDigits · 5 comments
PlasticDigits commented 2026-06-25 20:07:18 +00:00 (Migrated from gitlab.com)

Parent

Transition Gap Analysis (#342) — P1 #6.

Current codebase

  • ArenaCharmCredCard.tsx calls writeContractAsync({ functionName: "claimCred", … }) with no waitForTransactionReceipt and no query invalidation.
  • useArenaSaleSession.ts claimWarBowFlag pattern: writeContractAsync → waitForWriteReceipt → refetchAll().
  • ArenaSimplePage.tsx uses invalidateArenaWalletStatsQueries after buys; CRED card does not.

Why needed

After claiming CRED, wallet stats and claimable epoch UI stay stale until next poll — poor transition UX and inconsistent with WarBow flag claim.

Constraints / guardrails

  • Mirror established claimWarBowFlag receipt + invalidation pattern.
  • Respect ChainMismatchWriteBarrier (already wrapped).
  • Indexer-first: invalidate wallet stats queries, do not add RPC balance mirrors.

Relevant files

  • frontend/src/pages/arena/ArenaCharmCredCard.tsx
  • frontend/src/pages/arena/useArenaSaleSession.ts (reference)
  • frontend/src/lib/arenaWalletXpOptimistic.ts (invalidateArenaWalletStatsQueries)
  • frontend/src/hooks/useWalletStats.ts
  1. After claimCred tx, await waitForWriteReceipt(wagmiConfig, { hash }).
  2. Call invalidateArenaWalletStatsQueries(queryClient) (and any CRED-specific queries).
  3. Surface write errors via existing toast/error pattern.
  4. Add unit test mocking write + invalidation.

Acceptance criteria

  • Claim waits for receipt before clearing pending state.
  • Wallet stats refetch after successful claim.
  • Button disabled during pending write (existing claimWritePending).
  • ArenaCharmCredCard.test.tsx covers success path invalidation.

Test plan

Path Command
Frontend cd frontend && npm run typecheck && npm run lint && npm test
Unit ArenaCharmCredCard.test.tsx
Manual Anvil Claim CRED after epoch end → balance updates without full page reload

Attack / abuse test plan

  • Failed/reverted tx must not invalidate stats optimistically.
  • Double-click claim must not fire duplicate txs (button disabled).

Verification criteria

  • Unit test PASS.
  • Manual claim shows updated CRED balance within one poll cycle.
## Parent [Transition Gap Analysis (#342)](https://gitlab.com/PlasticDigits/yieldomega/-/issues/342) — P1 #6. ## Current codebase - `ArenaCharmCredCard.tsx` calls `writeContractAsync({ functionName: "claimCred", … })` with no `waitForTransactionReceipt` and no query invalidation. - `useArenaSaleSession.ts` `claimWarBowFlag` pattern: `writeContractAsync` → `waitForWriteReceipt` → `refetchAll()`. - `ArenaSimplePage.tsx` uses `invalidateArenaWalletStatsQueries` after buys; CRED card does not. ## Why needed After claiming CRED, wallet stats and claimable epoch UI stay stale until next poll — poor transition UX and inconsistent with WarBow flag claim. ## Constraints / guardrails - Mirror established `claimWarBowFlag` receipt + invalidation pattern. - Respect `ChainMismatchWriteBarrier` (already wrapped). - Indexer-first: invalidate wallet stats queries, do not add RPC balance mirrors. ## Relevant files - `frontend/src/pages/arena/ArenaCharmCredCard.tsx` - `frontend/src/pages/arena/useArenaSaleSession.ts` (reference) - `frontend/src/lib/arenaWalletXpOptimistic.ts` (`invalidateArenaWalletStatsQueries`) - `frontend/src/hooks/useWalletStats.ts` ## Recommended direction 1. After `claimCred` tx, `await waitForWriteReceipt(wagmiConfig, { hash })`. 2. Call `invalidateArenaWalletStatsQueries(queryClient)` (and any CRED-specific queries). 3. Surface write errors via existing toast/error pattern. 4. Add unit test mocking write + invalidation. ## Acceptance criteria - [ ] Claim waits for receipt before clearing pending state. - [ ] Wallet stats refetch after successful claim. - [ ] Button disabled during pending write (existing `claimWritePending`). - [ ] `ArenaCharmCredCard.test.tsx` covers success path invalidation. ## Test plan | Path | Command | |------|---------| | Frontend | `cd frontend && npm run typecheck && npm run lint && npm test` | | Unit | `ArenaCharmCredCard.test.tsx` | | Manual Anvil | Claim CRED after epoch end → balance updates without full page reload | ## Attack / abuse test plan - Failed/reverted tx must not invalidate stats optimistically. - Double-click claim must not fire duplicate txs (button disabled). ## Verification criteria - Unit test PASS. - Manual claim shows updated CRED balance within one poll cycle.
PlasticDigits commented 2026-06-25 20:07:49 +00:00 (Migrated from gitlab.com)

mentioned in issue #342

mentioned in issue #342
PlasticDigits commented 2026-06-25 21:59:14 +00:00 (Migrated from gitlab.com)

mentioned in commit da317e1d3d

mentioned in commit da317e1d3d854b4f16007ee22f59def7bf10b9ce
PlasticDigits commented 2026-06-25 21:59:23 +00:00 (Migrated from gitlab.com)

mentioned in merge request !518

mentioned in merge request !518
PlasticDigits commented 2026-06-25 22:23:05 +00:00 (Migrated from gitlab.com)

mentioned in commit 7e8129dc1a

mentioned in commit 7e8129dc1a4d4321f7cd2cb5c13e3778b8c3ee8c
PlasticDigits commented 2026-06-26 07:06:06 +00:00 (Migrated from gitlab.com)

Verification — GitLab #347 (agent:verify)

Result: PASS — claimCred post-transaction lifecycle matches claimWarBowFlag receipt + invalidation pattern on main.

Acceptance criteria

Criterion Result How verified
Claim waits for receipt before clearing pending state PASS executeClaimCred awaits waitForWriteReceipt; ArenaCharmCredCard keeps claimSubmitting until executeClaimCred resolves (claimPending = claimWritePending || claimSubmitting). Playwright: claim button disabled during tx+receipt wait.
Wallet stats refetch after successful claim PASS invalidateArenaWalletStatsQueries(queryClient) called only after receipt.status === "success". Playwright: CRED balance text updated without page reload; indexer claimable_cred → 0 within one poll cycle.
Button disabled during pending write PASS disabled={!claimReady || claimPending || !address}; Playwright asserted disabled immediately after click.
ArenaCharmCredCard.test.tsx covers success path invalidation PASS cd frontend && npm test — 8 tests in ArenaCharmCredCard.test.tsx + 4 in arenaCharmCredClaim.test.ts.

Test plan

Path Command Result
Frontend cd frontend && npm run typecheck && npm run lint && npm test PASS (143 files, 760 tests; 3 pre-existing lint warnings)
Unit ArenaCharmCredCard.test.tsx, arenaCharmCredClaim.test.ts PASS
Manual Anvil Anvil stack + preview (ANVIL_E2E=1) + Playwright mock wallet: claim after epoch 0 end PASS — balance updated without reload; GET /v1/arena/wallet/{addr}/stats claimable_cred cleared

Attack / abuse checks

Check Result How verified
Failed/reverted tx must not invalidate stats PASS Unit: executeClaimCred does not invalidate on reverted receipt (#347) in ArenaCharmCredCard.test.tsx and arenaCharmCredClaim.test.ts
Double-click claim must not fire duplicate txs PASS Button disabled while claimPending; Playwright asserted disabled state on click

Implementation reference (already on main)

  • frontend/src/lib/arenaCharmCredClaim.ts — executeClaimCred: write → receipt → invalidate
  • frontend/src/pages/arena/ArenaCharmCredCard.tsx — wires helper with ChainMismatchWriteBarrier, error toast via friendlyRevertFromUnknown
  • Invariant INV-FRONTEND-347-CLAIM-CRED-LIFECYCLE in docs/testing/invariants-and-business-logic.md
  • Docs: docs/frontend/arena-views.md § claim-cred-post-tx-lifecycle

No repo changes required; closing issue.

## Verification — GitLab #347 (agent:verify) **Result: PASS** — `claimCred` post-transaction lifecycle matches `claimWarBowFlag` receipt + invalidation pattern on `main`. ### Acceptance criteria | Criterion | Result | How verified | |-----------|--------|--------------| | Claim waits for receipt before clearing pending state | **PASS** | `executeClaimCred` awaits `waitForWriteReceipt`; `ArenaCharmCredCard` keeps `claimSubmitting` until `executeClaimCred` resolves (`claimPending = claimWritePending \|\| claimSubmitting`). Playwright: claim button disabled during tx+receipt wait. | | Wallet stats refetch after successful claim | **PASS** | `invalidateArenaWalletStatsQueries(queryClient)` called only after `receipt.status === "success"`. Playwright: CRED balance text updated without page reload; indexer `claimable_cred` → `0` within one poll cycle. | | Button disabled during pending write | **PASS** | `disabled={!claimReady \|\| claimPending \|\| !address}`; Playwright asserted disabled immediately after click. | | `ArenaCharmCredCard.test.tsx` covers success path invalidation | **PASS** | `cd frontend && npm test` — 8 tests in `ArenaCharmCredCard.test.tsx` + 4 in `arenaCharmCredClaim.test.ts`. | ### Test plan | Path | Command | Result | |------|---------|--------| | Frontend | `cd frontend && npm run typecheck && npm run lint && npm test` | **PASS** (143 files, 760 tests; 3 pre-existing lint warnings) | | Unit | `ArenaCharmCredCard.test.tsx`, `arenaCharmCredClaim.test.ts` | **PASS** | | Manual Anvil | Anvil stack + preview (`ANVIL_E2E=1`) + Playwright mock wallet: claim after epoch 0 end | **PASS** — balance updated without reload; `GET /v1/arena/wallet/{addr}/stats` `claimable_cred` cleared | ### Attack / abuse checks | Check | Result | How verified | |-------|--------|--------------| | Failed/reverted tx must not invalidate stats | **PASS** | Unit: `executeClaimCred does not invalidate on reverted receipt (#347)` in `ArenaCharmCredCard.test.tsx` and `arenaCharmCredClaim.test.ts` | | Double-click claim must not fire duplicate txs | **PASS** | Button disabled while `claimPending`; Playwright asserted disabled state on click | ### Implementation reference (already on `main`) - `frontend/src/lib/arenaCharmCredClaim.ts` — `executeClaimCred`: write → receipt → invalidate - `frontend/src/pages/arena/ArenaCharmCredCard.tsx` — wires helper with `ChainMismatchWriteBarrier`, error toast via `friendlyRevertFromUnknown` - Invariant `INV-FRONTEND-347-CLAIM-CRED-LIFECYCLE` in `docs/testing/invariants-and-business-logic.md` - Docs: `docs/frontend/arena-views.md` § claim-cred-post-tx-lifecycle No repo changes required; closing issue.
PlasticDigits (Migrated from gitlab.com) closed this issue 2026-06-26 07:06:13 +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#347
No description provided.