From 69e8bdec3276e35a809912053c27828ab49fc964 Mon Sep 17 00:00:00 2001 From: MK <53529533+magiziz@users.noreply.github.com> Date: Wed, 6 Nov 2024 10:20:25 +0000 Subject: [PATCH] chore: improve error handling (#3188) Co-authored-by: tomiir --- .changeset/bright-plants-speak.md | 24 ++++++++++++++ packages/adapters/ethers/src/client.ts | 2 +- packages/adapters/ethers5/src/client.ts | 2 +- packages/adapters/solana/src/client.ts | 2 +- .../wagmi/src/connectors/AuthConnector.ts | 2 +- packages/appkit-utils/src/ErrorUtil.ts | 20 ++++++++--- packages/appkit-utils/src/LoggerUtil.ts | 7 ++-- .../src/tests/universal-adapter.test.ts | 29 ++++++++++++++-- .../appkit/src/universal-adapter/client.ts | 33 +++++++++++++------ 9 files changed, 97 insertions(+), 24 deletions(-) create mode 100644 .changeset/bright-plants-speak.md diff --git a/.changeset/bright-plants-speak.md b/.changeset/bright-plants-speak.md new file mode 100644 index 0000000000..3ed6111c0c --- /dev/null +++ b/.changeset/bright-plants-speak.md @@ -0,0 +1,24 @@ +--- +'@reown/appkit-adapter-ethers5': patch +'@reown/appkit-adapter-ethers': patch +'@reown/appkit-adapter-solana': patch +'@reown/appkit-adapter-wagmi': patch +'@reown/appkit-utils': patch +'@reown/appkit': patch +'@apps/demo': patch +'@apps/gallery': patch +'@apps/laboratory': patch +'@reown/appkit-adapter-polkadot': patch +'@reown/appkit-cdn': patch +'@reown/appkit-common': patch +'@reown/appkit-core': patch +'@reown/appkit-experimental': patch +'@reown/appkit-polyfills': patch +'@reown/appkit-scaffold-ui': patch +'@reown/appkit-siwe': patch +'@reown/appkit-siwx': patch +'@reown/appkit-ui': patch +'@reown/appkit-wallet': patch +--- + +Improved error handling for auth and universal provider connectors. \ No newline at end of file diff --git a/packages/adapters/ethers/src/client.ts b/packages/adapters/ethers/src/client.ts index e5935910b7..df30e9eb8c 100644 --- a/packages/adapters/ethers/src/client.ts +++ b/packages/adapters/ethers/src/client.ts @@ -1170,7 +1170,7 @@ export class EthersAdapter { this.authProvider = W3mFrameProviderSingleton.getInstance({ projectId, onTimeout: () => { - AlertController.open(ErrorUtil.ALERT_ERRORS.INVALID_APP_CONFIGURATION_SOCIALS, 'error') + AlertController.open(ErrorUtil.ALERT_ERRORS.SOCIALS_TIMEOUT, 'error') } }) diff --git a/packages/adapters/ethers5/src/client.ts b/packages/adapters/ethers5/src/client.ts index 5179ce5b57..d9d2cfe349 100644 --- a/packages/adapters/ethers5/src/client.ts +++ b/packages/adapters/ethers5/src/client.ts @@ -1159,7 +1159,7 @@ export class Ethers5Adapter { this.authProvider = W3mFrameProviderSingleton.getInstance({ projectId, onTimeout: () => { - AlertController.open(ErrorUtil.ALERT_ERRORS.INVALID_APP_CONFIGURATION_SOCIALS, 'error') + AlertController.open(ErrorUtil.ALERT_ERRORS.SOCIALS_TIMEOUT, 'error') } }) diff --git a/packages/adapters/solana/src/client.ts b/packages/adapters/solana/src/client.ts index e438180024..5f4c05b344 100644 --- a/packages/adapters/solana/src/client.ts +++ b/packages/adapters/solana/src/client.ts @@ -631,7 +631,7 @@ export class SolanaAdapter implements ChainAdapter { projectId: opts.projectId, chainId: withSolanaNamespace(this.appKit?.getCaipNetwork(this.chainNamespace)?.id), onTimeout: () => { - AlertController.open(ErrorUtil.ALERT_ERRORS.INVALID_APP_CONFIGURATION_SOCIALS, 'error') + AlertController.open(ErrorUtil.ALERT_ERRORS.SOCIALS_TIMEOUT, 'error') } }) diff --git a/packages/adapters/wagmi/src/connectors/AuthConnector.ts b/packages/adapters/wagmi/src/connectors/AuthConnector.ts index ab58a4646e..56942d8449 100644 --- a/packages/adapters/wagmi/src/connectors/AuthConnector.ts +++ b/packages/adapters/wagmi/src/connectors/AuthConnector.ts @@ -91,7 +91,7 @@ export function authConnector(parameters: AuthParameters) { this.provider = W3mFrameProviderSingleton.getInstance({ projectId: parameters.options.projectId, onTimeout: () => { - AlertController.open(ErrorUtil.ALERT_ERRORS.INVALID_APP_CONFIGURATION_SOCIALS, 'error') + AlertController.open(ErrorUtil.ALERT_ERRORS.SOCIALS_TIMEOUT, 'error') } }) } diff --git a/packages/appkit-utils/src/ErrorUtil.ts b/packages/appkit-utils/src/ErrorUtil.ts index 8c6d2fb87a..a57142d5c7 100644 --- a/packages/appkit-utils/src/ErrorUtil.ts +++ b/packages/appkit-utils/src/ErrorUtil.ts @@ -1,6 +1,13 @@ export const ErrorUtil = { UniversalProviderErrors: { - UNAUTHORIZED_DOMAIN_NOT_ALLOWED: 'Unauthorized: origin not allowed' + UNAUTHORIZED_DOMAIN_NOT_ALLOWED: { + message: 'Unauthorized: origin not allowed', + alertErrorKey: 'INVALID_APP_CONFIGURATION' + }, + JWT_VALIDATION_ERROR: { + message: 'JWT validation error: JWT Token is not yet valid', + alertErrorKey: 'JWT_TOKEN_NOT_VALID' + } }, ALERT_ERRORS: { SWITCH_NETWORK_NOT_FOUND: { @@ -15,12 +22,15 @@ export const ErrorUtil = { isSafe() ? window.origin : 'unknown' } not found on Allowlist - update configuration` }, - INVALID_APP_CONFIGURATION_SOCIALS: { + SOCIALS_TIMEOUT: { shortMessage: 'Invalid App Configuration', longMessage: () => - `Origin ${ - isSafe() ? window.origin : 'unknown' - } not found on Allowlist - update configuration to enable social login` + 'There was an issue loading the embedded wallet. Please verify that your domain is allowed at cloud.reown.com' + }, + JWT_TOKEN_NOT_VALID: { + shortMessage: 'Session Expired', + longMessage: + 'Invalid session found on UniversalProvider - please check your time settings and connect again' }, PROJECT_ID_NOT_CONFIGURED: { shortMessage: 'Project ID Not Configured', diff --git a/packages/appkit-utils/src/LoggerUtil.ts b/packages/appkit-utils/src/LoggerUtil.ts index 71c0d9e66c..4111b0c2e3 100644 --- a/packages/appkit-utils/src/LoggerUtil.ts +++ b/packages/appkit-utils/src/LoggerUtil.ts @@ -1,7 +1,7 @@ import { generatePlatformLogger, getDefaultLoggerOptions } from '@walletconnect/logger' export const LoggerUtil = { - createLogger(onError: (error: Error, ...args: unknown[]) => void, level = 'error') { + createLogger(onError: (error?: Error, ...args: unknown[]) => void, level = 'error') { const loggerOptions = getDefaultLoggerOptions({ level }) @@ -14,11 +14,12 @@ export const LoggerUtil = { for (const arg of args) { if (arg instanceof Error) { onError(arg, ...args) - break + + return } } - onError(new Error(), ...args) + onError(undefined, ...args) } return logger diff --git a/packages/appkit/src/tests/universal-adapter.test.ts b/packages/appkit/src/tests/universal-adapter.test.ts index f12a22b835..c90e03da13 100644 --- a/packages/appkit/src/tests/universal-adapter.test.ts +++ b/packages/appkit/src/tests/universal-adapter.test.ts @@ -4,9 +4,9 @@ import { UniversalAdapterClient } from '../universal-adapter' import { mockOptions } from './mocks/Options' import mockProvider from './mocks/UniversalProvider' import type UniversalProvider from '@walletconnect/universal-provider' -import { ChainController } from '@reown/appkit-core' +import { AlertController, ChainController } from '@reown/appkit-core' import { ProviderUtil } from '../store/index.js' -import { CaipNetworksUtil, ConstantsUtil, PresetsUtil } from '@reown/appkit-utils' +import { CaipNetworksUtil, ConstantsUtil, ErrorUtil, PresetsUtil } from '@reown/appkit-utils' import mockAppKit from './mocks/AppKit' import type { CaipNetwork } from '@reown/appkit-common' @@ -21,6 +21,8 @@ const mockOptionsExtended = { defaultNetwork: mainnet } +vi.mock('@reown/appkit-core') + describe('UniversalAdapter', () => { let universalAdapter: UniversalAdapterClient @@ -205,4 +207,27 @@ describe('UniversalAdapter', () => { ]) }) }) + + describe('UniversalAdapter - Alert Errors', () => { + it('should handle alert errors based on error messages', () => { + const errors = [ + { + alert: ErrorUtil.ALERT_ERRORS.INVALID_APP_CONFIGURATION, + message: + 'Error: WebSocket connection closed abnormally with code: 3000 (Unauthorized: origin not allowed)' + }, + { + alert: ErrorUtil.ALERT_ERRORS.JWT_TOKEN_NOT_VALID, + message: + 'WebSocket connection closed abnormally with code: 3000 (JWT validation error: JWT Token is not yet valid:)' + } + ] + + for (const { alert, message } of errors) { + // @ts-expect-error + universalAdapter.handleAlertError(new Error(message)) + expect(AlertController.open).toHaveBeenCalledWith(alert, 'error') + } + }) + }) }) diff --git a/packages/appkit/src/universal-adapter/client.ts b/packages/appkit/src/universal-adapter/client.ts index 4a90552f2e..ccff9be1a0 100644 --- a/packages/appkit/src/universal-adapter/client.ts +++ b/packages/appkit/src/universal-adapter/client.ts @@ -84,7 +84,7 @@ export class UniversalAdapterClient { public adapterType: AdapterType = 'universal' - public reportErrors = true + public reportedAlertErrors: Record = {} public constructor(options: AppKitOptionsWithCaipNetworks) { const { siweConfig, metadata } = options @@ -405,17 +405,31 @@ export class UniversalAdapterClient { return this.walletConnectProviderInitPromise } - private async initWalletConnectProvider(projectId: string) { - const logger = LoggerUtil.createLogger((err, ...args) => { - if (err.message.includes(ErrorUtil.UniversalProviderErrors.UNAUTHORIZED_DOMAIN_NOT_ALLOWED)) { - if (this.reportErrors) { - AlertController.open(ErrorUtil.ALERT_ERRORS.INVALID_APP_CONFIGURATION, 'error') - this.reportErrors = false - } + private handleAlertError(error: Error) { + const matchedUniversalProviderError = Object.entries(ErrorUtil.UniversalProviderErrors).find( + ([, { message }]) => error.message.includes(message) + ) - return + const [errorKey, errorValue] = matchedUniversalProviderError ?? [] + + const { message, alertErrorKey } = errorValue ?? {} + + if (errorKey && message && !this.reportedAlertErrors[errorKey]) { + const alertError = + ErrorUtil.ALERT_ERRORS[alertErrorKey as keyof typeof ErrorUtil.ALERT_ERRORS] + + if (alertError) { + AlertController.open(alertError, 'error') + this.reportedAlertErrors[errorKey] = true } + } + } + private async initWalletConnectProvider(projectId: string) { + const logger = LoggerUtil.createLogger((error, ...args) => { + if (error) { + this.handleAlertError(error) + } // eslint-disable-next-line no-console console.error(...args) }) @@ -432,7 +446,6 @@ export class UniversalAdapterClient { } this.walletConnectProvider = await UniversalProvider.init(walletConnectProviderOptions) - await this.checkActiveWalletConnectProvider() }