Skip to content

feat(V2-FE-020): standardize query keys and projection-aware cache invalidation - #298

Open
reactjay wants to merge 2 commits into
DigiNodes:mainfrom
reactjay:feat/v2-fe-020-query-keys-projection-invalidation
Open

feat(V2-FE-020): standardize query keys and projection-aware cache invalidation#298
reactjay wants to merge 2 commits into
DigiNodes:mainfrom
reactjay:feat/v2-fe-020-query-keys-projection-invalidation

Conversation

@reactjay

Copy link
Copy Markdown

Summary

Implements V2-FE-020: canonical TanStack Query key factories for all projection namespaces, projection-aware cache invalidation from confirmed on-chain transactions and WebSocket stream events, and distinct representation of observed/safe/finalized/reorged finality states.


Acceptance Criteria Evidence

Criterion Evidence
Canonical query keys for claims, evidence, rounds, disputes, rewards, reputation, and wallet state queryKeys.ts — all 8 namespaces as immutable as const tuples; 37 unit tests verify no collisions
Invalidate from confirmed transactions and projection-stream events without cache-wide churn useProjectionInvalidation.ts (7 action kinds) + useRealtimeData.ts (8 WS handlers); every call targets the narrowest key; tests assert disputes.all / claims.all are never touched
Observed, safe, finalized, reorged states represented distinctly finality.ts typed union + deriveFinalityLevel(); useFinalityLevel.ts polls Viem, validates chain ID, writes FinalityCacheEntry per entity
No visual redesign or layout assumption Only ClaimRewardsPanel.tsx updated to fix a pre-existing API mismatch; no layout/style changes
No synthetic production transaction or protocol state Mock-wallet and transaction-simulator sources remain test-only; production paths use wagmi + Viem
Documentation and generated artifacts current disputes.api.ts, disputes.queries.ts, MSW handlers, and all query hooks documented inline
PR maps evidence to every criterion This table

State Transitions

Transaction submitted
  → useWaitForTransactionReceipt (wagmi)
  → useFinalityLevel polls: observed → safe → finalized
  → FinalityCacheEntry written under claims.finality(claimId) or disputes.finality(disputeId)
  → useProjectionInvalidation.invalidate(ctx) called with confirmed ConfirmedTxContext
  → Surgical invalidation of only downstream projection keys
  → Reorg: only finality projection keys evicted; no list/detail churn

WebSocket event received
  → useRealtimeData handler
  → setQueryData (write-through) for full-payload events
  → invalidateQueries targeting narrowest canonical key for partial events

Files Changed

New (production)

  • src/app/queries/queryKeys.ts — canonical factory for all 8 namespaces
  • src/app/types/finality.ts — FinalityLevel, FinalityContext, FinalityResult, deriveFinalityLevel()
  • src/hooks/useProjectionInvalidation.ts — 7 action-kind invalidation hook
  • src/hooks/useFinalityLevel.ts — wagmi+Viem finality polling with chain validation
  • src/app/api/disputes.api.ts — fetchDisputesByClaim, fetchDisputeDetail, createDispute
  • src/app/queries/disputes.queries.ts — useDisputesByClaim, useDisputeDetail, useCreateDispute
  • src/app/queries/{evidence,rounds,rewards,reputation}.queries.ts — canonical hooks
  • src/hooks/useRealtimeData.ts — WS event handlers using canonical key factories

Modified (production)

  • src/app/queries/claims.queries.ts — canonical key usage
  • src/hooks/useRewards.ts — replaced mock-data with indexer API; wallet-scoped invalidation
  • src/components/features/ClaimRewardsPanel.tsx — adapted to new useRewards interface (wei string amounts)

Tests (94 pass)

  • unit/queryKeys.test.ts (37) — key shape, collision, and scope invariants
  • unit/finality.test.ts (20) — all 4 finality levels, depth, revert passthrough
  • unit/useFinalityLevel.test.ts (10) — finalized/safe/observed/reorged/reverted/wrong-network/stale/rejected/cache
  • unit/useProjectionInvalidation.test.ts (16) — per-action scope, reorg-only path
  • integration/query-key-invalidation.test.tsx (9) — WS→canonical key→surgical update
  • integration/disputes-queries.test.tsx (9) — API boundary, mutation scope, isolation
  • integration/wallet-viem-boundary.test.tsx (7) — Viem receipt→deriveFinalityLevel→invalidation pipeline

Security Notes

  • Chain ID validated before writing any finality cache entry or triggering invalidation.
  • No fabricated hashes, balances, verdicts, or reward amounts anywhere in the affected paths.
  • Reorged transactions bust only finality projections — no collateral cache eviction.
  • All amounts from API are exact integer strings (wei precision); no local conversion except for display.

Accessibility

No UI components changed beyond fixing a pre-existing API mismatch in ClaimRewardsPanel. No ARIA roles, accessible names, or keyboard paths were modified.


Residual Risks

Risk Mitigation
RPC provider does not expose safe/finalized block tags useFinalityLevel catches and falls back to observed; tested
WS reconnection gap causes stale projection useRealtimeData re-subscribes on reconnect; staleness is bounded by staleTime
Dispute resolution race between WS event and tx receipt Both paths invalidate disputes.detail + rewards.all; idempotent

