diff --git a/shared/constants/metametrics.ts b/shared/constants/metametrics.ts index 32d6ea0e6..cbbca92ca 100644 --- a/shared/constants/metametrics.ts +++ b/shared/constants/metametrics.ts @@ -624,6 +624,7 @@ export enum MetaMetricsEventName { SwapCompleted = 'Swap Completed', TransactionFinalized = 'Transaction Finalized', ExitedSwaps = 'Exited Swaps', + SwapError = 'Swap Error', } export enum MetaMetricsEventAccountType { @@ -681,6 +682,10 @@ export enum MetaMetricsEventKeyType { Srp = 'srp', } +export enum MetaMetricsEventErrorType { + InsufficientGas = 'insufficient_gas', +} + export enum MetaMetricsNetworkEventSource { CustomNetworkForm = 'custom_network_form', PopularNetworkList = 'popular_network_list', diff --git a/ui/pages/swaps/prepare-swap-page/prepare-swap-page.js b/ui/pages/swaps/prepare-swap-page/prepare-swap-page.js index 4d8972db6..16a7da28a 100644 --- a/ui/pages/swaps/prepare-swap-page/prepare-swap-page.js +++ b/ui/pages/swaps/prepare-swap-page/prepare-swap-page.js @@ -99,6 +99,7 @@ import { setSwapsErrorKey, setBackgroundSwapRouteState, } from '../../../store/actions'; +import { SET_SMART_TRANSACTIONS_ERROR } from '../../../store/actionConstants'; import { countDecimals, fetchTokenPrice, @@ -556,7 +557,7 @@ export default function PrepareSwapPage({ dispatch(resetSwapsPostFetchState()); dispatch(setReviewSwapClickedTimestamp()); trackPrepareSwapPageLoadedEvent(); - }, [dispatch, trackPrepareSwapPageLoadedEvent]); + }, [dispatch]); const BlockExplorerLink = () => { return ( @@ -630,6 +631,10 @@ export default function PrepareSwapPage({ if (!isReviewSwapButtonDisabled) { if (isSmartTransaction) { clearSmartTransactionFees(); // Clean up STX fees eery time there is a form change. + dispatch({ + type: SET_SMART_TRANSACTIONS_ERROR, + payload: null, + }); } // Only do quotes prefetching if the Review swap button is enabled. prefetchQuotesWithoutRedirecting(); diff --git a/ui/pages/swaps/prepare-swap-page/review-quote.js b/ui/pages/swaps/prepare-swap-page/review-quote.js index 2f9a5b417..dd7bdaa6b 100644 --- a/ui/pages/swaps/prepare-swap-page/review-quote.js +++ b/ui/pages/swaps/prepare-swap-page/review-quote.js @@ -74,7 +74,6 @@ import { showModal, setSwapsQuotesPollingLimitEnabled, } from '../../../store/actions'; -import { SET_SMART_TRANSACTIONS_ERROR } from '../../../store/actionConstants'; import { ASSET_ROUTE, DEFAULT_ROUTE, @@ -120,7 +119,11 @@ import { ButtonLink, Text, } from '../../../components/component-library'; -import { MetaMetricsEventCategory } from '../../../../shared/constants/metametrics'; +import { + MetaMetricsEventCategory, + MetaMetricsEventName, + MetaMetricsEventErrorType, +} from '../../../../shared/constants/metametrics'; import { isEqualCaseInsensitive } from '../../../../shared/modules/string-utils'; import { parseStandardTokenTransactionData } from '../../../../shared/modules/transaction.utils'; import { getTokenValueParam } from '../../../../shared/lib/metamask-controller-utils'; @@ -176,6 +179,7 @@ export default function ReviewQuote({ setReceiveToAmount }) { }, [history, quotes, routeState]); const quotesLastFetched = useSelector(getQuotesLastFetched); + const prevQuotesLastFetched = usePrevious(quotesLastFetched); // Select necessary data const gasPrice = useSelector(getUsedSwapsGasPrice); @@ -504,6 +508,7 @@ export default function ReviewQuote({ setReceiveToAmount }) { smartTransactionsError.currentBalanceWei, ); } + const prevEthBalanceNeededStx = usePrevious(ethBalanceNeededStx); const destinationToken = useSelector(getDestinationTokenInfo, isEqual); useEffect(() => { @@ -518,8 +523,13 @@ export default function ReviewQuote({ setReceiveToAmount }) { } else if (balanceError && !insufficientTokens && !insufficientEth) { dispatch(setBalanceError(false)); } - // eslint-disable-next-line - }, [insufficientTokens, insufficientEth, dispatch, isSmartTransaction]); + }, [ + insufficientTokens, + insufficientEth, + dispatch, + isSmartTransaction, + balanceError, + ]); useEffect(() => { const currentTime = Date.now(); @@ -697,6 +707,42 @@ export default function ReviewQuote({ setReceiveToAmount }) { trackBestQuoteReviewedEvent, ]); + useEffect(() => { + if ( + ((isSmartTransaction && prevEthBalanceNeededStx) || + !isSmartTransaction) && + quotesLastFetched === prevQuotesLastFetched + ) { + return; + } + let additionalBalanceNeeded; + if (isSmartTransaction && ethBalanceNeededStx) { + additionalBalanceNeeded = ethBalanceNeededStx; + } else if (!isSmartTransaction && ethBalanceNeeded) { + additionalBalanceNeeded = ethBalanceNeeded; + } else { + return; // A user has enough balance for a gas fee, so we don't need to track it. + } + trackEvent({ + event: MetaMetricsEventName.SwapError, + category: MetaMetricsEventCategory.Swaps, + sensitiveProperties: { + ...eventObjectBase, + error_type: MetaMetricsEventErrorType.InsufficientGas, + additional_balance_needed: additionalBalanceNeeded, + }, + }); + }, [ + quotesLastFetched, + prevQuotesLastFetched, + ethBalanceNeededStx, + isSmartTransaction, + trackEvent, + prevEthBalanceNeededStx, + ethBalanceNeeded, + eventObjectBase, + ]); + const metaMaskFee = usedQuote.fee; /* istanbul ignore next */ @@ -793,6 +839,7 @@ export default function ReviewQuote({ setReceiveToAmount }) { 10, ); } + const prevPriceDifferencePercentage = usePrevious(priceDifferencePercentage); const shouldShowPriceDifferenceWarning = !tokenBalanceUnavailable && @@ -841,6 +888,25 @@ export default function ReviewQuote({ setReceiveToAmount }) { !smartTransactionFees?.tradeTxFees), ); + useEffect(() => { + if ( + shouldShowPriceDifferenceWarning && + acknowledgedPriceDifference && + quotesLastFetched !== prevQuotesLastFetched && + priceDifferencePercentage !== prevPriceDifferencePercentage + ) { + // Reset price difference acknowledgement if price diff % changed. + setAcknowledgedPriceDifference(false); + } + }, [ + acknowledgedPriceDifference, + prevQuotesLastFetched, + quotesLastFetched, + shouldShowPriceDifferenceWarning, + priceDifferencePercentage, + prevPriceDifferencePercentage, + ]); + useEffect(() => { if (isSmartTransaction && !insufficientTokens) { const unsignedTx = { @@ -948,16 +1014,6 @@ export default function ReviewQuote({ setReceiveToAmount }) { usedQuote, ]); - useEffect(() => { - if (isSmartTransaction) { - // Removes a smart transactions error when the component loads. - dispatch({ - type: SET_SMART_TRANSACTIONS_ERROR, - payload: null, - }); - } - }, [isSmartTransaction, dispatch]); - const destinationValue = calcTokenValue( destinationTokenValue, destinationTokenDecimals,