From 95ac83dcc9dfc028a8ac5bf1c0f95a54ca46f6be Mon Sep 17 00:00:00 2001 From: nacho <25931366+ignaciosantise@users.noreply.github.com> Date: Thu, 13 Nov 2025 17:11:20 -0300 Subject: [PATCH 1/6] fix: renew uri after failure --- .../src/views/w3m-connecting-view/index.tsx | 19 +++++-- packages/common/src/types/api/events.ts | 9 ++++ packages/common/src/utils/ErrorUtil.ts | 50 +++++++++++++++++++ 3 files changed, 74 insertions(+), 4 deletions(-) diff --git a/packages/appkit/src/views/w3m-connecting-view/index.tsx b/packages/appkit/src/views/w3m-connecting-view/index.tsx index 3408df30b..12a41e717 100644 --- a/packages/appkit/src/views/w3m-connecting-view/index.tsx +++ b/packages/appkit/src/views/w3m-connecting-view/index.tsx @@ -1,6 +1,6 @@ import { useSnapshot } from 'valtio'; import { useEffect, useLayoutEffect, useState } from 'react'; -import { type Platform } from '@reown/appkit-common-react-native'; +import { ErrorUtil, type Platform } from '@reown/appkit-common-react-native'; import { WcController, ConstantsUtil, @@ -43,26 +43,37 @@ export function ConnectingView() { try { const { wcPairingExpiry } = WcController.state; const { data: routeData } = RouterController.state; - if (retry || CoreHelperUtil.isPairingExpired(wcPairingExpiry)) { + const isPairingExpired = CoreHelperUtil.isPairingExpired(wcPairingExpiry); + if (retry || isPairingExpired) { WcController.setWcError(false); const connectPromise = connect({ wallet: routeData?.wallet }); WcController.setWcPromise(connectPromise); + await connectPromise; } } catch (error) { LogController.sendError(error, 'ConnectingView.tsx', 'initializeConnection'); WcController.setWcError(true); WcController.clearUri(); - SnackController.showError('Declined'); + if (isQr && CoreHelperUtil.isAllowedRetry(lastRetry)) { setLastRetry(Date.now()); initializeConnection(true); } + + const isUserRejected = ErrorUtil.isUserRejectedRequestError(error); + const isProposalExpired = ErrorUtil.isProposalExpiredError(error); + if (!isProposalExpired) { + SnackController.showError( + isUserRejected ? 'User rejected the request' : 'Something went wrong' + ); + } + EventsController.sendEvent({ type: 'track', - event: 'CONNECT_ERROR', + event: isUserRejected ? 'USER_REJECTED' : 'CONNECT_ERROR', properties: { message: (error as Error)?.message ?? 'Unknown' } diff --git a/packages/common/src/types/api/events.ts b/packages/common/src/types/api/events.ts index 8f5453bc8..bbb397dfb 100644 --- a/packages/common/src/types/api/events.ts +++ b/packages/common/src/types/api/events.ts @@ -33,6 +33,7 @@ export type EventName = | 'SELECT_WALLET' | 'CONNECT_SUCCESS' | 'CONNECT_ERROR' + | 'USER_REJECTED' | 'DISCONNECT_SUCCESS' | 'DISCONNECT_ERROR' | 'CLICK_WALLET_HELP' @@ -153,6 +154,14 @@ export type Event = message: string; }; } + | { + type: 'track'; + address?: string; + event: 'USER_REJECTED'; + properties: { + message: string; + }; + } | { type: 'track'; event: 'DISCONNECT_SUCCESS'; diff --git a/packages/common/src/utils/ErrorUtil.ts b/packages/common/src/utils/ErrorUtil.ts index f1d49aa22..9a04e0d26 100644 --- a/packages/common/src/utils/ErrorUtil.ts +++ b/packages/common/src/utils/ErrorUtil.ts @@ -1,4 +1,9 @@ export const ErrorUtil = { + RPC_ERROR_CODE: { + USER_REJECTED_REQUEST: 4001, + USER_REJECTED_METHODS: 5002, + USER_REJECTED: 5000 + } as const, UniversalProviderErrors: { UNAUTHORIZED_DOMAIN_NOT_ALLOWED: { message: 'Unauthorized: origin not allowed', @@ -31,5 +36,50 @@ export const ErrorUtil = { shortMessage: 'Project ID Not Configured', longMessage: 'Project ID Not Configured - update configuration' } + }, + isRpcProviderError(error: any) { + try { + if (typeof error === 'object' && error !== null) { + const objErr = error as Record; + + const hasMessage = typeof objErr['message'] === 'string'; + const hasCode = typeof objErr['code'] === 'number'; + + return hasMessage && hasCode; + } + + return false; + } catch { + return false; + } + }, + isUserRejectedMessage(message: string) { + return ( + message.toLowerCase().includes('user rejected') || + message.toLowerCase().includes('user cancelled') || + message.toLowerCase().includes('user canceled') + ); + }, + isUserRejectedRequestError(error: any) { + if (ErrorUtil.isRpcProviderError(error)) { + const isUserRejectedCode = error.code === ErrorUtil.RPC_ERROR_CODE.USER_REJECTED_REQUEST; + const isUserRejectedMethodsCode = + error?.code === ErrorUtil.RPC_ERROR_CODE.USER_REJECTED_METHODS; + + return ( + isUserRejectedCode || + isUserRejectedMethodsCode || + ErrorUtil.isUserRejectedMessage(error.message) + ); + } + + if (error instanceof Error) { + return ErrorUtil.isUserRejectedMessage(error.message); + } + + return false; + }, + isProposalExpiredError(error: any) { + return error?.message?.includes('expired'); } }; From 5308d3c0c02d7e516c3c689bd66308b6d6f6cf7d Mon Sep 17 00:00:00 2001 From: nacho <25931366+ignaciosantise@users.noreply.github.com> Date: Thu, 13 Nov 2025 17:13:23 -0300 Subject: [PATCH 2/6] chore: changeset --- .changeset/cool-parks-slide.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 .changeset/cool-parks-slide.md diff --git a/.changeset/cool-parks-slide.md b/.changeset/cool-parks-slide.md new file mode 100644 index 000000000..760829f36 --- /dev/null +++ b/.changeset/cool-parks-slide.md @@ -0,0 +1,13 @@ +--- +'@reown/appkit-react-native': patch +'@reown/appkit-common-react-native': patch +'@reown/appkit-bitcoin-react-native': patch +'@reown/appkit-coinbase-react-native': patch +'@reown/appkit-core-react-native': patch +'@reown/appkit-ethers-react-native': patch +'@reown/appkit-solana-react-native': patch +'@reown/appkit-ui-react-native': patch +'@reown/appkit-wagmi-react-native': patch +--- + +fix: renew uri after failure From bd88f3135f4c33167a32db6c08bf868342cec4aa Mon Sep 17 00:00:00 2001 From: nacho <25931366+ignaciosantise@users.noreply.github.com> Date: Thu, 13 Nov 2025 17:28:48 -0300 Subject: [PATCH 3/6] chore: code improvements --- .../w3m-connecting-external-view/index.tsx | 12 ++---- packages/common/src/utils/ErrorUtil.ts | 38 ++++++++++++++++--- 2 files changed, 37 insertions(+), 13 deletions(-) diff --git a/packages/appkit/src/views/w3m-connecting-external-view/index.tsx b/packages/appkit/src/views/w3m-connecting-external-view/index.tsx index a8cb5a03a..08933c90c 100644 --- a/packages/appkit/src/views/w3m-connecting-external-view/index.tsx +++ b/packages/appkit/src/views/w3m-connecting-external-view/index.tsx @@ -26,6 +26,7 @@ import styles from './styles'; import { useInternalAppKit } from '../../AppKitContext'; import { StoreLink } from '../../partials/w3m-connecting-mobile/components/StoreLink'; import { WcHelpersUtil } from '../../utils/HelpersUtil'; +import { ErrorUtil } from '@reown/appkit-common-react-native'; export function ConnectingExternalView() { const { data } = useSnapshot(RouterController.state); @@ -94,16 +95,11 @@ export function ConnectingExternalView() { } } catch (error) { LogController.sendError(error, 'ConnectingExternalView.tsx', 'onConnect'); - if (/(Wallet not found)/i.test((error as Error).message)) { - setErrorType('not_installed'); - } else if (/(rejected)/i.test((error as Error).message)) { - setErrorType('declined'); - } else { - setErrorType('default'); - } + const type = ErrorUtil.categorizeConnectionError(error); + setErrorType(type); EventsController.sendEvent({ type: 'track', - event: 'CONNECT_ERROR', + event: type === 'declined' ? 'USER_REJECTED' : 'CONNECT_ERROR', properties: { message: (error as Error)?.message ?? 'Unknown' } }); } diff --git a/packages/common/src/utils/ErrorUtil.ts b/packages/common/src/utils/ErrorUtil.ts index 9a04e0d26..64affef52 100644 --- a/packages/common/src/utils/ErrorUtil.ts +++ b/packages/common/src/utils/ErrorUtil.ts @@ -1,8 +1,7 @@ export const ErrorUtil = { RPC_ERROR_CODE: { USER_REJECTED_REQUEST: 4001, - USER_REJECTED_METHODS: 5002, - USER_REJECTED: 5000 + USER_REJECTED_METHODS: 5002 } as const, UniversalProviderErrors: { UNAUTHORIZED_DOMAIN_NOT_ALLOWED: { @@ -37,7 +36,7 @@ export const ErrorUtil = { longMessage: 'Project ID Not Configured - update configuration' } }, - isRpcProviderError(error: any) { + isRpcProviderError(error: any): error is { message: string; code: number } { try { if (typeof error === 'object' && error !== null) { const objErr = error as Record; @@ -55,7 +54,7 @@ export const ErrorUtil = { }, isUserRejectedMessage(message: string) { return ( - message.toLowerCase().includes('user rejected') || + message.toLowerCase().includes('rejected') || message.toLowerCase().includes('user cancelled') || message.toLowerCase().includes('user canceled') ); @@ -80,6 +79,35 @@ export const ErrorUtil = { return false; }, isProposalExpiredError(error: any) { - return error?.message?.includes('expired'); + if (ErrorUtil.isRpcProviderError(error)) { + return error.message?.toLowerCase().includes('proposal expired'); + } + + if (error) { + return ( + typeof error?.message === 'string' && + error.message?.toLowerCase().includes('proposal expired') + ); + } + + return false; + }, + isWalletNotFoundError(error: any) { + if (error && typeof error?.message === 'string') { + return /wallet not found/i.test(error.message); + } + + return false; + }, + categorizeConnectionError(error: any): 'not_installed' | 'declined' | 'default' { + if (ErrorUtil.isWalletNotFoundError(error)) { + return 'not_installed'; + } + + if (ErrorUtil.isUserRejectedRequestError(error)) { + return 'declined'; + } + + return 'default'; } }; From 2d7eb364edb4b342d3db4e4c11da8f75759a8fc1 Mon Sep 17 00:00:00 2001 From: nacho <25931366+ignaciosantise@users.noreply.github.com> Date: Thu, 13 Nov 2025 20:17:33 -0300 Subject: [PATCH 4/6] chore: code improvements --- packages/appkit/src/views/w3m-connecting-view/index.tsx | 2 ++ packages/common/src/utils/ErrorUtil.ts | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/appkit/src/views/w3m-connecting-view/index.tsx b/packages/appkit/src/views/w3m-connecting-view/index.tsx index 12a41e717..2e75b11ce 100644 --- a/packages/appkit/src/views/w3m-connecting-view/index.tsx +++ b/packages/appkit/src/views/w3m-connecting-view/index.tsx @@ -61,6 +61,8 @@ export function ConnectingView() { if (isQr && CoreHelperUtil.isAllowedRetry(lastRetry)) { setLastRetry(Date.now()); initializeConnection(true); + + return; } const isUserRejected = ErrorUtil.isUserRejectedRequestError(error); diff --git a/packages/common/src/utils/ErrorUtil.ts b/packages/common/src/utils/ErrorUtil.ts index 64affef52..8295c230d 100644 --- a/packages/common/src/utils/ErrorUtil.ts +++ b/packages/common/src/utils/ErrorUtil.ts @@ -63,7 +63,7 @@ export const ErrorUtil = { if (ErrorUtil.isRpcProviderError(error)) { const isUserRejectedCode = error.code === ErrorUtil.RPC_ERROR_CODE.USER_REJECTED_REQUEST; const isUserRejectedMethodsCode = - error?.code === ErrorUtil.RPC_ERROR_CODE.USER_REJECTED_METHODS; + error.code === ErrorUtil.RPC_ERROR_CODE.USER_REJECTED_METHODS; return ( isUserRejectedCode || From d77095e5915f7a4fab3bf58dd1931cf3e44bae1c Mon Sep 17 00:00:00 2001 From: nacho <25931366+ignaciosantise@users.noreply.github.com> Date: Thu, 13 Nov 2025 21:10:23 -0300 Subject: [PATCH 5/6] chore: adapt screen to large wallet names --- packages/appkit/src/partials/w3m-connecting-body/index.tsx | 4 +++- .../w3m-connecting-mobile/components/StoreLink.tsx | 5 ++++- packages/appkit/src/partials/w3m-header/index.tsx | 7 ++++++- packages/appkit/src/partials/w3m-header/styles.ts | 3 +++ 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/packages/appkit/src/partials/w3m-connecting-body/index.tsx b/packages/appkit/src/partials/w3m-connecting-body/index.tsx index a938594d2..44654da56 100644 --- a/packages/appkit/src/partials/w3m-connecting-body/index.tsx +++ b/packages/appkit/src/partials/w3m-connecting-body/index.tsx @@ -11,7 +11,9 @@ export interface ConnectingBodyProps { export function ConnectingBody({ title, description }: ConnectingBodyProps) { return ( - {title} + + {title} + {description ? ( {description} diff --git a/packages/appkit/src/partials/w3m-connecting-mobile/components/StoreLink.tsx b/packages/appkit/src/partials/w3m-connecting-mobile/components/StoreLink.tsx index c1f72476a..fa4de47d3 100644 --- a/packages/appkit/src/partials/w3m-connecting-mobile/components/StoreLink.tsx +++ b/packages/appkit/src/partials/w3m-connecting-mobile/components/StoreLink.tsx @@ -12,7 +12,7 @@ export function StoreLink({ visible, walletName = 'Wallet', onPress }: StoreLink return ( - + {`Don't have ${walletName}?`}