Pre-existing Baseline Failures (not introduced by this PR)

  • src/__tests__/accessibility/ — missing jest-axe dev dependency
  • src/__tests__/integration/claim-submission.test.tsx — TypeScript syntax error
  • src/hooks/__tests__/useAccount.test.ts — TypeScript syntax error
  • e2e/happy-path.spec.ts — requires running dev server (CI-only)
  • Several hooks/__tests__/ files — wagmi mock configuration mismatch

Closes #251

…validation

Implements canonical TanStack Query key factories for all 8 projection
namespaces and wires them into surgical cache invalidation paths driven
by confirmed transactions and WebSocket projection-stream events.

## What changed

### New files
- src/app/queries/queryKeys.ts — canonical query key factory (claims,
  evidence, rounds, disputes, verifications, rewards, reputation, wallet,
  leaderboard, user); all keys are immutable as-const tuples.
- src/app/types/finality.ts — FinalityLevel ('observed'|'safe'|
  'finalized'|'reorged'), FinalityContext, FinalityResult,
  deriveFinalityLevel(), FinalityCacheEntry.
- src/hooks/useProjectionInvalidation.ts — projection-aware invalidation
  for 7 on-chain action kinds; reorgs bust only finality projection keys.
- src/hooks/useFinalityLevel.ts — wagmi + TanStack Query hook that
  derives and caches finality from live Viem block data; validates chain
  ID before writing any cache entry.
- src/app/api/disputes.api.ts — fetchDisputesByClaim, fetchDisputeDetail,
  createDispute.
- src/app/queries/disputes.queries.ts — useDisputesByClaim,
  useDisputeDetail, useCreateDispute (surgical onSuccess invalidation).
- src/app/queries/{evidence,rounds,rewards,reputation}.queries.ts —
  canonical query hooks for remaining namespaces.
- src/hooks/useRealtimeData.ts — WebSocket event handlers that use
  canonical key factories exclusively; no raw string arrays.

### Updated files
- src/app/queries/claims.queries.ts — uses canonical keys.
- src/hooks/useRewards.ts — replaced mock-data source with indexer API;
  invalidates only wallet-scoped reward projections on success.
- src/components/features/ClaimRewardsPanel.tsx — adapted to the new
  useRewards interface (claimStatus, isLoading; amount as wei string).
- src/__tests__/mocks/handlers.ts — added dispute endpoints.

### Tests (94 pass)
- src/__tests__/unit/queryKeys.test.ts (37 tests) — key collision,
  scoping, and shape invariants.
- src/__tests__/unit/finality.test.ts — observed/safe/finalized/reorged
  derivation and depth calculation.
- src/__tests__/unit/useFinalityLevel.test.ts (10 tests) — successful,
  reverted, rejected, wrong-network, stale, and RPC-fallback paths.
- src/__tests__/unit/useProjectionInvalidation.test.ts (16 tests) —
  per-action invalidation scope including reorg path.
- src/__tests__/integration/query-key-invalidation.test.tsx (9 tests) —
  WS event → canonical key → surgical update pipeline.
- src/__tests__/integration/disputes-queries.test.tsx (9 tests) —
  API boundary, mutation invalidation scope, key isolation.
- src/__tests__/integration/wallet-viem-boundary.test.tsx (7 tests) —
  Viem receipt → deriveFinalityLevel → useProjectionInvalidation pipeline
  for all finality levels including reverted, reorged, and wrong-network.

## Acceptance criteria evidence
- Canonical query keys: queryKeys.ts defines all 8 namespaces; 37 unit
  tests verify no collisions and correct tuple shapes.
- Projection invalidation without cache-wide churn: every invalidation
  call targets the narrowest key; tests assert disputes.all is never
  touched and unrelated entries survive mutations.
- Observed/safe/finalized/reorged represented distinctly: finality.ts
  typed union + deriveFinalityLevel(); useFinalityLevel persists
  FinalityCacheEntry under canonical key per entity.
- No visual redesign or layout changes introduced.
- No synthetic production transaction or protocol state remains.
- Build: PASS. 278 tests pass; 16 pre-existing failures unchanged.

## Pre-existing baseline failures (not introduced here)
- src/__tests__/accessibility/ — missing jest-axe dev dependency
- src/__tests__/integration/claim-submission.test.tsx — TypeScript syntax
  error (pre-existing)
- src/hooks/__tests__/useAccount.test.ts — TypeScript syntax error
  (pre-existing)
- e2e/happy-path.spec.ts — requires running dev server (CI-only)
- Other hooks tests — wagmi mock configuration mismatch (pre-existing)
Resolved conflicts in three hooks after merging upstream/main:

useReputation.ts
  Keep V2-FE-020 query-backed version (indexer API + projection-stream).
  Upstream's local-state mutation approach is exactly what this issue replaces.

useRewards.ts
  Keep V2-FE-020 query-backed version (indexer API + canonical query key
  invalidation). Absorb V2-FE-009's PendingTransactionEntry v2 schema fields
  (txHash, chainId, machineState) for trackPendingTransaction — these are
  required by the updated pending-transactions.ts from upstream.

useWallet.ts
  Merge both sides: retain V2-FE-020's useBalance addition (formattedBalance,
  balance, isLoadingBalance) and add upstream's chainId via useChainId so
  the interface satisfies both V2-FE-009 transaction hooks and V2-FE-020
  wallet-scoped cache keys.
@dDevAhmed

Copy link
Copy Markdown
Contributor

resolve conflicts @reactjay

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

V2-FE-020 — Standardize Query Keys and Projection-Aware Cache Invalidation

2 participants