fix(frontend): in-flight TX link during Confirming phase (#330) #810

Merged
PlasticDigits merged 1 commit from cursor/gitlab-issue-workflow-e2b9 into main 2026-06-05 14:20:13 +00:00
PlasticDigits commented 2026-06-05 13:54:17 +00:00 (Migrated from gitlab.com)

Summary

Closes the GitLab #305 follow-up in #330.

On fast LocalTerra, pollTx often finished before React painted the confirming-phase TX: explorer link. Root cause: useTerraBroadcastMutation updates phase/hash from an async mutationFn, so React 18 batches setState until after pollTx resolves and onSettled clears state.

Fix: wrap phase/hash updates in flushSync so confirming + pendingTxHash commit before pollTx awaits.

Also adds data-testid="terra-broadcast-pending-tx", unit tests, and a Playwright tx spec that delays LCD /cosmos/tx/v1beta1/txs/* polling for stable CI observation.

Acceptance checklist

Criterion Verification Result
During confirming, TX: link visible on Swap with correct hash TerraBroadcastPendingLink + flushSync in hook; unit + E2E spec PASS (unit) / SKIP (E2E — LocalTerra not running on agent VM)
Link hidden during signing / broadcasting and after settle TerraBroadcastPendingLink.test.tsx PASS
Playwright asserts Confirming… + TX: (slow-poll mock) e2e/terra-broadcast-confirming-link-tx.spec.ts SKIP (needs make test-e2e-tx)
terraBroadcast phase-order unit tests make test-frontend (includes terraBroadcast.test.ts) PASS
#305 can close after merge + verify Manual / E2E on QA Pending merge

Commands run

npm run test:run -- src/hooks/__tests__/useTerraBroadcastMutation.test.tsx \
  src/components/ui/__tests__/TerraBroadcastPendingLink.test.tsx \
  src/services/terraclassic/__tests__/terraBroadcast.test.ts
make test-frontend   # 854 tests passed

Third-party verification

  1. make setup-cloud-localterra (or existing LocalTerra deploy)
  2. make dev → swap with Simulated Wallet; during confirming, TX: link appears under the button
  3. make test-e2e-tx (or npm run test:e2e:tx with deploy env) — includes terra-broadcast-confirming-link-tx.spec.ts

Note

Low Risk
Localized UX/state-timing fix in the broadcast hook and test hooks; no auth, payment, or contract logic changes.

Overview
Fixes a race on fast LocalTerra where the swap Confirming… state and TX: explorer link never appeared because React 18 batched phase / pendingTxHash updates from the async mutation until after pollTx finished and state was cleared.

useTerraBroadcastMutation now wraps onPhaseChange state updates in flushSync so confirming phase and hash commit before the poll await, making the in-flight link observable in the UI.

TerraBroadcastPendingLink gains data-testid="terra-broadcast-pending-tx" on the link/span for stable test targeting. New Vitest coverage asserts link visibility only in confirming and hook behavior while pollTx is pending; a Playwright tx spec delays **/cosmos/tx/v1beta1/txs/** responses so CI can assert Confirming… plus the TX: link during poll.

Reviewed by Cursor Bugbot for commit ea15444973. Bugbot is set up for automated code reviews on this repo. Configure here.

## Summary Closes the GitLab #305 follow-up in [#330](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/work_items/330). On fast LocalTerra, `pollTx` often finished before React painted the confirming-phase **TX:** explorer link. Root cause: `useTerraBroadcastMutation` updates phase/hash from an **async** `mutationFn`, so React 18 batches `setState` until after `pollTx` resolves and `onSettled` clears state. **Fix:** wrap phase/hash updates in `flushSync` so confirming + `pendingTxHash` commit before `pollTx` awaits. Also adds `data-testid="terra-broadcast-pending-tx"`, unit tests, and a Playwright tx spec that delays LCD `/cosmos/tx/v1beta1/txs/*` polling for stable CI observation. ## Acceptance checklist | Criterion | Verification | Result | |-----------|--------------|--------| | During `confirming`, **TX:** link visible on Swap with correct hash | `TerraBroadcastPendingLink` + `flushSync` in hook; unit + E2E spec | PASS (unit) / SKIP (E2E — LocalTerra not running on agent VM) | | Link hidden during `signing` / `broadcasting` and after settle | `TerraBroadcastPendingLink.test.tsx` | PASS | | Playwright asserts `Confirming…` + `TX:` (slow-poll mock) | `e2e/terra-broadcast-confirming-link-tx.spec.ts` | SKIP (needs `make test-e2e-tx`) | | `terraBroadcast` phase-order unit tests | `make test-frontend` (includes `terraBroadcast.test.ts`) | PASS | | #305 can close after merge + verify | Manual / E2E on QA | Pending merge | ## Commands run ```bash npm run test:run -- src/hooks/__tests__/useTerraBroadcastMutation.test.tsx \ src/components/ui/__tests__/TerraBroadcastPendingLink.test.tsx \ src/services/terraclassic/__tests__/terraBroadcast.test.ts make test-frontend # 854 tests passed ``` ## Third-party verification 1. `make setup-cloud-localterra` (or existing LocalTerra deploy) 2. `make dev` → swap with Simulated Wallet; during confirming, **TX:** link appears under the button 3. `make test-e2e-tx` (or `npm run test:e2e:tx` with deploy env) — includes `terra-broadcast-confirming-link-tx.spec.ts` ## Related - Parent UX: #305 - Issue: https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/work_items/330 <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Localized UX/state-timing fix in the broadcast hook and test hooks; no auth, payment, or contract logic changes. > > **Overview** > Fixes a race on fast LocalTerra where the swap **Confirming…** state and **TX:** explorer link never appeared because React 18 batched `phase` / `pendingTxHash` updates from the async mutation until after `pollTx` finished and state was cleared. > > **`useTerraBroadcastMutation`** now wraps `onPhaseChange` state updates in **`flushSync`** so confirming phase and hash commit before the poll await, making the in-flight link observable in the UI. > > **`TerraBroadcastPendingLink`** gains **`data-testid="terra-broadcast-pending-tx"`** on the link/span for stable test targeting. New **Vitest** coverage asserts link visibility only in `confirming` and hook behavior while `pollTx` is pending; a **Playwright** tx spec delays `**/cosmos/tx/v1beta1/txs/**` responses so CI can assert **Confirming…** plus the **TX:** link during poll. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit ea154449735b3177b81d993239c49beb22f56114. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
PlasticDigits commented 2026-06-05 13:54:24 +00:00 (Migrated from gitlab.com)

changed the description

changed the description
PlasticDigits commented 2026-06-05 13:54:24 +00:00 (Migrated from gitlab.com)

mentioned in issue #330

mentioned in issue #330
ghost1 commented 2026-06-05 13:54:27 +00:00 (Migrated from gitlab.com)

changed the description

changed the description
ghost1 commented 2026-06-05 13:54:33 +00:00 (Migrated from gitlab.com)

changed the description

changed the description
PlasticDigits commented 2026-06-05 13:55:36 +00:00 (Migrated from gitlab.com)

Security review

Commit reviewed: ea154449735b3177b81d993239c49beb22f56114
Scope: 5 files — useTerraBroadcastMutation.ts (flushSync on phase/hash state), TerraBroadcastPendingLink.tsx (data-testid), unit tests, Playwright E2E spec.

Outcome: FINDINGS: 0 medium+

Summary: This MR is a React 18 batching/timing fix so the confirming-phase TX explorer link paints before pollTx settles. No new auth boundaries, network endpoints, secrets, or dependency changes. pendingTxHash still originates from wallet.broadcastTx() via broadcastTerraExecuteContracts (unchanged trust model). Explorer URLs are still built by getExplorerTxUrl() with a fixed base prefix; link text uses React JSX escaping and existing rel="noopener noreferrer". flushSync is invoked only on discrete phase transitions (signing → broadcasting → confirming), not in a hot loop — no plausible DoS path introduced.

Inline threads: none (no medium+ findings on this diff).

## Security review **Commit reviewed:** `ea154449735b3177b81d993239c49beb22f56114` **Scope:** 5 files — `useTerraBroadcastMutation.ts` (`flushSync` on phase/hash state), `TerraBroadcastPendingLink.tsx` (`data-testid`), unit tests, Playwright E2E spec. **Outcome:** `FINDINGS: 0` medium+ **Summary:** This MR is a React 18 batching/timing fix so the confirming-phase TX explorer link paints before `pollTx` settles. No new auth boundaries, network endpoints, secrets, or dependency changes. `pendingTxHash` still originates from `wallet.broadcastTx()` via `broadcastTerraExecuteContracts` (unchanged trust model). Explorer URLs are still built by `getExplorerTxUrl()` with a fixed base prefix; link text uses React JSX escaping and existing `rel="noopener noreferrer"`. `flushSync` is invoked only on discrete phase transitions (signing → broadcasting → confirming), not in a hot loop — no plausible DoS path introduced. **Inline threads:** none (no medium+ findings on this diff).
PlasticDigits (Migrated from gitlab.com) merged commit c1e523960c into main 2026-06-05 14:20:13 +00:00
PlasticDigits commented 2026-06-05 14:20:15 +00:00 (Migrated from gitlab.com)

mentioned in commit c1e523960c

mentioned in commit c1e523960cd1d53caa923f539828f076fa5f5bb3
PlasticDigits commented 2026-06-08 08:43:13 +00:00 (Migrated from gitlab.com)

mentioned in commit 0a7fe6208f

mentioned in commit 0a7fe6208f9701ed54eb76af400cbefd46ba5da7
PlasticDigits commented 2026-06-08 13:42:27 +00:00 (Migrated from gitlab.com)

mentioned in commit f0d3841345

mentioned in commit f0d38413452e130379210f4f1b5233c519a41b73
Sign in to join this conversation.
No reviewers
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/cl8y-dex-terraclassic!810
No description provided.