diff --git a/app/components/Nav/Main/RootRPCMethodsUI.js b/app/components/Nav/Main/RootRPCMethodsUI.js index 85beb32120bc..2ee97ad6753c 100644 --- a/app/components/Nav/Main/RootRPCMethodsUI.js +++ b/app/components/Nav/Main/RootRPCMethodsUI.js @@ -23,7 +23,7 @@ import { getTokenAddressParam, calcTokenAmount, getTokenValueParamAsHex, - getIsInSwapFlowTransaction, + getIsSwapApproveOrSwapTransaction, } from '../../../util/transactions'; import { BN } from 'ethereumjs-util'; import Logger from '../../../util/Logger'; @@ -63,8 +63,9 @@ import { getLedgerKeyring } from '../../../core/Ledger/Ledger'; import { createLedgerTransactionModalNavDetails } from '../../UI/LedgerModals/LedgerTransactionModal'; import ExtendedKeyringTypes from '../../../constants/keyringTypes'; import { useMetrics } from '../../../components/hooks/useMetrics'; -import { getIsSmartTransaction } from '../../../selectors/smartTransactionsController'; -import { STX_NO_HASH_ERROR } from '../../../util/smart-transactions/smart-tx'; +import { selectShouldUseSmartTransaction } from '../../../selectors/smartTransactionsController'; +import { STX_NO_HASH_ERROR } from '../../../util/smart-transactions/smart-publish-hook'; +import { getSmartTransactionMetricsProperties } from '../../../util/smart-transactions'; ///: BEGIN:ONLY_INCLUDE_IF(snaps) import InstallSnapApproval from '../../Approvals/InstallSnapApproval'; @@ -169,21 +170,11 @@ const RootRPCMethodsUI = (props) => { delete newSwapsTransactions[transactionMeta.id].analytics; delete newSwapsTransactions[transactionMeta.id].paramsForAnalytics; - let smartTransactionMetadata = {}; - if (transactionMeta) { - const smartTransaction = - SmartTransactionsController.getSmartTransactionByMinedTxHash( - transactionMeta.transactionHash, - ); - - if (smartTransaction) { - smartTransactionMetadata = { - duplicated: smartTransaction.statusMetadata.duplicated, - timedOut: smartTransaction.statusMetadata.timedOut, - proxied: smartTransaction.statusMetadata.proxied, - }; - } - } + const smartTransactionMetricsProperties = + getSmartTransactionMetricsProperties( + SmartTransactionsController, + transactionMeta, + ); const parameters = { ...analyticsParams, @@ -191,8 +182,8 @@ const RootRPCMethodsUI = (props) => { estimated_vs_used_gasRatio: estimatedVsUsedGasRatio, quote_vs_executionRatio: quoteVsExecutionRatio, token_to_amount_received: tokenToAmountReceived.toString(), - is_smart_transaction: props.isSmartTransaction, - ...smartTransactionMetadata, + is_smart_transaction: props.shouldUseSmartTransaction, + ...smartTransactionMetricsProperties, }; trackAnonymousEvent(event, parameters); @@ -205,7 +196,7 @@ const RootRPCMethodsUI = (props) => { }, [ props.selectedAddress, - props.isSmartTransaction, + props.shouldUseSmartTransaction, trackEvent, trackAnonymousEvent, ], @@ -214,8 +205,7 @@ const RootRPCMethodsUI = (props) => { const autoSign = useCallback( async (transactionMeta) => { const { TransactionController, KeyringController } = Engine.context; - const swapsTransactions = - Engine.context.TransactionController.state.swapsTransactions; + const swapsTransactions = TransactionController.state.swapsTransactions; try { TransactionController.hub.once( `${transactionMeta.id}:finished`, @@ -302,7 +292,7 @@ const RootRPCMethodsUI = (props) => { const { data } = transactionMeta.txParams; if ( - getIsInSwapFlowTransaction( + getIsSwapApproveOrSwapTransaction( data, transactionMeta.origin, to, @@ -485,9 +475,9 @@ RootRPCMethodsUI.propTypes = { */ chainId: PropTypes.string, /** - * If the transaction is a smart transaction + * If smart tranactions should be used */ - isSmartTransaction: PropTypes.bool, + shouldUseSmartTransaction: PropTypes.bool, }; const mapStateToProps = (state) => ({ @@ -495,7 +485,7 @@ const mapStateToProps = (state) => ({ chainId: selectChainId(state), tokens: selectTokens(state), providerType: selectProviderType(state), - isSmartTransaction: getIsSmartTransaction(state), + shouldUseSmartTransaction: selectShouldUseSmartTransaction(state), }); const mapDispatchToProps = (dispatch) => ({ diff --git a/app/components/UI/Notification/TransactionNotification/index.js b/app/components/UI/Notification/TransactionNotification/index.js index cc7a92f50420..46d0a25a856f 100644 --- a/app/components/UI/Notification/TransactionNotification/index.js +++ b/app/components/UI/Notification/TransactionNotification/index.js @@ -277,9 +277,7 @@ function TransactionNotification(props) { // Don't show submitted notification for STX b/c we only know when it's confirmed, // o/w a submitted notification will show up after it's confirmed, then a confirmed notification will show up immediately after if (tx.status === 'submitted') { - const smartTx = smartTransactions.find( - (stx) => stx.transactionHash === tx.transactionHash, - ); + const smartTx = smartTransactions.find((stx) => stx.txHash === tx.hash); if (smartTx) { return null; } @@ -435,15 +433,22 @@ TransactionNotification.propTypes = { const mapStateToProps = (state) => { const chainId = selectChainId(state); + + const { + SmartTransactionsController, + TransactionController, + SwapsController, + } = state.engine.backgroundState; + const smartTransactions = - state.engine.backgroundState.SmartTransactionsController - ?.smartTransactionsState?.smartTransactions?.[chainId] || []; + SmartTransactionsController?.smartTransactionsState?.smartTransactions?.[ + chainId + ] || []; return { accounts: selectAccounts(state), selectedAddress: selectSelectedAddress(state), - transactions: - state.engine.backgroundState.TransactionController.transactions, + transactions: TransactionController.transactions, ticker: selectTicker(state), chainId, tokens: selectTokensByAddress(state), @@ -452,10 +457,8 @@ const mapStateToProps = (state) => { conversionRate: selectConversionRate(state), currentCurrency: selectCurrentCurrency(state), primaryCurrency: state.settings.primaryCurrency, - swapsTransactions: - state.engine.backgroundState.TransactionController.swapsTransactions || - {}, - swapsTokens: state.engine.backgroundState.SwapsController.tokens, + swapsTransactions: TransactionController.swapsTransactions || {}, + swapsTokens: SwapsController.tokens, smartTransactions, }; }; diff --git a/app/components/UI/Swaps/QuotesView.js b/app/components/UI/Swaps/QuotesView.js index 9e928b55d418..ce56a8fba5f9 100644 --- a/app/components/UI/Swaps/QuotesView.js +++ b/app/components/UI/Swaps/QuotesView.js @@ -100,6 +100,7 @@ import { useMetrics } from '../../../components/hooks/useMetrics'; import { addTransaction } from '../../../util/transaction-controller'; import trackErrorAsAnalytics from '../../../util/metrics/TrackError/trackErrorAsAnalytics'; import { selectGasFeeEstimates } from '../../../selectors/confirmTransaction'; +import { selectShouldUseSmartTransaction } from '../../../selectors/smartTransactionsController'; const POLLING_INTERVAL = 30000; const SLIPPAGE_BUCKETS = { @@ -388,6 +389,7 @@ function SwapsQuotesView({ usedCustomGas, setRecipient, resetTransaction, + shouldUseSmartTransaction, }) { const navigation = useNavigation(); /* Get params from navigation */ @@ -992,7 +994,7 @@ function SwapsQuotesView({ 16, ).toString(10), }; - if (isHardwareAddress) { + if (isHardwareAddress || shouldUseSmartTransaction) { TransactionController.hub.once( `${transactionMeta.id}:confirmed`, (transactionMeta) => { @@ -1021,6 +1023,7 @@ function SwapsQuotesView({ selectedAddress, setRecipient, resetTransaction, + shouldUseSmartTransaction, ], ); @@ -1053,12 +1056,17 @@ function SwapsQuotesView({ } } - handleSwapTransaction( - TransactionController, - newSwapsTransactions, - approvalTransactionMetaId, - isHardwareAddress, - ); + if ( + !shouldUseSmartTransaction || + (shouldUseSmartTransaction && !approvalTransaction) + ) { + handleSwapTransaction( + TransactionController, + newSwapsTransactions, + approvalTransactionMetaId, + isHardwareAddress, + ); + } navigation.dangerouslyGetParent()?.pop(); }, [ @@ -1069,6 +1077,7 @@ function SwapsQuotesView({ handleApprovaltransaction, handleSwapTransaction, navigation, + shouldUseSmartTransaction, ]); const onEditQuoteTransactionsGas = useCallback(() => { @@ -2302,6 +2311,7 @@ SwapsQuotesView.propTypes = { usedCustomGas: PropTypes.object, setRecipient: PropTypes.func, resetTransaction: PropTypes.func, + shouldUseSmartTransaction: PropTypes.bool, }; const mapStateToProps = (state) => ({ @@ -2334,6 +2344,7 @@ const mapStateToProps = (state) => ({ usedCustomGas: state.engine.backgroundState.SwapsController.usedCustomGas, primaryCurrency: state.settings.primaryCurrency, swapsTokens: swapsTokensSelector(state), + shouldUseSmartTransaction: selectShouldUseSmartTransaction(state), }); const mapDispatchToProps = (dispatch) => ({ diff --git a/app/components/UI/Swaps/components/TokenSelectModal.js b/app/components/UI/Swaps/components/TokenSelectModal.js index 61a860b52932..0236d5a44ecf 100644 --- a/app/components/UI/Swaps/components/TokenSelectModal.js +++ b/app/components/UI/Swaps/components/TokenSelectModal.js @@ -184,12 +184,11 @@ function TokenSelectModal({ const filteredInitialTokens = useMemo( () => initialTokens?.length > 0 - ? initialTokens - .filter((token) => token !== undefined) - .filter( - (token) => - !excludedAddresses.includes(token?.address?.toLowerCase()), - ) + ? initialTokens.filter( + (token) => + typeof token !== 'undefined' && + !excludedAddresses.includes(token?.address?.toLowerCase()), + ) : filteredTokens, [excludedAddresses, filteredTokens, initialTokens], ); diff --git a/app/components/UI/TemplateRenderer/SafeComponentList.ts b/app/components/UI/TemplateRenderer/SafeComponentList.ts index b2514e070684..35751a2064ca 100644 --- a/app/components/UI/TemplateRenderer/SafeComponentList.ts +++ b/app/components/UI/TemplateRenderer/SafeComponentList.ts @@ -12,9 +12,9 @@ export const safeComponentList = { Button, Icon, SheetHeader, + SmartTransactionStatus, Text, View, - SmartTransactionStatus, }; export type SafeComponentListValues = typeof safeComponentList; diff --git a/app/components/UI/TransactionElement/TransactionDetails/index.js b/app/components/UI/TransactionElement/TransactionDetails/index.js index bba0f8a85196..ded80f167bd6 100644 --- a/app/components/UI/TransactionElement/TransactionDetails/index.js +++ b/app/components/UI/TransactionElement/TransactionDetails/index.js @@ -40,7 +40,7 @@ import { selectTokensByAddress } from '../../../../selectors/tokensController'; import { selectContractExchangeRates } from '../../../../selectors/tokenRatesController'; import { selectSelectedAddress } from '../../../../selectors/preferencesController'; import { regex } from '../../../../../app/util/regex'; -import { getIsSmartTransaction } from '../../../../selectors/smartTransactionsController'; +import { selectShouldUseSmartTransaction } from '../../../../selectors/smartTransactionsController'; const createStyles = (colors) => StyleSheet.create({ @@ -126,9 +126,9 @@ class TransactionDetails extends PureComponent { primaryCurrency: PropTypes.string, /** - * Boolean that indicates if the transaction is a smart transaction + * Boolean that indicates if smart transaction should be used */ - isSmartTransaction: PropTypes.bool, + shouldUseSmartTransaction: PropTypes.bool, }; state = { @@ -306,12 +306,14 @@ class TransactionDetails extends PureComponent { const { chainId, transactionObject: { status, time, txParams }, - isSmartTransaction, + shouldUseSmartTransaction, } = this.props; const { updatedTransactionDetails } = this.state; const styles = this.getStyles(); - const renderTxActions = status === 'submitted' || status === 'approved'; + const renderTxActions = + (status === 'submitted' || status === 'approved') && + !shouldUseSmartTransaction; const { rpcBlockExplorer } = this.state; return updatedTransactionDetails ? ( @@ -322,7 +324,7 @@ class TransactionDetails extends PureComponent { {strings('transactions.status')} - {!!renderTxActions && !isSmartTransaction && ( + {!!renderTxActions && ( {this.renderSpeedUpButton()} {this.renderCancelButton()} @@ -432,7 +434,7 @@ const mapStateToProps = (state) => ({ swapsTransactions: state.engine.backgroundState.TransactionController.swapsTransactions || {}, swapsTokens: state.engine.backgroundState.SwapsController.tokens, - isSmartTransaction: getIsSmartTransaction(state), + shouldUseSmartTransaction: selectShouldUseSmartTransaction(state), }); TransactionDetails.contextType = ThemeContext; diff --git a/app/components/UI/TransactionElement/index.js b/app/components/UI/TransactionElement/index.js index 6ab1002cc82b..a916b87399e2 100644 --- a/app/components/UI/TransactionElement/index.js +++ b/app/components/UI/TransactionElement/index.js @@ -323,8 +323,9 @@ class TransactionElement extends PureComponent { const styles = createStyles(colors, typography); const { value, fiatValue = false, actionKey } = transactionElement; const renderNormalActions = - status === 'submitted' || - (status === 'approved' && !isQRHardwareAccount && !isLedgerAccount); + (status === 'submitted' || + (status === 'approved' && !isQRHardwareAccount && !isLedgerAccount)) && + !isSmartTransaction; const renderUnsignedQRActions = status === 'approved' && isQRHardwareAccount; const renderLedgerActions = status === 'approved' && isLedgerAccount; @@ -359,7 +360,7 @@ class TransactionElement extends PureComponent { )} - {renderNormalActions && !isSmartTransaction && ( + {renderNormalActions && ( {this.renderSpeedUpButton()} {this.renderCancelButton()} diff --git a/app/components/UI/TransactionElement/utils.js b/app/components/UI/TransactionElement/utils.js index a3ead34f35a9..bc391969aa10 100644 --- a/app/components/UI/TransactionElement/utils.js +++ b/app/components/UI/TransactionElement/utils.js @@ -681,10 +681,7 @@ function decodeSwapsTx(args) { contractExchangeRates, assetSymbol, } = args; - const swapTransaction = - (swapsTransactions && - (swapsTransactions[id] || swapsTransactions[transactionHash])) || - {}; + const swapTransaction = swapsTransactions?.[id] || {}; const totalGas = calculateTotalGas({ ...txParams, gas: swapTransaction.gasUsed || gas, diff --git a/app/components/Views/Asset/index.js b/app/components/Views/Asset/index.js index 2efe470a9965..49c19a649869 100644 --- a/app/components/Views/Asset/index.js +++ b/app/components/Views/Asset/index.js @@ -485,7 +485,9 @@ class Asset extends PureComponent { }; const goToSwaps = () => { + // Pop asset screen first as it's very slow when trying to load the STX status modal if we don't navigation.pop(); + navigation.navigate(Routes.SWAPS, { screen: 'SwapsAmountView', params: { diff --git a/app/components/Views/Settings/AdvancedSettings/index.js b/app/components/Views/Settings/AdvancedSettings/index.js index 025ac5fa7243..14dd6b069a01 100644 --- a/app/components/Views/Settings/AdvancedSettings/index.js +++ b/app/components/Views/Settings/AdvancedSettings/index.js @@ -36,7 +36,7 @@ import { selectSmartTransactionsOptInStatus, selectUseTokenDetection, } from '../../../../selectors/preferencesController'; -import { getSmartTransactionsEnabled } from '../../../../selectors/smartTransactionsController'; +import { selectSmartTransactionsEnabled } from '../../../../selectors/smartTransactionsController'; import Routes from '../../../../constants/navigation/Routes'; import { MetaMetricsEvents } from '../../../../core/Analytics'; @@ -369,6 +369,7 @@ class AdvancedSettings extends PureComponent { this.props.metrics.trackEvent(MetaMetricsEvents.SMART_TRANSACTION_OPT_IN, { stx_opt_in: smartTransactionsOptInStatus, + location: 'Advanced Settings', }); }; @@ -620,7 +621,7 @@ const mapStateToProps = (state) => ({ isTokenDetectionEnabled: selectUseTokenDetection(state), chainId: selectChainId(state), smartTransactionsOptInStatus: selectSmartTransactionsOptInStatus(state), - smartTransactionsEnabled: getSmartTransactionsEnabled(state), + smartTransactionsEnabled: selectSmartTransactionsEnabled(state), }); const mapDispatchToProps = (dispatch) => ({ diff --git a/app/components/Views/Settings/AdvancedSettings/index.test.tsx b/app/components/Views/Settings/AdvancedSettings/index.test.tsx index 0ce6ba84c4ae..0010d40fc65a 100644 --- a/app/components/Views/Settings/AdvancedSettings/index.test.tsx +++ b/app/components/Views/Settings/AdvancedSettings/index.test.tsx @@ -176,7 +176,7 @@ describe('AdvancedSettings', () => { ); expect(switchElement.props.value).toBe(false); }); - it('should call PreferencesController.setSmartTransactionsOptInStatus when smart transactions opt in is pressed', async () => { + it('should update smartTransactionsOptInStatus when smart transactions opt in is pressed', async () => { const { findByLabelText } = renderWithProvider( { }; }); -const PendingApprovals = { +const PENDING_APPROVALS = { Dapp: { pending: { id: '8pJ0jVaREyCysgt8DeHcO', @@ -306,14 +306,14 @@ describe('SmartTransactionStatus', () => { , { state: initialState }, @@ -329,15 +329,15 @@ describe('SmartTransactionStatus', () => { , { state: initialState }, @@ -356,8 +356,8 @@ describe('SmartTransactionStatus', () => { it('should render success when STX has success status', () => { const { getByText } = renderWithProvider( , { state: initialState }, @@ -373,8 +373,8 @@ describe('SmartTransactionStatus', () => { it('should navigate to Activity page on press of primary button', () => { const { getByText } = renderWithProvider( , { state: initialState }, @@ -390,8 +390,8 @@ describe('SmartTransactionStatus', () => { const onConfirm = jest.fn(); const { getByText } = renderWithProvider( , { state: initialState }, @@ -399,7 +399,7 @@ describe('SmartTransactionStatus', () => { const secondaryButton = getByText( strings('smart_transactions.return_to_dapp', { - dappName: PendingApprovals.Dapp.success.origin, + dappName: PENDING_APPROVALS.Dapp.success.origin, }), ); fireEvent.press(secondaryButton); @@ -412,8 +412,8 @@ describe('SmartTransactionStatus', () => { it('should navigate to Send page on press of primary button', () => { const { getByText } = renderWithProvider( , { state: initialState }, @@ -430,8 +430,8 @@ describe('SmartTransactionStatus', () => { it('should navigate to Activity page on press of secondary button', () => { const { getByText } = renderWithProvider( , { state: initialState }, @@ -449,8 +449,8 @@ describe('SmartTransactionStatus', () => { it('should navigate to Swaps page on press of primary button', () => { const { getByText } = renderWithProvider( , { state: initialState }, @@ -467,8 +467,8 @@ describe('SmartTransactionStatus', () => { it('should navigate to Activity page on press of secondary button', () => { const { getByText } = renderWithProvider( , { state: initialState }, @@ -487,8 +487,8 @@ describe('SmartTransactionStatus', () => { it('should render cancelled when STX has cancelled status', () => { const { getByText } = renderWithProvider( , { state: initialState }, @@ -505,8 +505,10 @@ describe('SmartTransactionStatus', () => { const onConfirm = jest.fn(); const { getByText } = renderWithProvider( , { state: initialState }, @@ -514,7 +516,7 @@ describe('SmartTransactionStatus', () => { const secondaryButton = getByText( strings('smart_transactions.return_to_dapp', { - dappName: PendingApprovals.Dapp.cancelled.origin, + dappName: PENDING_APPROVALS.Dapp.cancelled.origin, }), ); fireEvent.press(secondaryButton); @@ -526,8 +528,10 @@ describe('SmartTransactionStatus', () => { it('should navigate to Send page on press of primary button', () => { const { getByText } = renderWithProvider( , { state: initialState }, @@ -542,8 +546,10 @@ describe('SmartTransactionStatus', () => { it('should navigate to Activity page on press of secondary button', () => { const { getByText } = renderWithProvider( , { state: initialState }, @@ -560,8 +566,10 @@ describe('SmartTransactionStatus', () => { it('should navigate to Swaps page on press of primary button', () => { const { getByText } = renderWithProvider( , { state: initialState }, @@ -576,8 +584,10 @@ describe('SmartTransactionStatus', () => { it('should navigate to Activity page on press of secondary button', () => { const { getByText } = renderWithProvider( , { state: initialState }, @@ -596,8 +606,8 @@ describe('SmartTransactionStatus', () => { it('should render failed when STX has unknown status', () => { const { getByText } = renderWithProvider( , { state: initialState }, @@ -614,8 +624,8 @@ describe('SmartTransactionStatus', () => { const onConfirm = jest.fn(); const { getByText } = renderWithProvider( , { state: initialState }, @@ -623,7 +633,7 @@ describe('SmartTransactionStatus', () => { const secondaryButton = getByText( strings('smart_transactions.return_to_dapp', { - dappName: PendingApprovals.Dapp.unknown.origin, + dappName: PENDING_APPROVALS.Dapp.unknown.origin, }), ); fireEvent.press(secondaryButton); @@ -636,8 +646,8 @@ describe('SmartTransactionStatus', () => { const onConfirm = jest.fn(); const { getByText } = renderWithProvider( , { state: initialState }, @@ -653,8 +663,8 @@ describe('SmartTransactionStatus', () => { it('should navigate to Activity page on press of secondary button', () => { const { getByText } = renderWithProvider( , { state: initialState }, @@ -672,8 +682,8 @@ describe('SmartTransactionStatus', () => { const onConfirm = jest.fn(); const { getByText } = renderWithProvider( , { state: initialState }, @@ -689,8 +699,8 @@ describe('SmartTransactionStatus', () => { it('should navigate to Activity page on press of secondary button', () => { const { getByText } = renderWithProvider( , { state: initialState }, diff --git a/app/components/Views/SmartTransactionStatus/SmartTransactionStatus.tsx b/app/components/Views/SmartTransactionStatus/SmartTransactionStatus.tsx index 69e4f1072678..135549d82bce 100644 --- a/app/components/Views/SmartTransactionStatus/SmartTransactionStatus.tsx +++ b/app/components/Views/SmartTransactionStatus/SmartTransactionStatus.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import React from 'react'; import { View, Text, StyleSheet, TouchableOpacity } from 'react-native'; import { strings } from '../../../../locales/i18n'; import Icon, { @@ -16,7 +16,6 @@ import { import { useSelector } from 'react-redux'; import { selectProviderConfig } from '../../../selectors/networkController'; import { useNavigation } from '@react-navigation/native'; -import { getSwapsChainFeatureFlags } from '../../../reducers/swaps'; import Button, { ButtonVariants, } from '../../../component-library/components/Buttons/Button'; @@ -25,6 +24,7 @@ import TransactionBackgroundTop from '../../../images/transaction-background-top import TransactionBackgroundBottom from '../../../images/transaction-background-bottom.svg'; import LoopingScrollAnimation from './LoopingScrollAnimation'; import { hexToDecimal } from '../../../util/conversions'; +import useRemainingTime from './useRemainingTime'; const getPortfolioStxLink = (chainId: Hex, uuid: string) => { const chainIdDec = hexToDecimal(chainId); @@ -56,6 +56,145 @@ export const showRemainingTimeInMinAndSec = ( return `${minutes}:${seconds.toString().padStart(2, '0')}`; }; +interface getDisplayValuesArgs { + status: string | undefined; + isStxPending: boolean; + isStxPastEstimatedDeadline: boolean; + timeLeftForPendingStxInSec: number; + isDapp: boolean; + isInSwapFlow: boolean; + origin: string; + viewActivity: () => void; + closeStatusPage: () => void; + createNewSwap: () => void; + createNewSend: () => void; +} + +const getDisplayValuesAndHandlers = ({ + status, + isStxPending, + isStxPastEstimatedDeadline, + timeLeftForPendingStxInSec, + isDapp, + isInSwapFlow, + origin, + viewActivity, + closeStatusPage, + createNewSwap, + createNewSend, +}: getDisplayValuesArgs) => { + const returnTextDapp = strings('smart_transactions.return_to_dapp', { + dappName: origin, + }); + const returnTextMM = strings('smart_transactions.try_again'); + + // Set icon, header, desc, and buttons + let icon; + let iconColor; + let header; + let description; + let primaryButtonText; + let secondaryButtonText; + let handlePrimaryButtonPress; + let handleSecondaryButtonPress; + + if (isStxPending && isStxPastEstimatedDeadline) { + icon = IconName.Clock; + iconColor = IconColor.Primary; + header = strings( + 'smart_transactions.status_submitting_past_estimated_deadline_header', + ); + description = strings( + 'smart_transactions.status_submitting_past_estimated_deadline_description', + { + timeLeft: showRemainingTimeInMinAndSec(timeLeftForPendingStxInSec), + }, + ); + } else if (isStxPending) { + icon = IconName.Clock; + iconColor = IconColor.Primary; + header = strings('smart_transactions.status_submitting_header'); + description = strings('smart_transactions.status_submitting_description', { + timeLeft: showRemainingTimeInMinAndSec(timeLeftForPendingStxInSec), + }); + } else if (status === SmartTransactionStatuses.SUCCESS) { + icon = IconName.Confirmation; + iconColor = IconColor.Success; + header = strings('smart_transactions.status_success_header'); + description = undefined; + + if (isDapp) { + primaryButtonText = strings('smart_transactions.view_activity'); + handlePrimaryButtonPress = viewActivity; + secondaryButtonText = returnTextDapp; + handleSecondaryButtonPress = closeStatusPage; + } else { + if (isInSwapFlow) { + primaryButtonText = strings('smart_transactions.create_new', { + txType: strings('smart_transactions.swap'), + }); + handlePrimaryButtonPress = createNewSwap; + } else { + primaryButtonText = strings('smart_transactions.create_new', { + txType: strings('smart_transactions.send'), + }); + handlePrimaryButtonPress = createNewSend; + } + + secondaryButtonText = strings('smart_transactions.view_activity'); + handleSecondaryButtonPress = viewActivity; + } + } else if (status?.startsWith(SmartTransactionStatuses.CANCELLED)) { + icon = IconName.Danger; + iconColor = IconColor.Error; + header = strings('smart_transactions.status_cancelled_header'); + description = strings('smart_transactions.status_cancelled_description'); + + if (isDapp) { + secondaryButtonText = returnTextDapp; + handleSecondaryButtonPress = closeStatusPage; + } else { + primaryButtonText = returnTextMM; + + if (isInSwapFlow) { + handlePrimaryButtonPress = createNewSwap; + } else { + handlePrimaryButtonPress = createNewSend; + } + + secondaryButtonText = strings('smart_transactions.view_activity'); + handleSecondaryButtonPress = viewActivity; + } + } else { + // Reverted or unknown statuses (tx failed) + icon = IconName.Danger; + iconColor = IconColor.Error; + header = strings('smart_transactions.status_failed_header'); + description = strings('smart_transactions.status_failed_description'); + + if (isDapp) { + secondaryButtonText = returnTextDapp; + handleSecondaryButtonPress = closeStatusPage; + } else { + primaryButtonText = returnTextMM; + handlePrimaryButtonPress = closeStatusPage; + secondaryButtonText = strings('smart_transactions.view_activity'); + handleSecondaryButtonPress = viewActivity; + } + } + + return { + icon, + iconColor, + header, + description, + primaryButtonText, + secondaryButtonText, + handlePrimaryButtonPress, + handleSecondaryButtonPress, + }; +}; + const SmartTransactionStatus = ({ requestState: { smartTransaction, isDapp, isInSwapFlow }, origin, @@ -63,24 +202,10 @@ const SmartTransactionStatus = ({ }: Props) => { const { status, creationTime, uuid } = smartTransaction; const providerConfig = useSelector(selectProviderConfig); - const swapFeatureFlags = useSelector(getSwapsChainFeatureFlags); const navigation = useNavigation(); const { colors } = useTheme(); - const stxEstimatedDeadlineSec = - swapFeatureFlags?.smartTransactions?.expectedDeadline || - FALLBACK_STX_ESTIMATED_DEADLINE_SEC; - const stxMaxDeadlineSec = - swapFeatureFlags?.smartTransactions?.maxDeadline || - FALLBACK_STX_MAX_DEADLINE_SEC; - - const [isStxPastEstimatedDeadline, setIsStxPastEstimatedDeadline] = - useState(false); - const [timeLeftForPendingStxInSec, setTimeLeftForPendingStxInSec] = useState( - stxEstimatedDeadlineSec, - ); - // Setup styles const styles = StyleSheet.create({ wrapper: { @@ -134,60 +259,14 @@ const SmartTransactionStatus = ({ const isStxPending = status === SmartTransactionStatuses.PENDING; - // Calc time left for progress bar and timer display - const stxDeadlineSec = isStxPastEstimatedDeadline - ? stxMaxDeadlineSec - : stxEstimatedDeadlineSec; - - useEffect(() => { - let intervalId: NodeJS.Timeout; - if (isStxPending && creationTime) { - const calculateRemainingTime = () => { - const secondsAfterStxSubmission = Math.round( - (Date.now() - creationTime) / 1000, - ); - if (secondsAfterStxSubmission > stxDeadlineSec) { - if (isStxPastEstimatedDeadline) { - setTimeLeftForPendingStxInSec(0); - clearInterval(intervalId); - return; - } - setIsStxPastEstimatedDeadline(true); - } - setTimeLeftForPendingStxInSec( - stxDeadlineSec - secondsAfterStxSubmission, - ); - }; - intervalId = setInterval(calculateRemainingTime, 1000); - calculateRemainingTime(); - } - - return () => clearInterval(intervalId); - }, [isStxPending, isStxPastEstimatedDeadline, creationTime, stxDeadlineSec]); - - // Set block explorer link and show explorer on click - const txUrl = getPortfolioStxLink(providerConfig.chainId, uuid); - - const onViewTransaction = () => { - navigation.navigate('Webview', { - screen: 'SimpleWebview', - params: { - url: txUrl, - }, - }); - // Close SmartTransactionStatus - onConfirm(); - }; - - // Set icon, header, desc, and buttons - let icon; - let iconColor; - let header; - let description; - let primaryButtonText; - let secondaryButtonText; - let onPrimaryButtonPress; - let onSecondaryButtonPress; + const { + timeLeftForPendingStxInSec, + stxDeadlineSec, + isStxPastEstimatedDeadline, + } = useRemainingTime({ + creationTime, + isStxPending, + }); const viewActivity = () => { onConfirm(); @@ -208,99 +287,78 @@ const SmartTransactionStatus = ({ navigation.navigate('SendFlowView'); }; - const returnTextDapp = strings('smart_transactions.return_to_dapp', { - dappName: origin, + const { + icon, + iconColor, + header, + description, + primaryButtonText, + secondaryButtonText, + handlePrimaryButtonPress, + handleSecondaryButtonPress, + } = getDisplayValuesAndHandlers({ + status, + isStxPending, + isStxPastEstimatedDeadline, + timeLeftForPendingStxInSec, + isDapp, + isInSwapFlow, + origin, + viewActivity, + closeStatusPage, + createNewSwap, + createNewSend, }); - const returnTextMM = strings('smart_transactions.try_again'); - if (isStxPending && isStxPastEstimatedDeadline) { - icon = IconName.Clock; - iconColor = IconColor.Primary; - header = strings( - 'smart_transactions.status_submitting_past_estimated_deadline_header', - ); - description = strings( - 'smart_transactions.status_submitting_past_estimated_deadline_description', - { - timeLeft: showRemainingTimeInMinAndSec(timeLeftForPendingStxInSec), + // Set block explorer link and show explorer on click + const txUrl = getPortfolioStxLink(providerConfig.chainId, uuid); + + const onViewTransaction = () => { + navigation.navigate('Webview', { + screen: 'SimpleWebview', + params: { + url: txUrl, }, - ); - } else if (isStxPending) { - icon = IconName.Clock; - iconColor = IconColor.Primary; - header = strings('smart_transactions.status_submitting_header'); - description = strings('smart_transactions.status_submitting_description', { - timeLeft: showRemainingTimeInMinAndSec(timeLeftForPendingStxInSec), }); - } else if (status === SmartTransactionStatuses.SUCCESS) { - icon = IconName.Confirmation; - iconColor = IconColor.Success; - header = strings('smart_transactions.status_success_header'); - description = undefined; - - if (isDapp) { - primaryButtonText = strings('smart_transactions.view_activity'); - onPrimaryButtonPress = viewActivity; - secondaryButtonText = returnTextDapp; - onSecondaryButtonPress = closeStatusPage; - } else { - if (isInSwapFlow) { - primaryButtonText = strings('smart_transactions.create_new', { - txType: strings('smart_transactions.swap'), - }); - onPrimaryButtonPress = createNewSwap; - } else { - primaryButtonText = strings('smart_transactions.create_new', { - txType: strings('smart_transactions.send'), - }); - onPrimaryButtonPress = createNewSend; - } - - secondaryButtonText = strings('smart_transactions.view_activity'); - onSecondaryButtonPress = viewActivity; - } - } else if (status?.startsWith(SmartTransactionStatuses.CANCELLED)) { - icon = IconName.Danger; - iconColor = IconColor.Error; - header = strings('smart_transactions.status_cancelled_header'); - description = strings('smart_transactions.status_cancelled_description'); - - if (isDapp) { - secondaryButtonText = returnTextDapp; - onSecondaryButtonPress = closeStatusPage; - } else { - primaryButtonText = returnTextMM; - - if (isInSwapFlow) { - onPrimaryButtonPress = createNewSwap; - } else { - onPrimaryButtonPress = createNewSend; - } - - secondaryButtonText = strings('smart_transactions.view_activity'); - onSecondaryButtonPress = viewActivity; - } - } else { - // Reverted or unknown statuses (tx failed) - icon = IconName.Danger; - iconColor = IconColor.Error; - header = strings('smart_transactions.status_failed_header'); - description = strings('smart_transactions.status_failed_description'); - - if (isDapp) { - secondaryButtonText = returnTextDapp; - onSecondaryButtonPress = closeStatusPage; - } else { - primaryButtonText = returnTextMM; - onPrimaryButtonPress = closeStatusPage; - secondaryButtonText = strings('smart_transactions.view_activity'); - onSecondaryButtonPress = viewActivity; - } - } + // Close SmartTransactionStatus + onConfirm(); + }; const percentComplete = (1 - timeLeftForPendingStxInSec / stxDeadlineSec) * 100; + const PrimaryButton = () => + handlePrimaryButtonPress ? ( + + ) : null; + + const SecondaryButton = () => + handleSecondaryButtonPress ? ( + + ) : null; + + const ViewTransactionLink = () => ( + + + {strings('smart_transactions.view_transaction')} + + + ); + return ( @@ -318,11 +376,7 @@ const SmartTransactionStatus = ({ {description && {description}} - - - {strings('smart_transactions.view_transaction')} - - + @@ -330,26 +384,8 @@ const SmartTransactionStatus = ({ - {onPrimaryButtonPress && ( - - )} - {onSecondaryButtonPress && ( - - )} + + ); diff --git a/app/components/Views/SmartTransactionsOptInModal/SmartTranactionsOptInModal.tsx b/app/components/Views/SmartTransactionsOptInModal/SmartTranactionsOptInModal.tsx index a20e46f54e8d..ae80df6d0723 100644 --- a/app/components/Views/SmartTransactionsOptInModal/SmartTranactionsOptInModal.tsx +++ b/app/components/Views/SmartTransactionsOptInModal/SmartTranactionsOptInModal.tsx @@ -31,12 +31,13 @@ import Button, { } from '../../../component-library/components/Buttons/Button'; import AppConstants from '../../../core/AppConstants'; import backgroundImage from '../../../images/smart-transactions-opt-in-bg.png'; +import { MetaMetricsEvents, useMetrics } from '../../hooks/useMetrics'; -const modalMargin = 24; -const modalPadding = 24; +const MODAL_MARGIN = 24; +const MODAL_PADDING = 24; const screenWidth = Device.getDeviceWidth(); const screenHeight = Device.getDeviceHeight(); -const itemWidth = screenWidth - modalMargin * 2; +const itemWidth = screenWidth - MODAL_MARGIN * 2; const maxItemHeight = screenHeight - 200; const createStyles = (colors: Colors) => @@ -46,7 +47,7 @@ const createStyles = (colors: Colors) => }, content: { gap: 16, - paddingHorizontal: modalPadding, + paddingHorizontal: MODAL_PADDING, }, buttons: { gap: 10, @@ -56,6 +57,9 @@ const createStyles = (colors: Colors) => width: '100%', textAlign: 'center', }, + secondaryButtonText: { + color: colors.text.alternative, + }, header: { alignItems: 'center', }, @@ -66,7 +70,7 @@ const createStyles = (colors: Colors) => modal: { backgroundColor: colors.background.default, borderRadius: 10, - marginHorizontal: modalMargin, + marginHorizontal: MODAL_MARGIN, }, bodyContainer: { width: itemWidth, @@ -118,7 +122,7 @@ const Benefit = ({ iconName, text }: Props) => { {t} @@ -132,6 +136,8 @@ const Benefit = ({ iconName, text }: Props) => { const SmartTransactionsOptInModal = () => { const modalRef = useRef(null); const { colors } = useTheme(); + const { trackEvent } = useMetrics(); + const styles = createStyles(colors); const hasOptedIn = useRef(null); @@ -142,17 +148,27 @@ const SmartTransactionsOptInModal = () => { const optIn = () => { Engine.context.PreferencesController.setSmartTransactionsOptInStatus(true); + trackEvent(MetaMetricsEvents.SMART_TRANSACTION_OPT_IN, { + stx_opt_in: true, + location: 'SmartTransactionsOptInModal', + }); + hasOptedIn.current = true; dismissModal(); }; const optOut = () => { Engine.context.PreferencesController.setSmartTransactionsOptInStatus(false); + trackEvent(MetaMetricsEvents.SMART_TRANSACTION_OPT_IN, { + stx_opt_in: false, + location: 'SmartTransactionsOptInModal', + }); + hasOptedIn.current = false; dismissModal(); }; - const onDismiss = async () => { + const handleDismiss = async () => { // Opt out of STX if no prior decision made if (hasOptedIn.current === null) { optOut(); @@ -166,8 +182,89 @@ const SmartTransactionsOptInModal = () => { ); }; + const Header = () => ( + + + {strings('whats_new.stx.header')} + + + ); + + const Benefits = () => ( + + + + + + ); + + const Descriptions = () => ( + + {strings('whats_new.stx.description_1')} + + {strings('whats_new.stx.description_2')}{' '} + { + Linking.openURL(AppConstants.URLS.SMART_TXS); + }} + > + {strings('whats_new.stx.learn_more')} + + + + ); + + const PrimaryButton = () => ( + + ); + + const SecondaryButton = () => ( + + ); + return ( - + { resizeMode="cover" style={styles.backgroundImage} > - {/* Header */} - - - {strings('whats_new.stx.header')} - - - - {/* Benefits */} - - - - - +
+ {/* Content */} - - {strings('whats_new.stx.description_1')} - {strings('whats_new.stx.description_2')} - - {strings('whats_new.stx.description_3')}{' '} - { - Linking.openURL(AppConstants.URLS.SMART_TXS); - }} - > - {strings('whats_new.stx.learn_more')} - - - + - - - + + diff --git a/app/components/Views/SmartTransactionsOptInModal/SmartTransactionsOptInModal.test.tsx b/app/components/Views/SmartTransactionsOptInModal/SmartTransactionsOptInModal.test.tsx index 512e8fccf080..7a8a685c7d41 100644 --- a/app/components/Views/SmartTransactionsOptInModal/SmartTransactionsOptInModal.test.tsx +++ b/app/components/Views/SmartTransactionsOptInModal/SmartTransactionsOptInModal.test.tsx @@ -67,13 +67,10 @@ describe('SmartTransactionsOptInModal', () => { const description1 = getByText(strings('whats_new.stx.description_1')); expect(description1).toBeDefined(); - const description2 = getByText(strings('whats_new.stx.description_2')); - expect(description2).toBeDefined(); - - const description3 = getByText(strings('whats_new.stx.description_3'), { + const description2 = getByText(strings('whats_new.stx.description_2'), { exact: false, }); - expect(description3).toBeDefined(); + expect(description2).toBeDefined(); const primaryButton = getByText(strings('whats_new.stx.primary_button')); expect(primaryButton).toBeDefined(); diff --git a/app/components/Views/TransactionsView/index.js b/app/components/Views/TransactionsView/index.js index b5d6c6d976db..c86ae07e68de 100644 --- a/app/components/Views/TransactionsView/index.js +++ b/app/components/Views/TransactionsView/index.js @@ -35,7 +35,8 @@ import { import { WalletViewSelectorsIDs } from '../../../../e2e/selectors/WalletView.selectors'; import { store } from '../../../store'; import { NETWORK_ID_LOADING } from '../../../core/redux/slices/inpageProvider'; -import { SmartTransactionStatuses } from '@metamask/smart-transactions-controller/dist/types'; +import { selectPendingSmartTransactionsBySender } from '../../../selectors/smartTransactionsController'; +import { selectNonReplacedTransactions } from '../../../selectors/transactionController'; const styles = StyleSheet.create({ wrapper: { @@ -221,42 +222,13 @@ TransactionsView.propTypes = { const mapStateToProps = (state) => { const selectedAddress = selectSelectedAddress(state); const chainId = selectChainId(state); - const nonSmartTransactions = - state.engine.backgroundState.TransactionController.transactions; // these are transactionMeta objs - const smartTransactions = - state.engine.backgroundState.SmartTransactionsController - ?.smartTransactionsState?.smartTransactions?.[chainId] || []; // Remove duplicate confirmed STX - // for replaced txs, only hide the ones that are confirmed - const filteredNonSmartTransactions = nonSmartTransactions.filter( - (tx) => !(tx.replacedBy && tx.replacedById && tx.transactionHash), - ); + const nonReplacedTransactions = selectNonReplacedTransactions(state); - const filteredPendingSmartTransactions = - smartTransactions - ?.filter((stx) => { - const { transaction } = stx; - return ( - transaction?.from.toLowerCase() === selectedAddress.toLowerCase() && - stx.status && - stx.status !== SmartTransactionStatuses.SUCCESS && - stx.status !== SmartTransactionStatuses.CANCELLED - ); - }) - .map((stx) => ({ - ...stx, - // stx.uuid is one from sentinel API, not the same as tx.id which is generated client side - // Doesn't matter too much because we only care about the pending stx, confirmed txs are handled like normal - // However, this does make it impossible to read Swap data from TxController.swapsTransactions as that relies on client side tx.id - // To fix that we do transactionController.update({ swapsTransactions: newSwapsTransactions }) in app/util/smart-transactions/smart-tx.ts - id: stx.uuid, - status: stx.status?.startsWith(SmartTransactionStatuses.CANCELLED) - ? SmartTransactionStatuses.CANCELLED - : stx.status, - isSmartTransaction: true, - })) ?? []; + const pendingSmartTransactions = + selectPendingSmartTransactionsBySender(state); return { conversionRate: selectConversionRate(state), @@ -265,8 +237,8 @@ const mapStateToProps = (state) => { selectedAddress, identities: selectIdentities(state), transactions: [ - ...filteredNonSmartTransactions, - ...filteredPendingSmartTransactions, + ...nonReplacedTransactions, + ...pendingSmartTransactions, ].sort((a, b) => b.time - a.time), networkType: selectProviderType(state), chainId, diff --git a/app/components/Views/Wallet/index.tsx b/app/components/Views/Wallet/index.tsx index 86e0464a9bcf..e393395628b0 100644 --- a/app/components/Views/Wallet/index.tsx +++ b/app/components/Views/Wallet/index.tsx @@ -479,7 +479,7 @@ const Wallet = ({ navigation }: any) => { */ const renderOnboardingWizard = useCallback( () => - [1, 2, 3].includes(wizardStep) && ( + [1, 2, 3, 4, 5, 6, 7].includes(wizardStep) && ( { try { - const { chainId, transaction, selectedAddress, isSmartTransaction } = - this.props; + const { + chainId, + transaction, + selectedAddress, + shouldUseSmartTransaction, + } = this.props; const { selectedAsset } = transaction; const { TransactionController, SmartTransactionsController } = Engine.context; @@ -319,21 +324,11 @@ class Approval extends PureComponent { transaction.id, ); - let smartTransactionMetadata = {}; - if (transactionMeta) { - const smartTransaction = - SmartTransactionsController.getSmartTransactionByMinedTxHash( - transactionMeta.transactionHash, - ); - - if (smartTransaction) { - smartTransactionMetadata = { - duplicated: smartTransaction.statusMetadata.duplicated, - timedOut: smartTransaction.statusMetadata.timedOut, - proxied: smartTransaction.statusMetadata.proxied, - }; - } - } + const smartTransactionMetricsProperties = + getSmartTransactionMetricsProperties( + SmartTransactionsController, + transactionMeta, + ); return { account_type: getAddressAccountType(selectedAddress), @@ -349,8 +344,8 @@ class Approval extends PureComponent { : this.originIsWalletConnect ? AppConstants.REQUEST_SOURCES.WC : AppConstants.REQUEST_SOURCES.IN_APP_BROWSER, - is_smart_transaction: isSmartTransaction, - ...smartTransactionMetadata, + is_smart_transaction: shouldUseSmartTransaction, + ...smartTransactionMetricsProperties, }; } catch (error) { return {}; @@ -434,7 +429,7 @@ class Approval extends PureComponent { transaction: { assetType, selectedAsset }, showCustomNonce, chainId, - isSmartTransaction, + shouldUseSmartTransaction, } = this.props; let { transaction } = this.props; const { nonce } = transaction; @@ -471,7 +466,7 @@ class Approval extends PureComponent { } // For STX, don't wait for TxController to get finished event, since it will take some time to get hash for STX - if (isSmartTransaction) { + if (shouldUseSmartTransaction) { this.setState({ transactionHandled: true }); this.props.hideModal(); } @@ -698,7 +693,7 @@ const mapStateToProps = (state) => ({ showCustomNonce: state.settings.showCustomNonce, chainId: selectChainId(state), activeTabUrl: getActiveTabUrl(state), - isSmartTransaction: getIsSmartTransaction(state), + shouldUseSmartTransaction: selectShouldUseSmartTransaction(state), }); const mapDispatchToProps = (dispatch) => ({ diff --git a/app/components/Views/confirmations/ApproveView/Approve/index.js b/app/components/Views/confirmations/ApproveView/Approve/index.js index 7ecf3758fe21..7f5459a12b38 100644 --- a/app/components/Views/confirmations/ApproveView/Approve/index.js +++ b/app/components/Views/confirmations/ApproveView/Approve/index.js @@ -75,8 +75,8 @@ import { updateTransaction } from '../../../../../util/transaction-controller'; import { withMetricsAwareness } from '../../../../../components/hooks/useMetrics'; import { selectGasFeeEstimates } from '../../../../../selectors/confirmTransaction'; import { selectGasFeeControllerEstimateType } from '../../../../../selectors/gasFeeController'; -import { getIsSmartTransaction } from '../../../../../selectors/smartTransactionsController'; -import { STX_NO_HASH_ERROR } from '../../../../../util/smart-transactions/smart-tx'; +import { selectShouldUseSmartTransaction } from '../../../../../selectors/smartTransactionsController'; +import { STX_NO_HASH_ERROR } from '../../../../../util/smart-transactions/smart-publish-hook'; const EDIT = 'edit'; const REVIEW = 'review'; @@ -174,9 +174,9 @@ class Approve extends PureComponent { */ metrics: PropTypes.object, /** - * Indicates if a transaction is going to be routed through smart tx + * Boolean that indicates if smart transaction should be used */ - isSmartTransaction: PropTypes.bool, + shouldUseSmartTransaction: PropTypes.bool, }; state = { @@ -509,9 +509,9 @@ class Approve extends PureComponent { const { transactions, gasEstimateType, - isSmartTransaction, metrics, chainId, + shouldUseSmartTransaction, } = this.props; const { legacyGasTransaction, @@ -586,15 +586,11 @@ class Approve extends PureComponent { return; } - if (isSmartTransaction) { - await ApprovalController.accept(transaction.id, undefined, { - waitForResult: false, - }); + await ApprovalController.accept(transaction.id, undefined, { + waitForResult: !shouldUseSmartTransaction, + }); + if (shouldUseSmartTransaction) { this.props.hideModal(); - } else { - await ApprovalController.accept(transaction.id, undefined, { - waitForResult: true, - }); } metrics.trackEvent( @@ -935,7 +931,7 @@ const mapStateToProps = (state) => ({ providerType: selectProviderType(state), providerRpcTarget: selectRpcUrl(state), networkConfigurations: selectNetworkConfigurations(state), - isSmartTransaction: getIsSmartTransaction(state), + shouldUseSmartTransaction: selectShouldUseSmartTransaction(state), }); const mapDispatchToProps = (dispatch) => ({ diff --git a/app/components/Views/confirmations/Send/index.js b/app/components/Views/confirmations/Send/index.js index 82064682b174..43c96be83ab5 100644 --- a/app/components/Views/confirmations/Send/index.js +++ b/app/components/Views/confirmations/Send/index.js @@ -63,8 +63,8 @@ import { } from '../../../../selectors/preferencesController'; import { providerErrors } from '@metamask/rpc-errors'; import { withMetricsAwareness } from '../../../../components/hooks/useMetrics'; -import { getIsSmartTransaction } from '../../../../selectors/smartTransactionsController'; -import { STX_NO_HASH_ERROR } from '../../../../util/smart-transactions/smart-tx'; +import { selectShouldUseSmartTransaction } from '../../../../selectors/smartTransactionsController'; +import { STX_NO_HASH_ERROR } from '../../../../util/smart-transactions/smart-publish-hook'; const REVIEW = 'review'; const EDIT = 'edit'; @@ -158,9 +158,9 @@ class Send extends PureComponent { */ metrics: PropTypes.object, /** - * Boolean that represents whether the transaction is a smart transaction + * Boolean that indicates if smart transaction should be used */ - isSmartTransaction: PropTypes.bool, + shouldUseSmartTransaction: PropTypes.bool, }; state = { @@ -698,7 +698,7 @@ class Send extends PureComponent { networkType, transaction, transaction: { selectedAsset, assetType }, - isSmartTransaction, + shouldUseSmartTransaction, } = this.props; return { @@ -710,7 +710,7 @@ class Send extends PureComponent { 'ETH', assetType, ...getBlockaidTransactionMetricsParams(transaction), - is_smart_transaction: isSmartTransaction, + is_smart_transaction: shouldUseSmartTransaction, }; }; @@ -787,7 +787,7 @@ const mapStateToProps = (state) => ({ selectedAddress: selectSelectedAddress(state), dappTransactionModalVisible: state.modals.dappTransactionModalVisible, tokenList: selectTokenList(state), - isSmartTransaction: getIsSmartTransaction(state), + shouldUseSmartTransaction: selectShouldUseSmartTransaction(state), }); const mapDispatchToProps = (dispatch) => ({ diff --git a/app/components/Views/confirmations/SendFlow/Confirm/index.js b/app/components/Views/confirmations/SendFlow/Confirm/index.js index 0b4f21eb6f78..9330cce1afeb 100644 --- a/app/components/Views/confirmations/SendFlow/Confirm/index.js +++ b/app/components/Views/confirmations/SendFlow/Confirm/index.js @@ -119,8 +119,9 @@ import { withMetricsAwareness } from '../../../../../components/hooks/useMetrics import { selectTransactionGasFeeEstimates } from '../../../../../selectors/confirmTransaction'; import { selectGasFeeControllerEstimateType } from '../../../../../selectors/gasFeeController'; import { updateTransaction } from '../../../../../util/transaction-controller'; -import { getIsSmartTransaction } from '../../../../../selectors/smartTransactionsController'; -import { STX_NO_HASH_ERROR } from '../../../../../util/smart-transactions/smart-tx'; +import { selectShouldUseSmartTransaction } from '../../../../../selectors/smartTransactionsController'; +import { STX_NO_HASH_ERROR } from '../../../../../util/smart-transactions/smart-publish-hook'; +import { getSmartTransactionMetricsProperties } from '../../../../../util/smart-transactions'; const EDIT = 'edit'; const EDIT_NONCE = 'edit_nonce'; @@ -243,9 +244,9 @@ class Confirm extends PureComponent { */ setTransactionId: PropTypes.func, /** - * Indicates if a transaction is going to be routed through smart tx + * Boolean that indicates if smart transaction should be used */ - isSmartTransaction: PropTypes.bool, + shouldUseSmartTransaction: PropTypes.bool, }; state = { @@ -287,26 +288,20 @@ class Confirm extends PureComponent { getAnalyticsParams = (transactionMeta) => { try { - const { selectedAsset, gasEstimateType, chainId, isSmartTransaction } = - this.props; + const { + selectedAsset, + gasEstimateType, + chainId, + shouldUseSmartTransaction, + } = this.props; const { gasSelected, fromSelectedAddress } = this.state; const { SmartTransactionsController } = Engine.context; - let smartTransactionMetadata = {}; - if (transactionMeta) { - const smartTransaction = - SmartTransactionsController.getSmartTransactionByMinedTxHash( - transactionMeta.transactionHash, - ); - - if (smartTransaction) { - smartTransactionMetadata = { - duplicated: smartTransaction.statusMetadata.duplicated, - timedOut: smartTransaction.statusMetadata.timedOut, - proxied: smartTransaction.statusMetadata.proxied, - }; - } - } + const smartTransactionMetricsProperties = + getSmartTransactionMetricsProperties( + SmartTransactionsController, + transactionMeta, + ); return { active_currency: { value: selectedAsset?.symbol, anonymous: true }, @@ -321,8 +316,8 @@ class Confirm extends PureComponent { ? AppConstants.REQUEST_SOURCES.WC : AppConstants.REQUEST_SOURCES.IN_APP_BROWSER, - is_smart_transaction: isSmartTransaction, - ...smartTransactionMetadata, + is_smart_transaction: shouldUseSmartTransaction, + ...smartTransactionMetricsProperties, }; } catch (error) { return {}; @@ -831,7 +826,7 @@ class Confirm extends PureComponent { navigation, resetTransaction, gasEstimateType, - isSmartTransaction, + shouldUseSmartTransaction, } = this.props; const { @@ -897,7 +892,7 @@ class Confirm extends PureComponent { await KeyringController.resetQRKeyringState(); - if (isSmartTransaction) { + if (shouldUseSmartTransaction) { await ApprovalController.accept(transactionMeta.id, undefined, { waitForResult: false, }); @@ -930,7 +925,7 @@ class Confirm extends PureComponent { stopGasPolling(); resetTransaction(); - if (!isSmartTransaction) { + if (!shouldUseSmartTransaction) { // We popped it already earlier navigation && navigation.dangerouslyGetParent()?.pop(); } @@ -1211,7 +1206,7 @@ class Confirm extends PureComponent { chainId, gasEstimateType, isNativeTokenBuySupported, - isSmartTransaction, + shouldUseSmartTransaction, } = this.props; const { nonce } = this.props.transaction; const { @@ -1342,7 +1337,7 @@ class Confirm extends PureComponent { updateGasState={this.updateGasState} /> )} - {showCustomNonce && !isSmartTransaction && ( + {showCustomNonce && !shouldUseSmartTransaction && ( this.toggleConfirmationModal(EDIT_NONCE)} @@ -1446,7 +1441,7 @@ const mapStateToProps = (state) => ({ selectChainId(state), getRampNetworks(state), ), - isSmartTransaction: getIsSmartTransaction(state), + shouldUseSmartTransaction: selectShouldUseSmartTransaction(state), }); const mapDispatchToProps = (dispatch) => ({ diff --git a/app/components/Views/confirmations/components/Approval/TemplateConfirmation/Templates/SmartTransactionStatus.ts b/app/components/Views/confirmations/components/Approval/TemplateConfirmation/Templates/SmartTransactionStatus.ts index a81dcf6d6aa8..a4b68f9c70d9 100644 --- a/app/components/Views/confirmations/components/Approval/TemplateConfirmation/Templates/SmartTransactionStatus.ts +++ b/app/components/Views/confirmations/components/Approval/TemplateConfirmation/Templates/SmartTransactionStatus.ts @@ -1,8 +1,6 @@ import { ApprovalRequest } from '@metamask/approval-controller'; import { Actions } from '../TemplateConfirmation'; import { ConfirmationTemplateValues, ConfirmationTemplate } from '.'; -import Logger from '../../../../../../../util/Logger'; -import Engine from '../../../../../../../core/Engine'; function getValues( pendingApproval: ApprovalRequest, @@ -26,16 +24,9 @@ function getValues( ], onConfirm: () => actions.onConfirm, onCancel: () => { + // Need to stub out onCancel, otherwise the status modal will dismiss once the tx is complete // This is called when the stx is done for some reason, ALSO called when user swipes down // Cannot do onConfirm(), it will dismiss the status modal after tx complete, we want to keep it up after success - - try { - // Remove the loading spinner on swipe down if tx is in progress - // If swipe down after tx success an error is thrown b/c app/util/smart-transactions/smart-tx.ts ends the flow if tx success, so just catch - Engine.context.ApprovalController.endFlow({ id: pendingApproval.id }); - } catch (e: any) { - Logger.error(e, 'STX SmartTransactionStatus onCancel error'); - } }, hideCancelButton: true, hideSubmitButton: true, diff --git a/app/components/Views/confirmations/components/ApproveTransactionReview/index.js b/app/components/Views/confirmations/components/ApproveTransactionReview/index.js index 3cc8080e5812..2ab9c3469a93 100644 --- a/app/components/Views/confirmations/components/ApproveTransactionReview/index.js +++ b/app/components/Views/confirmations/components/ApproveTransactionReview/index.js @@ -97,7 +97,7 @@ import { ResultType } from '../BlockaidBanner/BlockaidBanner.types'; import TransactionBlockaidBanner from '../TransactionBlockaidBanner/TransactionBlockaidBanner'; import { regex } from '../../../../../util/regex'; import { withMetricsAwareness } from '../../../../../components/hooks/useMetrics'; -import { getIsSmartTransaction } from '../../../../../selectors/smartTransactionsController'; +import { selectShouldUseSmartTransaction } from '../../../../../selectors/smartTransactionsController'; const { ORIGIN_DEEPLINK, ORIGIN_QR_CODE } = AppConstants.DEEPLINKS; const POLLING_INTERVAL_ESTIMATED_L1_FEE = 30000; @@ -277,9 +277,9 @@ class ApproveTransactionReview extends PureComponent { */ metrics: PropTypes.object, /** - * Boolean that indicates if the transaction is a smart transaction + * Boolean that indicates if smart transaction should be used */ - isSmartTransaction: PropTypes.bool, + shouldUseSmartTransaction: PropTypes.bool, }; state = { @@ -517,8 +517,12 @@ class ApproveTransactionReview extends PureComponent { getAnalyticsParams = () => { try { - const { chainId, transaction, onSetAnalyticsParams, isSmartTransaction } = - this.props; + const { + chainId, + transaction, + onSetAnalyticsParams, + shouldUseSmartTransaction, + } = this.props; const { token: { tokenSymbol }, originalApproveAmount, @@ -544,7 +548,7 @@ class ApproveTransactionReview extends PureComponent { : this.originIsWalletConnect ? AppConstants.REQUEST_SOURCES.WC : AppConstants.REQUEST_SOURCES.IN_APP_BROWSER, - is_smart_transaction: isSmartTransaction, + is_smart_transaction: shouldUseSmartTransaction, }; // Send analytics params to parent component so it's available when cancelling and confirming onSetAnalyticsParams && onSetAnalyticsParams(params); @@ -1281,7 +1285,7 @@ const mapStateToProps = (state) => ({ selectChainId(state), getRampNetworks(state), ), - isSmartTransaction: getIsSmartTransaction(state), + shouldUseSmartTransaction: selectShouldUseSmartTransaction(state), }); const mapDispatchToProps = (dispatch) => ({ diff --git a/app/components/Views/confirmations/components/TransactionReview/TransactionReviewInformation/index.js b/app/components/Views/confirmations/components/TransactionReview/TransactionReviewInformation/index.js index ff7d450c6d21..a7c565fb357e 100644 --- a/app/components/Views/confirmations/components/TransactionReview/TransactionReviewInformation/index.js +++ b/app/components/Views/confirmations/components/TransactionReview/TransactionReviewInformation/index.js @@ -62,7 +62,7 @@ import { isNetworkRampNativeTokenSupported } from '../../../../../../components/ import { getRampNetworks } from '../../../../../../reducers/fiatOrders'; import Routes from '../../../../../../constants/navigation/Routes'; import { withMetricsAwareness } from '../../../../../../components/hooks/useMetrics'; -import { getIsSmartTransaction } from '../../../../../../selectors/smartTransactionsController'; +import { selectShouldUseSmartTransaction } from '../../../../../../selectors/smartTransactionsController'; const createStyles = (colors) => StyleSheet.create({ @@ -238,9 +238,9 @@ class TransactionReviewInformation extends PureComponent { */ metrics: PropTypes.object, /** - * Boolean that indicates if the transaction is a smart transaction + * Boolean that indicates if smart transaction should be used */ - isSmartTransaction: PropTypes.bool, + shouldUseSmartTransaction: PropTypes.bool, }; state = { @@ -657,7 +657,7 @@ class TransactionReviewInformation extends PureComponent { gasEstimateType, gasSelected, isNativeTokenBuySupported, - isSmartTransaction, + shouldUseSmartTransaction, } = this.props; const { nonce } = this.props.transaction; const colors = this.context.colors || mockTheme.colors; @@ -686,7 +686,7 @@ class TransactionReviewInformation extends PureComponent { warningMessage={strings('edit_gas_fee_eip1559.low_fee_warning')} /> )} - {showCustomNonce && !isSmartTransaction && ( + {showCustomNonce && !shouldUseSmartTransaction && ( )} {!!amountError && ( @@ -752,7 +752,7 @@ const mapStateToProps = (state) => ({ selectChainId(state), getRampNetworks(state), ), - isSmartTransaction: getIsSmartTransaction(state), + shouldUseSmartTransaction: selectShouldUseSmartTransaction(state), }); const mapDispatchToProps = (dispatch) => ({ diff --git a/app/components/Views/confirmations/components/TransactionReview/index.js b/app/components/Views/confirmations/components/TransactionReview/index.js index fb19d421413e..d9166f01872d 100644 --- a/app/components/Views/confirmations/components/TransactionReview/index.js +++ b/app/components/Views/confirmations/components/TransactionReview/index.js @@ -57,7 +57,7 @@ import AppConstants from '../../../../../core/AppConstants'; import TransactionBlockaidBanner from '../TransactionBlockaidBanner/TransactionBlockaidBanner'; import { ResultType } from '../BlockaidBanner/BlockaidBanner.types'; import { withMetricsAwareness } from '../../../../../components/hooks/useMetrics'; -import { getIsSmartTransaction } from '../../../../../selectors/smartTransactionsController'; +import { selectShouldUseSmartTransaction } from '../../../../../selectors/smartTransactionsController'; const POLLING_INTERVAL_ESTIMATED_L1_FEE = 30000; @@ -249,9 +249,9 @@ class TransactionReview extends PureComponent { */ metrics: PropTypes.object, /** - * Whether the transaction is a smart transaction + * Boolean that indicates if smart transaction should be used */ - isSmartTransaction: PropTypes.bool, + shouldUseSmartTransaction: PropTypes.bool, }; state = { @@ -297,7 +297,7 @@ class TransactionReview extends PureComponent { chainId, tokenList, metrics, - isSmartTransaction, + shouldUseSmartTransaction, } = this.props; let { showHexData } = this.props; let assetAmount, conversionRate, fiatValue; @@ -330,7 +330,7 @@ class TransactionReview extends PureComponent { }); metrics.trackEvent(MetaMetricsEvents.TRANSACTIONS_CONFIRM_STARTED, { - is_smart_transaction: isSmartTransaction, + is_smart_transaction: shouldUseSmartTransaction, }); if (isMultiLayerFeeNetwork(chainId)) { @@ -664,7 +664,7 @@ const mapStateToProps = (state) => ({ browser: state.browser, primaryCurrency: state.settings.primaryCurrency, tokenList: selectTokenList(state), - isSmartTransaction: getIsSmartTransaction(state), + shouldUseSmartTransaction: selectShouldUseSmartTransaction(state), }); TransactionReview.contextType = ThemeContext; diff --git a/app/core/Engine.test.js b/app/core/Engine.test.js index 70e386878a79..c801685c83d6 100644 --- a/app/core/Engine.test.js +++ b/app/core/Engine.test.js @@ -72,7 +72,7 @@ describe('Engine', () => { approvalTxFees: undefined, tradeTxFees: undefined, }, - '0x5': { + '0xaa36a7': { approvalTxFees: undefined, tradeTxFees: undefined, }, @@ -80,7 +80,7 @@ describe('Engine', () => { liveness: true, livenessByChainId: { '0x1': true, - '0x5': true, + '0xaa36a7': true, }, smartTransactions: { '0x1': [], diff --git a/app/core/Engine.ts b/app/core/Engine.ts index 21cbce98546b..540bcf71c636 100644 --- a/app/core/Engine.ts +++ b/app/core/Engine.ts @@ -192,10 +192,11 @@ import { } from '../core/redux/slices/inpageProvider'; import SmartTransactionsController from '@metamask/smart-transactions-controller'; import { NETWORKS_CHAIN_ID } from '../../app/constants/network'; -import { getIsSmartTransaction } from '../selectors/smartTransactionsController'; -import { getSwapsChainFeatureFlags } from '../reducers/swaps'; +import { selectShouldUseSmartTransaction } from '../selectors/smartTransactionsController'; +import { selectSwapsChainFeatureFlags } from '../reducers/swaps'; import { SmartTransactionStatuses } from '@metamask/smart-transactions-controller/dist/types'; -import { submitSmartTransactionHook } from '../util/smart-transactions/smart-tx'; +import { submitSmartTransactionHook } from '../util/smart-transactions/smart-publish-hook'; +import { SmartTransactionsControllerState } from '@metamask/smart-transactions-controller/dist/SmartTransactionsController'; const NON_EMPTY = 'NON_EMPTY'; @@ -277,7 +278,7 @@ export interface EngineState { TokenBalancesController: TokenBalancesState; TokenRatesController: TokenRatesState; TransactionController: TransactionState; - SmartTransactionsController: any; // TODO look into improving this, smart tx cont. needs to export the SmartTransactionsControllerState type + SmartTransactionsController: SmartTransactionsControllerState; SwapsController: SwapsState; GasFeeController: GasFeeState; TokensController: TokensState; @@ -322,6 +323,7 @@ interface Controllers { TokenRatesController: TokenRatesController; TokensController: TokensController; TransactionController: TransactionController; + SmartTransactionsController: SmartTransactionsController; SignatureController: SignatureController; ///: BEGIN:ONLY_INCLUDE_IF(snaps) SnapController: SnapController; @@ -375,8 +377,8 @@ class Engine { snapExecutionService: WebViewExecutionService; ///: END:ONLY_INCLUDE_IF - txController: TransactionController; - stxController: SmartTransactionsController; + transactionController: TransactionController; + smartTransactionsController: SmartTransactionsController; /** * Creates a CoreController instance @@ -1033,22 +1035,27 @@ class Engine { }); ///: END:ONLY_INCLUDE_IF - this.txController = new TransactionController({ + this.transactionController = new TransactionController({ // @ts-expect-error at this point in time the provider will be defined by the `networkController.initializeProvider` blockTracker: networkController.getProviderAndBlockTracker().blockTracker, + disableSendFlowHistory: true, + disableHistory: true, getGasFeeEstimates: () => gasFeeController.fetchGasFeeEstimates(), - //@ts-expect-error TransactionController needs to be updated to v13 for this error disappears + getCurrentNetworkEIP1559Compatibility: + networkController.getEIP1559Compatibility.bind(networkController), getNetworkState: () => networkController.state, getSelectedAddress: () => accountsController.getSelectedAccount().address, incomingTransactions: { - apiKey: process.env.MM_ETHERSCAN_KEY, isEnabled: () => { const currentHexChainId = networkController.state.providerConfig.chainId; + + const showIncomingTransactions = + preferencesController?.state?.showIncomingTransactions; + return Boolean( - preferencesController?.state?.showIncomingTransactions?.[ - currentHexChainId - ], + hasProperty(showIncomingTransactions, currentChainId) && + showIncomingTransactions?.[currentHexChainId], ); }, updateTransactions: true, @@ -1065,7 +1072,6 @@ class Engine { onNetworkStateChange: (listener) => this.controllerMessenger.subscribe( AppConstants.NETWORK_STATE_CHANGE_EVENT, - //@ts-expect-error TransactionController needs to be updated to v13 for this error disappears listener, ), // @ts-expect-error at this point in time the provider will be defined by the `networkController.initializeProvider` @@ -1074,22 +1080,24 @@ class Engine { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore getExternalPendingTransactions: (address: string) => - this.stxController.getTransactions({ + this.smartTransactionsController.getTransactions({ addressFrom: address, status: SmartTransactionStatuses.PENDING, }), hooks: { publish: (transactionMeta) => { - const isSmartTransaction = getIsSmartTransaction(store.getState()); + const shouldUseSmartTransaction = selectShouldUseSmartTransaction( + store.getState(), + ); return submitSmartTransactionHook({ transactionMeta, - transactionController: this.txController, - smartTransactionsController: this.stxController, - isSmartTransaction, + transactionController: this.transactionController, + smartTransactionsController: this.smartTransactionsController, + shouldUseSmartTransaction, approvalController, - featureFlags: getSwapsChainFeatureFlags(store.getState()), + featureFlags: selectSwapsChainFeatureFlags(store.getState()), }); }, }, @@ -1097,6 +1105,61 @@ class Engine { const codefiTokenApiV2 = new CodefiTokenPricesServiceV2(); + const smartTransactionsControllerTrackMetaMetricsEvent = (params: { + event: string; + category: string; + sensitiveProperties: any; + }) => { + const { event, category, ...restParams } = params; + + MetaMetrics.getInstance().trackEvent( + { + category, + properties: { + name: event, + // action?: string; + }, + }, + restParams, + ); + }; + this.smartTransactionsController = new SmartTransactionsController( + { + confirmExternalTransaction: + this.transactionController.confirmExternalTransaction.bind( + this.transactionController, + ), + // @ts-expect-error this fine, STX controller has been downgraded to network controller v15 + getNetworkClientById: + networkController.getNetworkClientById.bind(networkController), + getNonceLock: this.transactionController.getNonceLock.bind( + this.transactionController, + ), + // @ts-expect-error txController.getTransactions only uses txMeta.status and txMeta.hash, which v13 TxController has + getTransactions: this.transactionController.getTransactions.bind( + this.transactionController, + ), + onNetworkStateChange: (listener) => + this.controllerMessenger.subscribe( + AppConstants.NETWORK_STATE_CHANGE_EVENT, + listener, + ), + + provider: networkController.getProviderAndBlockTracker() + .provider as any, + + trackMetaMetricsEvent: smartTransactionsControllerTrackMetaMetricsEvent, + }, + { + supportedChainIds: [ + NETWORKS_CHAIN_ID.MAINNET, + NETWORKS_CHAIN_ID.GOERLI, + NETWORKS_CHAIN_ID.SEPOLIA, + ], + }, + initialState.SmartTransactionsController, + ); + const controllers: Controllers[keyof Controllers][] = [ keyringController, accountTrackerController, @@ -1191,50 +1254,10 @@ class Engine { tokenPricesService: codefiTokenApiV2, interval: 30 * 60 * 1000, }), - new TransactionController({ - // @ts-expect-error at this point in time the provider will be defined by the `networkController.initializeProvider` - blockTracker: - networkController.getProviderAndBlockTracker().blockTracker, - disableSendFlowHistory: true, - disableHistory: true, - getGasFeeEstimates: () => gasFeeController.fetchGasFeeEstimates(), - getCurrentNetworkEIP1559Compatibility: - networkController.getEIP1559Compatibility.bind(networkController), - getNetworkState: () => networkController.state, - getSelectedAddress: () => - accountsController.getSelectedAccount().address, - incomingTransactions: { - isEnabled: () => { - const currentHexChainId = - networkController.state.providerConfig.chainId; - - const showIncomingTransactions = - preferencesController?.state?.showIncomingTransactions; - - return Boolean( - hasProperty(showIncomingTransactions, currentChainId) && - showIncomingTransactions?.[currentHexChainId], - ); - }, - updateTransactions: true, - }, - // @ts-expect-error TODO: Resolve/patch mismatch between base-controller versions. Before: never, never. Now: string, string, which expects 3rd and 4th args to be informed for restrictedControllerMessengers - messenger: this.controllerMessenger.getRestricted< - 'TransactionController', - 'ApprovalController:addRequest', - never - >({ - name: 'TransactionController', - allowedActions: [`${approvalController.name}:addRequest`], - }), - onNetworkStateChange: (listener) => - this.controllerMessenger.subscribe( - AppConstants.NETWORK_STATE_CHANGE_EVENT, - listener, - ), - // @ts-expect-error at this point in time the provider will be defined by the `networkController.initializeProvider` - provider: networkController.getProviderAndBlockTracker().provider, - }), + + this.transactionController, + this.smartTransactionsController, + new SwapsController( { // @ts-expect-error TODO: Resolve mismatch between gas fee and swaps controller types diff --git a/app/reducers/swaps/index.js b/app/reducers/swaps/index.js index 779f84e75622..762072acb0eb 100644 --- a/app/reducers/swaps/index.js +++ b/app/reducers/swaps/index.js @@ -75,16 +75,15 @@ export const swapsLivenessSelector = createSelector( /** * Returns if smart transactions are enabled in feature flags */ +const DEVICE_KEY = 'mobileActive'; export const swapsSmartTxFlagEnabled = createSelector( swapsStateSelector, (swapsState) => { const globalFlags = swapsState.featureFlags; - const deviceKey = 'mobileActive'; - const isEnabled = Boolean( globalFlags?.smart_transactions?.mobile_active && - globalFlags?.smartTransactions?.[deviceKey], + globalFlags?.smartTransactions?.[DEVICE_KEY], ); return isEnabled; @@ -94,7 +93,7 @@ export const swapsSmartTxFlagEnabled = createSelector( /** * Returns the swaps feature flags */ -export const getSwapsChainFeatureFlags = createSelector( +export const selectSwapsChainFeatureFlags = createSelector( swapsStateSelector, chainIdSelector, (swapsState, chainId) => swapsState[chainId].featureFlags, diff --git a/app/reducers/swaps/swaps.test.ts b/app/reducers/swaps/swaps.test.ts index faba4001e9f5..2ffec8be4110 100644 --- a/app/reducers/swaps/swaps.test.ts +++ b/app/reducers/swaps/swaps.test.ts @@ -10,7 +10,7 @@ import reducer, { const emptyAction = { type: null }; -const defaultFeatureFlags = { +const DEFAULT_FEATURE_FLAGS = { ethereum: { mobile_active: true, extension_active: true, @@ -65,7 +65,7 @@ describe('swaps reducer', () => { const liveState = reducer(initalState, { type: SWAPS_SET_LIVENESS, payload: { - featureFlags: defaultFeatureFlags, + featureFlags: DEFAULT_FEATURE_FLAGS, chainId: '0x1', }, }); @@ -76,7 +76,7 @@ describe('swaps reducer', () => { Device.isAndroid = jest.fn().mockReturnValue(false); const initalState = reducer(undefined, emptyAction); - const featureFlags = cloneDeep(defaultFeatureFlags); + const featureFlags = cloneDeep(DEFAULT_FEATURE_FLAGS); featureFlags.ethereum = { mobile_active: false, extension_active: true, @@ -108,7 +108,7 @@ describe('swaps reducer', () => { Device.isAndroid = jest.fn().mockReturnValue(true); const initalState = reducer(undefined, emptyAction); - const featureFlags = cloneDeep(defaultFeatureFlags); + const featureFlags = cloneDeep(DEFAULT_FEATURE_FLAGS); featureFlags.ethereum = { mobile_active: true, extension_active: true, @@ -140,7 +140,7 @@ describe('swaps reducer', () => { Device.isAndroid = jest.fn().mockReturnValue(true); const initalState = reducer(undefined, emptyAction); - const featureFlags = cloneDeep(defaultFeatureFlags); + const featureFlags = cloneDeep(DEFAULT_FEATURE_FLAGS); featureFlags.ethereum = { mobile_active: false, extension_active: true, diff --git a/app/reducers/swaps/utils.ts b/app/reducers/swaps/utils.ts index c0f7794b2690..3a3d7d1e002f 100644 --- a/app/reducers/swaps/utils.ts +++ b/app/reducers/swaps/utils.ts @@ -11,15 +11,22 @@ export const getChainFeatureFlags = ( return chainFeatureFlags; }; -export const getFeatureFlagDeviceKey = () => { +type FeatureFlagDeviceKey = + | 'mobileActiveIOS' + | 'mobileActiveAndroid' + | 'mobileActive'; +export const getFeatureFlagDeviceKey: () => FeatureFlagDeviceKey = () => { const isIphone = Device.isIos(); const isAndroid = Device.isAndroid(); - const featureFlagDeviceKey = isIphone - ? 'mobileActiveIOS' - : isAndroid - ? 'mobileActiveAndroid' - : 'mobileActive'; + let featureFlagDeviceKey: FeatureFlagDeviceKey; + if (isIphone) { + featureFlagDeviceKey = 'mobileActiveIOS'; + } else if (isAndroid) { + featureFlagDeviceKey = 'mobileActiveAndroid'; + } else { + featureFlagDeviceKey = 'mobileActive'; + } return featureFlagDeviceKey; }; diff --git a/app/selectors/smartTransactionsController.test.ts b/app/selectors/smartTransactionsController.test.ts index ff455d132710..a69c64febaa1 100644 --- a/app/selectors/smartTransactionsController.test.ts +++ b/app/selectors/smartTransactionsController.test.ts @@ -1,6 +1,6 @@ import { - getIsSmartTransaction, - getSmartTransactionsEnabled, + selectShouldUseSmartTransaction, + selectSmartTransactionsEnabled, } from './smartTransactionsController'; import initialBackgroundState from '../util/test/initial-background-state.json'; import { isHardwareAccount } from '../util/address'; @@ -66,7 +66,7 @@ describe('SmartTransactionsController Selectors', () => { const state = getDefaultState(); state.swaps['0x1'].smartTransactions = smartTransactions; - const enabled = getSmartTransactionsEnabled(state); + const enabled = selectSmartTransactionsEnabled(state); expect(enabled).toEqual(false); }, ); @@ -74,20 +74,20 @@ describe('SmartTransactionsController Selectors', () => { const state = getDefaultState(); state.engine.backgroundState.SmartTransactionsController.smartTransactionsState.liveness = false; - const enabled = getSmartTransactionsEnabled(state); + const enabled = selectSmartTransactionsEnabled(state); expect(enabled).toEqual(false); }); it('should return false if address is hardware account', () => { (isHardwareAccount as jest.Mock).mockReturnValueOnce(true); const state = getDefaultState(); - const enabled = getSmartTransactionsEnabled(state); + const enabled = selectSmartTransactionsEnabled(state); expect(enabled).toEqual(false); }); it('should return false if is mainnet and not the default RPC', () => { const state = getDefaultState(); state.engine.backgroundState.NetworkController.providerConfig.rpcUrl = 'https://example.com'; - const enabled = getSmartTransactionsEnabled(state); + const enabled = selectSmartTransactionsEnabled(state); expect(enabled).toEqual(false); }); it('should return true if smart transactions are enabled', () => { @@ -95,31 +95,31 @@ describe('SmartTransactionsController Selectors', () => { state.swaps.featureFlags.smart_transactions.mobile_active = true; state.swaps.featureFlags.smartTransactions.mobileActive = true; - const enabled = getSmartTransactionsEnabled(state); + const enabled = selectSmartTransactionsEnabled(state); expect(enabled).toEqual(true); }); }); - describe('getIsSmartTransaction', () => { + describe('getShouldUseSmartTransaction', () => { it('should return false if smart transactions are not opted into', () => { const state = getDefaultState(); state.engine.backgroundState.PreferencesController.smartTransactionsOptInStatus = false; - const isSmartTransaction = getIsSmartTransaction(state); - expect(isSmartTransaction).toEqual(false); + const shouldUseSmartTransaction = selectShouldUseSmartTransaction(state); + expect(shouldUseSmartTransaction).toEqual(false); }); it('should return false if smart transactions are not enabled', () => { const state = getDefaultState(); state.swaps['0x1'].smartTransactions = {}; - const isSmartTransaction = getIsSmartTransaction(state); - expect(isSmartTransaction).toEqual(false); + const shouldUseSmartTransaction = selectShouldUseSmartTransaction(state); + expect(shouldUseSmartTransaction).toEqual(false); }); it('should return true if smart transactions are enabled and opted into', () => { const state = getDefaultState(); state.swaps.featureFlags.smart_transactions.mobile_active = true; state.swaps.featureFlags.smartTransactions.mobileActive = true; - const isSmartTransaction = getIsSmartTransaction(state); - expect(isSmartTransaction).toEqual(true); + const shouldUseSmartTransaction = selectShouldUseSmartTransaction(state); + expect(shouldUseSmartTransaction).toEqual(true); }); }); }); diff --git a/app/selectors/smartTransactionsController.ts b/app/selectors/smartTransactionsController.ts index c2addebea46c..94e20c8e4d85 100644 --- a/app/selectors/smartTransactionsController.ts +++ b/app/selectors/smartTransactionsController.ts @@ -7,13 +7,17 @@ import { RootState } from '../reducers'; import { swapsSmartTxFlagEnabled } from '../reducers/swaps'; import { isHardwareAccount } from '../util/address'; import { selectChainId, selectProviderConfig } from './networkController'; +import { + SmartTransaction, + SmartTransactionStatuses, +} from '@metamask/smart-transactions-controller/dist/types'; export const ALLOWED_SMART_TRANSACTIONS_CHAIN_IDS = [ NETWORKS_CHAIN_ID.MAINNET, NETWORKS_CHAIN_ID.GOERLI, NETWORKS_CHAIN_ID.SEPOLIA, ]; -export const getSmartTransactionsEnabled = (state: RootState) => { +export const selectSmartTransactionsEnabled = (state: RootState) => { const selectedAddress = selectSelectedAddress(state); const addrIshardwareAccount = isHardwareAccount(selectedAddress); const chainId = selectChainId(state); @@ -43,10 +47,46 @@ export const getSmartTransactionsEnabled = (state: RootState) => { smartTransactionsLiveness, ); }; -export const getIsSmartTransaction = (state: RootState) => { - const isSmartTransactionsEnabled = getSmartTransactionsEnabled(state); +export const selectShouldUseSmartTransaction = (state: RootState) => { + const isSmartTransactionsEnabled = selectSmartTransactionsEnabled(state); const smartTransactionsOptInStatus = selectSmartTransactionsOptInStatus(state); return isSmartTransactionsEnabled && smartTransactionsOptInStatus; }; + +export const selectPendingSmartTransactionsBySender = (state: RootState) => { + const selectedAddress = selectSelectedAddress(state); + const chainId = selectChainId(state); + + const smartTransactions: SmartTransaction[] = + state.engine.backgroundState.SmartTransactionsController + ?.smartTransactionsState?.smartTransactions?.[chainId] || []; + + const pendingSmartTransactions = + smartTransactions + ?.filter((stx) => { + const { txParams } = stx; + return ( + txParams?.from.toLowerCase() === selectedAddress.toLowerCase() && + ![ + SmartTransactionStatuses.SUCCESS, + SmartTransactionStatuses.CANCELLED, + ].includes(stx.status as SmartTransactionStatuses) + ); + }) + .map((stx) => ({ + ...stx, + // stx.uuid is one from sentinel API, not the same as tx.id which is generated client side + // Doesn't matter too much because we only care about the pending stx, confirmed txs are handled like normal + // However, this does make it impossible to read Swap data from TxController.swapsTransactions as that relies on client side tx.id + // To fix that we do transactionController.update({ swapsTransactions: newSwapsTransactions }) in app/util/smart-transactions/smart-tx.ts + id: stx.uuid, + status: stx.status?.startsWith(SmartTransactionStatuses.CANCELLED) + ? SmartTransactionStatuses.CANCELLED + : stx.status, + isSmartTransaction: true, + })) ?? []; + + return pendingSmartTransactions; +}; diff --git a/patches/@metamask+smart-transactions-controller+8.1.0.patch b/patches/@metamask+smart-transactions-controller+8.1.0.patch deleted file mode 100644 index bfed016c7eae..000000000000 --- a/patches/@metamask+smart-transactions-controller+8.1.0.patch +++ /dev/null @@ -1,152 +0,0 @@ -diff --git a/node_modules/@metamask/smart-transactions-controller/dist/SmartTransactionsController.d.ts b/node_modules/@metamask/smart-transactions-controller/dist/SmartTransactionsController.d.ts -index 8c1b820..589af61 100644 ---- a/node_modules/@metamask/smart-transactions-controller/dist/SmartTransactionsController.d.ts -+++ b/node_modules/@metamask/smart-transactions-controller/dist/SmartTransactionsController.d.ts -@@ -76,11 +76,11 @@ export default class SmartTransactionsController extends StaticIntervalPollingCo - getFees(tradeTx: UnsignedTransaction, approvalTx?: UnsignedTransaction, { networkClientId }?: { - networkClientId?: NetworkClientId; - }): Promise; -- submitSignedTransactions({ transactionMeta, txParams, signedTransactions, signedCanceledTransactions, networkClientId, }: { -+ submitSignedTransactions({ transactionMeta, transaction, signedTransactions, signedCanceledTransactions, networkClientId, }: { - signedTransactions: SignedTransaction[]; - signedCanceledTransactions: SignedCanceledTransaction[]; - transactionMeta?: any; -- txParams?: any; -+ transaction?: any; - networkClientId?: NetworkClientId; - }): Promise; - cancelSmartTransaction(uuid: string, { networkClientId, }?: { -diff --git a/node_modules/@metamask/smart-transactions-controller/dist/SmartTransactionsController.js b/node_modules/@metamask/smart-transactions-controller/dist/SmartTransactionsController.js -index 4bfa10a..473813f 100644 ---- a/node_modules/@metamask/smart-transactions-controller/dist/SmartTransactionsController.js -+++ b/node_modules/@metamask/smart-transactions-controller/dist/SmartTransactionsController.js -@@ -289,7 +289,7 @@ class SmartTransactionsController extends polling_controller_1.StaticIntervalPol - } - // * After this successful call client must add a nonce representative to - // * transaction controller external transactions list -- async submitSignedTransactions({ transactionMeta, txParams, signedTransactions, signedCanceledTransactions, networkClientId, }) { -+ async submitSignedTransactions({ transactionMeta, transaction, signedTransactions, signedCanceledTransactions, networkClientId, }) { - var _a; - const chainId = __classPrivateFieldGet(this, _SmartTransactionsController_instances, "m", _SmartTransactionsController_getChainId).call(this, { networkClientId }); - const ethQuery = __classPrivateFieldGet(this, _SmartTransactionsController_instances, "m", _SmartTransactionsController_getEthQuery).call(this, { networkClientId }); -@@ -304,23 +304,23 @@ class SmartTransactionsController extends polling_controller_1.StaticIntervalPol - let preTxBalance; - try { - const preTxBalanceBN = await (0, controller_utils_1.query)(ethQuery, 'getBalance', [ -- txParams === null || txParams === void 0 ? void 0 : txParams.from, -+ transaction === null || transaction === void 0 ? void 0 : transaction.from, - ]); - preTxBalance = new bignumber_js_1.BigNumber(preTxBalanceBN).toString(16); - } - catch (error) { - console.error('provider error', error); - } -- const requiresNonce = !txParams.nonce; -+ const requiresNonce = !transaction.nonce; - let nonce; - let nonceLock; - let nonceDetails = {}; - if (requiresNonce) { -- nonceLock = await this.getNonceLock(txParams === null || txParams === void 0 ? void 0 : txParams.from); -+ nonceLock = await this.getNonceLock(transaction === null || transaction === void 0 ? void 0 : transaction.from); - nonce = (0, bytes_1.hexlify)(nonceLock.nextNonce); - nonceDetails = nonceLock.nonceDetails; -- if (txParams) { -- (_a = txParams.nonce) !== null && _a !== void 0 ? _a : (txParams.nonce = nonce); -+ if (transaction) { -+ (_a = transaction.nonce) !== null && _a !== void 0 ? _a : (transaction.nonce = nonce); - } - } - const submitTransactionResponse = Object.assign(Object.assign({}, data), { txHash: (0, utils_1.getTxHash)(signedTransactions[0]) }); -@@ -331,9 +331,9 @@ class SmartTransactionsController extends polling_controller_1.StaticIntervalPol - preTxBalance, - status: types_1.SmartTransactionStatuses.PENDING, - time, -- txParams, -+ transaction, - uuid: submitTransactionResponse.uuid, -- txHash: submitTransactionResponse.txHash, -+ transactionHash: submitTransactionResponse.txHash, - cancellable: true, - type: (transactionMeta === null || transactionMeta === void 0 ? void 0 : transactionMeta.type) || 'swap', - }, { chainId, ethQuery }); -@@ -377,7 +377,7 @@ class SmartTransactionsController extends polling_controller_1.StaticIntervalPol - const currentSmartTransactions = __classPrivateFieldGet(this, _SmartTransactionsController_instances, "m", _SmartTransactionsController_getCurrentSmartTransactions).call(this); - return currentSmartTransactions.filter((stx) => { - var _a; -- return stx.status === status && ((_a = stx.txParams) === null || _a === void 0 ? void 0 : _a.from) === addressFrom; -+ return stx.status === status && ((_a = stx.transaction) === null || _a === void 0 ? void 0 : _a.from) === addressFrom; - }); - } - getSmartTransactionByMinedTxHash(txHash) { -@@ -410,7 +410,7 @@ _SmartTransactionsController_instances = new WeakSet(), _SmartTransactionsContro - // add smart transaction - const cancelledNonceIndex = currentSmartTransactions === null || currentSmartTransactions === void 0 ? void 0 : currentSmartTransactions.findIndex((stx) => { - var _a, _b, _c; -- return ((_a = stx.txParams) === null || _a === void 0 ? void 0 : _a.nonce) === ((_b = smartTransaction.txParams) === null || _b === void 0 ? void 0 : _b.nonce) && -+ return ((_a = stx.transaction) === null || _a === void 0 ? void 0 : _a.nonce) === ((_b = smartTransaction.transaction) === null || _b === void 0 ? void 0 : _b.nonce) && - ((_c = stx.status) === null || _c === void 0 ? void 0 : _c.startsWith('cancelled')); - }); - const snapshot = (0, cloneDeep_1.default)(smartTransaction); -@@ -454,7 +454,7 @@ _SmartTransactionsController_instances = new WeakSet(), _SmartTransactionsContro - const transactions = this.getRegularTransactions(); - const foundTransaction = transactions === null || transactions === void 0 ? void 0 : transactions.find((tx) => { - var _a; -- return ((_a = tx.hash) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === txHash.toLowerCase(); -+ return ((_a = tx.transactionHash) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === txHash.toLowerCase(); - }); - if (!foundTransaction) { - return true; -@@ -477,10 +477,10 @@ _SmartTransactionsController_instances = new WeakSet(), _SmartTransactionsContro - if (transactionReceipt === null || transactionReceipt === void 0 ? void 0 : transactionReceipt.blockNumber) { - const blockData = await (0, controller_utils_1.query)(ethQuery, 'getBlockByNumber', [transactionReceipt === null || transactionReceipt === void 0 ? void 0 : transactionReceipt.blockNumber, false]); - const baseFeePerGas = blockData === null || blockData === void 0 ? void 0 : blockData.baseFeePerGas; -- const updatedTxParams = Object.assign(Object.assign({}, smartTransaction.txParams), { maxFeePerGas, -+ const updatedTxParams = Object.assign(Object.assign({}, smartTransaction.transaction), { maxFeePerGas, - maxPriorityFeePerGas }); - // call confirmExternalTransaction -- const originalTxMeta = Object.assign(Object.assign({}, smartTransaction), { id: smartTransaction.uuid, status: transaction_controller_1.TransactionStatus.confirmed, hash: txHash, txParams: updatedTxParams }); -+ const originalTxMeta = Object.assign(Object.assign({}, smartTransaction), { id: smartTransaction.uuid, status: transaction_controller_1.TransactionStatus.confirmed, transactionHash: txHash, transaction: updatedTxParams }); - // create txMeta snapshot for history - const snapshot = (0, utils_1.snapshotFromTxMeta)(originalTxMeta); - // recover previous tx state obj -diff --git a/node_modules/@metamask/smart-transactions-controller/dist/SmartTransactionsController.js.map b/node_modules/@metamask/smart-transactions-controller/dist/SmartTransactionsController.js.map -index c8c1920..3d8a598 100644 ---- a/node_modules/@metamask/smart-transactions-controller/dist/SmartTransactionsController.js.map -+++ b/node_modules/@metamask/smart-transactions-controller/dist/SmartTransactionsController.js.map -@@ -1 +1 @@ --{"version":3,"file":"SmartTransactionsController.js","sourceRoot":"","sources":["../src/SmartTransactionsController.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,oDAAoD;AACpD,gDAA+C;AAE/C,iEAAkE;AAElE,oEAA2C;AAM3C,qEAAiF;AAEjF,6EAAqE;AACrE,+CAAyC;AACzC,oDAAoD;AACpD,oDAAkC;AAClC,iEAAyC;AAEzC,2CAIqB;AAYrB,mCAA4D;AAC5D,mCAYiB;AAEjB,MAAM,MAAM,GAAG,IAAI,CAAC;AACP,QAAA,gBAAgB,GAAG,MAAM,GAAG,CAAC,CAAC;AAC3C,MAAM,mBAAmB,GACvB,0DAA0D,CAAC;AA0B7D,MAAqB,2BAA4B,SAAQ,sDAGxD;IAsCC,YACE,EACE,oBAAoB,EACpB,YAAY,EACZ,QAAQ,EACR,0BAA0B,EAC1B,eAAe,EACf,qBAAqB,EACrB,oBAAoB,GAWrB,EACD,MAAmD,EACnD,KAAiD;QAEjD,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;;QA5DvB;;WAEG;QACM,SAAI,GAAG,6BAA6B,CAAC;QA2D5C,IAAI,CAAC,aAAa,GAAG;YACnB,QAAQ,EAAE,wBAAgB;YAC1B,OAAO,EAAE,qBAAS,CAAC,QAAQ;YAC3B,QAAQ,EAAE,SAAS;YACnB,iBAAiB,EAAE,CAAC,qBAAS,CAAC,QAAQ,EAAE,qBAAS,CAAC,MAAM,CAAC;SAC1D,CAAC;QAEF,IAAI,CAAC,YAAY,GAAG;YAClB,sBAAsB,EAAE;gBACtB,iBAAiB,EAAE,EAAE;gBACrB,SAAS,EAAE,SAAS;gBACpB,WAAW,EAAE,SAAS;gBACtB,IAAI,EAAE;oBACJ,cAAc,EAAE,SAAS;oBACzB,WAAW,EAAE,SAAS;iBACvB;gBACD,QAAQ,EAAE,IAAI;gBACd,iBAAiB,EAAE;oBACjB,CAAC,qBAAS,CAAC,QAAQ,CAAC,EAAE,IAAI;oBAC1B,CAAC,qBAAS,CAAC,MAAM,CAAC,EAAE,IAAI;iBACzB;gBACD,aAAa,EAAE;oBACb,CAAC,qBAAS,CAAC,QAAQ,CAAC,EAAE;wBACpB,cAAc,EAAE,SAAS;wBACzB,WAAW,EAAE,SAAS;qBACvB;oBACD,CAAC,qBAAS,CAAC,MAAM,CAAC,EAAE;wBAClB,cAAc,EAAE,SAAS;wBACzB,WAAW,EAAE,SAAS;qBACvB;iBACF;aACF;SACF,CAAC;QAEF,IAAI,CAAC,UAAU,EAAE,CAAC;QAClB,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC7C,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;QACjC,IAAI,CAAC,QAAQ,GAAG,SAAS,CAAC;QAC1B,IAAI,CAAC,0BAA0B,GAAG,0BAA0B,CAAC;QAC7D,IAAI,CAAC,sBAAsB,GAAG,eAAe,CAAC;QAC9C,IAAI,CAAC,qBAAqB,GAAG,qBAAqB,CAAC;QACnD,IAAI,CAAC,oBAAoB,GAAG,oBAAoB,CAAC;QAEjD,IAAI,CAAC,qCAAqC,EAAE,CAAC;QAE7C,oBAAoB,CAAC,CAAC,EAAE,cAAc,EAAE,WAAW,EAAE,EAAE,EAAE;YACvD,MAAM,EAAE,OAAO,EAAE,GAAG,WAAW,CAAC;YAChC,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;YAC5B,IAAI,CAAC,qCAAqC,EAAE,CAAC;YAC7C,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC3B,IAAI,CAAC,QAAQ,GAAG,IAAI,mBAAQ,CAAC,QAAQ,CAAC,CAAC;QACzC,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,SAAS,CAAC,CAAC,YAAiB,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC,CAAC;QACpE,IAAI,CAAC,YAAY,GAAG,IAAI,gBAAY,EAAE,CAAC;IACzC,CAAC;IA9FD,0BAA0B;IAClB,KAAK,CAAC,KAAK,CAAC,OAAe,EAAE,OAAqB;QACxD,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC;QACjC,MAAM,YAAY,mCACb,OAAO,KACV,OAAO,kBACL,cAAc,EAAE,kBAAkB,IAC/B,CAAC,QAAQ,IAAI,EAAE,aAAa,EAAE,QAAQ,EAAE,CAAC,IAE/C,CAAC;QAEF,OAAO,IAAA,mBAAW,EAAC,OAAO,EAAE,YAAY,CAAC,CAAC;IAC5C,CAAC;IAoFD,KAAK,CAAC,YAAY,CAAC,eAAuB;QACxC,gFAAgF;QAChF,qFAAqF;QACrF,+FAA+F;QAC/F,wBAAwB;QACxB,MAAM,OAAO,GAAG,uBAAA,IAAI,uFAAY,MAAhB,IAAI,EAAa,EAAE,eAAe,EAAE,CAAC,CAAC;QACtD,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE;YACpD,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;SAC1B;QACD,OAAO,IAAI,CAAC,uBAAuB,CAAC,EAAE,eAAe,EAAE,CAAC,CAAC;IAC3D,CAAC;IAED,SAAS,CAAC,KAAU;QAClB,MAAM,EAAE,iBAAiB,EAAE,GAAG,KAAK,CAAC,sBAAsB,CAAC;QAC3D,MAAM,wBAAwB,GAAG,iBAAiB,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACxE,MAAM,mBAAmB,GAAG,wBAAwB,aAAxB,wBAAwB,uBAAxB,wBAAwB,CAAE,MAAM,CAC1D,iCAAyB,CAC1B,CAAC;QACF,IAAI,CAAC,IAAI,CAAC,aAAa,IAAI,CAAA,mBAAmB,aAAnB,mBAAmB,uBAAnB,mBAAmB,CAAE,MAAM,IAAG,CAAC,EAAE;YAC1D,IAAI,CAAC,IAAI,EAAE,CAAC;SACb;aAAM,IAAI,IAAI,CAAC,aAAa,IAAI,CAAA,mBAAmB,aAAnB,mBAAmB,uBAAnB,mBAAmB,CAAE,MAAM,MAAK,CAAC,EAAE;YAClE,IAAI,CAAC,IAAI,EAAE,CAAC;SACb;IACH,CAAC;IAED,qCAAqC;;QACnC,IAAI,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE;YAC/D,MAAM,EAAE,sBAAsB,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;YAC9C,IAAI,CAAC,MAAM,CAAC;gBACV,sBAAsB,kCACjB,sBAAsB,KACzB,iBAAiB,kCACZ,sBAAsB,CAAC,iBAAiB,KAC3C,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,EACnB,MAAA,sBAAsB,CAAC,iBAAiB,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,mCAC7D,EAAE,MAEP;aACF,CAAC,CAAC;SACJ;IACH,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,QAAiB;QAC1B,MAAM,EAAE,OAAO,EAAE,iBAAiB,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC;QACnD,QAAQ,IAAI,IAAI,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;QACvD,IAAI,CAAC,aAAa,IAAI,aAAa,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QACxD,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE;YACxC,OAAO;SACR;QACD,MAAM,IAAA,gCAAa,EAAC,KAAK,IAAI,EAAE,CAAC,IAAI,CAAC,uBAAuB,EAAE,CAAC,CAAC;QAChE,IAAI,CAAC,aAAa,GAAG,WAAW,CAAC,GAAG,EAAE;YACpC,IAAA,gCAAa,EAAC,KAAK,IAAI,EAAE,CAAC,IAAI,CAAC,uBAAuB,EAAE,CAAC,CAAC;QAC5D,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC3B,CAAC;IAED,KAAK,CAAC,IAAI;QACR,IAAI,CAAC,aAAa,IAAI,aAAa,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QACxD,IAAI,CAAC,aAAa,GAAG,SAAS,CAAC;IACjC,CAAC;IAED,aAAa,CAAC,KAA0B;QACtC,IAAI,CAAC,MAAM,CAAC;YACV,sBAAsB,kCACjB,IAAI,CAAC,KAAK,CAAC,sBAAsB,KACpC,WAAW,EAAE,KAAK,GACnB;SACF,CAAC,CAAC;IACL,CAAC;IAED,oBAAoB,CAClB,gBAAkC,EAClC,oBAAuC;QAEvC,IAAI,CAAC,oBAAoB,EAAE;YACzB,OAAO,CAAC,2EAA2E;SACpF;QAED,IAAI,uBAAuB,GAAG,IAAA,mBAAS,EAAC,gBAAgB,CAAC,CAAC;QAC1D,uBAAuB,mCAClB,IAAA,mBAAS,EAAC,oBAAoB,CAAC,GAC/B,uBAAuB,CAC3B,CAAC;QAEF,IACE,CAAC,uBAAuB,CAAC,YAAY;YACrC,CAAC,uBAAuB,CAAC,MAAM,KAAK,oBAAoB,CAAC,MAAM;gBAC7D,oBAAoB,CAAC,YAAY,CAAC,EACpC;YACA,OAAO,CAAC,kDAAkD;SAC3D;QAED,MAAM,mBAAmB,GAAG;YAC1B,UAAU,EAAE,uBAAuB,CAAC,MAAM;YAC1C,iBAAiB,EAAE,uBAAuB,CAAC,iBAAiB;YAC5D,eAAe,EAAE,uBAAuB,CAAC,sBAAsB;YAC/D,eAAe,EAAE,IAAA,4BAAoB,EAAC,uBAAuB,CAAC,IAAI,CAAC;YACnE,WAAW,EAAE,IAAI;YACjB,mBAAmB,EAAE,IAAI;YACzB,eAAe,EAAE,IAAI;SACtB,CAAC;QAEF,IAAI,CAAC,qBAAqB,CAAC;YACzB,KAAK,EAAE,gCAAoB,CAAC,gBAAgB;YAC5C,QAAQ,EAAE,oCAAwB,CAAC,YAAY;YAC/C,mBAAmB;SACpB,CAAC,CAAC;IACL,CAAC;IAED,qBAAqB,CAAC,oBAA4B;QAChD,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC;QAChC,MAAM,EAAE,sBAAsB,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;QAC9C,MAAM,EAAE,iBAAiB,EAAE,GAAG,sBAAsB,CAAC;QACrD,MAAM,wBAAwB,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC;QAC5D,MAAM,YAAY,GAAG,wBAAwB,aAAxB,wBAAwB,uBAAxB,wBAAwB,CAAE,SAAS,CACtD,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,KAAK,oBAAoB,CAC3C,CAAC;QACF,OAAO,YAAY,KAAK,CAAC,CAAC,IAAI,YAAY,KAAK,SAAS,CAAC;IAC3D,CAAC;IAED,sBAAsB,CACpB,gBAAkC,EAClC,EAAE,eAAe,KAA4C,EAAE;QAE/D,IAAI,EACF,QAAQ,EACR,MAAM,EAAE,EAAE,OAAO,EAAE,GACpB,GAAG,IAAI,CAAC;QACT,IAAI,eAAe,EAAE;YACnB,MAAM,aAAa,GAAG,IAAI,CAAC,oBAAoB,CAAC,eAAe,CAAC,CAAC;YACjE,OAAO,GAAG,aAAa,CAAC,aAAa,CAAC,OAAO,CAAC;YAC9C,QAAQ,GAAG,IAAI,mBAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;SACjD;QAED,uBAAA,IAAI,mGAAwB,MAA5B,IAAI,EAAyB,gBAAgB,EAAE;YAC7C,OAAO;YACP,QAAQ;SACT,CAAC,CAAC;IACL,CAAC;IAoGD,KAAK,CAAC,uBAAuB,CAAC,EAC5B,eAAe,MAGb,EAAE;QACJ,MAAM,EAAE,iBAAiB,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,sBAAsB,CAAC;QAChE,MAAM,OAAO,GAAG,uBAAA,IAAI,uFAAY,MAAhB,IAAI,EAAa,EAAE,eAAe,EAAE,CAAC,CAAC;QACtD,MAAM,2BAA2B,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC;QAE/D,MAAM,oBAAoB,GAAa,2BAA2B;aAC/D,MAAM,CAAC,iCAAyB,CAAC;aACjC,GAAG,CAAC,CAAC,gBAAgB,EAAE,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC;QAEpD,IAAI,oBAAoB,CAAC,MAAM,GAAG,CAAC,EAAE;YACnC,IAAI,CAAC,4BAA4B,CAAC,oBAAoB,EAAE;gBACtD,eAAe;aAChB,CAAC,CAAC;SACJ;IACH,CAAC;IAoHD,sDAAsD;IACtD,KAAK,CAAC,4BAA4B,CAChC,KAAe,EACf,EAAE,eAAe,KAA4C,EAAE;QAE/D,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC;YACjC,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC;SACvB,CAAC,CAAC;QACH,MAAM,OAAO,GAAG,uBAAA,IAAI,uFAAY,MAAhB,IAAI,EAAa,EAAE,eAAe,EAAE,CAAC,CAAC;QACtD,MAAM,QAAQ,GAAG,uBAAA,IAAI,wFAAa,MAAjB,IAAI,EAAc,EAAE,eAAe,EAAE,CAAC,CAAC;QACxD,MAAM,GAAG,GAAG,GAAG,IAAA,wBAAgB,EAC7B,eAAO,CAAC,YAAY,EACpB,OAAO,CACR,IAAI,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC;QAEzB,MAAM,IAAI,GAAG,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAGlC,CAAC;QAEF,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,SAAS,CAAC,EAAE,EAAE;YACjD,MAAM,gBAAgB,GAAG;gBACvB,cAAc,EAAE,SAAS;gBACzB,MAAM,EAAE,IAAA,uBAAe,EAAC,SAAS,CAAC;gBAClC,WAAW,EAAE,IAAA,qCAA6B,EAAC,SAAS,CAAC;gBACrD,IAAI;aACL,CAAC;YACF,uBAAA,IAAI,mGAAwB,MAA5B,IAAI,EAAyB,gBAAgB,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC;QACxE,CAAC,CAAC,CAAC;QAEH,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK,CAAC,qBAAqB,CACzB,WAAgC;QAEhC,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QAC5D,MAAM,KAAK,GAAG,SAAS,CAAC,SAAS,CAAC;QAClC,SAAS,CAAC,WAAW,EAAE,CAAC;QACxB,uCACK,WAAW,KACd,KAAK,EAAE,KAAK,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,IAChC;IACJ,CAAC;IAED,SAAS;QACP,MAAM,IAAI,GAAG;YACX,cAAc,EAAE,SAAS;YACzB,WAAW,EAAE,SAAS;SACvB,CAAC;QACF,IAAI,CAAC,MAAM,CAAC;YACV,sBAAsB,kCACjB,IAAI,CAAC,KAAK,CAAC,sBAAsB,KACpC,IAAI,GACL;SACF,CAAC,CAAC;QACH,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK,CAAC,OAAO,CACX,OAA4B,EAC5B,UAAgC,EAChC,EAAE,eAAe,KAA4C,EAAE;QAE/D,MAAM,OAAO,GAAG,uBAAA,IAAI,uFAAY,MAAhB,IAAI,EAAa,EAAE,eAAe,EAAE,CAAC,CAAC;QACtD,MAAM,YAAY,GAAG,EAAE,CAAC;QACxB,IAAI,iCAAiC,CAAC;QACtC,IAAI,UAAU,EAAE;YACd,MAAM,oCAAoC,GACxC,MAAM,IAAI,CAAC,qBAAqB,CAAC,UAAU,CAAC,CAAC;YAC/C,YAAY,CAAC,IAAI,CAAC,oCAAoC,CAAC,CAAC;YACxD,iCAAiC,mCAC5B,OAAO;gBACV,sEAAsE;gBACtE,KAAK,EAAE,IAAA,2BAAmB,EAAC,oCAAoC,CAAC,KAAK,CAAC,GACvE,CAAC;SACH;aAAM,IAAI,OAAO,CAAC,KAAK,EAAE;YACxB,iCAAiC,GAAG,OAAO,CAAC;SAC7C;aAAM;YACL,iCAAiC,GAAG,MAAM,IAAI,CAAC,qBAAqB,CAClE,OAAO,CACR,CAAC;SACH;QACD,YAAY,CAAC,IAAI,CAAC,iCAAiC,CAAC,CAAC;QACrD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,IAAA,wBAAgB,EAAC,eAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,EAAE;YACzE,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACnB,GAAG,EAAE,YAAY;aAClB,CAAC;SACH,CAAC,CAAC;QACH,IAAI,cAAc,CAAC;QACnB,IAAI,WAAW,CAAC;QAChB,IAAI,UAAU,EAAE;YACd,cAAc,GAAG,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,GAAG,CAAC,CAAC,CAAC,CAAC;YAC9B,WAAW,GAAG,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,GAAG,CAAC,CAAC,CAAC,CAAC;SAC5B;aAAM;YACL,WAAW,GAAG,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,GAAG,CAAC,CAAC,CAAC,CAAC;SAC5B;QAED,IAAI,CAAC,MAAM,CAAC;YACV,sBAAsB,gDACjB,IAAI,CAAC,KAAK,CAAC,sBAAsB,GACjC,CAAC,OAAO,KAAK,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI;gBACrC,IAAI,EAAE;oBACJ,cAAc;oBACd,WAAW;iBACZ;aACF,CAAC,KACF,aAAa,kCACR,IAAI,CAAC,KAAK,CAAC,sBAAsB,CAAC,aAAa,KAClD,CAAC,OAAO,CAAC,EAAE;wBACT,cAAc;wBACd,WAAW;qBACZ,MAEJ;SACF,CAAC,CAAC;QAEH,OAAO;YACL,cAAc;YACd,WAAW;SACZ,CAAC;IACJ,CAAC;IAED,yEAAyE;IACzE,sDAAsD;IACtD,KAAK,CAAC,wBAAwB,CAAC,EAC7B,eAAe,EACf,QAAQ,EACR,kBAAkB,EAClB,0BAA0B,EAC1B,eAAe,GAOhB;;QACC,MAAM,OAAO,GAAG,uBAAA,IAAI,uFAAY,MAAhB,IAAI,EAAa,EAAE,eAAe,EAAE,CAAC,CAAC;QACtD,MAAM,QAAQ,GAAG,uBAAA,IAAI,wFAAa,MAAjB,IAAI,EAAc,EAAE,eAAe,EAAE,CAAC,CAAC;QACxD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,KAAK,CAC3B,IAAA,wBAAgB,EAAC,eAAO,CAAC,mBAAmB,EAAE,OAAO,CAAC,EACtD;YACE,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACnB,MAAM,EAAE,kBAAkB;gBAC1B,YAAY,EAAE,0BAA0B;aACzC,CAAC;SACH,CACF,CAAC;QACF,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACxB,IAAI,YAAY,CAAC;QACjB,IAAI;YACF,MAAM,cAAc,GAAG,MAAM,IAAA,wBAAK,EAAC,QAAQ,EAAE,YAAY,EAAE;gBACzD,QAAQ,aAAR,QAAQ,uBAAR,QAAQ,CAAE,IAAI;aACf,CAAC,CAAC;YACH,YAAY,GAAG,IAAI,wBAAS,CAAC,cAAc,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;SAC3D;QAAC,OAAO,KAAK,EAAE;YACd,OAAO,CAAC,KAAK,CAAC,gBAAgB,EAAE,KAAK,CAAC,CAAC;SACxC;QAED,MAAM,aAAa,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC;QACtC,IAAI,KAAK,CAAC;QACV,IAAI,SAAS,CAAC;QACd,IAAI,YAAY,GAAG,EAAE,CAAC;QAEtB,IAAI,aAAa,EAAE;YACjB,SAAS,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,QAAQ,aAAR,QAAQ,uBAAR,QAAQ,CAAE,IAAI,CAAC,CAAC;YACpD,KAAK,GAAG,IAAA,eAAO,EAAC,SAAS,CAAC,SAAS,CAAC,CAAC;YACrC,YAAY,GAAG,SAAS,CAAC,YAAY,CAAC;YACtC,IAAI,QAAQ,EAAE;gBACZ,MAAA,QAAQ,CAAC,KAAK,oCAAd,QAAQ,CAAC,KAAK,GAAK,KAAK,EAAC;aAC1B;SACF;QACD,MAAM,yBAAyB,mCAC1B,IAAI,KACP,MAAM,EAAE,IAAA,iBAAS,EAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,GACzC,CAAC;QAEF,IAAI;YACF,uBAAA,IAAI,mGAAwB,MAA5B,IAAI,EACF;gBACE,OAAO;gBACP,YAAY;gBACZ,YAAY;gBACZ,MAAM,EAAE,gCAAwB,CAAC,OAAO;gBACxC,IAAI;gBACJ,QAAQ;gBACR,IAAI,EAAE,yBAAyB,CAAC,IAAI;gBACpC,MAAM,EAAE,yBAAyB,CAAC,MAAM;gBACxC,WAAW,EAAE,IAAI;gBACjB,IAAI,EAAE,CAAA,eAAe,aAAf,eAAe,uBAAf,eAAe,CAAE,IAAI,KAAI,MAAM;aACtC,EACD,EAAE,OAAO,EAAE,QAAQ,EAAE,CACtB,CAAC;SACH;gBAAS;YACR,SAAS,aAAT,SAAS,uBAAT,SAAS,CAAE,WAAW,EAAE,CAAC;SAC1B;QAED,OAAO,yBAAyB,CAAC;IACnC,CAAC;IA0BD,0FAA0F;IAC1F,qEAAqE;IACrE,uDAAuD;IACvD,KAAK,CAAC,sBAAsB,CAC1B,IAAY,EACZ,EACE,eAAe,MAGb,EAAE;QAEN,MAAM,OAAO,GAAG,uBAAA,IAAI,uFAAY,MAAhB,IAAI,EAAa,EAAE,eAAe,EAAE,CAAC,CAAC;QACtD,MAAM,IAAI,CAAC,KAAK,CAAC,IAAA,wBAAgB,EAAC,eAAO,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE;YAC1D,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,CAAC;SAC/B,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,EAClB,eAAe,MAGb,EAAE;QACJ,MAAM,OAAO,GAAG,uBAAA,IAAI,uFAAY,MAAhB,IAAI,EAAa,EAAE,eAAe,EAAE,CAAC,CAAC;QACtD,IAAI,QAAQ,GAAG,KAAK,CAAC;QACrB,IAAI;YACF,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,KAAK,CAC/B,IAAA,wBAAgB,EAAC,eAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,CAC5C,CAAC;YACF,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;SACxC;QAAC,OAAO,KAAK,EAAE;YACd,OAAO,CAAC,GAAG,CAAC,iCAAiC,CAAC,CAAC;SAChD;QAED,IAAI,CAAC,MAAM,CAAC;YACV,sBAAsB,gDACjB,IAAI,CAAC,KAAK,CAAC,sBAAsB,GACjC,CAAC,OAAO,KAAK,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,QAAQ,EAAE,CAAC,KACpD,iBAAiB,kCACZ,IAAI,CAAC,KAAK,CAAC,sBAAsB,CAAC,iBAAiB,KACtD,CAAC,OAAO,CAAC,EAAE,QAAQ,MAEtB;SACF,CAAC,CAAC;QAEH,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,KAAK,CAAC,wBAAwB,CAAC,QAAgB;QAC7C,IAAI,QAAQ,KAAK,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE;YACrC,IAAI,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;SAC5C;IACH,CAAC;IAYD,eAAe,CAAC,EACd,WAAW,EACX,MAAM,GAIP;QACC,MAAM,wBAAwB,GAAG,uBAAA,IAAI,wGAA6B,MAAjC,IAAI,CAA+B,CAAC;QACrE,OAAO,wBAAwB,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE;;YAC7C,OAAO,GAAG,CAAC,MAAM,KAAK,MAAM,IAAI,CAAA,MAAA,GAAG,CAAC,QAAQ,0CAAE,IAAI,MAAK,WAAW,CAAC;QACrE,CAAC,CAAC,CAAC;IACL,CAAC;IAED,gCAAgC,CAC9B,MAA0B;QAE1B,IAAI,CAAC,MAAM,EAAE;YACX,OAAO,SAAS,CAAC;SAClB;QACD,MAAM,wBAAwB,GAAG,uBAAA,IAAI,wGAA6B,MAAjC,IAAI,CAA+B,CAAC;QACrE,OAAO,wBAAwB,CAAC,IAAI,CAAC,CAAC,gBAAgB,EAAE,EAAE;;YACxD,OAAO,CACL,CAAA,MAAA,MAAA,gBAAgB,CAAC,cAAc,0CAAE,SAAS,0CAAE,WAAW,EAAE;gBACzD,MAAM,CAAC,WAAW,EAAE,CACrB,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC;CACF;AA5yBD,8CA4yBC;8GAtiBC,KAAK,8DACH,gBAAkC,EAClC,EACE,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,EAC7B,QAAQ,GAAG,IAAI,CAAC,QAAQ,GAIzB;;IAED,MAAM,EAAE,sBAAsB,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;IAC9C,MAAM,EAAE,iBAAiB,EAAE,GAAG,sBAAsB,CAAC;IACrD,MAAM,wBAAwB,GAAG,MAAA,iBAAiB,CAAC,OAAO,CAAC,mCAAI,EAAE,CAAC;IAClE,MAAM,YAAY,GAAG,wBAAwB,aAAxB,wBAAwB,uBAAxB,wBAAwB,CAAE,SAAS,CACtD,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,KAAK,gBAAgB,CAAC,IAAI,CAC5C,CAAC;IACF,MAAM,qBAAqB,GAAG,IAAI,CAAC,qBAAqB,CACtD,gBAAgB,CAAC,IAAI,CACtB,CAAC;IACF,IAAI,IAAI,CAAC,QAAQ,KAAK,SAAS,EAAE;QAC/B,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;KACtC;IAED,IAAI,CAAC,oBAAoB,CACvB,gBAAgB,EAChB,qBAAqB;QACnB,CAAC,CAAC,SAAS;QACX,CAAC,CAAC,wBAAwB,CAAC,YAAY,CAAC,CAC3C,CAAC;IAEF,IAAI,qBAAqB,EAAE;QACzB,wBAAwB;QACxB,MAAM,mBAAmB,GAAG,wBAAwB,aAAxB,wBAAwB,uBAAxB,wBAAwB,CAAE,SAAS,CAC7D,CAAC,GAAqB,EAAE,EAAE;;YACxB,OAAA,CAAA,MAAA,GAAG,CAAC,QAAQ,0CAAE,KAAK,OAAK,MAAA,gBAAgB,CAAC,QAAQ,0CAAE,KAAK,CAAA;iBACxD,MAAA,GAAG,CAAC,MAAM,0CAAE,UAAU,CAAC,WAAW,CAAC,CAAA,CAAA;SAAA,CACtC,CAAC;QACF,MAAM,QAAQ,GAAG,IAAA,mBAAS,EAAC,gBAAgB,CAAC,CAAC;QAC7C,MAAM,OAAO,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC3B,MAAM,2BAA2B,mCAAQ,gBAAgB,KAAE,OAAO,GAAE,CAAC;QACrE,MAAM,qBAAqB,GACzB,mBAAmB,GAAG,CAAC,CAAC;YACtB,CAAC,CAAC,wBAAwB;iBACrB,KAAK,CAAC,CAAC,EAAE,mBAAmB,CAAC;iBAC7B,MAAM,CAAC,wBAAwB,CAAC,KAAK,CAAC,mBAAmB,GAAG,CAAC,CAAC,CAAC;iBAC/D,MAAM,CAAC,2BAA2B,CAAC;YACxC,CAAC,CAAC,wBAAwB,CAAC,MAAM,CAAC,2BAA2B,CAAC,CAAC;QACnE,IAAI,CAAC,MAAM,CAAC;YACV,sBAAsB,kCACjB,sBAAsB,KACzB,iBAAiB,kCACZ,sBAAsB,CAAC,iBAAiB,KAC3C,CAAC,OAAO,CAAC,EAAE,qBAAqB,MAEnC;SACF,CAAC,CAAC;QACH,OAAO;KACR;IAED,IACE,CAAC,gBAAgB,CAAC,MAAM,KAAK,gCAAwB,CAAC,OAAO;QAC3D,gBAAgB,CAAC,MAAM,KAAK,gCAAwB,CAAC,QAAQ,CAAC;QAChE,CAAC,gBAAgB,CAAC,SAAS,EAC3B;QACA,4BAA4B;QAC5B,MAAM,uBAAuB,GAAG,wBAAwB,CAAC,YAAY,CAAC,CAAC;QACvE,MAAM,oBAAoB,mCACrB,uBAAuB,GACvB,gBAAgB,CACpB,CAAC;QACF,MAAM,uBAAA,IAAI,oGAAyB,MAA7B,IAAI,EAA0B,oBAAoB,EAAE;YACxD,OAAO;YACP,QAAQ;SACT,CAAC,CAAC;KACJ;SAAM;QACL,IAAI,CAAC,MAAM,CAAC;YACV,sBAAsB,kCACjB,sBAAsB,KACzB,iBAAiB,kCACZ,sBAAsB,CAAC,iBAAiB,KAC3C,CAAC,OAAO,CAAC,EAAE,sBAAsB,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC,GAAG,CAC9D,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;wBACd,OAAO,KAAK,KAAK,YAAY;4BAC3B,CAAC,iCAAM,IAAI,GAAK,gBAAgB,EAChC,CAAC,CAAC,IAAI,CAAC;oBACX,CAAC,CACF,MAEJ;SACF,CAAC,CAAC;KACJ;IAED,IAAI,CAAC,YAAY,CAAC,IAAI,CACpB,GAAG,gBAAgB,CAAC,IAAI,mBAAmB,EAC3C,gBAAgB,CACjB,CAAC;AACJ,CAAC,uIAsBgC,MAA0B;IACzD,IAAI,CAAC,MAAM,EAAE;QACX,OAAO,IAAI,CAAC;KACb;IACD,MAAM,YAAY,GAAG,IAAI,CAAC,sBAAsB,EAAE,CAAC;IACnD,MAAM,gBAAgB,GAAG,YAAY,aAAZ,YAAY,uBAAZ,YAAY,CAAE,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE;;QACjD,OAAO,CAAA,MAAA,EAAE,CAAC,IAAI,0CAAE,WAAW,EAAE,MAAK,MAAM,CAAC,WAAW,EAAE,CAAC;IACzD,CAAC,CAAC,CAAC;IACH,IAAI,CAAC,gBAAgB,EAAE;QACrB,OAAO,IAAI,CAAC;KACb;IACD,iHAAiH;IACjH,iGAAiG;IACjG,uCAAuC;IACvC,OAAO,CAAC,CAAC,0CAAiB,CAAC,SAAS,EAAE,0CAAiB,CAAC,SAAS,CAAC,CAAC,QAAQ,CACzE,gBAAgB,CAAC,MAAM,CACxB,CAAC;AACJ,CAAC,yDAED,KAAK,+DACH,gBAAkC,EAClC,EACE,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,EAC7B,QAAQ,GAAG,IAAI,CAAC,QAAQ,GAIzB;;IAED,IAAI,QAAQ,KAAK,SAAS,EAAE;QAC1B,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;KACtC;IACD,MAAM,MAAM,GAAG,MAAA,gBAAgB,CAAC,cAAc,0CAAE,SAAS,CAAC;IAC1D,IAAI;QACF,MAAM,kBAAkB,GAIb,MAAM,IAAA,wBAAK,EAAC,QAAQ,EAAE,uBAAuB,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;QACpE,MAAM,WAAW,GAGN,MAAM,IAAA,wBAAK,EAAC,QAAQ,EAAE,sBAAsB,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;QAEnE,MAAM,YAAY,GAAG,WAAW,aAAX,WAAW,uBAAX,WAAW,CAAE,YAAY,CAAC;QAC/C,MAAM,oBAAoB,GAAG,WAAW,aAAX,WAAW,uBAAX,WAAW,CAAE,oBAAoB,CAAC;QAC/D,IAAI,kBAAkB,aAAlB,kBAAkB,uBAAlB,kBAAkB,CAAE,WAAW,EAAE;YACnC,MAAM,SAAS,GAAsC,MAAM,IAAA,wBAAK,EAC9D,QAAQ,EACR,kBAAkB,EAClB,CAAC,kBAAkB,aAAlB,kBAAkB,uBAAlB,kBAAkB,CAAE,WAAW,EAAE,KAAK,CAAC,CACzC,CAAC;YACF,MAAM,aAAa,GAAG,SAAS,aAAT,SAAS,uBAAT,SAAS,CAAE,aAAa,CAAC;YAC/C,MAAM,eAAe,mCAChB,gBAAgB,CAAC,QAAQ,KAC5B,YAAY;gBACZ,oBAAoB,GACrB,CAAC;YACF,kCAAkC;YAClC,MAAM,cAAc,mCACf,gBAAgB,KACnB,EAAE,EAAE,gBAAgB,CAAC,IAAI,EACzB,MAAM,EAAE,0CAAiB,CAAC,SAAS,EACnC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,eAAe,GAC1B,CAAC;YACF,qCAAqC;YACrC,MAAM,QAAQ,GAAG,IAAA,0BAAkB,EAAC,cAAc,CAAC,CAAC;YACpD,gCAAgC;YAChC,MAAM,aAAa,GAAG,IAAA,qBAAa,EAAC,cAAc,CAAC,OAAO,CAAC,CAAC;YAC5D,4CAA4C;YAC5C,MAAM,KAAK,GAAG,IAAA,4BAAoB,EAChC,aAAa,EACb,QAAQ,EACR,6CAA6C,CAC9C,CAAC;YACF,MAAM,MAAM,GACV,KAAK,CAAC,MAAM,GAAG,CAAC;gBACd,CAAC,iCACM,cAAc,KACjB,OAAO,EAAE,cAAc,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAEjD,CAAC,CAAC,cAAc,CAAC;YAErB,IAAI,uBAAA,IAAI,4GAAiC,MAArC,IAAI,EAAkC,MAAM,CAAC,EAAE;gBACjD,IAAI,CAAC,0BAA0B,CAC7B,MAAM,EACN,kBAAkB,EAClB,aAAa,CACd,CAAC;aACH;YAED,IAAI,CAAC,qBAAqB,CAAC;gBACzB,KAAK,EAAE,gCAAoB,CAAC,YAAY;gBACxC,QAAQ,EAAE,oCAAwB,CAAC,YAAY;aAChD,CAAC,CAAC;YAEH,uBAAA,IAAI,mGAAwB,MAA5B,IAAI,kCAEG,gBAAgB,KACnB,SAAS,EAAE,IAAI,KAEjB,EAAE,OAAO,EAAE,QAAQ,EAAE,CACtB,CAAC;SACH;KACF;IAAC,OAAO,KAAK,EAAE;QACd,IAAI,CAAC,qBAAqB,CAAC;YACzB,KAAK,EAAE,gCAAoB,CAAC,qBAAqB;YACjD,QAAQ,EAAE,oCAAwB,CAAC,YAAY;SAChD,CAAC,CAAC;QACH,OAAO,CAAC,KAAK,CAAC,eAAe,EAAE,KAAK,CAAC,CAAC;KACvC;AACH,CAAC,6FA6MW,EACV,eAAe,MAC0B,EAAE;IAC3C,OAAO,eAAe;QACpB,CAAC,CAAC,IAAI,CAAC,oBAAoB,CAAC,eAAe,CAAC,CAAC,aAAa,CAAC,OAAO;QAClE,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC;AAC1B,CAAC,+FAEY,EACX,eAAe,MAGb,EAAE;IACJ,IAAI,eAAe,EAAE;QACnB,OAAO,IAAI,mBAAQ,CAAC,IAAI,CAAC,oBAAoB,CAAC,eAAe,CAAC,CAAC,QAAQ,CAAC,CAAC;KAC1E;IAED,IAAI,IAAI,CAAC,QAAQ,KAAK,SAAS,EAAE;QAC/B,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;KACtC;IAED,OAAO,IAAI,CAAC,QAAQ,CAAC;AACvB,CAAC;IAyDC,MAAM,EAAE,iBAAiB,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,sBAAsB,CAAC;IAChE,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC;IAChC,MAAM,wBAAwB,GAAG,iBAAiB,aAAjB,iBAAiB,uBAAjB,iBAAiB,CAAG,OAAO,CAAC,CAAC;IAC9D,IAAI,CAAC,wBAAwB,IAAI,wBAAwB,CAAC,MAAM,KAAK,CAAC,EAAE;QACtE,OAAO,EAAE,CAAC;KACX;IACD,OAAO,wBAAwB,CAAC;AAClC,CAAC","sourcesContent":["// eslint-disable-next-line import/no-nodejs-modules\nimport { hexlify } from '@ethersproject/bytes';\nimport type { BaseConfig, BaseState } from '@metamask/base-controller';\nimport { query, safelyExecute } from '@metamask/controller-utils';\nimport type { Provider } from '@metamask/eth-query';\nimport EthQuery from '@metamask/eth-query';\nimport type {\n NetworkClientId,\n NetworkController,\n NetworkState,\n} from '@metamask/network-controller';\nimport { StaticIntervalPollingControllerV1 } from '@metamask/polling-controller';\nimport type { TransactionMeta } from '@metamask/transaction-controller';\nimport { TransactionStatus } from '@metamask/transaction-controller';\nimport { BigNumber } from 'bignumber.js';\n// eslint-disable-next-line import/no-nodejs-modules\nimport EventEmitter from 'events';\nimport cloneDeep from 'lodash/cloneDeep';\n\nimport {\n CHAIN_IDS,\n MetaMetricsEventCategory,\n MetaMetricsEventName,\n} from './constants';\nimport type {\n Fees,\n Hex,\n IndividualTxFees,\n SignedCanceledTransaction,\n SignedTransaction,\n SmartTransaction,\n SmartTransactionsStatus,\n UnsignedTransaction,\n GetTransactionsOptions,\n} from './types';\nimport { APIType, SmartTransactionStatuses } from './types';\nimport {\n calculateStatus,\n generateHistoryEntry,\n getAPIRequestURL,\n getStxProcessingTime,\n handleFetch,\n incrementNonceInHex,\n isSmartTransactionCancellable,\n isSmartTransactionPending,\n replayHistory,\n snapshotFromTxMeta,\n getTxHash,\n} from './utils';\n\nconst SECOND = 1000;\nexport const DEFAULT_INTERVAL = SECOND * 5;\nconst ETH_QUERY_ERROR_MSG =\n '`ethQuery` is not defined on SmartTransactionsController';\n\nexport type SmartTransactionsControllerConfig = BaseConfig & {\n interval: number;\n clientId: string;\n chainId: Hex;\n supportedChainIds: string[];\n};\n\ntype FeeEstimates = {\n approvalTxFees: IndividualTxFees | undefined;\n tradeTxFees: IndividualTxFees | undefined;\n};\n\nexport type SmartTransactionsControllerState = BaseState & {\n smartTransactionsState: {\n smartTransactions: Record;\n userOptIn: boolean | undefined;\n userOptInV2: boolean | undefined;\n liveness: boolean | undefined;\n fees: FeeEstimates;\n feesByChainId: Record;\n livenessByChainId: Record;\n };\n};\n\nexport default class SmartTransactionsController extends StaticIntervalPollingControllerV1<\n SmartTransactionsControllerConfig,\n SmartTransactionsControllerState\n> {\n /**\n * Name of this controller used during composition\n */\n override name = 'SmartTransactionsController';\n\n public timeoutHandle?: NodeJS.Timeout;\n\n private readonly getNonceLock: any;\n\n private ethQuery: EthQuery | undefined;\n\n public confirmExternalTransaction: any;\n\n public getRegularTransactions: (\n options?: GetTransactionsOptions,\n ) => TransactionMeta[];\n\n private readonly trackMetaMetricsEvent: any;\n\n public eventEmitter: EventEmitter;\n\n private readonly getNetworkClientById: NetworkController['getNetworkClientById'];\n\n /* istanbul ignore next */\n private async fetch(request: string, options?: RequestInit) {\n const { clientId } = this.config;\n const fetchOptions = {\n ...options,\n headers: {\n 'Content-Type': 'application/json',\n ...(clientId && { 'X-Client-Id': clientId }),\n },\n };\n\n return handleFetch(request, fetchOptions);\n }\n\n constructor(\n {\n onNetworkStateChange,\n getNonceLock,\n provider,\n confirmExternalTransaction,\n getTransactions,\n trackMetaMetricsEvent,\n getNetworkClientById,\n }: {\n onNetworkStateChange: (\n listener: (networkState: NetworkState) => void,\n ) => void;\n getNonceLock: any;\n provider: Provider;\n confirmExternalTransaction: any;\n getTransactions: (options?: GetTransactionsOptions) => TransactionMeta[];\n trackMetaMetricsEvent: any;\n getNetworkClientById: NetworkController['getNetworkClientById'];\n },\n config?: Partial,\n state?: Partial,\n ) {\n super(config, state);\n\n this.defaultConfig = {\n interval: DEFAULT_INTERVAL,\n chainId: CHAIN_IDS.ETHEREUM,\n clientId: 'default',\n supportedChainIds: [CHAIN_IDS.ETHEREUM, CHAIN_IDS.GOERLI],\n };\n\n this.defaultState = {\n smartTransactionsState: {\n smartTransactions: {},\n userOptIn: undefined,\n userOptInV2: undefined,\n fees: {\n approvalTxFees: undefined,\n tradeTxFees: undefined,\n },\n liveness: true,\n livenessByChainId: {\n [CHAIN_IDS.ETHEREUM]: true,\n [CHAIN_IDS.GOERLI]: true,\n },\n feesByChainId: {\n [CHAIN_IDS.ETHEREUM]: {\n approvalTxFees: undefined,\n tradeTxFees: undefined,\n },\n [CHAIN_IDS.GOERLI]: {\n approvalTxFees: undefined,\n tradeTxFees: undefined,\n },\n },\n },\n };\n\n this.initialize();\n this.setIntervalLength(this.config.interval);\n this.getNonceLock = getNonceLock;\n this.ethQuery = undefined;\n this.confirmExternalTransaction = confirmExternalTransaction;\n this.getRegularTransactions = getTransactions;\n this.trackMetaMetricsEvent = trackMetaMetricsEvent;\n this.getNetworkClientById = getNetworkClientById;\n\n this.initializeSmartTransactionsForChainId();\n\n onNetworkStateChange(({ providerConfig: newProvider }) => {\n const { chainId } = newProvider;\n this.configure({ chainId });\n this.initializeSmartTransactionsForChainId();\n this.checkPoll(this.state);\n this.ethQuery = new EthQuery(provider);\n });\n\n this.subscribe((currentState: any) => this.checkPoll(currentState));\n this.eventEmitter = new EventEmitter();\n }\n\n async _executePoll(networkClientId: string): Promise {\n // if this is going to be truly UI driven polling we shouldn't really reach here\n // with a networkClientId that is not supported, but for now I'll add a check in case\n // wondering if we should add some kind of predicate to the polling controller to check whether\n // we should poll or not\n const chainId = this.#getChainId({ networkClientId });\n if (!this.config.supportedChainIds.includes(chainId)) {\n return Promise.resolve();\n }\n return this.updateSmartTransactions({ networkClientId });\n }\n\n checkPoll(state: any) {\n const { smartTransactions } = state.smartTransactionsState;\n const currentSmartTransactions = smartTransactions[this.config.chainId];\n const pendingTransactions = currentSmartTransactions?.filter(\n isSmartTransactionPending,\n );\n if (!this.timeoutHandle && pendingTransactions?.length > 0) {\n this.poll();\n } else if (this.timeoutHandle && pendingTransactions?.length === 0) {\n this.stop();\n }\n }\n\n initializeSmartTransactionsForChainId() {\n if (this.config.supportedChainIds.includes(this.config.chainId)) {\n const { smartTransactionsState } = this.state;\n this.update({\n smartTransactionsState: {\n ...smartTransactionsState,\n smartTransactions: {\n ...smartTransactionsState.smartTransactions,\n [this.config.chainId]:\n smartTransactionsState.smartTransactions[this.config.chainId] ??\n [],\n },\n },\n });\n }\n }\n\n async poll(interval?: number): Promise {\n const { chainId, supportedChainIds } = this.config;\n interval && this.configure({ interval }, false, false);\n this.timeoutHandle && clearInterval(this.timeoutHandle);\n if (!supportedChainIds.includes(chainId)) {\n return;\n }\n await safelyExecute(async () => this.updateSmartTransactions());\n this.timeoutHandle = setInterval(() => {\n safelyExecute(async () => this.updateSmartTransactions());\n }, this.config.interval);\n }\n\n async stop() {\n this.timeoutHandle && clearInterval(this.timeoutHandle);\n this.timeoutHandle = undefined;\n }\n\n setOptInState(state: boolean | undefined): void {\n this.update({\n smartTransactionsState: {\n ...this.state.smartTransactionsState,\n userOptInV2: state,\n },\n });\n }\n\n trackStxStatusChange(\n smartTransaction: SmartTransaction,\n prevSmartTransaction?: SmartTransaction,\n ) {\n if (!prevSmartTransaction) {\n return; // Don't track the first STX, because it doesn't have all necessary params.\n }\n\n let updatedSmartTransaction = cloneDeep(smartTransaction);\n updatedSmartTransaction = {\n ...cloneDeep(prevSmartTransaction),\n ...updatedSmartTransaction,\n };\n\n if (\n !updatedSmartTransaction.swapMetaData ||\n (updatedSmartTransaction.status === prevSmartTransaction.status &&\n prevSmartTransaction.swapMetaData)\n ) {\n return; // If status hasn't changed, don't track it again.\n }\n\n const sensitiveProperties = {\n stx_status: updatedSmartTransaction.status,\n token_from_symbol: updatedSmartTransaction.sourceTokenSymbol,\n token_to_symbol: updatedSmartTransaction.destinationTokenSymbol,\n processing_time: getStxProcessingTime(updatedSmartTransaction.time),\n stx_enabled: true,\n current_stx_enabled: true,\n stx_user_opt_in: true,\n };\n\n this.trackMetaMetricsEvent({\n event: MetaMetricsEventName.StxStatusUpdated,\n category: MetaMetricsEventCategory.Transactions,\n sensitiveProperties,\n });\n }\n\n isNewSmartTransaction(smartTransactionUuid: string): boolean {\n const { chainId } = this.config;\n const { smartTransactionsState } = this.state;\n const { smartTransactions } = smartTransactionsState;\n const currentSmartTransactions = smartTransactions[chainId];\n const currentIndex = currentSmartTransactions?.findIndex(\n (stx) => stx.uuid === smartTransactionUuid,\n );\n return currentIndex === -1 || currentIndex === undefined;\n }\n\n updateSmartTransaction(\n smartTransaction: SmartTransaction,\n { networkClientId }: { networkClientId?: NetworkClientId } = {},\n ) {\n let {\n ethQuery,\n config: { chainId },\n } = this;\n if (networkClientId) {\n const networkClient = this.getNetworkClientById(networkClientId);\n chainId = networkClient.configuration.chainId;\n ethQuery = new EthQuery(networkClient.provider);\n }\n\n this.#updateSmartTransaction(smartTransaction, {\n chainId,\n ethQuery,\n });\n }\n\n async #updateSmartTransaction(\n smartTransaction: SmartTransaction,\n {\n chainId = this.config.chainId,\n ethQuery = this.ethQuery,\n }: {\n chainId: Hex;\n ethQuery: EthQuery | undefined;\n },\n ): Promise {\n const { smartTransactionsState } = this.state;\n const { smartTransactions } = smartTransactionsState;\n const currentSmartTransactions = smartTransactions[chainId] ?? [];\n const currentIndex = currentSmartTransactions?.findIndex(\n (stx) => stx.uuid === smartTransaction.uuid,\n );\n const isNewSmartTransaction = this.isNewSmartTransaction(\n smartTransaction.uuid,\n );\n if (this.ethQuery === undefined) {\n throw new Error(ETH_QUERY_ERROR_MSG);\n }\n\n this.trackStxStatusChange(\n smartTransaction,\n isNewSmartTransaction\n ? undefined\n : currentSmartTransactions[currentIndex],\n );\n\n if (isNewSmartTransaction) {\n // add smart transaction\n const cancelledNonceIndex = currentSmartTransactions?.findIndex(\n (stx: SmartTransaction) =>\n stx.txParams?.nonce === smartTransaction.txParams?.nonce &&\n stx.status?.startsWith('cancelled'),\n );\n const snapshot = cloneDeep(smartTransaction);\n const history = [snapshot];\n const historifiedSmartTransaction = { ...smartTransaction, history };\n const nextSmartTransactions =\n cancelledNonceIndex > -1\n ? currentSmartTransactions\n .slice(0, cancelledNonceIndex)\n .concat(currentSmartTransactions.slice(cancelledNonceIndex + 1))\n .concat(historifiedSmartTransaction)\n : currentSmartTransactions.concat(historifiedSmartTransaction);\n this.update({\n smartTransactionsState: {\n ...smartTransactionsState,\n smartTransactions: {\n ...smartTransactionsState.smartTransactions,\n [chainId]: nextSmartTransactions,\n },\n },\n });\n return;\n }\n\n if (\n (smartTransaction.status === SmartTransactionStatuses.SUCCESS ||\n smartTransaction.status === SmartTransactionStatuses.REVERTED) &&\n !smartTransaction.confirmed\n ) {\n // confirm smart transaction\n const currentSmartTransaction = currentSmartTransactions[currentIndex];\n const nextSmartTransaction = {\n ...currentSmartTransaction,\n ...smartTransaction,\n };\n await this.#confirmSmartTransaction(nextSmartTransaction, {\n chainId,\n ethQuery,\n });\n } else {\n this.update({\n smartTransactionsState: {\n ...smartTransactionsState,\n smartTransactions: {\n ...smartTransactionsState.smartTransactions,\n [chainId]: smartTransactionsState.smartTransactions[chainId].map(\n (item, index) => {\n return index === currentIndex\n ? { ...item, ...smartTransaction }\n : item;\n },\n ),\n },\n },\n });\n }\n\n this.eventEmitter.emit(\n `${smartTransaction.uuid}:smartTransaction`,\n smartTransaction,\n );\n }\n\n async updateSmartTransactions({\n networkClientId,\n }: {\n networkClientId?: NetworkClientId;\n } = {}): Promise {\n const { smartTransactions } = this.state.smartTransactionsState;\n const chainId = this.#getChainId({ networkClientId });\n const smartTransactionsForChainId = smartTransactions[chainId];\n\n const transactionsToUpdate: string[] = smartTransactionsForChainId\n .filter(isSmartTransactionPending)\n .map((smartTransaction) => smartTransaction.uuid);\n\n if (transactionsToUpdate.length > 0) {\n this.fetchSmartTransactionsStatus(transactionsToUpdate, {\n networkClientId,\n });\n }\n }\n\n #doesTransactionNeedConfirmation(txHash: string | undefined): boolean {\n if (!txHash) {\n return true;\n }\n const transactions = this.getRegularTransactions();\n const foundTransaction = transactions?.find((tx) => {\n return tx.hash?.toLowerCase() === txHash.toLowerCase();\n });\n if (!foundTransaction) {\n return true;\n }\n // If a found transaction is either confirmed or submitted, it doesn't need confirmation from the STX controller.\n // When it's in the submitted state, the TransactionController checks its status and confirms it,\n // so no need to confirm it again here.\n return ![TransactionStatus.confirmed, TransactionStatus.submitted].includes(\n foundTransaction.status,\n );\n }\n\n async #confirmSmartTransaction(\n smartTransaction: SmartTransaction,\n {\n chainId = this.config.chainId,\n ethQuery = this.ethQuery,\n }: {\n chainId: Hex;\n ethQuery: EthQuery | undefined;\n },\n ) {\n if (ethQuery === undefined) {\n throw new Error(ETH_QUERY_ERROR_MSG);\n }\n const txHash = smartTransaction.statusMetadata?.minedHash;\n try {\n const transactionReceipt: {\n maxFeePerGas?: string;\n maxPriorityFeePerGas?: string;\n blockNumber: string;\n } | null = await query(ethQuery, 'getTransactionReceipt', [txHash]);\n const transaction: {\n maxFeePerGas?: string;\n maxPriorityFeePerGas?: string;\n } | null = await query(ethQuery, 'getTransactionByHash', [txHash]);\n\n const maxFeePerGas = transaction?.maxFeePerGas;\n const maxPriorityFeePerGas = transaction?.maxPriorityFeePerGas;\n if (transactionReceipt?.blockNumber) {\n const blockData: { baseFeePerGas?: string } | null = await query(\n ethQuery,\n 'getBlockByNumber',\n [transactionReceipt?.blockNumber, false],\n );\n const baseFeePerGas = blockData?.baseFeePerGas;\n const updatedTxParams = {\n ...smartTransaction.txParams,\n maxFeePerGas,\n maxPriorityFeePerGas,\n };\n // call confirmExternalTransaction\n const originalTxMeta = {\n ...smartTransaction,\n id: smartTransaction.uuid,\n status: TransactionStatus.confirmed,\n hash: txHash,\n txParams: updatedTxParams,\n };\n // create txMeta snapshot for history\n const snapshot = snapshotFromTxMeta(originalTxMeta);\n // recover previous tx state obj\n const previousState = replayHistory(originalTxMeta.history);\n // generate history entry and add to history\n const entry = generateHistoryEntry(\n previousState,\n snapshot,\n 'txStateManager: setting status to confirmed',\n );\n const txMeta =\n entry.length > 0\n ? {\n ...originalTxMeta,\n history: originalTxMeta.history.concat(entry),\n }\n : originalTxMeta;\n\n if (this.#doesTransactionNeedConfirmation(txHash)) {\n this.confirmExternalTransaction(\n txMeta,\n transactionReceipt,\n baseFeePerGas,\n );\n }\n\n this.trackMetaMetricsEvent({\n event: MetaMetricsEventName.StxConfirmed,\n category: MetaMetricsEventCategory.Transactions,\n });\n\n this.#updateSmartTransaction(\n {\n ...smartTransaction,\n confirmed: true,\n },\n { chainId, ethQuery },\n );\n }\n } catch (error) {\n this.trackMetaMetricsEvent({\n event: MetaMetricsEventName.StxConfirmationFailed,\n category: MetaMetricsEventCategory.Transactions,\n });\n console.error('confirm error', error);\n }\n }\n\n // ! Ask backend API to accept list of uuids as params\n async fetchSmartTransactionsStatus(\n uuids: string[],\n { networkClientId }: { networkClientId?: NetworkClientId } = {},\n ): Promise> {\n const params = new URLSearchParams({\n uuids: uuids.join(','),\n });\n const chainId = this.#getChainId({ networkClientId });\n const ethQuery = this.#getEthQuery({ networkClientId });\n const url = `${getAPIRequestURL(\n APIType.BATCH_STATUS,\n chainId,\n )}?${params.toString()}`;\n\n const data = (await this.fetch(url)) as Record<\n string,\n SmartTransactionsStatus\n >;\n\n Object.entries(data).forEach(([uuid, stxStatus]) => {\n const smartTransaction = {\n statusMetadata: stxStatus,\n status: calculateStatus(stxStatus),\n cancellable: isSmartTransactionCancellable(stxStatus),\n uuid,\n };\n this.#updateSmartTransaction(smartTransaction, { chainId, ethQuery });\n });\n\n return data;\n }\n\n async addNonceToTransaction(\n transaction: UnsignedTransaction,\n ): Promise {\n const nonceLock = await this.getNonceLock(transaction.from);\n const nonce = nonceLock.nextNonce;\n nonceLock.releaseLock();\n return {\n ...transaction,\n nonce: `0x${nonce.toString(16)}`,\n };\n }\n\n clearFees(): Fees {\n const fees = {\n approvalTxFees: undefined,\n tradeTxFees: undefined,\n };\n this.update({\n smartTransactionsState: {\n ...this.state.smartTransactionsState,\n fees,\n },\n });\n return fees;\n }\n\n async getFees(\n tradeTx: UnsignedTransaction,\n approvalTx?: UnsignedTransaction,\n { networkClientId }: { networkClientId?: NetworkClientId } = {},\n ): Promise {\n const chainId = this.#getChainId({ networkClientId });\n const transactions = [];\n let unsignedTradeTransactionWithNonce;\n if (approvalTx) {\n const unsignedApprovalTransactionWithNonce =\n await this.addNonceToTransaction(approvalTx);\n transactions.push(unsignedApprovalTransactionWithNonce);\n unsignedTradeTransactionWithNonce = {\n ...tradeTx,\n // If there is an approval tx, the trade tx's nonce is increased by 1.\n nonce: incrementNonceInHex(unsignedApprovalTransactionWithNonce.nonce),\n };\n } else if (tradeTx.nonce) {\n unsignedTradeTransactionWithNonce = tradeTx;\n } else {\n unsignedTradeTransactionWithNonce = await this.addNonceToTransaction(\n tradeTx,\n );\n }\n transactions.push(unsignedTradeTransactionWithNonce);\n const data = await this.fetch(getAPIRequestURL(APIType.GET_FEES, chainId), {\n method: 'POST',\n body: JSON.stringify({\n txs: transactions,\n }),\n });\n let approvalTxFees;\n let tradeTxFees;\n if (approvalTx) {\n approvalTxFees = data?.txs[0];\n tradeTxFees = data?.txs[1];\n } else {\n tradeTxFees = data?.txs[0];\n }\n\n this.update({\n smartTransactionsState: {\n ...this.state.smartTransactionsState,\n ...(chainId === this.config.chainId && {\n fees: {\n approvalTxFees,\n tradeTxFees,\n },\n }),\n feesByChainId: {\n ...this.state.smartTransactionsState.feesByChainId,\n [chainId]: {\n approvalTxFees,\n tradeTxFees,\n },\n },\n },\n });\n\n return {\n approvalTxFees,\n tradeTxFees,\n };\n }\n\n // * After this successful call client must add a nonce representative to\n // * transaction controller external transactions list\n async submitSignedTransactions({\n transactionMeta,\n txParams,\n signedTransactions,\n signedCanceledTransactions,\n networkClientId,\n }: {\n signedTransactions: SignedTransaction[];\n signedCanceledTransactions: SignedCanceledTransaction[];\n transactionMeta?: any;\n txParams?: any;\n networkClientId?: NetworkClientId;\n }) {\n const chainId = this.#getChainId({ networkClientId });\n const ethQuery = this.#getEthQuery({ networkClientId });\n const data = await this.fetch(\n getAPIRequestURL(APIType.SUBMIT_TRANSACTIONS, chainId),\n {\n method: 'POST',\n body: JSON.stringify({\n rawTxs: signedTransactions,\n rawCancelTxs: signedCanceledTransactions,\n }),\n },\n );\n const time = Date.now();\n let preTxBalance;\n try {\n const preTxBalanceBN = await query(ethQuery, 'getBalance', [\n txParams?.from,\n ]);\n preTxBalance = new BigNumber(preTxBalanceBN).toString(16);\n } catch (error) {\n console.error('provider error', error);\n }\n\n const requiresNonce = !txParams.nonce;\n let nonce;\n let nonceLock;\n let nonceDetails = {};\n\n if (requiresNonce) {\n nonceLock = await this.getNonceLock(txParams?.from);\n nonce = hexlify(nonceLock.nextNonce);\n nonceDetails = nonceLock.nonceDetails;\n if (txParams) {\n txParams.nonce ??= nonce;\n }\n }\n const submitTransactionResponse = {\n ...data,\n txHash: getTxHash(signedTransactions[0]),\n };\n\n try {\n this.#updateSmartTransaction(\n {\n chainId,\n nonceDetails,\n preTxBalance,\n status: SmartTransactionStatuses.PENDING,\n time,\n txParams,\n uuid: submitTransactionResponse.uuid,\n txHash: submitTransactionResponse.txHash,\n cancellable: true,\n type: transactionMeta?.type || 'swap',\n },\n { chainId, ethQuery },\n );\n } finally {\n nonceLock?.releaseLock();\n }\n\n return submitTransactionResponse;\n }\n\n #getChainId({\n networkClientId,\n }: { networkClientId?: NetworkClientId } = {}): Hex {\n return networkClientId\n ? this.getNetworkClientById(networkClientId).configuration.chainId\n : this.config.chainId;\n }\n\n #getEthQuery({\n networkClientId,\n }: {\n networkClientId?: NetworkClientId;\n } = {}): EthQuery {\n if (networkClientId) {\n return new EthQuery(this.getNetworkClientById(networkClientId).provider);\n }\n\n if (this.ethQuery === undefined) {\n throw new Error(ETH_QUERY_ERROR_MSG);\n }\n\n return this.ethQuery;\n }\n\n // TODO: This should return if the cancellation was on chain or not (for nonce management)\n // After this successful call client must update nonce representative\n // in transaction controller external transactions list\n async cancelSmartTransaction(\n uuid: string,\n {\n networkClientId,\n }: {\n networkClientId?: NetworkClientId;\n } = {},\n ): Promise {\n const chainId = this.#getChainId({ networkClientId });\n await this.fetch(getAPIRequestURL(APIType.CANCEL, chainId), {\n method: 'POST',\n body: JSON.stringify({ uuid }),\n });\n }\n\n async fetchLiveness({\n networkClientId,\n }: {\n networkClientId?: NetworkClientId;\n } = {}): Promise {\n const chainId = this.#getChainId({ networkClientId });\n let liveness = false;\n try {\n const response = await this.fetch(\n getAPIRequestURL(APIType.LIVENESS, chainId),\n );\n liveness = Boolean(response.lastBlock);\n } catch (error) {\n console.log('\"fetchLiveness\" API call failed');\n }\n\n this.update({\n smartTransactionsState: {\n ...this.state.smartTransactionsState,\n ...(chainId === this.config.chainId && { liveness }),\n livenessByChainId: {\n ...this.state.smartTransactionsState.livenessByChainId,\n [chainId]: liveness,\n },\n },\n });\n\n return liveness;\n }\n\n async setStatusRefreshInterval(interval: number): Promise {\n if (interval !== this.config.interval) {\n this.configure({ interval }, false, false);\n }\n }\n\n #getCurrentSmartTransactions(): SmartTransaction[] {\n const { smartTransactions } = this.state.smartTransactionsState;\n const { chainId } = this.config;\n const currentSmartTransactions = smartTransactions?.[chainId];\n if (!currentSmartTransactions || currentSmartTransactions.length === 0) {\n return [];\n }\n return currentSmartTransactions;\n }\n\n getTransactions({\n addressFrom,\n status,\n }: {\n addressFrom: string;\n status: SmartTransactionStatuses;\n }): SmartTransaction[] {\n const currentSmartTransactions = this.#getCurrentSmartTransactions();\n return currentSmartTransactions.filter((stx) => {\n return stx.status === status && stx.txParams?.from === addressFrom;\n });\n }\n\n getSmartTransactionByMinedTxHash(\n txHash: string | undefined,\n ): SmartTransaction | undefined {\n if (!txHash) {\n return undefined;\n }\n const currentSmartTransactions = this.#getCurrentSmartTransactions();\n return currentSmartTransactions.find((smartTransaction) => {\n return (\n smartTransaction.statusMetadata?.minedHash?.toLowerCase() ===\n txHash.toLowerCase()\n );\n });\n }\n}\n"]} -\ No newline at end of file -+{"version":3,"file":"SmartTransactionsController.js","sourceRoot":"","sources":["../src/SmartTransactionsController.ts"],"names":[],"mappings":"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","sourcesContent":["// eslint-disable-next-line import/no-nodejs-modules\nimport { hexlify } from '@ethersproject/bytes';\nimport type { BaseConfig, BaseState } from '@metamask/base-controller';\nimport { query, safelyExecute } from '@metamask/controller-utils';\nimport type { Provider } from '@metamask/eth-query';\nimport EthQuery from '@metamask/eth-query';\nimport type {\n NetworkClientId,\n NetworkController,\n NetworkState,\n} from '@metamask/network-controller';\nimport { StaticIntervalPollingControllerV1 } from '@metamask/polling-controller';\nimport type { TransactionMeta } from '@metamask/transaction-controller';\nimport { TransactionStatus } from '@metamask/transaction-controller';\nimport { BigNumber } from 'bignumber.js';\n// eslint-disable-next-line import/no-nodejs-modules\nimport EventEmitter from 'events';\nimport cloneDeep from 'lodash/cloneDeep';\n\nimport {\n CHAIN_IDS,\n MetaMetricsEventCategory,\n MetaMetricsEventName,\n} from './constants';\nimport type {\n Fees,\n Hex,\n IndividualTxFees,\n SignedCanceledTransaction,\n SignedTransaction,\n SmartTransaction,\n SmartTransactionsStatus,\n UnsignedTransaction,\n GetTransactionsOptions,\n} from './types';\nimport { APIType, SmartTransactionStatuses } from './types';\nimport {\n calculateStatus,\n generateHistoryEntry,\n getAPIRequestURL,\n getStxProcessingTime,\n handleFetch,\n incrementNonceInHex,\n isSmartTransactionCancellable,\n isSmartTransactionPending,\n replayHistory,\n snapshotFromTxMeta,\n getTxHash,\n} from './utils';\n\nconst SECOND = 1000;\nexport const DEFAULT_INTERVAL = SECOND * 5;\nconst ETH_QUERY_ERROR_MSG =\n '`ethQuery` is not defined on SmartTransactionsController';\n\nexport type SmartTransactionsControllerConfig = BaseConfig & {\n interval: number;\n clientId: string;\n chainId: Hex;\n supportedChainIds: string[];\n};\n\ntype FeeEstimates = {\n approvalTxFees: IndividualTxFees | undefined;\n tradeTxFees: IndividualTxFees | undefined;\n};\n\nexport type SmartTransactionsControllerState = BaseState & {\n smartTransactionsState: {\n smartTransactions: Record;\n userOptIn: boolean | undefined;\n userOptInV2: boolean | undefined;\n liveness: boolean | undefined;\n fees: FeeEstimates;\n feesByChainId: Record;\n livenessByChainId: Record;\n };\n};\n\nexport default class SmartTransactionsController extends StaticIntervalPollingControllerV1<\n SmartTransactionsControllerConfig,\n SmartTransactionsControllerState\n> {\n /**\n * Name of this controller used during composition\n */\n override name = 'SmartTransactionsController';\n\n public timeoutHandle?: NodeJS.Timeout;\n\n private readonly getNonceLock: any;\n\n private ethQuery: EthQuery | undefined;\n\n public confirmExternalTransaction: any;\n\n public getRegularTransactions: (\n options?: GetTransactionsOptions,\n ) => TransactionMeta[];\n\n private readonly trackMetaMetricsEvent: any;\n\n public eventEmitter: EventEmitter;\n\n private readonly getNetworkClientById: NetworkController['getNetworkClientById'];\n\n /* istanbul ignore next */\n private async fetch(request: string, options?: RequestInit) {\n const { clientId } = this.config;\n const fetchOptions = {\n ...options,\n headers: {\n 'Content-Type': 'application/json',\n ...(clientId && { 'X-Client-Id': clientId }),\n },\n };\n\n return handleFetch(request, fetchOptions);\n }\n\n constructor(\n {\n onNetworkStateChange,\n getNonceLock,\n provider,\n confirmExternalTransaction,\n getTransactions,\n trackMetaMetricsEvent,\n getNetworkClientById,\n }: {\n onNetworkStateChange: (\n listener: (networkState: NetworkState) => void,\n ) => void;\n getNonceLock: any;\n provider: Provider;\n confirmExternalTransaction: any;\n getTransactions: (options?: GetTransactionsOptions) => TransactionMeta[];\n trackMetaMetricsEvent: any;\n getNetworkClientById: NetworkController['getNetworkClientById'];\n },\n config?: Partial,\n state?: Partial,\n ) {\n super(config, state);\n\n this.defaultConfig = {\n interval: DEFAULT_INTERVAL,\n chainId: CHAIN_IDS.ETHEREUM,\n clientId: 'default',\n supportedChainIds: [CHAIN_IDS.ETHEREUM, CHAIN_IDS.GOERLI],\n };\n\n this.defaultState = {\n smartTransactionsState: {\n smartTransactions: {},\n userOptIn: undefined,\n userOptInV2: undefined,\n fees: {\n approvalTxFees: undefined,\n tradeTxFees: undefined,\n },\n liveness: true,\n livenessByChainId: {\n [CHAIN_IDS.ETHEREUM]: true,\n [CHAIN_IDS.GOERLI]: true,\n },\n feesByChainId: {\n [CHAIN_IDS.ETHEREUM]: {\n approvalTxFees: undefined,\n tradeTxFees: undefined,\n },\n [CHAIN_IDS.GOERLI]: {\n approvalTxFees: undefined,\n tradeTxFees: undefined,\n },\n },\n },\n };\n\n this.initialize();\n this.setIntervalLength(this.config.interval);\n this.getNonceLock = getNonceLock;\n this.ethQuery = undefined;\n this.confirmExternalTransaction = confirmExternalTransaction;\n this.getRegularTransactions = getTransactions;\n this.trackMetaMetricsEvent = trackMetaMetricsEvent;\n this.getNetworkClientById = getNetworkClientById;\n\n this.initializeSmartTransactionsForChainId();\n\n onNetworkStateChange(({ providerConfig: newProvider }) => {\n const { chainId } = newProvider;\n this.configure({ chainId });\n this.initializeSmartTransactionsForChainId();\n this.checkPoll(this.state);\n this.ethQuery = new EthQuery(provider);\n });\n\n this.subscribe((currentState: any) => this.checkPoll(currentState));\n this.eventEmitter = new EventEmitter();\n }\n\n async _executePoll(networkClientId: string): Promise {\n // if this is going to be truly UI driven polling we shouldn't really reach here\n // with a networkClientId that is not supported, but for now I'll add a check in case\n // wondering if we should add some kind of predicate to the polling controller to check whether\n // we should poll or not\n const chainId = this.#getChainId({ networkClientId });\n if (!this.config.supportedChainIds.includes(chainId)) {\n return Promise.resolve();\n }\n return this.updateSmartTransactions({ networkClientId });\n }\n\n checkPoll(state: any) {\n const { smartTransactions } = state.smartTransactionsState;\n const currentSmartTransactions = smartTransactions[this.config.chainId];\n const pendingTransactions = currentSmartTransactions?.filter(\n isSmartTransactionPending,\n );\n if (!this.timeoutHandle && pendingTransactions?.length > 0) {\n this.poll();\n } else if (this.timeoutHandle && pendingTransactions?.length === 0) {\n this.stop();\n }\n }\n\n initializeSmartTransactionsForChainId() {\n if (this.config.supportedChainIds.includes(this.config.chainId)) {\n const { smartTransactionsState } = this.state;\n this.update({\n smartTransactionsState: {\n ...smartTransactionsState,\n smartTransactions: {\n ...smartTransactionsState.smartTransactions,\n [this.config.chainId]:\n smartTransactionsState.smartTransactions[this.config.chainId] ??\n [],\n },\n },\n });\n }\n }\n\n async poll(interval?: number): Promise {\n const { chainId, supportedChainIds } = this.config;\n interval && this.configure({ interval }, false, false);\n this.timeoutHandle && clearInterval(this.timeoutHandle);\n if (!supportedChainIds.includes(chainId)) {\n return;\n }\n await safelyExecute(async () => this.updateSmartTransactions());\n this.timeoutHandle = setInterval(() => {\n safelyExecute(async () => this.updateSmartTransactions());\n }, this.config.interval);\n }\n\n async stop() {\n this.timeoutHandle && clearInterval(this.timeoutHandle);\n this.timeoutHandle = undefined;\n }\n\n setOptInState(state: boolean | undefined): void {\n this.update({\n smartTransactionsState: {\n ...this.state.smartTransactionsState,\n userOptInV2: state,\n },\n });\n }\n\n trackStxStatusChange(\n smartTransaction: SmartTransaction,\n prevSmartTransaction?: SmartTransaction,\n ) {\n if (!prevSmartTransaction) {\n return; // Don't track the first STX, because it doesn't have all necessary params.\n }\n\n let updatedSmartTransaction = cloneDeep(smartTransaction);\n updatedSmartTransaction = {\n ...cloneDeep(prevSmartTransaction),\n ...updatedSmartTransaction,\n };\n\n if (\n !updatedSmartTransaction.swapMetaData ||\n (updatedSmartTransaction.status === prevSmartTransaction.status &&\n prevSmartTransaction.swapMetaData)\n ) {\n return; // If status hasn't changed, don't track it again.\n }\n\n const sensitiveProperties = {\n stx_status: updatedSmartTransaction.status,\n token_from_symbol: updatedSmartTransaction.sourceTokenSymbol,\n token_to_symbol: updatedSmartTransaction.destinationTokenSymbol,\n processing_time: getStxProcessingTime(updatedSmartTransaction.time),\n stx_enabled: true,\n current_stx_enabled: true,\n stx_user_opt_in: true,\n };\n\n this.trackMetaMetricsEvent({\n event: MetaMetricsEventName.StxStatusUpdated,\n category: MetaMetricsEventCategory.Transactions,\n sensitiveProperties,\n });\n }\n\n isNewSmartTransaction(smartTransactionUuid: string): boolean {\n const { chainId } = this.config;\n const { smartTransactionsState } = this.state;\n const { smartTransactions } = smartTransactionsState;\n const currentSmartTransactions = smartTransactions[chainId];\n const currentIndex = currentSmartTransactions?.findIndex(\n (stx) => stx.uuid === smartTransactionUuid,\n );\n return currentIndex === -1 || currentIndex === undefined;\n }\n\n updateSmartTransaction(\n smartTransaction: SmartTransaction,\n { networkClientId }: { networkClientId?: NetworkClientId } = {},\n ) {\n let {\n ethQuery,\n config: { chainId },\n } = this;\n if (networkClientId) {\n const networkClient = this.getNetworkClientById(networkClientId);\n chainId = networkClient.configuration.chainId;\n ethQuery = new EthQuery(networkClient.provider);\n }\n\n this.#updateSmartTransaction(smartTransaction, {\n chainId,\n ethQuery,\n });\n }\n\n async #updateSmartTransaction(\n smartTransaction: SmartTransaction,\n {\n chainId = this.config.chainId,\n ethQuery = this.ethQuery,\n }: {\n chainId: Hex;\n ethQuery: EthQuery | undefined;\n },\n ): Promise {\n const { smartTransactionsState } = this.state;\n const { smartTransactions } = smartTransactionsState;\n const currentSmartTransactions = smartTransactions[chainId] ?? [];\n const currentIndex = currentSmartTransactions?.findIndex(\n (stx) => stx.uuid === smartTransaction.uuid,\n );\n const isNewSmartTransaction = this.isNewSmartTransaction(\n smartTransaction.uuid,\n );\n if (this.ethQuery === undefined) {\n throw new Error(ETH_QUERY_ERROR_MSG);\n }\n\n this.trackStxStatusChange(\n smartTransaction,\n isNewSmartTransaction\n ? undefined\n : currentSmartTransactions[currentIndex],\n );\n\n if (isNewSmartTransaction) {\n // add smart transaction\n const cancelledNonceIndex = currentSmartTransactions?.findIndex(\n (stx: SmartTransaction) =>\n stx.transaction?.nonce === smartTransaction.transaction?.nonce &&\n stx.status?.startsWith('cancelled'),\n );\n const snapshot = cloneDeep(smartTransaction);\n const history = [snapshot];\n const historifiedSmartTransaction = { ...smartTransaction, history };\n const nextSmartTransactions =\n cancelledNonceIndex > -1\n ? currentSmartTransactions\n .slice(0, cancelledNonceIndex)\n .concat(currentSmartTransactions.slice(cancelledNonceIndex + 1))\n .concat(historifiedSmartTransaction)\n : currentSmartTransactions.concat(historifiedSmartTransaction);\n this.update({\n smartTransactionsState: {\n ...smartTransactionsState,\n smartTransactions: {\n ...smartTransactionsState.smartTransactions,\n [chainId]: nextSmartTransactions,\n },\n },\n });\n return;\n }\n\n if (\n (smartTransaction.status === SmartTransactionStatuses.SUCCESS ||\n smartTransaction.status === SmartTransactionStatuses.REVERTED) &&\n !smartTransaction.confirmed\n ) {\n // confirm smart transaction\n const currentSmartTransaction = currentSmartTransactions[currentIndex];\n const nextSmartTransaction = {\n ...currentSmartTransaction,\n ...smartTransaction,\n };\n await this.#confirmSmartTransaction(nextSmartTransaction, {\n chainId,\n ethQuery,\n });\n } else {\n this.update({\n smartTransactionsState: {\n ...smartTransactionsState,\n smartTransactions: {\n ...smartTransactionsState.smartTransactions,\n [chainId]: smartTransactionsState.smartTransactions[chainId].map(\n (item, index) => {\n return index === currentIndex\n ? { ...item, ...smartTransaction }\n : item;\n },\n ),\n },\n },\n });\n }\n\n this.eventEmitter.emit(\n `${smartTransaction.uuid}:smartTransaction`,\n smartTransaction,\n );\n }\n\n async updateSmartTransactions({\n networkClientId,\n }: {\n networkClientId?: NetworkClientId;\n } = {}): Promise {\n const { smartTransactions } = this.state.smartTransactionsState;\n const chainId = this.#getChainId({ networkClientId });\n const smartTransactionsForChainId = smartTransactions[chainId];\n\n const transactionsToUpdate: string[] = smartTransactionsForChainId\n .filter(isSmartTransactionPending)\n .map((smartTransaction) => smartTransaction.uuid);\n\n if (transactionsToUpdate.length > 0) {\n this.fetchSmartTransactionsStatus(transactionsToUpdate, {\n networkClientId,\n });\n }\n }\n\n #doesTransactionNeedConfirmation(txHash: string | undefined): boolean {\n if (!txHash) {\n return true;\n }\n const transactions = this.getRegularTransactions();\n const foundTransaction = transactions?.find((tx) => {\n return tx.transactionHash?.toLowerCase() === txHash.toLowerCase();\n });\n if (!foundTransaction) {\n return true;\n }\n // If a found transaction is either confirmed or submitted, it doesn't need confirmation from the STX controller.\n // When it's in the submitted state, the TransactionController checks its status and confirms it,\n // so no need to confirm it again here.\n return ![TransactionStatus.confirmed, TransactionStatus.submitted].includes(\n foundTransaction.status,\n );\n }\n\n async #confirmSmartTransaction(\n smartTransaction: SmartTransaction,\n {\n chainId = this.config.chainId,\n ethQuery = this.ethQuery,\n }: {\n chainId: Hex;\n ethQuery: EthQuery | undefined;\n },\n ) {\n if (ethQuery === undefined) {\n throw new Error(ETH_QUERY_ERROR_MSG);\n }\n const txHash = smartTransaction.statusMetadata?.minedHash;\n try {\n const transactionReceipt: {\n maxFeePerGas?: string;\n maxPriorityFeePerGas?: string;\n blockNumber: string;\n } | null = await query(ethQuery, 'getTransactionReceipt', [txHash]);\n const transaction: {\n maxFeePerGas?: string;\n maxPriorityFeePerGas?: string;\n } | null = await query(ethQuery, 'getTransactionByHash', [txHash]);\n\n const maxFeePerGas = transaction?.maxFeePerGas;\n const maxPriorityFeePerGas = transaction?.maxPriorityFeePerGas;\n if (transactionReceipt?.blockNumber) {\n const blockData: { baseFeePerGas?: string } | null = await query(\n ethQuery,\n 'getBlockByNumber',\n [transactionReceipt?.blockNumber, false],\n );\n const baseFeePerGas = blockData?.baseFeePerGas;\n const updatedTxParams = {\n ...smartTransaction.transaction,\n maxFeePerGas,\n maxPriorityFeePerGas,\n };\n // call confirmExternalTransaction\n const originalTxMeta = {\n ...smartTransaction,\n id: smartTransaction.uuid,\n status: TransactionStatus.confirmed,\n transactionHash: txHash,\n transaction: updatedTxParams,\n };\n // create txMeta snapshot for history\n const snapshot = snapshotFromTxMeta(originalTxMeta);\n // recover previous tx state obj\n const previousState = replayHistory(originalTxMeta.history);\n // generate history entry and add to history\n const entry = generateHistoryEntry(\n previousState,\n snapshot,\n 'txStateManager: setting status to confirmed',\n );\n const txMeta =\n entry.length > 0\n ? {\n ...originalTxMeta,\n history: originalTxMeta.history.concat(entry),\n }\n : originalTxMeta;\n\n if (this.#doesTransactionNeedConfirmation(txHash)) {\n this.confirmExternalTransaction(\n txMeta,\n transactionReceipt,\n baseFeePerGas,\n );\n }\n\n this.trackMetaMetricsEvent({\n event: MetaMetricsEventName.StxConfirmed,\n category: MetaMetricsEventCategory.Transactions,\n });\n\n this.#updateSmartTransaction(\n {\n ...smartTransaction,\n confirmed: true,\n },\n { chainId, ethQuery },\n );\n }\n } catch (error) {\n this.trackMetaMetricsEvent({\n event: MetaMetricsEventName.StxConfirmationFailed,\n category: MetaMetricsEventCategory.Transactions,\n });\n console.error('confirm error', error);\n }\n }\n\n // ! Ask backend API to accept list of uuids as params\n async fetchSmartTransactionsStatus(\n uuids: string[],\n { networkClientId }: { networkClientId?: NetworkClientId } = {},\n ): Promise> {\n const params = new URLSearchParams({\n uuids: uuids.join(','),\n });\n const chainId = this.#getChainId({ networkClientId });\n const ethQuery = this.#getEthQuery({ networkClientId });\n const url = `${getAPIRequestURL(\n APIType.BATCH_STATUS,\n chainId,\n )}?${params.toString()}`;\n\n const data = (await this.fetch(url)) as Record<\n string,\n SmartTransactionsStatus\n >;\n\n Object.entries(data).forEach(([uuid, stxStatus]) => {\n const smartTransaction = {\n statusMetadata: stxStatus,\n status: calculateStatus(stxStatus),\n cancellable: isSmartTransactionCancellable(stxStatus),\n uuid,\n };\n this.#updateSmartTransaction(smartTransaction, { chainId, ethQuery });\n });\n\n return data;\n }\n\n async addNonceToTransaction(\n transaction: UnsignedTransaction,\n ): Promise {\n const nonceLock = await this.getNonceLock(transaction.from);\n const nonce = nonceLock.nextNonce;\n nonceLock.releaseLock();\n return {\n ...transaction,\n nonce: `0x${nonce.toString(16)}`,\n };\n }\n\n clearFees(): Fees {\n const fees = {\n approvalTxFees: undefined,\n tradeTxFees: undefined,\n };\n this.update({\n smartTransactionsState: {\n ...this.state.smartTransactionsState,\n fees,\n },\n });\n return fees;\n }\n\n async getFees(\n tradeTx: UnsignedTransaction,\n approvalTx?: UnsignedTransaction,\n { networkClientId }: { networkClientId?: NetworkClientId } = {},\n ): Promise {\n const chainId = this.#getChainId({ networkClientId });\n const transactions = [];\n let unsignedTradeTransactionWithNonce;\n if (approvalTx) {\n const unsignedApprovalTransactionWithNonce =\n await this.addNonceToTransaction(approvalTx);\n transactions.push(unsignedApprovalTransactionWithNonce);\n unsignedTradeTransactionWithNonce = {\n ...tradeTx,\n // If there is an approval tx, the trade tx's nonce is increased by 1.\n nonce: incrementNonceInHex(unsignedApprovalTransactionWithNonce.nonce),\n };\n } else if (tradeTx.nonce) {\n unsignedTradeTransactionWithNonce = tradeTx;\n } else {\n unsignedTradeTransactionWithNonce = await this.addNonceToTransaction(\n tradeTx,\n );\n }\n transactions.push(unsignedTradeTransactionWithNonce);\n const data = await this.fetch(getAPIRequestURL(APIType.GET_FEES, chainId), {\n method: 'POST',\n body: JSON.stringify({\n txs: transactions,\n }),\n });\n let approvalTxFees;\n let tradeTxFees;\n if (approvalTx) {\n approvalTxFees = data?.txs[0];\n tradeTxFees = data?.txs[1];\n } else {\n tradeTxFees = data?.txs[0];\n }\n\n this.update({\n smartTransactionsState: {\n ...this.state.smartTransactionsState,\n ...(chainId === this.config.chainId && {\n fees: {\n approvalTxFees,\n tradeTxFees,\n },\n }),\n feesByChainId: {\n ...this.state.smartTransactionsState.feesByChainId,\n [chainId]: {\n approvalTxFees,\n tradeTxFees,\n },\n },\n },\n });\n\n return {\n approvalTxFees,\n tradeTxFees,\n };\n }\n\n // * After this successful call client must add a nonce representative to\n // * transaction controller external transactions list\n async submitSignedTransactions({\n transactionMeta,\n transaction,\n signedTransactions,\n signedCanceledTransactions,\n networkClientId,\n }: {\n signedTransactions: SignedTransaction[];\n signedCanceledTransactions: SignedCanceledTransaction[];\n transactionMeta?: any;\n transaction?: any;\n networkClientId?: NetworkClientId;\n }) {\n const chainId = this.#getChainId({ networkClientId });\n const ethQuery = this.#getEthQuery({ networkClientId });\n const data = await this.fetch(\n getAPIRequestURL(APIType.SUBMIT_TRANSACTIONS, chainId),\n {\n method: 'POST',\n body: JSON.stringify({\n rawTxs: signedTransactions,\n rawCancelTxs: signedCanceledTransactions,\n }),\n },\n );\n const time = Date.now();\n let preTxBalance;\n try {\n const preTxBalanceBN = await query(ethQuery, 'getBalance', [\n transaction?.from,\n ]);\n preTxBalance = new BigNumber(preTxBalanceBN).toString(16);\n } catch (error) {\n console.error('provider error', error);\n }\n\n const requiresNonce = !transaction.nonce;\n let nonce;\n let nonceLock;\n let nonceDetails = {};\n\n if (requiresNonce) {\n nonceLock = await this.getNonceLock(transaction?.from);\n nonce = hexlify(nonceLock.nextNonce);\n nonceDetails = nonceLock.nonceDetails;\n if (transaction) {\n transaction.nonce ??= nonce;\n }\n }\n const submitTransactionResponse = {\n ...data,\n txHash: getTxHash(signedTransactions[0]),\n };\n\n try {\n this.#updateSmartTransaction(\n {\n chainId,\n nonceDetails,\n preTxBalance,\n status: SmartTransactionStatuses.PENDING,\n time,\n transaction,\n uuid: submitTransactionResponse.uuid,\n transactionHash: submitTransactionResponse.txHash,\n cancellable: true,\n type: transactionMeta?.type || 'swap',\n },\n { chainId, ethQuery },\n );\n } finally {\n nonceLock?.releaseLock();\n }\n\n return submitTransactionResponse;\n }\n\n #getChainId({\n networkClientId,\n }: { networkClientId?: NetworkClientId } = {}): Hex {\n return networkClientId\n ? this.getNetworkClientById(networkClientId).configuration.chainId\n : this.config.chainId;\n }\n\n #getEthQuery({\n networkClientId,\n }: {\n networkClientId?: NetworkClientId;\n } = {}): EthQuery {\n if (networkClientId) {\n return new EthQuery(this.getNetworkClientById(networkClientId).provider);\n }\n\n if (this.ethQuery === undefined) {\n throw new Error(ETH_QUERY_ERROR_MSG);\n }\n\n return this.ethQuery;\n }\n\n // TODO: This should return if the cancellation was on chain or not (for nonce management)\n // After this successful call client must update nonce representative\n // in transaction controller external transactions list\n async cancelSmartTransaction(\n uuid: string,\n {\n networkClientId,\n }: {\n networkClientId?: NetworkClientId;\n } = {},\n ): Promise {\n const chainId = this.#getChainId({ networkClientId });\n await this.fetch(getAPIRequestURL(APIType.CANCEL, chainId), {\n method: 'POST',\n body: JSON.stringify({ uuid }),\n });\n }\n\n async fetchLiveness({\n networkClientId,\n }: {\n networkClientId?: NetworkClientId;\n } = {}): Promise {\n const chainId = this.#getChainId({ networkClientId });\n let liveness = false;\n try {\n const response = await this.fetch(\n getAPIRequestURL(APIType.LIVENESS, chainId),\n );\n liveness = Boolean(response.lastBlock);\n } catch (error) {\n console.log('\"fetchLiveness\" API call failed');\n }\n\n this.update({\n smartTransactionsState: {\n ...this.state.smartTransactionsState,\n ...(chainId === this.config.chainId && { liveness }),\n livenessByChainId: {\n ...this.state.smartTransactionsState.livenessByChainId,\n [chainId]: liveness,\n },\n },\n });\n\n return liveness;\n }\n\n async setStatusRefreshInterval(interval: number): Promise {\n if (interval !== this.config.interval) {\n this.configure({ interval }, false, false);\n }\n }\n\n #getCurrentSmartTransactions(): SmartTransaction[] {\n const { smartTransactions } = this.state.smartTransactionsState;\n const { chainId } = this.config;\n const currentSmartTransactions = smartTransactions?.[chainId];\n if (!currentSmartTransactions || currentSmartTransactions.length === 0) {\n return [];\n }\n return currentSmartTransactions;\n }\n\n getTransactions({\n addressFrom,\n status,\n }: {\n addressFrom: string;\n status: SmartTransactionStatuses;\n }): SmartTransaction[] {\n const currentSmartTransactions = this.#getCurrentSmartTransactions();\n return currentSmartTransactions.filter((stx) => {\n return stx.status === status && stx.transaction?.from === addressFrom;\n });\n }\n\n getSmartTransactionByMinedTxHash(\n txHash: string | undefined,\n ): SmartTransaction | undefined {\n if (!txHash) {\n return undefined;\n }\n const currentSmartTransactions = this.#getCurrentSmartTransactions();\n return currentSmartTransactions.find((smartTransaction) => {\n return (\n smartTransaction.statusMetadata?.minedHash?.toLowerCase() ===\n txHash.toLowerCase()\n );\n });\n }\n}\n"]} -\ No newline at end of file -diff --git a/node_modules/@metamask/smart-transactions-controller/dist/types.d.ts b/node_modules/@metamask/smart-transactions-controller/dist/types.d.ts -index 977c983..c6493f7 100644 ---- a/node_modules/@metamask/smart-transactions-controller/dist/types.d.ts -+++ b/node_modules/@metamask/smart-transactions-controller/dist/types.d.ts -@@ -61,7 +61,7 @@ export declare type SmartTransactionsStatus = { - }; - export declare type SmartTransaction = { - uuid: string; -- txHash?: string; -+ transactionHash?: string; - chainId?: string; - destinationTokenAddress?: string; - destinationTokenDecimals?: string; -@@ -77,7 +77,7 @@ export declare type SmartTransaction = { - swapTokenValue?: string; - time?: number; - creationTime?: number; -- txParams?: any; -+ transaction?: any; - type?: string; - confirmed?: boolean; - cancellable?: boolean; -diff --git a/node_modules/@metamask/smart-transactions-controller/dist/types.js.map b/node_modules/@metamask/smart-transactions-controller/dist/types.js.map -index 5be3df1..9965800 100644 ---- a/node_modules/@metamask/smart-transactions-controller/dist/types.js.map -+++ b/node_modules/@metamask/smart-transactions-controller/dist/types.js.map -@@ -1 +1 @@ --{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";;;AAEA,UAAU;AACV,IAAY,OAOX;AAPD,WAAY,OAAO;IACjB,6CAAU,CAAA;IACV,qDAAc,CAAA;IACd,mEAAqB,CAAA;IACrB,yCAAQ,CAAA;IACR,qDAAc,CAAA;IACd,6CAAU,CAAA;AACZ,CAAC,EAPW,OAAO,GAAP,eAAO,KAAP,eAAO,QAOlB;AAED,wBAAwB;AACxB,IAAY,uBAMX;AAND,WAAY,uBAAuB;IACjC,kDAAuB,CAAA;IACvB,8CAAmB,CAAA;IACnB,kDAAuB,CAAA;IACvB,gDAAqB,CAAA;IACrB,8CAAmB,CAAA;AACrB,CAAC,EANW,uBAAuB,GAAvB,+BAAuB,KAAvB,+BAAuB,QAMlC;AAED,IAAY,kCAQX;AARD,WAAY,kCAAkC;IAC5C,mEAA6B,CAAA;IAC7B,6DAAuB,CAAA;IACvB,yEAAmC,CAAA;IACnC,qEAA+B,CAAA;IAC/B,uEAAiC,CAAA;IACjC,qEAA+B,CAAA;IAC/B,qFAA+C,CAAA;AACjD,CAAC,EARW,kCAAkC,GAAlC,0CAAkC,KAAlC,0CAAkC,QAQ7C;AAED,IAAY,wBAaX;AAbD,WAAY,wBAAwB;IAClC,+CAAmB,CAAA;IACnB,+CAAmB,CAAA;IACnB,iDAAqB,CAAA;IACrB,+CAAmB,CAAA;IACnB,mDAAuB,CAAA;IACvB,6EAAiD,CAAA;IACjD,uEAA2C,CAAA;IAC3C,mFAAuD,CAAA;IACvD,+EAAmD,CAAA;IACnD,iFAAqD,CAAA;IACrD,+FAAmE,CAAA;IACnE,iDAAqB,CAAA;AACvB,CAAC,EAbW,wBAAwB,GAAxB,gCAAwB,KAAxB,gCAAwB,QAanC;AAEY,QAAA,6BAA6B,GAAG;IAC3C,CAAC,kCAAkC,CAAC,YAAY,CAAC,EAC/C,wBAAwB,CAAC,sBAAsB;IACjD,CAAC,kCAAkC,CAAC,SAAS,CAAC,EAC5C,wBAAwB,CAAC,mBAAmB;IAC9C,CAAC,kCAAkC,CAAC,eAAe,CAAC,EAClD,wBAAwB,CAAC,yBAAyB;IACpD,CAAC,kCAAkC,CAAC,aAAa,CAAC,EAChD,wBAAwB,CAAC,uBAAuB;IAClD,CAAC,kCAAkC,CAAC,cAAc,CAAC,EACjD,wBAAwB,CAAC,wBAAwB;IACnD,CAAC,kCAAkC,CAAC,qBAAqB,CAAC,EACxD,wBAAwB,CAAC,+BAA+B;CAC3D,CAAC","sourcesContent":["import type { TransactionMeta } from '@metamask/transaction-controller';\n\n/** API */\nexport enum APIType {\n 'GET_FEES',\n 'ESTIMATE_GAS',\n 'SUBMIT_TRANSACTIONS',\n 'CANCEL',\n 'BATCH_STATUS',\n 'LIVENESS',\n}\n\n/** SmartTransactions */\nexport enum SmartTransactionMinedTx {\n NOT_MINED = 'not_mined',\n SUCCESS = 'success',\n CANCELLED = 'cancelled',\n REVERTED = 'reverted',\n UNKNOWN = 'unknown',\n}\n\nexport enum SmartTransactionCancellationReason {\n WOULD_REVERT = 'would_revert',\n TOO_CHEAP = 'too_cheap',\n DEADLINE_MISSED = 'deadline_missed',\n INVALID_NONCE = 'invalid_nonce',\n USER_CANCELLED = 'user_cancelled',\n NOT_CANCELLED = 'not_cancelled',\n PREVIOUS_TX_CANCELLED = 'previous_tx_cancelled',\n}\n\nexport enum SmartTransactionStatuses {\n PENDING = 'pending',\n SUCCESS = 'success',\n REVERTED = 'reverted',\n UNKNOWN = 'unknown',\n CANCELLED = 'cancelled',\n CANCELLED_WOULD_REVERT = 'cancelled_would_revert',\n CANCELLED_TOO_CHEAP = 'cancelled_too_cheap',\n CANCELLED_DEADLINE_MISSED = 'cancelled_deadline_missed',\n CANCELLED_INVALID_NONCE = 'cancelled_invalid_nonce',\n CANCELLED_USER_CANCELLED = 'cancelled_user_cancelled',\n CANCELLED_PREVIOUS_TX_CANCELLED = 'cancelled_previous_tx_cancelled',\n RESOLVED = 'resolved',\n}\n\nexport const cancellationReasonToStatusMap = {\n [SmartTransactionCancellationReason.WOULD_REVERT]:\n SmartTransactionStatuses.CANCELLED_WOULD_REVERT,\n [SmartTransactionCancellationReason.TOO_CHEAP]:\n SmartTransactionStatuses.CANCELLED_TOO_CHEAP,\n [SmartTransactionCancellationReason.DEADLINE_MISSED]:\n SmartTransactionStatuses.CANCELLED_DEADLINE_MISSED,\n [SmartTransactionCancellationReason.INVALID_NONCE]:\n SmartTransactionStatuses.CANCELLED_INVALID_NONCE,\n [SmartTransactionCancellationReason.USER_CANCELLED]:\n SmartTransactionStatuses.CANCELLED_USER_CANCELLED,\n [SmartTransactionCancellationReason.PREVIOUS_TX_CANCELLED]:\n SmartTransactionStatuses.CANCELLED_PREVIOUS_TX_CANCELLED,\n};\n\nexport type SmartTransactionsStatus = {\n error?: string;\n cancellationFeeWei: number;\n cancellationReason?: SmartTransactionCancellationReason;\n deadlineRatio: number;\n minedHash: string;\n minedTx: SmartTransactionMinedTx;\n isSettled: boolean;\n duplicated?: boolean;\n timedOut?: boolean;\n proxied?: boolean;\n};\n\nexport type SmartTransaction = {\n uuid: string;\n txHash?: string;\n chainId?: string;\n destinationTokenAddress?: string;\n destinationTokenDecimals?: string;\n destinationTokenSymbol?: string;\n history?: any;\n nonceDetails?: any;\n origin?: string;\n preTxBalance?: string;\n status?: string;\n statusMetadata?: SmartTransactionsStatus;\n sourceTokenSymbol?: string;\n swapMetaData?: any;\n swapTokenValue?: string;\n time?: number; // @deprecated We should use creationTime instead.\n creationTime?: number;\n txParams?: any;\n type?: string;\n confirmed?: boolean;\n cancellable?: boolean;\n};\n\nexport type Fee = {\n maxFeePerGas: number;\n maxPriorityFeePerGas: number;\n};\n\nexport type IndividualTxFees = {\n fees: Fee[];\n cancelFees: Fee[];\n feeEstimate: number;\n gasLimit: number;\n gasUsed: number;\n};\n\nexport type Fees = {\n approvalTxFees: IndividualTxFees | undefined;\n tradeTxFees: IndividualTxFees | undefined;\n};\n\n// TODO\nexport type UnsignedTransaction = any;\n\n// TODO\nexport type SignedTransaction = any;\n\n// TODO\nexport type SignedCanceledTransaction = any;\n\nexport type Hex = `0x${string}`;\n\nexport type GetTransactionsOptions = {\n searchCriteria?: any;\n initialList?: TransactionMeta[];\n filterToCurrentNetwork?: boolean;\n limit?: number;\n};\n"]} -\ No newline at end of file -+{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";;;AAEA,UAAU;AACV,IAAY,OAOX;AAPD,WAAY,OAAO;IACjB,6CAAU,CAAA;IACV,qDAAc,CAAA;IACd,mEAAqB,CAAA;IACrB,yCAAQ,CAAA;IACR,qDAAc,CAAA;IACd,6CAAU,CAAA;AACZ,CAAC,EAPW,OAAO,GAAP,eAAO,KAAP,eAAO,QAOlB;AAED,wBAAwB;AACxB,IAAY,uBAMX;AAND,WAAY,uBAAuB;IACjC,kDAAuB,CAAA;IACvB,8CAAmB,CAAA;IACnB,kDAAuB,CAAA;IACvB,gDAAqB,CAAA;IACrB,8CAAmB,CAAA;AACrB,CAAC,EANW,uBAAuB,GAAvB,+BAAuB,KAAvB,+BAAuB,QAMlC;AAED,IAAY,kCAQX;AARD,WAAY,kCAAkC;IAC5C,mEAA6B,CAAA;IAC7B,6DAAuB,CAAA;IACvB,yEAAmC,CAAA;IACnC,qEAA+B,CAAA;IAC/B,uEAAiC,CAAA;IACjC,qEAA+B,CAAA;IAC/B,qFAA+C,CAAA;AACjD,CAAC,EARW,kCAAkC,GAAlC,0CAAkC,KAAlC,0CAAkC,QAQ7C;AAED,IAAY,wBAaX;AAbD,WAAY,wBAAwB;IAClC,+CAAmB,CAAA;IACnB,+CAAmB,CAAA;IACnB,iDAAqB,CAAA;IACrB,+CAAmB,CAAA;IACnB,mDAAuB,CAAA;IACvB,6EAAiD,CAAA;IACjD,uEAA2C,CAAA;IAC3C,mFAAuD,CAAA;IACvD,+EAAmD,CAAA;IACnD,iFAAqD,CAAA;IACrD,+FAAmE,CAAA;IACnE,iDAAqB,CAAA;AACvB,CAAC,EAbW,wBAAwB,GAAxB,gCAAwB,KAAxB,gCAAwB,QAanC;AAEY,QAAA,6BAA6B,GAAG;IAC3C,CAAC,kCAAkC,CAAC,YAAY,CAAC,EAC/C,wBAAwB,CAAC,sBAAsB;IACjD,CAAC,kCAAkC,CAAC,SAAS,CAAC,EAC5C,wBAAwB,CAAC,mBAAmB;IAC9C,CAAC,kCAAkC,CAAC,eAAe,CAAC,EAClD,wBAAwB,CAAC,yBAAyB;IACpD,CAAC,kCAAkC,CAAC,aAAa,CAAC,EAChD,wBAAwB,CAAC,uBAAuB;IAClD,CAAC,kCAAkC,CAAC,cAAc,CAAC,EACjD,wBAAwB,CAAC,wBAAwB;IACnD,CAAC,kCAAkC,CAAC,qBAAqB,CAAC,EACxD,wBAAwB,CAAC,+BAA+B;CAC3D,CAAC","sourcesContent":["import type { TransactionMeta } from '@metamask/transaction-controller';\n\n/** API */\nexport enum APIType {\n 'GET_FEES',\n 'ESTIMATE_GAS',\n 'SUBMIT_TRANSACTIONS',\n 'CANCEL',\n 'BATCH_STATUS',\n 'LIVENESS',\n}\n\n/** SmartTransactions */\nexport enum SmartTransactionMinedTx {\n NOT_MINED = 'not_mined',\n SUCCESS = 'success',\n CANCELLED = 'cancelled',\n REVERTED = 'reverted',\n UNKNOWN = 'unknown',\n}\n\nexport enum SmartTransactionCancellationReason {\n WOULD_REVERT = 'would_revert',\n TOO_CHEAP = 'too_cheap',\n DEADLINE_MISSED = 'deadline_missed',\n INVALID_NONCE = 'invalid_nonce',\n USER_CANCELLED = 'user_cancelled',\n NOT_CANCELLED = 'not_cancelled',\n PREVIOUS_TX_CANCELLED = 'previous_tx_cancelled',\n}\n\nexport enum SmartTransactionStatuses {\n PENDING = 'pending',\n SUCCESS = 'success',\n REVERTED = 'reverted',\n UNKNOWN = 'unknown',\n CANCELLED = 'cancelled',\n CANCELLED_WOULD_REVERT = 'cancelled_would_revert',\n CANCELLED_TOO_CHEAP = 'cancelled_too_cheap',\n CANCELLED_DEADLINE_MISSED = 'cancelled_deadline_missed',\n CANCELLED_INVALID_NONCE = 'cancelled_invalid_nonce',\n CANCELLED_USER_CANCELLED = 'cancelled_user_cancelled',\n CANCELLED_PREVIOUS_TX_CANCELLED = 'cancelled_previous_tx_cancelled',\n RESOLVED = 'resolved',\n}\n\nexport const cancellationReasonToStatusMap = {\n [SmartTransactionCancellationReason.WOULD_REVERT]:\n SmartTransactionStatuses.CANCELLED_WOULD_REVERT,\n [SmartTransactionCancellationReason.TOO_CHEAP]:\n SmartTransactionStatuses.CANCELLED_TOO_CHEAP,\n [SmartTransactionCancellationReason.DEADLINE_MISSED]:\n SmartTransactionStatuses.CANCELLED_DEADLINE_MISSED,\n [SmartTransactionCancellationReason.INVALID_NONCE]:\n SmartTransactionStatuses.CANCELLED_INVALID_NONCE,\n [SmartTransactionCancellationReason.USER_CANCELLED]:\n SmartTransactionStatuses.CANCELLED_USER_CANCELLED,\n [SmartTransactionCancellationReason.PREVIOUS_TX_CANCELLED]:\n SmartTransactionStatuses.CANCELLED_PREVIOUS_TX_CANCELLED,\n};\n\nexport type SmartTransactionsStatus = {\n error?: string;\n cancellationFeeWei: number;\n cancellationReason?: SmartTransactionCancellationReason;\n deadlineRatio: number;\n minedHash: string;\n minedTx: SmartTransactionMinedTx;\n isSettled: boolean;\n duplicated?: boolean;\n timedOut?: boolean;\n proxied?: boolean;\n};\n\nexport type SmartTransaction = {\n uuid: string;\n transactionHash?: string;\n chainId?: string;\n destinationTokenAddress?: string;\n destinationTokenDecimals?: string;\n destinationTokenSymbol?: string;\n history?: any;\n nonceDetails?: any;\n origin?: string;\n preTxBalance?: string;\n status?: string;\n statusMetadata?: SmartTransactionsStatus;\n sourceTokenSymbol?: string;\n swapMetaData?: any;\n swapTokenValue?: string;\n time?: number; // @deprecated We should use creationTime instead.\n creationTime?: number;\n transaction?: any;\n type?: string;\n confirmed?: boolean;\n cancellable?: boolean;\n};\n\nexport type Fee = {\n maxFeePerGas: number;\n maxPriorityFeePerGas: number;\n};\n\nexport type IndividualTxFees = {\n fees: Fee[];\n cancelFees: Fee[];\n feeEstimate: number;\n gasLimit: number;\n gasUsed: number;\n};\n\nexport type Fees = {\n approvalTxFees: IndividualTxFees | undefined;\n tradeTxFees: IndividualTxFees | undefined;\n};\n\n// TODO\nexport type UnsignedTransaction = any;\n\n// TODO\nexport type SignedTransaction = any;\n\n// TODO\nexport type SignedCanceledTransaction = any;\n\nexport type Hex = `0x${string}`;\n\nexport type GetTransactionsOptions = {\n searchCriteria?: any;\n initialList?: TransactionMeta[];\n filterToCurrentNetwork?: boolean;\n limit?: number;\n};\n"]} -\ No newline at end of file diff --git a/patches/@metamask+swaps-controller+6.9.3.patch b/patches/@metamask+swaps-controller+6.9.3.patch index 3001514cbc0f..11b97016349a 100644 --- a/patches/@metamask+swaps-controller+6.9.3.patch +++ b/patches/@metamask+swaps-controller+6.9.3.patch @@ -114,33 +114,46 @@ index 9d8a521..601fd18 100644 [exports.ETH_CHAIN_ID]: 'ethereum', [exports.BSC_CHAIN_ID]: 'bsc', diff --git a/node_modules/@metamask/swaps-controller/dist/swapsInterfaces.d.ts b/node_modules/@metamask/swaps-controller/dist/swapsInterfaces.d.ts -index 43f7356..74a382b 100644 +index 43f7356..5416244 100644 --- a/node_modules/@metamask/swaps-controller/dist/swapsInterfaces.d.ts +++ b/node_modules/@metamask/swaps-controller/dist/swapsInterfaces.d.ts -@@ -19,14 +19,38 @@ export interface SwapsToken extends SwapsAsset { +@@ -19,14 +19,51 @@ export interface SwapsToken extends SwapsAsset { occurrences?: number; iconUrl?: string; } +-export interface NetworkFeatureFlags { + - export interface NetworkFeatureFlags { ++export type NetworkFeatureFlags = { mobile_active: boolean; extension_active: boolean; -- fallback_to_v1?: boolean; -+ fallback_to_v1: boolean; + fallback_to_v1?: boolean; +-} ++ }; + export interface NetworksFeatureStatus { + [network: string]: NetworkFeatureFlags; + } ++ ++export type NetworkFeatureFlagsAll = { ++ mobile_active: boolean; ++ extension_active: boolean; ++ fallback_to_v1?: boolean; + fallbackToV1: boolean; + mobileActive: boolean; + extensionActive: boolean; + mobileActiveIOS: boolean; + mobileActiveAndroid: boolean; ++ + smartTransactions: { -+ expectedDeadline: number; -+ maxDeadline: number; -+ returnTxHashAsap: boolean; ++ expectedDeadline: number; ++ maxDeadline: number; ++ returnTxHashAsap: boolean; + }; - } - export interface NetworksFeatureStatus { - [network: string]: NetworkFeatureFlags; - } ++}; ++ ++export type NetworksFeatureStatusAll = { ++ [network: string]: NetworkFeatureFlagsAll; ++ }; ++ +export interface GlobalFeatureFlags { + smart_transactions: { + mobile_active: boolean; @@ -153,7 +166,8 @@ index 43f7356..74a382b 100644 + mobileActiveAndroid: boolean; + }; +} -+export declare type FeatureFlags = NetworksFeatureStatus & GlobalFeatureFlags; ++export type FeatureFlags = NetworksFeatureStatusAll & GlobalFeatureFlags; ++ /** * Metadata needed to fetch quotes * diff --git a/yarn.lock b/yarn.lock index 6e04968efdcb..8bc75fe94f07 100644 --- a/yarn.lock +++ b/yarn.lock @@ -29210,4 +29210,4 @@ zod@^3.22.4: zxcvbn@4.4.2: version "4.4.2" resolved "https://registry.yarnpkg.com/zxcvbn/-/zxcvbn-4.4.2.tgz#28ec17cf09743edcab056ddd8b1b06262cc73c30" - integrity sha1-KOwXzwl0PtyrBW3dixsGJizHPDA= \ No newline at end of file + integrity sha1-KOwXzwl0PtyrBW3dixsGJizHPDA=