Skip to content

feat(frontend): fix and harden order recovery from coordinator after reload - #228

Open
dykdee wants to merge 1 commit into
karagozemin:masterfrom
dykdee:feat/frontend-order-recovery
Open

feat(frontend): fix and harden order recovery from coordinator after reload#228
dykdee wants to merge 1 commit into
karagozemin:masterfrom
dykdee:feat/frontend-order-recovery

Conversation

@dykdee

@dykdee dykdee commented Jul 30, 2026

Copy link
Copy Markdown

Summary

The frontend already attempted to recover pending/refundable swaps from the coordinator on mount (TransactionHistory.tsx), but the request was silently broken: it queried /api/orders/history?eth=...&stellar=..., while the coordinator's route (coordinator/src/server/routes/orders.ts) only accepts a single address query param (matched against either side of the order via src_address OR dst_address). Every recovery request hit the address_required 400 path, was swallowed by the catch block, and silently fell back to local-only state — so recovery never actually worked after a reload in production.

This PR fixes that bug and hardens the recovery path per the issue's acceptance criteria.

Changes

  • frontend/src/lib/orderRecovery.ts (new) — small, testable service for order recovery:
    • fetchCoordinatorOrders(apiBase, { ethAddress, stellarAddress }) — issues one correctly-shaped request per connected address (since the coordinator only supports a single address filter), tolerates one side failing, and only throws (triggering the local-cache fallback) when every request fails.
    • mergeTransactions(local, remote) — de-dupes local and recovered orders by hashlock / on-chain order id / tx hash (falling back to id), letting the coordinator's authoritative record win when both sides describe the same order.
    • mapCoordinatorOrderToTransaction, isRealHash, isRealTransaction — moved out of the component so they're independently testable, with hashlock now carried through the mapping.
  • frontend/src/components/TransactionHistory.tsx — wired to the fixed service; refreshFromCoordinator now uses the corrected request shape and mergeTransactions instead of a naive id-only Map.
  • RefundDialog — no changes needed; its refund-eligibility gating already reads onChainOrderId / htlcContractAddress / timelockUnixSeconds off the mapped Transaction, which recovered orders now populate correctly since the request actually succeeds.

Tests added

  • frontend/src/lib/orderRecovery.test.ts (17 tests): mapping coordinator orders → UI transactions, dedup rules across hashlock/order id/tx hash/id, per-address fetch fanout, partial vs. total coordinator failure, fake/demo hash filtering.
  • frontend/src/components/TransactionHistory.test.tsx (4 tests): recovery renders after reconnecting a wallet, duplicate suppression between a locally-pending order and its coordinator-recovered counterpart, fallback to the local cache when the coordinator is unreachable, and no coordinator call when no wallet is connected.

Verification

  • tsc --noEmit — clean
  • vite build — succeeds
  • vitest run (frontend) — all new tests pass (21/21); pre-existing failures in App.test.tsx (5, DeploymentSelfCheck mock issue) are unrelated and present on master prior to this change (verified via git stash).
  • vitest run (coordinator) — http-routes.test.ts (20 tests) passes unmodified; no backend changes were required.

Closes #415

…reload

The frontend already attempted to recover pending/refundable swaps from
the coordinator on mount, but the request was silently broken: it sent
?eth=&stellar= query params while the coordinator's /api/orders/history
route only accepts a single `address` param (matched against either
side of the order). Every recovery request hit the address_required
400 path and fell back to local-only state, so recovery never actually
worked after a reload.

- Extract recovery logic into frontend/src/lib/orderRecovery.ts:
  - fetchCoordinatorOrders() issues one correctly-shaped request per
    connected address, tolerates one side failing, and only throws
    (triggering the local-cache fallback) when every request fails.
  - mergeTransactions() dedupes local and recovered orders by
    hashlock / on-chain order id / tx hash (falling back to id),
    letting the coordinator's authoritative record win on conflicts.
  - mapCoordinatorOrderToTransaction()/isRealHash()/isRealTransaction()
    moved out of the component so they're independently testable.
- Wire TransactionHistory.tsx to the fixed service.
- Add unit tests for mapping, dedup rules, per-address fetch fanout,
  partial/total coordinator failure, and fake-hash filtering.
- Add component tests covering: recovery after reconnecting a wallet,
  duplicate suppression between local and recovered orders, and
  fallback to the local cache when the coordinator is unreachable.

Closes #415
@vercel

vercel Bot commented Jul 30, 2026

Copy link
Copy Markdown

@dykdee is attempting to deploy a commit to the karagoz's projects Team on Vercel.

A member of the Team first needs to authorize it.

@dykdee

dykdee commented Jul 31, 2026

Copy link
Copy Markdown
Author

Hello repo maintainer, please can you approve the workflows quickly? the issue is fixed but you have not attended to the case.

@dykdee

dykdee commented Aug 2, 2026

Copy link
Copy Markdown
Author

Hi repo maintainer, please can you check that this issue is resolved and close it ASAP.

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.

1 participant