fix(trade): preserve deep-link notices across Layout keyed-Outlet remount (#358) #865

Merged
PlasticDigits merged 1 commit from fix/358-trade-notice-layout-remount into main 2026-06-11 15:34:00 +00:00
PlasticDigits commented 2026-06-11 15:26:59 +00:00 (Migrated from gitlab.com)

Summary

Fixes #358: unknown/invalid /trade/:pairAddr deep-link notices were wiped when TradePage navigated to bare /trade, because Layout.tsx keys <Outlet> on location.pathname (#138/#182) and remounts the route subtree.

Change: carry notices in router location.state (invalidPair / unknownPair) via navigate('/trade', { replace: true, state: … }) instead of component useState. State survives the pathname-keyed remount; auto-pick stays blocked while a notice is present.

Acceptance checklist

Criterion Verification Result
Unknown valid-format pair deep link parks at /trade with notice npm test -- --run src/pages/TradePage.test.tsx — keeps pair-not-found notice after Layout keyed-Outlet remount PASS
Invalid pair deep link parks at /trade with notice Same suite — keeps invalid pair link notice after Layout keyed-Outlet remount PASS
Auto-pick does not snap to pairs[0] while notice active Layout-parity tests assert pathname stays /trade, not /trade/<PAIR> PASS
Existing #175/#176 flat-router regressions unchanged npm test -- --run src/pages/TradePage.test.tsx src/utils/__tests__/tradePairRoute.test.ts (31 tests) PASS
#357 known-pair deep link unaffected keeps non-default deep link after factory pairs resolve in same suite PASS
Docs/skills updated for location-state pattern docs/frontend.md, skills/AGENTS_FRONTEND_TRADE_INVALID_PAIR_LINK.md PASS

Third-party verification

cd frontend-dapp
npm test -- --run src/pages/TradePage.test.tsx src/utils/__tests__/tradePairRoute.test.ts

Manual (with make dev + deploy env):

  1. Open /trade/terra1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx in a fresh tab → URL settles on /trade, Pair not found notice visible, pair selector empty, no workspace.
  2. Open /trade/lilwayne%20babyyy → URL settles on /trade, Invalid pair link notice visible.
  3. Open /trade/<known-non-default-factory-pair> → stays on requested pair (#357).

Closes #358 when merged.


Note

Low Risk
Localized trade routing UX fix with tests and docs; no auth, payments, or contract changes.

Overview
Fixes #358: invalid and unknown /trade/:pairAddr deep links still replace the URL with bare /trade, but Invalid pair link / Pair not found alerts no longer vanish when Layout remounts the route via <Outlet key={location.pathname} />.

TradePage drops local useState for those notices and instead reads invalidPair / unknownPair from location.state on navigate('/trade', { replace: true, state: … }). Dismiss clears state with navigate(..., { state: null }). Auto-pick and workspace gating still key off the same notice presence.

tradePairRoute.ts adds TradePageLocationState plus getTradePageInvalidLinkNotice / getTradePageUnknownPairNotice. Tests add layout-parity routing (keyed Outlet shell) and unit coverage for the state helpers; docs and skills document the location-state pattern.

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

## Summary Fixes [#358](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/358): unknown/invalid `/trade/:pairAddr` deep-link notices were wiped when `TradePage` navigated to bare `/trade`, because [`Layout.tsx`](frontend-dapp/src/components/common/Layout.tsx) keys `<Outlet>` on `location.pathname` (#138/#182) and remounts the route subtree. **Change:** carry notices in **router `location.state`** (`invalidPair` / `unknownPair`) via `navigate('/trade', { replace: true, state: … })` instead of component `useState`. State survives the pathname-keyed remount; auto-pick stays blocked while a notice is present. ## Acceptance checklist | Criterion | Verification | Result | |-----------|--------------|--------| | Unknown valid-format pair deep link parks at `/trade` with notice | `npm test -- --run src/pages/TradePage.test.tsx` — `keeps pair-not-found notice after Layout keyed-Outlet remount` | PASS | | Invalid pair deep link parks at `/trade` with notice | Same suite — `keeps invalid pair link notice after Layout keyed-Outlet remount` | PASS | | Auto-pick does not snap to `pairs[0]` while notice active | Layout-parity tests assert pathname stays `/trade`, not `/trade/<PAIR>` | PASS | | Existing #175/#176 flat-router regressions unchanged | `npm test -- --run src/pages/TradePage.test.tsx src/utils/__tests__/tradePairRoute.test.ts` (31 tests) | PASS | | #357 known-pair deep link unaffected | `keeps non-default deep link after factory pairs resolve` in same suite | PASS | | Docs/skills updated for location-state pattern | `docs/frontend.md`, `skills/AGENTS_FRONTEND_TRADE_INVALID_PAIR_LINK.md` | PASS | ## Third-party verification ```bash cd frontend-dapp npm test -- --run src/pages/TradePage.test.tsx src/utils/__tests__/tradePairRoute.test.ts ``` Manual (with `make dev` + deploy env): 1. Open `/trade/terra1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx` in a fresh tab → URL settles on `/trade`, **Pair not found** notice visible, pair selector empty, no workspace. 2. Open `/trade/lilwayne%20babyyy` → URL settles on `/trade`, **Invalid pair link** notice visible. 3. Open `/trade/<known-non-default-factory-pair>` → stays on requested pair (#357). Closes #358 when merged. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Localized trade routing UX fix with tests and docs; no auth, payments, or contract changes. > > **Overview** > Fixes **#358**: invalid and unknown `/trade/:pairAddr` deep links still **replace** the URL with bare `/trade`, but **Invalid pair link** / **Pair not found** alerts no longer vanish when `Layout` remounts the route via `<Outlet key={location.pathname} />`. > > **TradePage** drops local `useState` for those notices and instead reads **`invalidPair` / `unknownPair` from `location.state`** on `navigate('/trade', { replace: true, state: … })`. Dismiss clears state with `navigate(..., { state: null })`. Auto-pick and workspace gating still key off the same notice presence. > > **`tradePairRoute.ts`** adds `TradePageLocationState` plus `getTradePageInvalidLinkNotice` / `getTradePageUnknownPairNotice`. Tests add **layout-parity** routing (keyed `Outlet` shell) and unit coverage for the state helpers; **docs** and **skills** document the location-state pattern. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit a52c9e2ec6f76d281792a918b06b37734e53045a. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
ghost1 commented 2026-06-11 15:27:08 +00:00 (Migrated from gitlab.com)

changed the description

changed the description
ghost1 commented 2026-06-11 15:27:19 +00:00 (Migrated from gitlab.com)

changed the description

changed the description
PlasticDigits commented 2026-06-11 15:29:15 +00:00 (Migrated from gitlab.com)

Security review

Commit reviewed: a52c9e2ec6f76d281792a918b06b37734e53045a
Scope: Trade deep-link notice persistence via React Router location.state (TradePage.tsx, tradePairRoute.ts, tests, docs/skills).

Outcome: FINDINGS: 0 medium+

Summary

Reviewed attacker-controlled input paths for the new invalidPair / unknownPair location-state carriers:

  1. Normal flow — state is written only by TradePage after getInvalidTradePairRouteParam / getUnknownTradePairRouteParam / indexer-not-found handling on /trade/:pairAddr. Same validation as the prior useState approach; only the persistence layer changed.
  2. Direct /trade visits — location.state is not URL-serializable; cold loads cannot receive forged notice payloads from an external link without same-origin script execution (out of scope for this diff).
  3. Rendering sink — InvalidPairLinkNotice / PairNotFoundLinkNotice interpolate notice text in JSX text nodes (no dangerouslySetInnerHTML). React escaping applies; invalid segments may contain arbitrary characters from the path segment, but this predates the MR and is not worsened by location state.
  4. Authorization / funds — notices gate workspace mount and auto-pick via shouldShowTradeWorkspace / shouldAutoPickDefaultTradePair; they do not set pairAddr or contract call targets. No authn/authz, secret, SSRF, or dependency changes in this diff.

No inline threads — nothing to flag at medium or higher severity.

Security review: no medium+ findings on this diff.

## Security review **Commit reviewed:** `a52c9e2ec6f76d281792a918b06b37734e53045a` **Scope:** Trade deep-link notice persistence via React Router `location.state` (`TradePage.tsx`, `tradePairRoute.ts`, tests, docs/skills). **Outcome:** `FINDINGS: 0` medium+ ### Summary Reviewed attacker-controlled input paths for the new `invalidPair` / `unknownPair` location-state carriers: 1. **Normal flow** — state is written only by `TradePage` after `getInvalidTradePairRouteParam` / `getUnknownTradePairRouteParam` / indexer-not-found handling on `/trade/:pairAddr`. Same validation as the prior `useState` approach; only the persistence layer changed. 2. **Direct `/trade` visits** — `location.state` is not URL-serializable; cold loads cannot receive forged notice payloads from an external link without same-origin script execution (out of scope for this diff). 3. **Rendering sink** — `InvalidPairLinkNotice` / `PairNotFoundLinkNotice` interpolate notice text in JSX text nodes (no `dangerouslySetInnerHTML`). React escaping applies; invalid segments may contain arbitrary characters from the path segment, but this predates the MR and is not worsened by location state. 4. **Authorization / funds** — notices gate workspace mount and auto-pick via `shouldShowTradeWorkspace` / `shouldAutoPickDefaultTradePair`; they do not set `pairAddr` or contract call targets. No authn/authz, secret, SSRF, or dependency changes in this diff. No inline threads — nothing to flag at medium or higher severity. **Security review: no medium+ findings on this diff.**
PlasticDigits commented 2026-06-11 15:34:01 +00:00 (Migrated from gitlab.com)

mentioned in commit cf275720ef

mentioned in commit cf275720eff7b4df98455fc25762fabd2d4cbeba
PlasticDigits (Migrated from gitlab.com) merged commit cf275720ef into main 2026-06-11 15:34:01 +00:00
Brouie commented 2026-06-12 03:37:17 +00:00 (Migrated from gitlab.com)

mentioned in issue #358

mentioned in issue #358
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!865
No description provided.