From 9d47965e6071436aefdda933b12584ad5538bd74 Mon Sep 17 00:00:00 2001 From: Alexander Khrushkov Date: Fri, 19 Jun 2026 03:16:40 +0300 Subject: [PATCH 1/2] fix(connect): reject intent on send failure, human-readable errors, drop dead prefill MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three Connect follow-ups after send/payment_request moved to base-units confirm-only modals: - Send/payment_request confirm modals now REJECT the dApp intent on failure (TRANSFER_FAILED / INTERNAL_ERROR) instead of leaving it hanging — the dApp is informed and the wallet's global query handler toasts the error. - getErrorMessage sanitizes raw backend error pages: an HTML / 5xx response (e.g. a gateway "503 Service Unavailable") is shown as "Service temporarily unavailable. Try again later" instead of dumping raw HTML into the UI. - Drop the now-dead prefill/asModal props (+ SendPrefill / PaymentRequestPrefill types and the prefill effects) from SendModal/SendPaymentRequestModal: the Connect intents moved to their own confirm-only modals and no caller passes them. Manual send / payment-request flows are unaffected. --- .../connect/ConnectIntentHandler.tsx | 2 + src/components/connect/IntentConfirmModal.tsx | 9 ++-- .../connect/PaymentRequestIntentModal.tsx | 15 +++---- src/components/connect/SendIntentModal.tsx | 16 +++---- src/components/wallet/L3/modals/SendModal.tsx | 44 ++----------------- .../L3/modals/SendPaymentRequestModal.tsx | 43 ++---------------- src/sdk/errors.ts | 25 ++++++++++- 7 files changed, 50 insertions(+), 104 deletions(-) diff --git a/src/components/connect/ConnectIntentHandler.tsx b/src/components/connect/ConnectIntentHandler.tsx index aa33b087..fb48de95 100644 --- a/src/components/connect/ConnectIntentHandler.tsx +++ b/src/components/connect/ConnectIntentHandler.tsx @@ -109,6 +109,7 @@ export function ConnectIntentHandler() { coinId={params.coinId as string} memo={params.memo as string | undefined} onResolve={() => resolveIntent({ success: true })} + onReject={(message) => rejectIntent(ERROR_CODES.TRANSFER_FAILED, message)} onCancel={handleClose} /> ); @@ -123,6 +124,7 @@ export function ConnectIntentHandler() { coinId={params.coinId as string} message={params.message as string | undefined} onResolve={(requestId) => resolveIntent({ success: true, requestId })} + onReject={(message) => rejectIntent(ERROR_CODES.INTERNAL_ERROR, message)} onCancel={handleClose} /> ); diff --git a/src/components/connect/IntentConfirmModal.tsx b/src/components/connect/IntentConfirmModal.tsx index bdca96d3..36e38933 100644 --- a/src/components/connect/IntentConfirmModal.tsx +++ b/src/components/connect/IntentConfirmModal.tsx @@ -9,8 +9,6 @@ interface IntentConfirmModalProps { icon: LucideIcon; /** Body content (the amount/recipient card). */ children: ReactNode; - /** Inline error shown above the action buttons. */ - error?: string | null; /** Disables the confirm button (e.g. insufficient balance). */ confirmDisabled?: boolean; /** True while the underlying action is running. */ @@ -32,12 +30,15 @@ interface IntentConfirmModalProps { * amount input here. This matches how every major wallet treats a * dApp-requested transfer (MetaMask, Phantom, WalletConnect): the amount * travels in base units and the wallet only formats it for display. + * + * On failure the consumer rejects the intent (so the dApp is informed) and the + * wallet's global query handler toasts the error — so this shell renders no + * inline error. */ export function IntentConfirmModal({ title, icon, children, - error, confirmDisabled, busy, confirmLabel, @@ -52,8 +53,6 @@ export function IntentConfirmModal({
{children} - {error &&
{error}
} -