mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 01:39:44 +01:00
Improvements for Smart Transactions / Swaps (#14022)
- Don’t call /estimateGas if a user doesn’t have enough funds - Hardcode block explorer URLs for Swaps - Track the "stx_prev_user_opt_in" param - Add fee estimates tracking for regular txs and STX - Track estimated_gas and estimated_vs_used_gasRatio for STX - Only track the "Error Smart Transactions" event once - Don't overwrite "maxGasLimit" for STX on the View Quote page for better "balance needed" estimations - Update description for Transak - Fix styles for the input field on the Build Quote page - Refactor variables for STX error types and add translation for each STX error type - Do additional logging for the "current_stx_enabled" param - Add a close icon for an STX notification, update STX content
This commit is contained in:
parent
5fec2b53a7
commit
115971d79c
24
app/_locales/en/messages.json
generated
24
app/_locales/en/messages.json
generated
@ -416,8 +416,8 @@
|
||||
"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"
|
||||
@ -2849,7 +2849,7 @@
|
||||
"message": "Slow"
|
||||
},
|
||||
"smartTransaction": {
|
||||
"message": "Smart transaction"
|
||||
"message": "Smart Transaction"
|
||||
},
|
||||
"snapAccess": {
|
||||
"message": "$1 snap has access to:",
|
||||
@ -3039,6 +3039,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"
|
||||
},
|
||||
@ -3046,8 +3052,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..."
|
||||
@ -3074,8 +3083,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"
|
||||
|
@ -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;
|
||||
|
@ -15,6 +15,7 @@ jest.mock('../../store/actions.js', () => ({
|
||||
setSwapsLiveness: jest.fn(),
|
||||
setSwapsFeatureFlags: jest.fn(),
|
||||
fetchSmartTransactionsLiveness: jest.fn(),
|
||||
getTransactions: jest.fn(),
|
||||
}));
|
||||
|
||||
const providerState = {
|
||||
|
@ -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',
|
||||
|
@ -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({
|
||||
|
@ -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,
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -128,6 +128,13 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__input {
|
||||
div {
|
||||
border: 1px solid var(--color-border-default);
|
||||
border-left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__open-to-dropdown {
|
||||
|
@ -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',
|
||||
|
@ -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 <Redirect to={{ pathname: DEFAULT_ROUTE }} />;
|
||||
}
|
||||
|
||||
const isStxNotEnoughFundsError =
|
||||
currentSmartTransactionsError === 'not_enough_funds';
|
||||
currentSmartTransactionsError === stxErrorTypes.NOT_ENOUGH_FUNDS;
|
||||
const isStxRegularTxPendingError =
|
||||
currentSmartTransactionsError === stxErrorTypes.REGULAR_TX_PENDING;
|
||||
|
||||
return (
|
||||
<div className="swaps">
|
||||
@ -371,10 +382,20 @@ export default function Swap() {
|
||||
</div>
|
||||
) : (
|
||||
<div className="build-quote__token-verification-warning-message">
|
||||
<div className="build-quote__bold">
|
||||
<button
|
||||
onClick={() => {
|
||||
dispatch(dismissCurrentSmartTransactionsErrorMessage());
|
||||
}}
|
||||
className="swaps__notification-close-button"
|
||||
/>
|
||||
<div className="swaps__notification-title">
|
||||
{t('stxUnavailable')}
|
||||
</div>
|
||||
<div>{t('stxFallbackToNormal')}</div>
|
||||
<div>
|
||||
{isStxRegularTxPendingError
|
||||
? t('stxFallbackPendingTx')
|
||||
: t('stxFallbackUnavailable')}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -383,16 +404,6 @@ export default function Swap() {
|
||||
? 'swaps__error-message'
|
||||
: 'actionable-message--left-aligned actionable-message--warning swaps__error-message'
|
||||
}
|
||||
primaryAction={
|
||||
isStxNotEnoughFundsError
|
||||
? null
|
||||
: {
|
||||
label: t('dismiss'),
|
||||
onClick: () =>
|
||||
dispatch(dismissCurrentSmartTransactionsErrorMessage()),
|
||||
}
|
||||
}
|
||||
withRightButton
|
||||
/>
|
||||
)}
|
||||
<Switch>
|
||||
|
@ -120,4 +120,28 @@
|
||||
padding-left: 24px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
&__notification-close-button {
|
||||
background-color: transparent;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 2px;
|
||||
|
||||
&::after {
|
||||
position: absolute;
|
||||
content: '\00D7';
|
||||
font-size: 29px;
|
||||
font-weight: 200;
|
||||
color: var(--color-text-default);
|
||||
background-color: transparent;
|
||||
top: 0;
|
||||
right: 12px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
&__notification-title {
|
||||
font-weight: bold;
|
||||
margin-right: 14px;
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import {
|
||||
getQuotesFetchStartTime,
|
||||
getSmartTransactionsOptInStatus,
|
||||
getSmartTransactionsEnabled,
|
||||
getCurrentSmartTransactionsEnabled,
|
||||
} from '../../../ducks/swaps/swaps';
|
||||
import {
|
||||
isHardwareWallet,
|
||||
@ -41,6 +42,9 @@ export default function LoadingSwapsQuotes({
|
||||
getSmartTransactionsOptInStatus,
|
||||
);
|
||||
const smartTransactionsEnabled = useSelector(getSmartTransactionsEnabled);
|
||||
const currentSmartTransactionsEnabled = useSelector(
|
||||
getCurrentSmartTransactionsEnabled,
|
||||
);
|
||||
const quotesRequestCancelledEventConfig = {
|
||||
event: 'Quotes Request Cancelled',
|
||||
category: 'swaps',
|
||||
@ -55,6 +59,7 @@ export default function LoadingSwapsQuotes({
|
||||
is_hardware_wallet: hardwareWalletUsed,
|
||||
hardware_wallet_type: hardwareWalletType,
|
||||
stx_enabled: smartTransactionsEnabled,
|
||||
current_stx_enabled: currentSmartTransactionsEnabled,
|
||||
stx_user_opt_in: smartTransactionsOptInStatus,
|
||||
},
|
||||
};
|
||||
|
@ -14,7 +14,7 @@ import {
|
||||
ALIGN_ITEMS,
|
||||
DISPLAY,
|
||||
} from '../../../helpers/constants/design-system';
|
||||
import { smartTransactionsErrorMessages } from '../swaps.util';
|
||||
import { getTranslatedStxErrorMessage } from '../swaps.util';
|
||||
|
||||
export default function SlippageButtons({
|
||||
onSelect,
|
||||
@ -208,8 +208,9 @@ export default function SlippageButtons({
|
||||
{currentSmartTransactionsError ? (
|
||||
<InfoTooltip
|
||||
position="top"
|
||||
contentText={smartTransactionsErrorMessages(
|
||||
contentText={getTranslatedStxErrorMessage(
|
||||
currentSmartTransactionsError,
|
||||
t,
|
||||
)}
|
||||
/>
|
||||
) : (
|
||||
@ -219,7 +220,7 @@ export default function SlippageButtons({
|
||||
<ToggleButton
|
||||
value={smartTransactionsOptInStatus}
|
||||
onToggle={(value) => {
|
||||
setSmartTransactionsOptInStatus(!value);
|
||||
setSmartTransactionsOptInStatus(!value, value);
|
||||
}}
|
||||
offLabel={t('off')}
|
||||
onLabel={t('on')}
|
||||
|
@ -45,6 +45,6 @@ describe('SlippageButtons', () => {
|
||||
expect(
|
||||
document.querySelector('.slippage-buttons__button-group'),
|
||||
).toMatchSnapshot();
|
||||
expect(getByText('Smart transaction')).toBeInTheDocument();
|
||||
expect(getByText('Smart Transaction')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
@ -184,6 +184,8 @@ export default function SmartTransactionStatus() {
|
||||
headerText = t('stxPendingFinalizing');
|
||||
} else if (timeLeftForPendingStxInSec < 150) {
|
||||
headerText = t('stxPendingPrivatelySubmitting');
|
||||
} else if (cancelSwapLinkClicked) {
|
||||
headerText = t('stxTryingToCancel');
|
||||
}
|
||||
}
|
||||
if (smartTransactionStatus === SMART_TRANSACTION_STATUSES.SUCCESS) {
|
||||
@ -192,7 +194,11 @@ export default function SmartTransactionStatus() {
|
||||
description = t('stxSuccessDescription', [destinationTokenInfo.symbol]);
|
||||
}
|
||||
icon = <SuccessIcon />;
|
||||
} else if (smartTransactionStatus === 'cancelled_user_cancelled') {
|
||||
} else if (
|
||||
smartTransactionStatus === 'cancelled_user_cancelled' ||
|
||||
latestSmartTransaction?.statusMetadata?.minedTx ===
|
||||
SMART_TRANSACTION_STATUSES.CANCELLED
|
||||
) {
|
||||
headerText = t('stxUserCancelled');
|
||||
description = t('stxUserCancelledDescription');
|
||||
icon = <CanceledIcon />;
|
||||
|
@ -55,6 +55,7 @@ const TOKEN_TRANSFER_LOG_TOPIC_HASH =
|
||||
'0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef';
|
||||
|
||||
const CACHE_REFRESH_FIVE_MINUTES = 300000;
|
||||
const USD_CURRENCY_CODE = 'usd';
|
||||
|
||||
const clientIdHeader = { 'X-Client-Id': SWAPS_CLIENT_ID };
|
||||
|
||||
@ -514,12 +515,25 @@ export const getFeeForSmartTransaction = ({
|
||||
conversionRate,
|
||||
numberOfDecimals: 2,
|
||||
});
|
||||
let feeInUsd;
|
||||
if (currentCurrency === USD_CURRENCY_CODE) {
|
||||
feeInUsd = rawNetworkFees;
|
||||
} else {
|
||||
feeInUsd = getValueFromWeiHex({
|
||||
value: feeInWeiHex,
|
||||
toCurrency: USD_CURRENCY_CODE,
|
||||
conversionRate,
|
||||
numberOfDecimals: 2,
|
||||
});
|
||||
}
|
||||
const formattedNetworkFee = formatCurrency(rawNetworkFees, currentCurrency);
|
||||
const chainCurrencySymbolToUse =
|
||||
nativeCurrencySymbol || SWAPS_CHAINID_DEFAULT_TOKEN_MAP[chainId].symbol;
|
||||
return {
|
||||
feeInUsd,
|
||||
feeInFiat: formattedNetworkFee,
|
||||
feeInEth: `${ethFee} ${chainCurrencySymbolToUse}`,
|
||||
rawEthFee: ethFee,
|
||||
};
|
||||
};
|
||||
|
||||
@ -564,11 +578,24 @@ export function getRenderableNetworkFeesForQuote({
|
||||
});
|
||||
const formattedNetworkFee = formatCurrency(rawNetworkFees, currentCurrency);
|
||||
|
||||
let feeInUsd;
|
||||
if (currentCurrency === USD_CURRENCY_CODE) {
|
||||
feeInUsd = rawNetworkFees;
|
||||
} else {
|
||||
feeInUsd = getValueFromWeiHex({
|
||||
value: totalWeiCost,
|
||||
toCurrency: USD_CURRENCY_CODE,
|
||||
conversionRate,
|
||||
numberOfDecimals: 2,
|
||||
});
|
||||
}
|
||||
|
||||
const chainCurrencySymbolToUse =
|
||||
nativeCurrencySymbol || SWAPS_CHAINID_DEFAULT_TOKEN_MAP[chainId].symbol;
|
||||
|
||||
return {
|
||||
rawNetworkFees,
|
||||
feeInUsd,
|
||||
rawEthFee: ethFee,
|
||||
feeInFiat: formattedNetworkFee,
|
||||
feeInEth: `${ethFee} ${chainCurrencySymbolToUse}`,
|
||||
@ -903,18 +930,22 @@ export const showRemainingTimeInMinAndSec = (remainingTimeInSec) => {
|
||||
return `${minutes}:${seconds.toString().padStart(2, '0')}`;
|
||||
};
|
||||
|
||||
export const stxErrorTypes = ['unavailable', 'not_enough_funds'];
|
||||
|
||||
const smartTransactionsErrorMap = {
|
||||
unavailable: 'Smart Transactions are temporarily unavailable.',
|
||||
not_enough_funds: 'Not enough funds for a smart transaction.',
|
||||
export const stxErrorTypes = {
|
||||
UNAVAILABLE: 'unavailable',
|
||||
NOT_ENOUGH_FUNDS: 'not_enough_funds',
|
||||
REGULAR_TX_PENDING: 'regular_tx_pending',
|
||||
};
|
||||
|
||||
export const smartTransactionsErrorMessages = (errorType) => {
|
||||
return (
|
||||
smartTransactionsErrorMap[errorType] ||
|
||||
smartTransactionsErrorMap.unavailable
|
||||
);
|
||||
export const getTranslatedStxErrorMessage = (errorType, t) => {
|
||||
switch (errorType) {
|
||||
case stxErrorTypes.UNAVAILABLE:
|
||||
case stxErrorTypes.REGULAR_TX_PENDING:
|
||||
return t('stxErrorUnavailable');
|
||||
case stxErrorTypes.NOT_ENOUGH_FUNDS:
|
||||
return t('stxErrorNotEnoughFunds');
|
||||
default:
|
||||
return t('stxErrorUnavailable');
|
||||
}
|
||||
};
|
||||
|
||||
export const parseSmartTransactionsError = (errorMessage) => {
|
||||
|
@ -205,40 +205,6 @@ export default function ViewQuote() {
|
||||
const swapsRefreshRates = useSelector(getSwapsRefreshStates);
|
||||
const unsignedTransaction = usedQuote.trade;
|
||||
|
||||
useEffect(() => {
|
||||
if (currentSmartTransactionsEnabled && smartTransactionsOptInStatus) {
|
||||
const unsignedTx = {
|
||||
from: unsignedTransaction.from,
|
||||
to: unsignedTransaction.to,
|
||||
value: unsignedTransaction.value,
|
||||
data: unsignedTransaction.data,
|
||||
gas: unsignedTransaction.gas,
|
||||
chainId,
|
||||
};
|
||||
intervalId = setInterval(() => {
|
||||
dispatch(
|
||||
estimateSwapsSmartTransactionsGas(unsignedTx, approveTxParams),
|
||||
);
|
||||
}, swapsRefreshRates.stxGetTransactionsRefreshTime);
|
||||
dispatch(estimateSwapsSmartTransactionsGas(unsignedTx, approveTxParams));
|
||||
} else if (intervalId) {
|
||||
clearInterval(intervalId);
|
||||
}
|
||||
return () => clearInterval(intervalId);
|
||||
// eslint-disable-next-line
|
||||
}, [
|
||||
dispatch,
|
||||
currentSmartTransactionsEnabled,
|
||||
smartTransactionsOptInStatus,
|
||||
unsignedTransaction.data,
|
||||
unsignedTransaction.from,
|
||||
unsignedTransaction.value,
|
||||
unsignedTransaction.gas,
|
||||
unsignedTransaction.to,
|
||||
chainId,
|
||||
swapsRefreshRates.stxGetTransactionsRefreshTime,
|
||||
]);
|
||||
|
||||
let gasFeeInputs;
|
||||
if (networkAndAccountSupports1559) {
|
||||
// For Swaps we want to get 'high' estimations by default.
|
||||
@ -252,6 +218,17 @@ export default function ViewQuote() {
|
||||
|
||||
const fetchParamsSourceToken = fetchParams?.sourceToken;
|
||||
|
||||
const additionalTrackingParams = {
|
||||
reg_tx_fee_in_usd: undefined,
|
||||
reg_tx_fee_in_eth: undefined,
|
||||
reg_tx_max_fee_in_usd: undefined,
|
||||
reg_tx_max_fee_in_eth: undefined,
|
||||
stx_fee_in_usd: undefined,
|
||||
stx_fee_in_eth: undefined,
|
||||
stx_max_fee_in_usd: undefined,
|
||||
stx_max_fee_in_eth: undefined,
|
||||
};
|
||||
|
||||
const usedGasLimit =
|
||||
usedQuote?.gasEstimateWithRefund ||
|
||||
`0x${decimalToHex(usedQuote?.averageGas || 0)}`;
|
||||
@ -266,7 +243,7 @@ export default function ViewQuote() {
|
||||
const nonCustomMaxGasLimit = usedQuote?.gasEstimate
|
||||
? usedGasLimitWithMultiplier
|
||||
: `0x${decimalToHex(usedQuote?.maxGas || 0)}`;
|
||||
let maxGasLimit = customMaxGas || nonCustomMaxGasLimit;
|
||||
const maxGasLimit = customMaxGas || nonCustomMaxGasLimit;
|
||||
|
||||
let maxFeePerGas;
|
||||
let maxPriorityFeePerGas;
|
||||
@ -289,17 +266,6 @@ export default function ViewQuote() {
|
||||
);
|
||||
}
|
||||
|
||||
// Smart Transactions gas fees.
|
||||
if (
|
||||
currentSmartTransactionsEnabled &&
|
||||
smartTransactionsOptInStatus &&
|
||||
smartTransactionEstimatedGas?.txData
|
||||
) {
|
||||
maxGasLimit = `0x${decimalToHex(
|
||||
smartTransactionEstimatedGas?.txData.gasLimit || 0,
|
||||
)}`;
|
||||
}
|
||||
|
||||
const gasTotalInWeiHex = calcGasTotal(maxGasLimit, maxFeePerGas || gasPrice);
|
||||
|
||||
const { tokensWithBalances } = useTokenTracker(swapsTokens, true);
|
||||
@ -374,7 +340,12 @@ export default function ViewQuote() {
|
||||
sourceTokenIconUrl,
|
||||
} = renderableDataForUsedQuote;
|
||||
|
||||
let { feeInFiat, feeInEth } = getRenderableNetworkFeesForQuote({
|
||||
let {
|
||||
feeInFiat,
|
||||
feeInEth,
|
||||
rawEthFee,
|
||||
feeInUsd,
|
||||
} = getRenderableNetworkFeesForQuote({
|
||||
tradeGas: usedGasLimit,
|
||||
approveGas,
|
||||
gasPrice: networkAndAccountSupports1559
|
||||
@ -388,6 +359,8 @@ export default function ViewQuote() {
|
||||
chainId,
|
||||
nativeCurrencySymbol,
|
||||
});
|
||||
additionalTrackingParams.reg_tx_fee_in_usd = Number(feeInUsd);
|
||||
additionalTrackingParams.reg_tx_fee_in_eth = Number(rawEthFee);
|
||||
|
||||
const renderableMaxFees = getRenderableNetworkFeesForQuote({
|
||||
tradeGas: maxGasLimit,
|
||||
@ -401,8 +374,15 @@ export default function ViewQuote() {
|
||||
chainId,
|
||||
nativeCurrencySymbol,
|
||||
});
|
||||
let { feeInFiat: maxFeeInFiat, feeInEth: maxFeeInEth } = renderableMaxFees;
|
||||
let {
|
||||
feeInFiat: maxFeeInFiat,
|
||||
feeInEth: maxFeeInEth,
|
||||
rawEthFee: maxRawEthFee,
|
||||
feeInUsd: maxFeeInUsd,
|
||||
} = renderableMaxFees;
|
||||
const { nonGasFee } = renderableMaxFees;
|
||||
additionalTrackingParams.reg_tx_max_fee_in_usd = Number(maxFeeInUsd);
|
||||
additionalTrackingParams.reg_tx_max_fee_in_eth = Number(maxRawEthFee);
|
||||
|
||||
if (
|
||||
currentSmartTransactionsEnabled &&
|
||||
@ -413,16 +393,22 @@ export default function ViewQuote() {
|
||||
smartTransactionEstimatedGas.txData.feeEstimate +
|
||||
(smartTransactionEstimatedGas.approvalTxData?.feeEstimate || 0);
|
||||
const stxMaxFeeInWeiDec = stxEstimatedFeeInWeiDec * 2;
|
||||
({ feeInFiat, feeInEth } = getFeeForSmartTransaction({
|
||||
({ feeInFiat, feeInEth, rawEthFee, feeInUsd } = getFeeForSmartTransaction({
|
||||
chainId,
|
||||
currentCurrency,
|
||||
conversionRate,
|
||||
nativeCurrencySymbol,
|
||||
feeInWeiDec: stxEstimatedFeeInWeiDec,
|
||||
}));
|
||||
additionalTrackingParams.stx_fee_in_usd = Number(feeInUsd);
|
||||
additionalTrackingParams.stx_fee_in_eth = Number(rawEthFee);
|
||||
additionalTrackingParams.estimated_gas =
|
||||
smartTransactionEstimatedGas.txData.gasLimit;
|
||||
({
|
||||
feeInFiat: maxFeeInFiat,
|
||||
feeInEth: maxFeeInEth,
|
||||
rawEthFee: maxRawEthFee,
|
||||
feeInUsd: maxFeeInUsd,
|
||||
} = getFeeForSmartTransaction({
|
||||
chainId,
|
||||
currentCurrency,
|
||||
@ -430,6 +416,8 @@ export default function ViewQuote() {
|
||||
nativeCurrencySymbol,
|
||||
feeInWeiDec: stxMaxFeeInWeiDec,
|
||||
}));
|
||||
additionalTrackingParams.stx_max_fee_in_usd = Number(maxFeeInUsd);
|
||||
additionalTrackingParams.stx_max_fee_in_eth = Number(maxRawEthFee);
|
||||
}
|
||||
|
||||
const tokenCost = new BigNumber(usedQuote.sourceAmount);
|
||||
@ -520,7 +508,7 @@ export default function ViewQuote() {
|
||||
available_quotes: numberOfQuotes,
|
||||
is_hardware_wallet: hardwareWalletUsed,
|
||||
hardware_wallet_type: hardwareWalletType,
|
||||
stx_enabled: currentSmartTransactionsEnabled,
|
||||
stx_enabled: smartTransactionsEnabled,
|
||||
current_stx_enabled: currentSmartTransactionsEnabled,
|
||||
stx_user_opt_in: smartTransactionsOptInStatus,
|
||||
};
|
||||
@ -782,6 +770,55 @@ export default function ViewQuote() {
|
||||
const isShowingWarning =
|
||||
showInsufficientWarning || shouldShowPriceDifferenceWarning;
|
||||
|
||||
const isSwapButtonDisabled =
|
||||
submitClicked ||
|
||||
balanceError ||
|
||||
tokenBalanceUnavailable ||
|
||||
disableSubmissionDueToPriceWarning ||
|
||||
(networkAndAccountSupports1559 && baseAndPriorityFeePerGas === undefined) ||
|
||||
(!networkAndAccountSupports1559 &&
|
||||
(gasPrice === null || gasPrice === undefined)) ||
|
||||
(currentSmartTransactionsEnabled && currentSmartTransactionsError);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
currentSmartTransactionsEnabled &&
|
||||
smartTransactionsOptInStatus &&
|
||||
!isSwapButtonDisabled
|
||||
) {
|
||||
const unsignedTx = {
|
||||
from: unsignedTransaction.from,
|
||||
to: unsignedTransaction.to,
|
||||
value: unsignedTransaction.value,
|
||||
data: unsignedTransaction.data,
|
||||
gas: unsignedTransaction.gas,
|
||||
chainId,
|
||||
};
|
||||
intervalId = setInterval(() => {
|
||||
dispatch(
|
||||
estimateSwapsSmartTransactionsGas(unsignedTx, approveTxParams),
|
||||
);
|
||||
}, swapsRefreshRates.stxGetTransactionsRefreshTime);
|
||||
dispatch(estimateSwapsSmartTransactionsGas(unsignedTx, approveTxParams));
|
||||
} else if (intervalId) {
|
||||
clearInterval(intervalId);
|
||||
}
|
||||
return () => clearInterval(intervalId);
|
||||
// eslint-disable-next-line
|
||||
}, [
|
||||
dispatch,
|
||||
currentSmartTransactionsEnabled,
|
||||
smartTransactionsOptInStatus,
|
||||
unsignedTransaction.data,
|
||||
unsignedTransaction.from,
|
||||
unsignedTransaction.value,
|
||||
unsignedTransaction.gas,
|
||||
unsignedTransaction.to,
|
||||
chainId,
|
||||
swapsRefreshRates.stxGetTransactionsRefreshTime,
|
||||
isSwapButtonDisabled,
|
||||
]);
|
||||
|
||||
const onCloseEditGasPopover = () => {
|
||||
setShowEditGasPopover(false);
|
||||
};
|
||||
@ -967,10 +1004,17 @@ export default function ViewQuote() {
|
||||
unsignedTransaction,
|
||||
metaMetricsEvent,
|
||||
history,
|
||||
additionalTrackingParams,
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
dispatch(signAndSendTransactions(history, metaMetricsEvent));
|
||||
dispatch(
|
||||
signAndSendTransactions(
|
||||
history,
|
||||
metaMetricsEvent,
|
||||
additionalTrackingParams,
|
||||
),
|
||||
);
|
||||
}
|
||||
} else if (destinationToken.symbol === defaultSwapsToken.symbol) {
|
||||
history.push(DEFAULT_ROUTE);
|
||||
@ -986,17 +1030,7 @@ export default function ViewQuote() {
|
||||
: t('swap')
|
||||
}
|
||||
hideCancel
|
||||
disabled={
|
||||
submitClicked ||
|
||||
balanceError ||
|
||||
tokenBalanceUnavailable ||
|
||||
disableSubmissionDueToPriceWarning ||
|
||||
(networkAndAccountSupports1559 &&
|
||||
baseAndPriorityFeePerGas === undefined) ||
|
||||
(!networkAndAccountSupports1559 &&
|
||||
(gasPrice === null || gasPrice === undefined)) ||
|
||||
(currentSmartTransactionsEnabled && currentSmartTransactionsError)
|
||||
}
|
||||
disabled={isSwapButtonDisabled}
|
||||
className={isShowingWarning && 'view-quote__thin-swaps-footer'}
|
||||
showTopBorder
|
||||
/>
|
||||
|
@ -3243,7 +3243,10 @@ export async function setWeb3ShimUsageAlertDismissed(origin) {
|
||||
}
|
||||
|
||||
// Smart Transactions Controller
|
||||
export async function setSmartTransactionsOptInStatus(optInState) {
|
||||
export async function setSmartTransactionsOptInStatus(
|
||||
optInState,
|
||||
prevOptInState,
|
||||
) {
|
||||
trackMetaMetricsEvent({
|
||||
event: 'STX OptIn',
|
||||
category: 'swaps',
|
||||
@ -3251,6 +3254,7 @@ export async function setSmartTransactionsOptInStatus(optInState) {
|
||||
stx_enabled: true,
|
||||
current_stx_enabled: true,
|
||||
stx_user_opt_in: optInState,
|
||||
stx_prev_user_opt_in: prevOptInState,
|
||||
},
|
||||
});
|
||||
await promisifiedBackground.setSmartTransactionsOptInStatus(optInState);
|
||||
|
Loading…
Reference in New Issue
Block a user