import React, { useContext, useEffect, useState } from 'react';
import { useDispatch, useSelector, shallowEqual } from 'react-redux';
import { useHistory } from 'react-router-dom';
import { getBlockExplorerLink } from '@metamask/etherscan-link';
import { isEqual } from 'lodash';
import { I18nContext } from '../../../contexts/i18n';
import {
getFetchParams,
prepareToLeaveSwaps,
getCurrentSmartTransactions,
getSelectedQuote,
getTopQuote,
getSmartTransactionsOptInStatus,
getSmartTransactionsEnabled,
getCurrentSmartTransactionsEnabled,
getSwapsNetworkConfig,
cancelSwapsSmartTransaction,
} from '../../../ducks/swaps/swaps';
import {
isHardwareWallet,
getHardwareWalletType,
getCurrentChainId,
getUSDConversionRate,
conversionRateSelector,
getCurrentCurrency,
getRpcPrefsForCurrentProvider,
} from '../../../selectors';
import { SWAPS_CHAINID_DEFAULT_BLOCK_EXPLORER_URL_MAP } from '../../../../shared/constants/swaps';
import { getNativeCurrency } from '../../../ducks/metamask/metamask';
import {
DEFAULT_ROUTE,
BUILD_QUOTE_ROUTE,
} from '../../../helpers/constants/routes';
import Typography from '../../../components/ui/typography';
import Box from '../../../components/ui/box';
import UrlIcon from '../../../components/ui/url-icon';
import {
BLOCK_SIZES,
COLORS,
TYPOGRAPHY,
JUSTIFY_CONTENT,
DISPLAY,
FONT_WEIGHT,
ALIGN_ITEMS,
} from '../../../helpers/constants/design-system';
import {
stopPollingForQuotes,
setBackgroundSwapRouteState,
} from '../../../store/actions';
import { EVENT } from '../../../../shared/constants/metametrics';
import { SMART_TRANSACTION_STATUSES } from '../../../../shared/constants/transaction';
import SwapsFooter from '../swaps-footer';
import { calcTokenAmount } from '../../../helpers/utils/token-util';
import {
showRemainingTimeInMinAndSec,
getFeeForSmartTransaction,
} from '../swaps.util';
import { MetaMetricsContext } from '../../../contexts/metametrics';
import CreateNewSwap from '../create-new-swap';
import ViewOnBlockExplorer from '../view-on-block-explorer';
import SuccessIcon from './success-icon';
import RevertedIcon from './reverted-icon';
import CanceledIcon from './canceled-icon';
import UnknownIcon from './unknown-icon';
import ArrowIcon from './arrow-icon';
import TimerIcon from './timer-icon';
export default function SmartTransactionStatus() {
const [cancelSwapLinkClicked, setCancelSwapLinkClicked] = useState(false);
const t = useContext(I18nContext);
const history = useHistory();
const dispatch = useDispatch();
const fetchParams = useSelector(getFetchParams, isEqual) || {};
const { destinationTokenInfo = {}, sourceTokenInfo = {} } =
fetchParams?.metaData || {};
const hardwareWalletUsed = useSelector(isHardwareWallet);
const hardwareWalletType = useSelector(getHardwareWalletType);
const needsTwoConfirmations = true;
const selectedQuote = useSelector(getSelectedQuote, isEqual);
const topQuote = useSelector(getTopQuote, isEqual);
const usedQuote = selectedQuote || topQuote;
const currentSmartTransactions = useSelector(
getCurrentSmartTransactions,
isEqual,
);
const smartTransactionsOptInStatus = useSelector(
getSmartTransactionsOptInStatus,
);
const chainId = useSelector(getCurrentChainId);
const rpcPrefs = useSelector(getRpcPrefsForCurrentProvider, shallowEqual);
const swapsNetworkConfig = useSelector(getSwapsNetworkConfig, shallowEqual);
const smartTransactionsEnabled = useSelector(getSmartTransactionsEnabled);
const currentSmartTransactionsEnabled = useSelector(
getCurrentSmartTransactionsEnabled,
);
const baseNetworkUrl =
rpcPrefs.blockExplorerUrl ??
SWAPS_CHAINID_DEFAULT_BLOCK_EXPLORER_URL_MAP[chainId] ??
null;
const nativeCurrencySymbol = useSelector(getNativeCurrency);
const conversionRate = useSelector(conversionRateSelector);
const USDConversionRate = useSelector(getUSDConversionRate);
const currentCurrency = useSelector(getCurrentCurrency);
let smartTransactionStatus = SMART_TRANSACTION_STATUSES.PENDING;
let latestSmartTransaction = {};
let latestSmartTransactionUuid;
let cancellationFeeWei;
if (currentSmartTransactions && currentSmartTransactions.length > 0) {
latestSmartTransaction =
currentSmartTransactions[currentSmartTransactions.length - 1];
latestSmartTransactionUuid = latestSmartTransaction?.uuid;
smartTransactionStatus =
latestSmartTransaction?.status || SMART_TRANSACTION_STATUSES.PENDING;
cancellationFeeWei =
latestSmartTransaction?.statusMetadata?.cancellationFeeWei;
}
const [timeLeftForPendingStxInSec, setTimeLeftForPendingStxInSec] = useState(
swapsNetworkConfig.stxStatusDeadline,
);
const sensitiveProperties = {
needs_two_confirmations: needsTwoConfirmations,
token_from: sourceTokenInfo?.symbol,
token_from_amount: fetchParams?.value,
token_to: destinationTokenInfo?.symbol,
request_type: fetchParams?.balanceError ? 'Quote' : 'Order',
slippage: fetchParams?.slippage,
custom_slippage: fetchParams?.slippage === 2,
is_hardware_wallet: hardwareWalletUsed,
hardware_wallet_type: hardwareWalletType,
stx_enabled: smartTransactionsEnabled,
current_stx_enabled: currentSmartTransactionsEnabled,
stx_user_opt_in: smartTransactionsOptInStatus,
};
let destinationValue;
if (usedQuote?.destinationAmount) {
destinationValue = calcTokenAmount(
usedQuote?.destinationAmount,
destinationTokenInfo.decimals,
).toPrecision(8);
}
const trackEvent = useContext(MetaMetricsContext);
const isSmartTransactionPending =
smartTransactionStatus === SMART_TRANSACTION_STATUSES.PENDING;
const showCloseButtonOnly =
isSmartTransactionPending ||
smartTransactionStatus === SMART_TRANSACTION_STATUSES.SUCCESS;
const txHash = latestSmartTransaction?.statusMetadata?.minedHash;
useEffect(() => {
trackEvent({
event: 'STX Status Page Loaded',
category: EVENT.CATEGORIES.SWAPS,
sensitiveProperties,
});
// eslint-disable-next-line
}, []);
useEffect(() => {
let intervalId;
if (isSmartTransactionPending && latestSmartTransactionUuid) {
const calculateRemainingTime = () => {
const secondsAfterStxSubmission = Math.round(
(Date.now() - latestSmartTransaction.time) / 1000,
);
if (secondsAfterStxSubmission > swapsNetworkConfig.stxStatusDeadline) {
setTimeLeftForPendingStxInSec(0);
clearInterval(intervalId);
return;
}
setTimeLeftForPendingStxInSec(
swapsNetworkConfig.stxStatusDeadline - secondsAfterStxSubmission,
);
};
intervalId = setInterval(calculateRemainingTime, 1000);
calculateRemainingTime();
}
return () => clearInterval(intervalId);
}, [
dispatch,
isSmartTransactionPending,
latestSmartTransactionUuid,
latestSmartTransaction.time,
swapsNetworkConfig.stxStatusDeadline,
]);
useEffect(() => {
dispatch(setBackgroundSwapRouteState('smartTransactionStatus'));
setTimeout(() => {
// We don't need to poll for quotes on the status page.
dispatch(stopPollingForQuotes());
}, 1000); // Stop polling for quotes after 1s.
}, [dispatch]);
let headerText = t('stxPendingPrivatelySubmittingSwap');
let description;
let subDescription;
let icon;
let blockExplorerUrl;
if (isSmartTransactionPending) {
if (cancelSwapLinkClicked) {
headerText = t('stxTryingToCancel');
} else if (cancellationFeeWei > 0) {
headerText = t('stxPendingPubliclySubmittingSwap');
}
}
if (smartTransactionStatus === SMART_TRANSACTION_STATUSES.SUCCESS) {
headerText = t('stxSuccess');
if (destinationTokenInfo?.symbol) {
description = t('stxSuccessDescription', [destinationTokenInfo.symbol]);
}
icon =