From 418068b359725e72cb383058daf15e9755d9b353 Mon Sep 17 00:00:00 2001 From: Artur Sapek Date: Fri, 20 Sep 2024 16:35:32 -0400 Subject: [PATCH] Add `getHelpUrl` config where users can go with error logs (#2685) * optional supportUrl & error log copy button * dont freak out when user rejects in their wallet * remove g flag because it behaves funny * get help bro * the * more generic property name * dont say help twice --- wormhole-connect/src/config/ui.ts | 3 + wormhole-connect/src/utils/errors.ts | 9 +-- .../v2/Bridge/ReviewTransaction/SendError.tsx | 72 +++++++++++++++++++ .../v2/Bridge/ReviewTransaction/index.tsx | 48 +++++++------ 4 files changed, 107 insertions(+), 25 deletions(-) create mode 100644 wormhole-connect/src/views/v2/Bridge/ReviewTransaction/SendError.tsx diff --git a/wormhole-connect/src/config/ui.ts b/wormhole-connect/src/config/ui.ts index e45b4ded2..11c81ce03 100644 --- a/wormhole-connect/src/config/ui.ts +++ b/wormhole-connect/src/config/ui.ts @@ -20,6 +20,8 @@ export type UiConfig = { walletConnectProjectId?: string; showHamburgerMenu: boolean; previewMode?: boolean; // Disables making transfers + + getHelpUrl?: string; }; export interface DefaultInputs { @@ -85,6 +87,7 @@ export function createUiConfig(customConfig: Partial): UiConfig { pageHeader: customConfig?.pageHeader, pageSubHeader: customConfig?.pageSubHeader, menu: customConfig?.menu ?? [], + getHelpUrl: customConfig?.getHelpUrl, searchTx: customConfig?.searchTx, moreTokens: customConfig?.moreTokens, moreChains: customConfig?.moreChains, diff --git a/wormhole-connect/src/utils/errors.ts b/wormhole-connect/src/utils/errors.ts index f24cce770..b7cefa00b 100644 --- a/wormhole-connect/src/utils/errors.ts +++ b/wormhole-connect/src/utils/errors.ts @@ -13,10 +13,11 @@ import { Chain } from '@wormhole-foundation/sdk'; // TODO SDKV2 // attempt to capture errors using regex -export const INSUFFICIENT_ALLOWANCE_REGEX = - /[I|i]nsufficient token allowance/gm; -export const USER_REJECTED_REGEX = - /rejected the request|[R|r]ejected from user|user cancel|aborted by user/gm; +export const INSUFFICIENT_ALLOWANCE_REGEX = /insufficient token allowance/im; +export const USER_REJECTED_REGEX = new RegExp( + 'user rejected|rejected the request|rejected from user|user cancel|aborted by user', + 'mi', +); export function interpretTransferError( e: any, diff --git a/wormhole-connect/src/views/v2/Bridge/ReviewTransaction/SendError.tsx b/wormhole-connect/src/views/v2/Bridge/ReviewTransaction/SendError.tsx new file mode 100644 index 000000000..e8fa87036 --- /dev/null +++ b/wormhole-connect/src/views/v2/Bridge/ReviewTransaction/SendError.tsx @@ -0,0 +1,72 @@ +import React, { useState } from 'react'; +import { makeStyles } from 'tss-react/mui'; +import config from 'config'; +import AlertBannerV2 from 'components/v2/AlertBanner'; +import { copyTextToClipboard } from 'utils'; +import { Box, Typography } from '@mui/material'; +import CopyIcon from '@mui/icons-material/ContentCopy'; +import DoneIcon from '@mui/icons-material/Done'; + +type Props = { + humanError?: string; + internalError?: any; +}; + +const useStyles = makeStyles()((theme: any) => ({ + copyIcon: { + fontSize: '14px', + }, + doneIcon: { + fontSize: '14px', + color: theme.palette.success.main, + }, +})); + +export default ({ humanError, internalError }: Props) => { + const { classes } = useStyles(); + + const [justCopied, setJustCopied] = useState(false); + + if (humanError === undefined) { + return null; + } + + const getHelp = + internalError && internalError.message && config.ui.getHelpUrl ? ( + + Having trouble?{' '} + { + copyTextToClipboard(internalError.message); + setJustCopied(true); + setTimeout(() => setJustCopied(false), 3000); + }} + > + Copy the error logs{' '} + {justCopied ? ( + + ) : ( + + )} + + {' and '} + + ask for help + + . + + ) : null; + + return ( + + + {getHelp} + + ); +}; diff --git a/wormhole-connect/src/views/v2/Bridge/ReviewTransaction/index.tsx b/wormhole-connect/src/views/v2/Bridge/ReviewTransaction/index.tsx index c624e9204..4c620a209 100644 --- a/wormhole-connect/src/views/v2/Bridge/ReviewTransaction/index.tsx +++ b/wormhole-connect/src/views/v2/Bridge/ReviewTransaction/index.tsx @@ -11,7 +11,6 @@ import IconButton from '@mui/material/IconButton'; import { getTokenDetails } from 'telemetry'; import { Context } from 'sdklegacy'; -import AlertBannerV2 from 'components/v2/AlertBanner'; import Button from 'components/v2/Button'; import config from 'config'; import { RoutesConfig } from 'config/routes'; @@ -42,6 +41,8 @@ import { RelayerFee } from 'store/relay'; import { amount as sdkAmount } from '@wormhole-foundation/sdk'; import { toDecimals } from 'utils/balance'; import { useUSDamountGetter } from 'hooks/useUSDamountGetter'; +import SendError from './SendError'; +import { ERR_USER_REJECTED } from 'telemetry/types'; const useStyles = makeStyles()((theme) => ({ container: { @@ -71,7 +72,10 @@ const ReviewTransaction = (props: Props) => { const mobile = useMediaQuery(theme.breakpoints.down('sm')); - const [sendError, setSendError] = useState(''); + const [sendError, setSendError] = useState(undefined); + const [sendErrorInternal, setSendErrorInternal] = useState( + undefined, + ); const routeContext = useContext(RouteContext); @@ -112,7 +116,7 @@ const ReviewTransaction = (props: Props) => { : undefined; const send = async () => { - setSendError(''); + setSendError(undefined); // Pre-check of required values if ( @@ -160,6 +164,7 @@ const ReviewTransaction = (props: Props) => { } } catch (e) { setSendError('Error validating transfer'); + setSendErrorInternal(e); console.error(e); return; } @@ -276,21 +281,27 @@ const ReviewTransaction = (props: Props) => { dispatch(setSendTx(txId)); dispatch(setRedeemRoute(route)); dispatch(setAppRoute('redeem')); - setSendError(''); + setSendError(undefined); } catch (e: any) { - console.error('Wormhole Connect: error completing transfer', e); - const [uiError, transferError] = interpretTransferError(e, sourceChain); - // Show error in UI - setSendError(uiError); - - // Trigger transfer error event to integrator - config.triggerEvent({ - type: 'transfer.error', - error: transferError, - details: transferDetails, - }); + if (transferError.type === ERR_USER_REJECTED) { + // User intentionally rejected in their wallet. This is not an error in the sense + // that something went wrong. + } else { + console.error('Wormhole Connect: error completing transfer', e); + + // Show error in UI + setSendError(uiError); + setSendErrorInternal(e); + + // Trigger transfer error event to integrator + config.triggerEvent({ + type: 'transfer.error', + error: transferError, + details: transferDetails, + }); + } } finally { dispatch(setIsTransactionInProgress(false)); } @@ -383,12 +394,7 @@ const ReviewTransaction = (props: Props) => { disabled={isGasSliderDisabled} /> - + {confirmTransactionButton} );