Skip to content

fix(connect): harden intent handling — validate params, implement receive, scope DM auto-approve (A1-A3, #609)#381

Merged
KruGoL merged 5 commits into
mainfrom
fix/connect-intent-validation
Jun 18, 2026
Merged

fix(connect): harden intent handling — validate params, implement receive, scope DM auto-approve (A1-A3, #609)#381
KruGoL merged 5 commits into
mainfrom
fix/connect-intent-validation

Conversation

@KruGoL

@KruGoL KruGoL commented Jun 18, 2026

Copy link
Copy Markdown
Collaborator

What

Hardens the wallet's Connect intent handling so dApp intents are validated and handled correctly instead of silently hanging, crashing, granting-then-rejecting, or over-permitting.

Part of #609 — tracks A1 / A2 / A3.

A1 — param validation & clean rejection (ConnectIntentHandler.tsx)

  • Centralized validateIntent() + a one-shot effect that rejects the pending intent before any modal renders.
  • W3sign_message no longer crashes the render when message is missing (was undefined.match() in the render body); missing/invalid messageINVALID_PARAMS.
  • W4 / W5send / payment_request now require a canonical 64-hex coinId (same check as mint); missing/non-hex → INVALID_PARAMS. The old default symbol 'UCT' never matched the 64-hex Asset.coinId, causing a silent hang — removed (no symbol resolution, no symbol default).
  • W6dm validates to/messageINVALID_PARAMS.
  • W1 (UX) — unsupported intents (create_invoiceset_auto_return) reject immediately with METHOD_NOT_FOUND ("not supported by this wallet") instead of granting then USER_REJECTED, matching the gate decision that invoicing is experimental and not enabled (#609).

A2 — implement receive (ConnectIntentHandler.tsx)

  • The receive intent now fetches pending incoming transfers via sphere.payments.receive() behind a confirmation modal, and resolves with { transfers } (previously rejected as unsupported).

A3 — scope DM auto-approve to the approved recipient (ConnectIntentHandler.tsx, ConnectProvider.tsx, ConnectContext.ts)

  • After the user enables "send DMs without confirmation", the auto-approve is now scoped to the recipient they approved. A DM to any other recipient falls back to the normal confirmation modal (the auto-handler returns null) instead of being sent silently; the message param is validated.
  • Adds a null ("declined → show modal") return to the auto-intent handler contract.

Behavior change

send / payment_request now require a canonical 64-hex coinId (consistent with mint). Omitting it or passing a symbol like 'UCT' returns INVALID_PARAMS instead of the previous silent hang. The dApp-facing docs (#609 track B1) will be updated to state coinId is the 64-hex id.

Not in this PR (tracked separately)

  • W8 (approval-modal setState during render) → A4 (separate PR).
  • Invoicing stays disabled by decision (#609).
  • Residual: a valid-but-not-held coinId still opens the send modal on the asset-selection step (the user can cancel → USER_REJECTED).

Verification

  • tsc -b → clean (exit 0).
  • eslint . → 0 errors (only 3 pre-existing warnings in an unrelated file ExplorePage.tsx).

KruGoL added 4 commits June 18, 2026 22:19
… of hang/crash

- Add centralized validateIntent + resolveCoinId helpers in ConnectIntentHandler.
- Reject INVALID_PARAMS for malformed send/payment_request/dm/sign_message instead
  of opening a modal that silently hangs or (sign_message) crashes the render.
- Resolve coinId symbol->64-hex via TokenRegistry so the default 'UCT' no longer
  fails to match held assets (was a guaranteed silent hang).
- Unsupported intents (receive + the 9 invoice intents) now return METHOD_NOT_FOUND
  immediately instead of granting then rejecting with USER_REJECTED.

Part of #609 (track A1): wallet-side intent-handling bugs W1/W3/W4/W5/W6.
…sleading 'UCT' fallback

The `resolveCoinId(...) ?? 'UCT'` tail could (only in theory — validateIntent already
rejects an unresolvable coinId upstream) ship a non-canonical symbol as coinId. Replace
it with a single resolve into a local + null guard, and extract DEFAULT_COIN.
…ution & default

coinId is always 64-hex (same contract as the mint intent, and the form Asset.coinId
uses), so the symbol-resolution helper and the 'UCT' symbol default were unnecessary
and a foot-gun. validateIntent now rejects a non-hex/missing coinId with INVALID_PARAMS
(matching mint), and send/payment_request pass params.coinId through unchanged.
Removes resolveCoinId and DEFAULT_COIN.
…proved recipient

- A2: implement the `receive` intent via sphere.payments.receive() with a confirmation modal.
- A3: DM auto-approve is now scoped to the recipient the user approved; a DM to any
  other recipient falls back to the normal confirmation modal (the auto-handler returns
  null) instead of being sent silently, and the message param is validated. Adds a
  null (declined) return to the auto-intent handler contract (ConnectProvider/Context).

Part of #609 (tracks A2, A3).
@KruGoL KruGoL changed the title fix(connect): validate intent params up front; reject cleanly instead of hang/crash (A1, #609) fix(connect): harden intent handling — validate params, implement receive, scope DM auto-approve (A1-A3, #609) Jun 18, 2026
… intents

Connect `send` and `payment_request` intents now take `amount` in BASE UNITS
(smallest indivisible unit) — a positive integer string, like the `mint` intent
and the token engine/SDK — instead of a whole-token decimal. This matches every
major wallet (MetaMask, Phantom, WalletConnect): a dApp-requested transfer
amount travels in base units (exact integers, no float), with human<->base
conversion done at the dApp's UI edge.

Both intents are now CONFIRM-ONLY (approve/reject), modeled on the `mint` intent,
instead of reusing the editable manual SendModal/SendPaymentRequestModal:

- IntentConfirmModal — shared confirm-only shell (BaseModal + header + buttons)
- SendIntentModal / PaymentRequestIntentModal — the base-unit amount is shown via
  formatAmount (one-way, display only) and handed to the SDK verbatim; the amount
  is fixed (not user-editable) — a different amount would be a different request
- SendIntentModal shows an insufficient-balance warning and disables confirm
- validateIntent now rejects non-integer / non-positive amounts for
  send/payment_request

The manual SendModal/SendPaymentRequestModal are no longer used by the intent
handler; they remain for the wallet's own send / payment-request flows.
@KruGoL KruGoL merged commit 1653759 into main Jun 18, 2026
7 checks passed
@KruGoL KruGoL deleted the fix/connect-intent-validation branch June 18, 2026 23:55
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.

2 participants