Skip to content

[Bug] Swap dialog closes & resets on backdrop click during active/submitted swap #110

@illithics

Description

@illithics

Summary

Clicking outside the swap dialog (on the dark backdrop) while a swap is in the submitted state (actively tracking on-chain progress) closes the dialog and resets all user inputs and swap state — including amounts, selected assets, and the live txid tracker.

Why This Is Bad UX

The state reset is particularly harmful to users. If someone accidentally clicks outside the window, they lose:

  • Their selected assets (from/to)
  • Their entered amounts
  • The live swap progress tracker (txid, confirmations, stage progress)

They must then rebuild the entire transaction from scratch — re-select assets, re-enter the amount, re-request a quote — with no way to recover the previous inputs. This is especially frustrating for cross-chain swaps where amounts and routing matter, and during high-traffic periods where re-quoting may return a worse rate.

Root Cause

In SwapDialog, the backdrop close handler guards against accidental closure during signing, broadcasting, and approving states — but not submitted:

ft = useCallback(() => {
  if (b === "signing" || b === "broadcasting" || b === "approving") return; // protected
  onClose();                  // closes dialog
  setTimeout(resetAllState, 200); // ← nukes txid, assets, amounts, everything
}, [b, onClose, reset])

Once the swap is broadcast and tracking begins (b === "submitted"), backdrop clicks go through unblocked.

State Protection Gap

State Backdrop close blocked?
signing ✅ Yes
broadcasting ✅ Yes
approving ✅ Yes
submitted No — Bug
input / quoting / review ✅ Acceptable

Steps to Reproduce

  1. Open swap, select assets, enter amount, get quote
  2. Confirm swap on device
  3. While the progress tracker is visible (Stage 1 → Protocol → Stage 2)
  4. Click anywhere on the dark backdrop outside the modal
  5. Result: Dialog closes, all state resets, swap tracking is gone

Suggested Fix

Add "submitted" to the protected states:

- if (b === "signing" || b === "broadcasting" || b === "approving") return;
+ if (b === "signing" || b === "broadcasting" || b === "approving" || b === "submitted") return;

Or: only reset user inputs on explicit "New Swap" / "Done" button actions — never on backdrop dismiss.

Note: The firmware-flash dialog in the same codebase already handles this correctly: onClick={n === "flashing" ? undefined : closeHandler}

Additional Notes

  • The swap itself still processes on-chain — no funds are lost
  • The Activity panel will eventually reflect the result, but live progress tracking is gone
  • Tested on v1.2.15 macOS

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions