Skip to content

feat: browser swap page (PR #2 of browser-swap roadmap)#86

Open
LandynDev wants to merge 2 commits into
testfrom
feat/browser-swap
Open

feat: browser swap page (PR #2 of browser-swap roadmap)#86
LandynDev wants to merge 2 commits into
testfrom
feat/browser-swap

Conversation

@LandynDev
Copy link
Copy Markdown
Collaborator

Implements PR #2 of the browser-swap roadmap (docs/swap-api/pr-roadmap.md). Replaces the blurred /swap placeholder with a real Uniswap-style swap form wired to the new FastAPI swap-api service.

Summary

  • WalletProvider context with two production-wired adapters (Polkadot.js / Unisat) and detection-only stubs for Talisman, SubWallet, Xverse, Leather. Connect dialog auto-detects installed extensions.
  • useSwapFlow state machine drives the full lifecycle: idle → awaitingReserveSig → reserving → awaitingSend → sending → awaitingConfirmSig → confirming → watching → done with explicit error and RateChanged branches.
  • usePendingSwap persists intent + signature + swap id in localStorage keyed on (fromAddress, miner, blockAnchor) — see spec §4c. Cross-tab BroadcastChannel arbitration is parked for v2.
  • ClaimSlashedButton signs allways.claim_slash extrinsic directly via @polkadot/api when the connected Substrate wallet matches the swap's user_address; falls back to a CLI hint otherwise. Wired into SwapDetailPage for TIMED_OUT swaps where slash is still pending.
  • MySwapsPage reuses useAllSwaps({ search }) filtered by the connected wallet address. Top nav surfaces "My Swaps" only when a wallet is connected.
  • RateChangedDialog handles 409 RateChanged responses; user re-accepts the new rate and the flow restarts in place.
  • Adds VITE_SWAP_API_URL (default http://localhost:8000) and VITE_SUBTENSOR_WS_URL to .env.example.

Dependencies

  • Requires allways PR #311 (feat/swap-api) running locally for the UI to function. The roadmap calls this out as ordering: PR ff main #1 must land before this can merge.
  • Adds @polkadot/extension-dapp, @polkadot/api, @polkadot/util-crypto, @polkadot/types. All lazy-loaded — none import at app bootstrap.

Spec coverage

  • §4 — architecture decisions (zero-tolerance rate, client-side keys, optional Substrate for BTC source)
  • §5 — full lifecycle (reserve → send → confirm → watch, plus claim)
  • §6 — service contracts (/healthz, /chains, /miners, /miners/best, /proofs/reserve, /proofs/confirm, POST /reserve, POST /confirm)
  • §7 — wallet matrix (Polkadot.js + Unisat fully wired; others detection-only with "coming soon")

v2 deferrals

  • BroadcastChannel ownership arbitration for multi-tab safety (storage events keep tabs roughly in sync today).
  • Xverse / Leather full adapters (stubbed behind detection so they show in the modal disabled).
  • TAO-source sendFunds via @polkadot/api extrinsic build — error reads "use the CLI" for now; PR Feat/realtime dashboard #2 ships BTC-source happy path.

Test plan

  • npm run build (~6 s; one chunk-size warning from polkadot deps)
  • npm run lint
  • npm run dev boots clean (96 ms), GET / returns 200
  • Manual browser drill (localhost:9080/swap) with full dev-env + swap-api on :8000
  • Connect Polkadot.js + Unisat in dev, execute BTC→TAO end-to-end to COMPLETED
  • Reload mid-flow — confirm localStorage resume works
  • Force on-chain rate change between quote and submit — confirm RateChangedDialog appears
  • Force a slash, click Claim on the detail page, verify extrinsic lands

Replaces the blurred /swap teaser with a working swap form, wallet connect,
state-machine-driven reserve/send/confirm flow, progress UI, browser-side
claim button, and a My Swaps tab. Consumes the swap-api FastAPI service
introduced in allways#311.

- WalletProvider with Polkadot.js (full) and Unisat (full); Xverse/Leather
  surface as detection-only stubs until v2.
- useSwapFlow state machine: idle → awaitingReserveSig → reserving →
  awaitingSend → sending → awaitingConfirmSig → confirming → watching →
  done. RateChangedDialog handles 409 responses with re-accept.
- usePendingSwap persists intent + signatures + swap id in localStorage
  keyed by (fromAddress, miner, blockAnchor) so a refresh doesn't lose
  context. Cross-tab BroadcastChannel arbitration deferred to v2.
- ClaimSlashedButton signs the claim_slash extrinsic directly via
  @polkadot/api when the connected Substrate wallet matches the swap user;
  falls back to a CLI hint otherwise.
- Adds VITE_SWAP_API_URL (default http://localhost:8000) and
  VITE_SUBTENSOR_WS_URL to .env.example.

Refs spec §4–§7 and roadmap PR #2.
… source

Cross-repo review surfaced three problems in the browser-swap PR:

1. Xverse and Leather were shipped as `stubAdapter` that throws on every
   call. The connect dialog showed them as buttons with "(coming soon)".
   Better to ship Unisat as the BTC adapter and let v2 add others when
   the signing/sending surfaces actually exist. Removes ~30 lines of
   apologetic facade.

2. `claimSlash` targeted `api.tx.allways.claimSlash`, but `claim_slash`
   is an ink! contract selector — not a pallet extrinsic. Replace with
   `api.tx.contracts.call(VITE_CONTRACT_ADDRESS, 0, gas, null, data)`
   where `data` is the selector `0xcf3c3dd9` + u64-LE `swap_id`. Same
   path the validator uses server-side via
   `contract_client.exec_contract_raw`. Selector kept in sync with
   `allways/contract_client.py::CONTRACT_SELECTORS`.

3. `STORAGE_KEY = 'allways.pendingSwap'` was a single global slot — a
   second swap silently nuked the first. Key by
   (fromAddress, miner, blockAnchor) per spec §4c so parallel swaps
   coexist; the "pending" pointer is the most-recently-updated entry.

4. TAO-source swaps were gated only inside `sendFunds`, after the
   reservation had already broadcast on-chain — burning a miner slot.
   Add a quote-time guard in `SwapForm` so users hit the "use the CLI"
   message before paying that cost.

Build clean; lint clean.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant