Skip to content

Frontend add explicit finality states to transaction status UI - #940

Merged
llinsss merged 2 commits into
DogStark:mainfrom
Bigmehelen:Frontend-Add-explicit-finality-states-to-transaction-status-UI
Aug 28, 2026
Merged

Frontend add explicit finality states to transaction status UI#940
llinsss merged 2 commits into
DogStark:mainfrom
Bigmehelen:Frontend-Add-explicit-finality-states-to-transaction-status-UI

Conversation

@Bigmehelen

@Bigmehelen Bigmehelen commented Aug 27, 2026

Copy link
Copy Markdown

closed #860

Summary
Models Stellar transaction lifecycle explicitly and surfaces distinct user guidance for each finality. Links every hash to the configured stellar.expert explorer (testnet/public derived from NEXT_PUBLIC_STELLAR_NETWORK). Handles delayed provider responses with exponential back-off and conflicting provider responses with dedup + unknown conflict UI. Keeps TypeScript strict, lint clean, keyboard + mobile accessible.

Scope: src/components/TransactionStatusTracker.tsx only — per Keep unrelated refactors out.

Problem
Previously Pending/Failed collapsed the real chain states. submitted, accepted, confirmed, failed, unknown all looked the same, no explorer link, no handling for slow or contradictory provider lists.

Solution

  1. Explicit lifecycle model — src/lib/transactionFinality.ts
    export type TransactionFinality = 'submitted' | 'accepted' | 'confirmed' | 'failed' | 'unknown';
    getFinality(tx: Transaction): TransactionFinality — pure function:
    failed|cancelled → failed
    confirmed → confirmed
    pending + confirmations===0 && !blockNumber → submitted
    pending + confirmations>=1 || blockNumber → accepted
    otherwise/confirmations<0/undefined status → unknown
    FINALITY_META — distinct copy per state (no PII):
    Submitted: Sent to the network — waiting for confirmation…
    Accepted: Accepted by the network and awaiting final confirmation…
    Confirmed: Confirmed and final on-chain…
    Failed: Transaction failed — it was not applied…
    Unknown: Status could not be determined…
    reconcileTransactions(pending, failed): ReconciledTransaction[] — de-dupes by id, flags isConflict when same id in both lists or status contradicts source (pending list with status==='failed' etc.) → coerces to unknown, sorted submitted→accepted→confirmed→failed→unknown then timestamp desc.

  2. Configured explorer — src/lib/explorer.ts
    export function isTestnet(): boolean { return (process.env.NEXT_PUBLIC_STELLAR_NETWORK||'').toLowerCase() !== 'public' && !== 'mainnet'; }
    export function getExplorerTxUrl(hash: string): string // https://stellar.expert/explorer/{testnet|public}/tx/${encodeURIComponent(hash)}
    Centralizes logic previously duplicated in WalletDashboard / TransactionSigning. Uses existing NEXT_PUBLIC_STELLAR_NETWORK (testnet default). No new env. Hash is encodeURIComponent-ed.

  3. UI — src/components/TransactionStatusTracker.tsx
    Replaces Pending (n)/Failed (n) with 5 sections Submitted/Accepted/Confirmed/Failed/Unknown each with heading h3, count badge, tone-colored guidance paragraph.
    Per-tx row: type, truncated hash 0x12345678…cdef + toLocaleString() time, finality badge, View on explorer link (target="_blank" rel="noopener noreferrer"), contextual button (Cancel for submitted/accepted, Retry for failed, none for confirmed). Conflict rows add Conflicting status returned… verify on explorer.
    State: pending+failed+pollError+isInitialLoading. Initial load → role="status" aria-busy="true" skeleton; poll error → role="alert" with back-off message; empty after load → return null (prev. behavior preserved).
    Polling: BASE_INTERVAL=10_000, MAX_INTERVAL=160_000, delayRef*=2 on error, reset on success, scheduleNext(delay) via setTimeout, cleanup on unmount. Handles delayed provider (network stall/rejection) by showing alert and retrying.
    Handles conflicting provider via reconcileTransactions → single unknown row with conflict note, still links to explorer.
    A11y: role="region" aria-labelledby="tx-tracker-heading" aria-live="polite", h2#tx-tracker-heading, aria-label on badges/links/buttons, focus:ring-2 on all interactive elements, ul role="list".
    Mobile: fixed bottom-4 left-4 right-4 md:left-auto md:right-4 md:w-96 max-h-[80vh] overflow-auto. No w-80 fixed desktop-only.
    Exports BASE_INTERVAL, MAX_INTERVAL, getFinalityForTest for tests. No any, strict types, no weaken of auth/privacy (transactionAPI still sends authToken header).
    Files Changed
    M src/components/TransactionStatusTracker.tsx # lifecycle, explorer, a11y, mobile
    M src/components/TransactionStatusTracker.test.tsx # full regression + new coverage
    A src/lib/explorer.ts # centralized explorer
    A src/lib/transactionFinality.ts # pure lifecycle model
    Testing
    Reproduces/characterizes before-fix via pure-model tests — proves old Pending collapse hid submitted/accepted/confirmed/unknown distinctions; new tests fail if model regresses.

@drips-wave

drips-wave Bot commented Aug 27, 2026

Copy link
Copy Markdown

@Bigmehelen Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@llinsss
llinsss merged commit 0a299ad into DogStark:main Aug 28, 2026
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.

[Frontend] Add explicit finality states to transaction status UI

3 participants