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
feature/default_network_editable
Daniel 3 years ago committed by GitHub
parent 386a760285
commit 4c16b583c3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 26
      app/_locales/en/messages.json
  2. 33
      ui/ducks/swaps/swaps.js
  3. 1
      ui/ducks/swaps/swaps.test.js
  4. 5
      ui/pages/swaps/awaiting-signatures/awaiting-signatures.js
  5. 5
      ui/pages/swaps/awaiting-swap/awaiting-swap.js
  6. 8
      ui/pages/swaps/build-quote/build-quote.js
  7. 7
      ui/pages/swaps/build-quote/index.scss
  8. 5
      ui/pages/swaps/dropdown-search-list/dropdown-search-list.js
  9. 43
      ui/pages/swaps/index.js
  10. 24
      ui/pages/swaps/index.scss
  11. 5
      ui/pages/swaps/loading-swaps-quotes/loading-swaps-quotes.js
  12. 7
      ui/pages/swaps/slippage-buttons/slippage-buttons.js
  13. 2
      ui/pages/swaps/slippage-buttons/slippage-buttons.test.js
  14. 8
      ui/pages/swaps/smart-transaction-status/smart-transaction-status.js
  15. 51
      ui/pages/swaps/swaps.util.js
  16. 158
      ui/pages/swaps/view-quote/view-quote.js
  17. 6
      ui/store/actions.js

@ -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"

@ -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,
},
);

@ -129,6 +129,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
/>

@ -3230,7 +3230,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',
@ -3238,6 +3241,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…
Cancel
Save