diff --git a/src/hooks/useEthersProvider.ts b/src/hooks/useEthersProvider.ts index fe4d5d8102..dfbd82b602 100644 --- a/src/hooks/useEthersProvider.ts +++ b/src/hooks/useEthersProvider.ts @@ -13,7 +13,7 @@ function clientToProvider(client?: Client, chainId?: number) { } const { chain, transport } = client - const ensAddress = chain.contracts?.ensRegistry?.address + const ensAddress = chain?.contracts?.ensRegistry?.address const network = chain ? { chainId: chain.id, diff --git a/src/hooks/useLogin.tsx b/src/hooks/useLogin.tsx index df2f68a208..9b0a8af4c6 100644 --- a/src/hooks/useLogin.tsx +++ b/src/hooks/useLogin.tsx @@ -71,7 +71,7 @@ const useLogin = (autoLogin = false) => { } catch (error) { const e = new Error('createProfile Error', { cause: error }) e.name = 'createProfile Error' - captureException(e, { extra: { walletAddress, account } }) + captureException(e, { extra: { walletAddress, account }, level: 'warning' }) setProfile({ profile: undefined, isAnonymous, account }) } }, diff --git a/src/index.tsx b/src/index.tsx index efdb0d8048..9a774fa105 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -50,10 +50,29 @@ if (ENV_LEVEL > ENV_TYPE.LOCAL) { environment: 'production', ignoreErrors: ['AbortError'], integrations: [Sentry.browserTracingIntegration(), Sentry.replayIntegration()], - tracesSampleRate: 0.1, + tracesSampleRate: 1.0, normalizeDepth: 5, - replaysSessionSampleRate: 0.1, + replaysSessionSampleRate: 1.0, replaysOnErrorSampleRate: 1.0, + beforeSend(event, hint) { + const error = hint?.originalException as Error + const { name, message } = error + if ( + (name === 'TypeError' && message === 'Load failed') || // Almost come from mobile safari fetch API issues + (name === 'ChunkLoadError' && message.includes('Failed to fetch')) || // https://sentry.io/answers/chunk-load-errors-javascript/ + (name === 'Error' && message.includes('Java object is gone')) || // coming from the WebView to Java bridge in Chrome, something went wrong with Chrome Mobile WebView from some Android devices + (name === 'UnhandledRejection' && message.includes('Non-Error promise rejection captured with value')) || + (name === '' && message.includes('Non-Error promise rejection captured with value')) || // this always happens when a some external library throws an error, checked with all issues in Sentry logs + (name === '' && message.includes('Object captured as promise rejection with keys')) // this always happens when a some external library throws an error, checked with all issues in Sentry logs + ) + return null + + if (name === 'TypeError' && message.includes('Failed to fetch')) { + event.level = 'warning' + } + + return event + }, }) Sentry.setTag('request_id', sentryRequestId) Sentry.setTag('version', TAG) diff --git a/src/state/transactions/reducer.ts b/src/state/transactions/reducer.ts index df7c6e293d..e5c871acfd 100644 --- a/src/state/transactions/reducer.ts +++ b/src/state/transactions/reducer.ts @@ -24,7 +24,7 @@ const initialState: TransactionState = {} const clearOldTransactions = (transactions: GroupedTxsByHash | undefined): GroupedTxsByHash | undefined => { if (!transactions) return undefined const chainTxs = Object.values(transactions ?? {}).filter(Boolean) as TransactionDetails[][] - chainTxs.sort((a, b) => a[0].addedTime - b[0].addedTime) + chainTxs.sort((a, b) => (a[0]?.addedTime || 0) - (b[0]?.addedTime || 0)) const slicedChainTxs = chainTxs.slice(-10).filter(tx => tx[0].addedTime > Date.now() - 7 * 24 * 60 * 60 * 1000) const result = slicedChainTxs.reduce((acc, cur) => ({ ...acc, [cur[0].hash]: cur }), {}) as GroupedTxsByHash return result diff --git a/src/utils/errorMessage.ts b/src/utils/errorMessage.ts index df098f837d..e628cbbb1c 100644 --- a/src/utils/errorMessage.ts +++ b/src/utils/errorMessage.ts @@ -6,14 +6,32 @@ import { capitalizeFirstLetter } from 'utils/string' const matchPatterns = (patterns: string[], error: string) => patterns.some(pattern => error.toLowerCase().includes(pattern.toLowerCase())) +export const knownPatterns = { + insufficient_erc20_balance: 'Insufficient ERC20 balance to pay gas fee', + router_expired: 'An error occurred. Refresh the page and try again.', + already_pending: 'Pending request(s), please approve it in your wallet.', + mintotalamountout: 'An error occurred. Try refreshing the price rate or increase max slippage.', + from_address_mismatch: 'The requested account and/or method has not been authorized by the user.', + insufficient_funds: 'Your current balance falls short of covering the required gas fee.', + swap_failed: + 'An error occurred. Refresh the page and try again. If the issue still persists, it might be an issue with your RPC node settings in Metamask.', + underlying_network_changed: 'Your chain is mismatched, please make sure your wallet is switch to the expected chain.', + user_rejected: 'User rejected the transaction.', + insufficient: 'An error occurred. Please try increasing max slippage.', + permit: 'An error occurred. Invalid Permit Signature.', + burn_amount_exceeds_balance: + 'Insufficient fee rewards amount, try to remove your liquidity without claiming fees for now and you can try to claim it later.', + object_object: 'Something went wrong. Please try again.', +} + function parseKnownPattern(text: string): string | undefined { const error = text?.toLowerCase?.() || '' - if (matchPatterns(['insufficient erc20 balance'], error)) return t`Insufficient ERC20 balance to pay gas fee` + if (matchPatterns(['insufficient erc20 balance'], error)) return knownPatterns.insufficient_erc20_balance - if (!error || error.includes('router: expired')) return t`An error occurred. Refresh the page and try again.` + if (!error || error.includes('router: expired')) return knownPatterns.router_expired - if (matchPatterns(['already pending'], error)) return t`Pending request(s), please approve it in your wallet.` + if (matchPatterns(['already pending'], error)) return knownPatterns.already_pending if ( matchPatterns( @@ -28,7 +46,7 @@ function parseKnownPattern(text: string): string | undefined { error, ) ) - return t`An error occurred. Try refreshing the price rate or increase max slippage.` + return knownPatterns.mintotalamountout if ( matchPatterns( @@ -36,7 +54,7 @@ function parseKnownPattern(text: string): string | undefined { error, ) ) - return t`The requested account and/or method has not been authorized by the user.` + return knownPatterns.from_address_mismatch if ( matchPatterns( @@ -44,25 +62,22 @@ function parseKnownPattern(text: string): string | undefined { error, ) ) - return t`Your current balance falls short of covering the required gas fee.` + return knownPatterns.insufficient_funds - if (matchPatterns(['header not found', 'swap failed'], error)) - return t`An error occurred. Refresh the page and try again. If the issue still persists, it might be an issue with your RPC node settings in Metamask.` + if (matchPatterns(['header not found', 'swap failed'], error)) return knownPatterns.swap_failed - if (matchPatterns(['underlying network changed'], error)) - return t`Your chain is mismatched, please make sure your wallet is switch to the expected chain.` + if (matchPatterns(['underlying network changed'], error)) return knownPatterns.underlying_network_changed - if (didUserReject(error)) return t`User rejected the transaction.` + if (didUserReject(error)) return knownPatterns.user_rejected // classic/elastic remove liquidity error - if (matchPatterns(['insufficient'], error)) return t`An error occurred. Please try increasing max slippage.` + if (matchPatterns(['insufficient'], error)) return knownPatterns.insufficient - if (matchPatterns(['permit'], error)) return t`An error occurred. Invalid Permit Signature.` + if (matchPatterns(['permit'], error)) return knownPatterns.permit - if (matchPatterns(['burn amount exceeds balance'], error)) - return t`Insufficient fee rewards amount, try to remove your liquidity without claiming fees for now and you can try to claim it later.` + if (matchPatterns(['burn amount exceeds balance'], error)) return knownPatterns.burn_amount_exceeds_balance - if (error === '[object Object]') return t`Something went wrong. Please try again.` + if (error === '[object Object]') return knownPatterns.object_object return undefined } diff --git a/src/utils/sentry.ts b/src/utils/sentry.ts index 3744e26c25..cdc092056c 100644 --- a/src/utils/sentry.ts +++ b/src/utils/sentry.ts @@ -4,7 +4,7 @@ import { Deferrable } from 'ethers/lib/utils' import { didUserReject } from 'constants/connectors/utils' -import { friendlyError } from './errorMessage' +import { friendlyError, knownPatterns } from './errorMessage' export enum ErrorName { LimitOrderError = 'LimitOrderError', @@ -34,8 +34,14 @@ export function captureSwapError(error: TransactionError) { ? 'returnAmountIsNotEnough' : 'other' + const level = Object.keys(knownPatterns) + .map(key => knownPatterns[key]) + .includes(friendlyErrorResult) + ? 'warning' + : 'error' + captureException(e, { - level: 'fatal', + level, extra: { rawData: error.rawData }, tags: { type: tag,