diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json
index b20c8eba5..43d5a4ccd 100644
--- a/app/_locales/en/messages.json
+++ b/app/_locales/en/messages.json
@@ -410,15 +410,15 @@
"description": "$1 represents the cypto symbol to be purchased"
},
"buyCryptoWithMoonPayDescription": {
- "message": "MoonPay supports popular payment methods, including Visa, Mastercard, Apple / Google / Samsung Pay, and bank transfers in 146+ countries. Tokens deposit into your MetaMask account."
+ "message": "MoonPay supports popular payment methods, including Visa, Mastercard, Apple / Google / Samsung Pay, and bank transfers in 145+ countries. Tokens deposit into your MetaMask account."
},
"buyCryptoWithTransak": {
"message": "Buy $1 with Transak",
"description": "$1 represents the cypto symbol to be purchased"
},
"buyCryptoWithTransakDescription": {
- "message": "Transak supports debit card and bank transfers (depending on location) in 59+ countries. $1 deposits into your MetaMask account.",
- "description": "$1 represents the cypto symbol to be purchased"
+ "message": "Transak supports credit & debit cards, Apple Pay, MobiKwik, and bank transfers (depending on location) in 100+ countries. $1 deposits directly into your MetaMask account.",
+ "description": "$1 represents the crypto symbol to be purchased"
},
"buyEth": {
"message": "Buy ETH"
@@ -2857,7 +2857,7 @@
"message": "Slow"
},
"smartTransaction": {
- "message": "Smart transaction"
+ "message": "Smart Transaction"
},
"snapAccess": {
"message": "$1 snap has access to:",
@@ -3047,6 +3047,12 @@
"stxDescription": {
"message": "MetaMask Swaps just got a whole lot smarter! Enabling Smart Transactions will allow MetaMask to programmatically optimize your Swap to help:"
},
+ "stxErrorNotEnoughFunds": {
+ "message": "Not enough funds for a smart transaction."
+ },
+ "stxErrorUnavailable": {
+ "message": "Smart Transactions are temporarily unavailable."
+ },
"stxFailure": {
"message": "Swap failed"
},
@@ -3054,8 +3060,11 @@
"message": "Sudden market changes can cause failures. If the problem persists, please reach out to $1.",
"description": "This message is shown to a user if their swap fails. The $1 will be replaced by support.metamask.io"
},
- "stxFallbackToNormal": {
- "message": "You can still swap using the normal method or wait for cheaper gas fees and less failures with smart transactions."
+ "stxFallbackPendingTx": {
+ "message": "Smart Transactions are temporarily unavailable because you have a pending transaction."
+ },
+ "stxFallbackUnavailable": {
+ "message": "You can still swap your tokens even while Smart Transactions are unavailable."
},
"stxPendingFinalizing": {
"message": "Finalizing..."
@@ -3082,8 +3091,11 @@
"stxTryRegular": {
"message": "Try a regular swap."
},
+ "stxTryingToCancel": {
+ "message": "Trying to cancel your transaction..."
+ },
"stxUnavailable": {
- "message": "Smart transactions temporarily unavailable"
+ "message": "Smart Transactions are disabled"
},
"stxUnknown": {
"message": "Status unknown"
diff --git a/ui/ducks/swaps/swaps.js b/ui/ducks/swaps/swaps.js
index 7c854a98d..7d1dd91a5 100644
--- a/ui/ducks/swaps/swaps.js
+++ b/ui/ducks/swaps/swaps.js
@@ -33,6 +33,7 @@ import {
fetchSmartTransactionFees,
estimateSmartTransactionsGas,
cancelSmartTransaction,
+ getTransactions,
} from '../../store/actions';
import {
AWAITING_SIGNATURES_ROUTE,
@@ -81,6 +82,7 @@ import {
} from '../../../shared/constants/swaps';
import {
TRANSACTION_TYPES,
+ TRANSACTION_STATUSES,
SMART_TRANSACTION_STATUSES,
} from '../../../shared/constants/transaction';
import { getGasFeeEstimates } from '../metamask/metamask';
@@ -199,9 +201,9 @@ const slice = createSlice({
state.customGas.fallBackPrice = action.payload;
},
setCurrentSmartTransactionsError: (state, action) => {
- const errorType = stxErrorTypes.includes(action.payload)
+ const errorType = Object.values(stxErrorTypes).includes(action.payload)
? action.payload
- : stxErrorTypes[0];
+ : stxErrorTypes.UNAVAILABLE;
state.currentSmartTransactionsError = errorType;
},
dismissCurrentSmartTransactionsErrorMessage: (state) => {
@@ -554,12 +556,24 @@ export const fetchSwapsLivenessAndFeatureFlags = () => {
let swapsLivenessForNetwork = {
swapsFeatureIsLive: false,
};
- const chainId = getCurrentChainId(getState());
+ const state = getState();
+ const chainId = getCurrentChainId(state);
try {
const swapsFeatureFlags = await fetchSwapsFeatureFlags();
await dispatch(setSwapsFeatureFlags(swapsFeatureFlags));
if (ALLOWED_SMART_TRANSACTIONS_CHAIN_IDS.includes(chainId)) {
await dispatch(fetchSmartTransactionsLiveness());
+ const pendingTransactions = await getTransactions({
+ searchCriteria: {
+ status: TRANSACTION_STATUSES.PENDING,
+ from: state.metamask?.selectedAddress,
+ },
+ });
+ if (pendingTransactions?.length > 0) {
+ dispatch(
+ setCurrentSmartTransactionsError(stxErrorTypes.REGULAR_TX_PENDING),
+ );
+ }
}
swapsLivenessForNetwork = getSwapsLivenessForNetwork(
swapsFeatureFlags,
@@ -820,6 +834,7 @@ export const signAndSendSwapsSmartTransaction = ({
unsignedTransaction,
metaMetricsEvent,
history,
+ additionalTrackingParams,
}) => {
return async (dispatch, getState) => {
dispatch(setSwapsSTXSubmitLoading(true));
@@ -871,6 +886,7 @@ export const signAndSendSwapsSmartTransaction = ({
stx_enabled: smartTransactionsEnabled,
current_stx_enabled: currentSmartTransactionsEnabled,
stx_user_opt_in: smartTransactionsOptInStatus,
+ ...additionalTrackingParams,
};
metaMetricsEvent({
event: 'STX Swap Started',
@@ -966,7 +982,11 @@ export const signAndSendSwapsSmartTransaction = ({
};
};
-export const signAndSendTransactions = (history, metaMetricsEvent) => {
+export const signAndSendTransactions = (
+ history,
+ metaMetricsEvent,
+ additionalTrackingParams,
+) => {
return async (dispatch, getState) => {
const state = getState();
const chainId = getCurrentChainId(state);
@@ -1079,6 +1099,9 @@ export const signAndSendTransactions = (history, metaMetricsEvent) => {
});
const smartTransactionsOptInStatus = getSmartTransactionsOptInStatus(state);
const smartTransactionsEnabled = getSmartTransactionsEnabled(state);
+ const currentSmartTransactionsEnabled = getCurrentSmartTransactionsEnabled(
+ state,
+ );
const swapMetaData = {
token_from: sourceTokenInfo.symbol,
token_from_amount: String(swapTokenValue),
@@ -1105,7 +1128,9 @@ export const signAndSendTransactions = (history, metaMetricsEvent) => {
is_hardware_wallet: hardwareWalletUsed,
hardware_wallet_type: getHardwareWalletType(state),
stx_enabled: smartTransactionsEnabled,
+ current_stx_enabled: currentSmartTransactionsEnabled,
stx_user_opt_in: smartTransactionsOptInStatus,
+ ...additionalTrackingParams,
};
if (networkAndAccountSupports1559) {
swapMetaData.max_fee_per_gas = maxFeePerGas;
diff --git a/ui/ducks/swaps/swaps.test.js b/ui/ducks/swaps/swaps.test.js
index bf5454918..9808b0392 100644
--- a/ui/ducks/swaps/swaps.test.js
+++ b/ui/ducks/swaps/swaps.test.js
@@ -15,6 +15,7 @@ jest.mock('../../store/actions.js', () => ({
setSwapsLiveness: jest.fn(),
setSwapsFeatureFlags: jest.fn(),
fetchSmartTransactionsLiveness: jest.fn(),
+ getTransactions: jest.fn(),
}));
const providerState = {
diff --git a/ui/pages/swaps/awaiting-signatures/awaiting-signatures.js b/ui/pages/swaps/awaiting-signatures/awaiting-signatures.js
index a03db90a0..69db3a8cd 100644
--- a/ui/pages/swaps/awaiting-signatures/awaiting-signatures.js
+++ b/ui/pages/swaps/awaiting-signatures/awaiting-signatures.js
@@ -11,6 +11,7 @@ import {
prepareToLeaveSwaps,
getSmartTransactionsOptInStatus,
getSmartTransactionsEnabled,
+ getCurrentSmartTransactionsEnabled,
} from '../../../ducks/swaps/swaps';
import {
isHardwareWallet,
@@ -47,6 +48,9 @@ export default function AwaitingSignatures() {
getSmartTransactionsOptInStatus,
);
const smartTransactionsEnabled = useSelector(getSmartTransactionsEnabled);
+ const currentSmartTransactionsEnabled = useSelector(
+ getCurrentSmartTransactionsEnabled,
+ );
const needsTwoConfirmations = Boolean(approveTxParams);
const awaitingSignaturesEvent = useNewMetricEvent({
@@ -62,6 +66,7 @@ export default function AwaitingSignatures() {
is_hardware_wallet: hardwareWalletUsed,
hardware_wallet_type: hardwareWalletType,
stx_enabled: smartTransactionsEnabled,
+ current_stx_enabled: currentSmartTransactionsEnabled,
stx_user_opt_in: smartTransactionsOptInStatus,
},
category: 'swaps',
diff --git a/ui/pages/swaps/awaiting-swap/awaiting-swap.js b/ui/pages/swaps/awaiting-swap/awaiting-swap.js
index c7fff3358..2ace4ba5f 100644
--- a/ui/pages/swaps/awaiting-swap/awaiting-swap.js
+++ b/ui/pages/swaps/awaiting-swap/awaiting-swap.js
@@ -31,6 +31,7 @@ import {
prepareToLeaveSwaps,
getSmartTransactionsOptInStatus,
getSmartTransactionsEnabled,
+ getCurrentSmartTransactionsEnabled,
getFromTokenInputValue,
getMaxSlippage,
setSwapsFromToken,
@@ -113,6 +114,9 @@ export default function AwaitingSwap({
getSmartTransactionsOptInStatus,
);
const smartTransactionsEnabled = useSelector(getSmartTransactionsEnabled);
+ const currentSmartTransactionsEnabled = useSelector(
+ getCurrentSmartTransactionsEnabled,
+ );
const sensitiveProperties = {
token_from: sourceTokenInfo?.symbol,
token_from_amount: fetchParams?.value,
@@ -124,6 +128,7 @@ export default function AwaitingSwap({
is_hardware_wallet: hardwareWalletUsed,
hardware_wallet_type: hardwareWalletType,
stx_enabled: smartTransactionsEnabled,
+ current_stx_enabled: currentSmartTransactionsEnabled,
stx_user_opt_in: smartTransactionsOptInStatus,
};
const quotesExpiredEvent = useNewMetricEvent({
diff --git a/ui/pages/swaps/build-quote/build-quote.js b/ui/pages/swaps/build-quote/build-quote.js
index 6641255af..b9b9c794c 100644
--- a/ui/pages/swaps/build-quote/build-quote.js
+++ b/ui/pages/swaps/build-quote/build-quote.js
@@ -177,11 +177,11 @@ export default function BuildQuote({
const onCloseSmartTransactionsOptInPopover = (e) => {
e?.preventDefault();
- setSmartTransactionsOptInStatus(false);
+ setSmartTransactionsOptInStatus(false, smartTransactionsOptInStatus);
};
const onEnableSmartTransactionsClick = () =>
- setSmartTransactionsOptInStatus(true);
+ setSmartTransactionsOptInStatus(true, smartTransactionsOptInStatus);
const fetchParamsFromToken = isSwapsDefaultTokenSymbol(
sourceTokenInfo?.symbol,
@@ -338,9 +338,7 @@ export default function BuildQuote({
null, // no holderAddress
{
blockExplorerUrl:
- rpcPrefs.blockExplorerUrl ??
- SWAPS_CHAINID_DEFAULT_BLOCK_EXPLORER_URL_MAP[chainId] ??
- null,
+ SWAPS_CHAINID_DEFAULT_BLOCK_EXPLORER_URL_MAP[chainId] ?? null,
},
);
diff --git a/ui/pages/swaps/build-quote/index.scss b/ui/pages/swaps/build-quote/index.scss
index b38e83259..e062d492d 100644
--- a/ui/pages/swaps/build-quote/index.scss
+++ b/ui/pages/swaps/build-quote/index.scss
@@ -129,6 +129,13 @@
}
}
}
+
+ &__input {
+ div {
+ border: 1px solid var(--color-border-default);
+ border-left: 0;
+ }
+ }
}
&__open-to-dropdown {
diff --git a/ui/pages/swaps/dropdown-search-list/dropdown-search-list.js b/ui/pages/swaps/dropdown-search-list/dropdown-search-list.js
index c4759b578..b76d86d80 100644
--- a/ui/pages/swaps/dropdown-search-list/dropdown-search-list.js
+++ b/ui/pages/swaps/dropdown-search-list/dropdown-search-list.js
@@ -27,6 +27,7 @@ import { getURLHostName } from '../../../helpers/utils/util';
import {
getSmartTransactionsOptInStatus,
getSmartTransactionsEnabled,
+ getCurrentSmartTransactionsEnabled,
} from '../../../ducks/swaps/swaps';
export default function DropdownSearchList({
@@ -63,6 +64,9 @@ export default function DropdownSearchList({
getSmartTransactionsOptInStatus,
);
const smartTransactionsEnabled = useSelector(getSmartTransactionsEnabled);
+ const currentSmartTransactionsEnabled = useSelector(
+ getCurrentSmartTransactionsEnabled,
+ );
const tokenImportedEvent = useNewMetricEvent({
event: 'Token Imported',
@@ -73,6 +77,7 @@ export default function DropdownSearchList({
is_hardware_wallet: hardwareWalletUsed,
hardware_wallet_type: hardwareWalletType,
stx_enabled: smartTransactionsEnabled,
+ current_stx_enabled: currentSmartTransactionsEnabled,
stx_user_opt_in: smartTransactionsOptInStatus,
},
category: 'swaps',
diff --git a/ui/pages/swaps/index.js b/ui/pages/swaps/index.js
index 06e9587a3..fd49f1c35 100644
--- a/ui/pages/swaps/index.js
+++ b/ui/pages/swaps/index.js
@@ -1,4 +1,4 @@
-import React, { useEffect, useRef, useContext } from 'react';
+import React, { useEffect, useRef, useContext, useState } from 'react';
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
import {
Switch,
@@ -37,6 +37,7 @@ import {
getPendingSmartTransactions,
getSmartTransactionsOptInStatus,
getSmartTransactionsEnabled,
+ getCurrentSmartTransactionsEnabled,
getCurrentSmartTransactionsError,
dismissCurrentSmartTransactionsErrorMessage,
getCurrentSmartTransactionsErrorMessageDismissed,
@@ -84,6 +85,7 @@ import {
fetchTopAssets,
getSwapsTokensReceivedFromTxMeta,
fetchAggregatorMetadata,
+ stxErrorTypes,
} from './swaps.util';
import AwaitingSignatures from './awaiting-signatures';
import SmartTransactionStatus from './smart-transaction-status';
@@ -106,6 +108,7 @@ export default function Swap() {
pathname === SMART_TRANSACTION_STATUS_ROUTE;
const isViewQuoteRoute = pathname === VIEW_QUOTE_ROUTE;
+ const [currentStxErrorTracked, setCurrentStxErrorTracked] = useState(false);
const fetchParams = useSelector(getFetchParams, isEqual);
const { destinationTokenInfo = {} } = fetchParams?.metaData || {};
@@ -134,6 +137,9 @@ export default function Swap() {
getSmartTransactionsOptInStatus,
);
const smartTransactionsEnabled = useSelector(getSmartTransactionsEnabled);
+ const currentSmartTransactionsEnabled = useSelector(
+ getCurrentSmartTransactionsEnabled,
+ );
const currentSmartTransactionsError = useSelector(
getCurrentSmartTransactionsError,
);
@@ -244,6 +250,7 @@ export default function Swap() {
is_hardware_wallet: hardwareWalletUsed,
hardware_wallet_type: hardwareWalletType,
stx_enabled: smartTransactionsEnabled,
+ current_stx_enabled: currentSmartTransactionsEnabled,
stx_user_opt_in: smartTransactionsOptInStatus,
},
});
@@ -302,22 +309,26 @@ export default function Swap() {
is_hardware_wallet: hardwareWalletUsed,
hardware_wallet_type: hardwareWalletType,
stx_enabled: smartTransactionsEnabled,
+ current_stx_enabled: currentSmartTransactionsEnabled,
stx_user_opt_in: smartTransactionsOptInStatus,
stx_error: currentSmartTransactionsError,
},
});
useEffect(() => {
- if (currentSmartTransactionsError) {
+ if (currentSmartTransactionsError && !currentStxErrorTracked) {
+ setCurrentStxErrorTracked(true);
errorStxEvent();
}
- }, [errorStxEvent, currentSmartTransactionsError]);
+ }, [errorStxEvent, currentSmartTransactionsError, currentStxErrorTracked]);
if (!isSwapsChain) {
return