diff --git a/app/actions/transaction/index.js b/app/actions/transaction/index.js
index 030e78570cd..f2ecbae38d1 100644
--- a/app/actions/transaction/index.js
+++ b/app/actions/transaction/index.js
@@ -108,6 +108,18 @@ export function setTransactionObject(transaction) {
};
}
+/**
+ * Sets the current transaction ID only.
+ *
+ * @param {object} transactionId - Id of the current transaction.
+ */
+export function setTransactionId(transactionId) {
+ return {
+ type: 'SET_TRANSACTION_ID',
+ transactionId,
+ };
+}
+
/**
* Enable selectable tokens (ERC20 and Ether) to send in a transaction
*
diff --git a/app/components/UI/BrowserBottomBar/__snapshots__/index.test.tsx.snap b/app/components/UI/BrowserBottomBar/__snapshots__/index.test.tsx.snap
index 9f9d573a63e..8e98692769f 100644
--- a/app/components/UI/BrowserBottomBar/__snapshots__/index.test.tsx.snap
+++ b/app/components/UI/BrowserBottomBar/__snapshots__/index.test.tsx.snap
@@ -1,204 +1,28 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`BrowserBottomBar should render correctly 1`] = `
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ showTabs={[Function]}
+ showUrlModal={[Function]}
+ toggleOptions={[Function]}
+/>
`;
diff --git a/app/components/UI/BrowserBottomBar/index.js b/app/components/UI/BrowserBottomBar/index.js
index 0f0a5ab0bbe..b005dfe0c15 100644
--- a/app/components/UI/BrowserBottomBar/index.js
+++ b/app/components/UI/BrowserBottomBar/index.js
@@ -8,7 +8,6 @@ import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
import SimpleLineIcons from 'react-native-vector-icons/SimpleLineIcons';
import FeatherIcons from 'react-native-vector-icons/Feather';
import { MetaMetricsEvents } from '../../../core/Analytics';
-import AnalyticsV2 from '../../../util/analyticsV2';
import Device from '../../../util/device';
import { ThemeContext, mockTheme } from '../../../util/theme';
@@ -21,6 +20,7 @@ import {
OPTIONS_BUTTON,
SEARCH_BUTTON,
} from '../../../../wdio/screen-objects/testIDs/BrowserScreen/BrowserScreen.testIds';
+import { withMetricsAwareness } from '../../../components/hooks/useMetrics';
// NOTE: not needed anymore. The use of BottomTabBar already accomodates the home indicator height
// TODO: test on an android device
@@ -67,7 +67,7 @@ const createStyles = (colors) =>
* Browser bottom bar that contains icons for navigation
* tab management, url change and other options
*/
-export default class BrowserBottomBar extends PureComponent {
+class BrowserBottomBar extends PureComponent {
static propTypes = {
/**
* Boolean that determines if you can navigate back
@@ -101,17 +101,21 @@ export default class BrowserBottomBar extends PureComponent {
* Function that toggles the options menu
*/
toggleOptions: PropTypes.func,
+ /**
+ * Metrics injected by withMetricsAwareness HOC
+ */
+ metrics: PropTypes.object,
};
trackSearchEvent = () => {
- AnalyticsV2.trackEvent(MetaMetricsEvents.BROWSER_SEARCH_USED, {
+ this.props.metrics.trackEvent(MetaMetricsEvents.BROWSER_SEARCH_USED, {
option_chosen: 'Browser Bottom Bar Menu',
number_of_tabs: undefined,
});
};
trackNavigationEvent = (navigationOption) => {
- AnalyticsV2.trackEvent(MetaMetricsEvents.BROWSER_NAVIGATION, {
+ this.props.metrics.trackEvent(MetaMetricsEvents.BROWSER_NAVIGATION, {
option_chosen: navigationOption,
os: Platform.OS,
});
@@ -213,3 +217,4 @@ export default class BrowserBottomBar extends PureComponent {
}
BrowserBottomBar.contextType = ThemeContext;
+export default withMetricsAwareness(BrowserBottomBar);
diff --git a/app/components/UI/CollectibleContracts/index.js b/app/components/UI/CollectibleContracts/index.js
index 5939091cc02..4dbd431d91c 100644
--- a/app/components/UI/CollectibleContracts/index.js
+++ b/app/components/UI/CollectibleContracts/index.js
@@ -4,7 +4,6 @@ import {
TouchableOpacity,
StyleSheet,
View,
- InteractionManager,
Image,
Platform,
FlatList,
@@ -15,7 +14,6 @@ import { fontStyles } from '../../../styles/common';
import { strings } from '../../../../locales/i18n';
import Engine from '../../../core/Engine';
import CollectibleContractElement from '../CollectibleContractElement';
-import Analytics from '../../../core/Analytics/Analytics';
import { MetaMetricsEvents } from '../../../core/Analytics';
import {
collectibleContractsSelector,
@@ -46,6 +44,7 @@ import {
NFT_TAB_CONTAINER_ID,
} from '../../../../wdio/screen-objects/testIDs/Screens/WalletView.testIds';
import Logger from '../../../util/Logger';
+import { useMetrics } from '../../../components/hooks/useMetrics';
const createStyles = (colors) =>
StyleSheet.create({
@@ -110,6 +109,7 @@ const CollectibleContracts = ({
(singleCollectible) => singleCollectible.isCurrentlyOwned === true,
);
const { colors } = useTheme();
+ const { trackEvent } = useMetrics();
const styles = createStyles(colors);
const [isAddNFTEnabled, setIsAddNFTEnabled] = useState(true);
const [refreshing, setRefreshing] = useState(false);
@@ -274,11 +274,9 @@ const CollectibleContracts = ({
const goToAddCollectible = useCallback(() => {
setIsAddNFTEnabled(false);
navigation.push('AddAsset', { assetType: 'collectible' });
- InteractionManager.runAfterInteractions(() => {
- Analytics.trackEvent(MetaMetricsEvents.WALLET_ADD_COLLECTIBLES);
- setIsAddNFTEnabled(true);
- });
- }, [navigation]);
+ trackEvent(MetaMetricsEvents.WALLET_ADD_COLLECTIBLES);
+ setIsAddNFTEnabled(true);
+ }, [navigation, trackEvent]);
const renderFooter = useCallback(
() => (
diff --git a/app/components/UI/ComponentErrorBoundary/index.js b/app/components/UI/ComponentErrorBoundary/index.js
index caac84bec33..d532728cdc3 100644
--- a/app/components/UI/ComponentErrorBoundary/index.js
+++ b/app/components/UI/ComponentErrorBoundary/index.js
@@ -1,7 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import Logger from '../../../util/Logger';
-import { trackErrorAsAnalytics } from '../../../util/analyticsV2';
+import trackErrorAsAnalytics from '../../../util/metrics/TrackError/trackErrorAsAnalytics';
class ComponentErrorBoundary extends React.Component {
state = { error: null };
diff --git a/app/components/UI/DeleteWalletModal/index.tsx b/app/components/UI/DeleteWalletModal/index.tsx
index 8f0672525aa..c67ca098349 100644
--- a/app/components/UI/DeleteWalletModal/index.tsx
+++ b/app/components/UI/DeleteWalletModal/index.tsx
@@ -23,8 +23,8 @@ import Device from '../../../util/device';
import Routes from '../../../constants/navigation/Routes';
import { DeleteWalletModalSelectorsIDs } from '../../../../e2e/selectors/Modals/DeleteWalletModal.selectors';
import generateTestId from '../../../../wdio/utils/generateTestId';
-import { trackEventV2 as trackEvent } from '../../../util/analyticsV2';
import { MetaMetricsEvents } from '../../../core/Analytics';
+import { useMetrics } from '../../../components/hooks/useMetrics';
const DELETE_KEYWORD = 'delete';
@@ -35,6 +35,7 @@ if (Device.isAndroid() && UIManager.setLayoutAnimationEnabledExperimental) {
const DeleteWalletModal = () => {
const navigation = useNavigation();
const { colors, themeAppearance } = useTheme();
+ const { trackEvent } = useMetrics();
const styles = createStyles(colors);
const modalRef = useRef(null);
diff --git a/app/components/UI/DrawerView/index.js b/app/components/UI/DrawerView/index.js
index a63f0ae3cbe..fb48b8e3c45 100644
--- a/app/components/UI/DrawerView/index.js
+++ b/app/components/UI/DrawerView/index.js
@@ -41,7 +41,6 @@ import Engine from '../../../core/Engine';
import Logger from '../../../util/Logger';
import Device from '../../../util/device';
import ReceiveRequest from '../ReceiveRequest';
-import Analytics from '../../../core/Analytics/Analytics';
import AppConstants from '../../../core/AppConstants';
import { MetaMetricsEvents } from '../../../core/Analytics';
import URL from 'url-parse';
@@ -53,7 +52,6 @@ import DeeplinkManager from '../../../core/DeeplinkManager/SharedDeeplinkManager
import SettingsNotification from '../SettingsNotification';
import { RPC } from '../../../constants/network';
import { findRouteNameFromNavigatorState } from '../../../util/general';
-import AnalyticsV2 from '../../../util/analyticsV2';
import {
isDefaultAccountName,
doENSReverseLookup,
@@ -90,6 +88,7 @@ import {
import { createAccountSelectorNavDetails } from '../../Views/AccountSelector';
import NetworkInfo from '../NetworkInfo';
+import { withMetricsAwareness } from '../../../components/hooks/useMetrics';
const createStyles = (colors) =>
StyleSheet.create({
@@ -452,6 +451,10 @@ class DrawerView extends PureComponent {
* Redux action to close info network modal
*/
toggleInfoNetworkModal: PropTypes.func,
+ /**
+ * Metrics injected by withMetricsAwareness HOC
+ */
+ metrics: PropTypes.object,
};
state = {
@@ -546,15 +549,14 @@ class DrawerView extends PureComponent {
) {
// eslint-disable-next-line react/no-did-update-set-state
this.setState({ showProtectWalletModal: true });
- InteractionManager.runAfterInteractions(() => {
- AnalyticsV2.trackEvent(
- MetaMetricsEvents.WALLET_SECURITY_PROTECT_VIEWED,
- {
- wallet_protection_required: false,
- source: 'Backup Alert',
- },
- );
- });
+
+ this.props.metrics.trackEvent(
+ MetaMetricsEvents.WALLET_SECURITY_PROTECT_VIEWED,
+ {
+ wallet_protection_required: false,
+ source: 'Backup Alert',
+ },
+ );
} else {
// eslint-disable-next-line react/no-did-update-set-state
this.setState({ showProtectWalletModal: false });
@@ -624,15 +626,13 @@ class DrawerView extends PureComponent {
};
trackEvent = (event) => {
- InteractionManager.runAfterInteractions(() => {
- Analytics.trackEvent(event);
- });
+ this.props.metrics.trackEvent(event);
};
// NOTE: do we need this event?
trackOpenBrowserEvent = () => {
const { providerConfig } = this.props;
- AnalyticsV2.trackEvent(MetaMetricsEvents.BROWSER_OPENED, {
+ this.props.metrics.trackEvent(MetaMetricsEvents.BROWSER_OPENED, {
source: 'In-app Navigation',
chain_id: getDecimalChainId(providerConfig.chainId),
});
@@ -928,7 +928,7 @@ class DrawerView extends PureComponent {
this.props.passwordSet ? { screen: 'AccountBackupStep1' } : undefined,
);
InteractionManager.runAfterInteractions(() => {
- AnalyticsV2.trackEvent(
+ this.props.metrics.trackEvent(
MetaMetricsEvents.WALLET_SECURITY_PROTECT_ENGAGED,
{
wallet_protection_required: true,
@@ -1273,4 +1273,7 @@ const mapDispatchToProps = (dispatch) => ({
DrawerView.contextType = ThemeContext;
-export default connect(mapStateToProps, mapDispatchToProps)(DrawerView);
+export default connect(
+ mapStateToProps,
+ mapDispatchToProps,
+)(withMetricsAwareness(DrawerView));
diff --git a/app/components/UI/EditGasFee1559/index.js b/app/components/UI/EditGasFee1559/index.js
index 430f213e352..048ac447ef8 100644
--- a/app/components/UI/EditGasFee1559/index.js
+++ b/app/components/UI/EditGasFee1559/index.js
@@ -23,7 +23,6 @@ import PropTypes from 'prop-types';
import BigNumber from 'bignumber.js';
import FadeAnimationView from '../FadeAnimationView';
import { MetaMetricsEvents } from '../../../core/Analytics';
-import AnalyticsV2 from '../../../util/analyticsV2';
import TimeEstimateInfoModal from '../TimeEstimateInfoModal';
import useModalHandler from '../../Base/hooks/useModalHandler';
@@ -35,6 +34,7 @@ import {
GAS_LIMIT_MIN,
GAS_PRICE_MIN as GAS_MIN,
} from '../../../util/gasUtils';
+import { useMetrics } from '../../../components/hooks/useMetrics';
const createStyles = (colors) =>
StyleSheet.create({
@@ -199,6 +199,8 @@ const EditGasFee1559 = ({
hideTimeEstimateInfoModal,
] = useModalHandler(false);
const { colors } = useTheme();
+ const { trackEvent } = useMetrics();
+
const styles = createStyles(colors);
const getAnalyticsParams = useCallback(() => {
@@ -217,26 +219,23 @@ const EditGasFee1559 = ({
const toggleAdvancedOptions = useCallback(() => {
if (!showAdvancedOptions) {
- AnalyticsV2.trackEvent(
+ trackEvent(
MetaMetricsEvents.GAS_ADVANCED_OPTIONS_CLICKED,
getAnalyticsParams(),
);
}
setShowAdvancedOptions((showAdvancedOptions) => !showAdvancedOptions);
- }, [getAnalyticsParams, showAdvancedOptions]);
+ }, [getAnalyticsParams, showAdvancedOptions, trackEvent]);
const toggleLearnMoreModal = useCallback(() => {
setShowLearnMoreModal((showLearnMoreModal) => !showLearnMoreModal);
}, []);
const save = useCallback(() => {
- AnalyticsV2.trackEvent(
- MetaMetricsEvents.GAS_FEE_CHANGED,
- getAnalyticsParams(),
- );
+ trackEvent(MetaMetricsEvents.GAS_FEE_CHANGED, getAnalyticsParams());
onSave(selectedOption);
- }, [getAnalyticsParams, onSave, selectedOption]);
+ }, [getAnalyticsParams, onSave, selectedOption, trackEvent]);
const changeGas = useCallback(
(gas, selectedOption) => {
diff --git a/app/components/UI/EditGasFeeLegacy/index.js b/app/components/UI/EditGasFeeLegacy/index.js
index d1529909f31..4bfcdb2923c 100644
--- a/app/components/UI/EditGasFeeLegacy/index.js
+++ b/app/components/UI/EditGasFeeLegacy/index.js
@@ -23,7 +23,6 @@ import Device from '../../../util/device';
import { getDecimalChainId, isMainnetByChainId } from '../../../util/networks';
import FadeAnimationView from '../FadeAnimationView';
import { MetaMetricsEvents } from '../../../core/Analytics';
-import AnalyticsV2 from '../../../util/analyticsV2';
import AppConstants from '../../../core/AppConstants';
import { useTheme } from '../../../util/theme';
@@ -33,6 +32,7 @@ import {
GAS_LIMIT_MIN,
GAS_PRICE_MIN,
} from '../../../util/gasUtils';
+import { useMetrics } from '../../../components/hooks/useMetrics';
const createStyles = (colors) =>
StyleSheet.create({
@@ -148,6 +148,7 @@ const EditGasFeeLegacy = ({
const [selectedOption, setSelectedOption] = useState(selected);
const [gasPriceError, setGasPriceError] = useState();
const { colors } = useTheme();
+ const { trackEvent } = useMetrics();
const styles = createStyles(colors);
const getAnalyticsParams = useCallback(() => {
@@ -166,22 +167,19 @@ const EditGasFeeLegacy = ({
const toggleAdvancedOptions = useCallback(() => {
if (!showAdvancedOptions) {
- AnalyticsV2.trackEvent(
+ trackEvent(
MetaMetricsEvents.GAS_ADVANCED_OPTIONS_CLICKED,
getAnalyticsParams(),
);
}
setShowAdvancedOptions((showAdvancedOptions) => !showAdvancedOptions);
- }, [getAnalyticsParams, showAdvancedOptions]);
+ }, [getAnalyticsParams, showAdvancedOptions, trackEvent]);
const save = useCallback(() => {
- AnalyticsV2.trackEvent(
- MetaMetricsEvents.GAS_FEE_CHANGED,
- getAnalyticsParams(),
- );
+ trackEvent(MetaMetricsEvents.GAS_FEE_CHANGED, getAnalyticsParams());
onSave(selectedOption);
- }, [getAnalyticsParams, onSave, selectedOption]);
+ }, [getAnalyticsParams, onSave, selectedOption, trackEvent]);
const changeGas = useCallback(
(gas, selectedOption) => {
diff --git a/app/components/UI/Swaps/QuotesView.js b/app/components/UI/Swaps/QuotesView.js
index 12ef5230bb4..ebc6d36f840 100644
--- a/app/components/UI/Swaps/QuotesView.js
+++ b/app/components/UI/Swaps/QuotesView.js
@@ -99,6 +99,7 @@ import {
import { useMetrics } from '../../../components/hooks/useMetrics';
import { addTransaction } from '../../../util/transaction-controller';
import trackErrorAsAnalytics from '../../../util/metrics/TrackError/trackErrorAsAnalytics';
+import { selectGasFeeEstimates } from '../../../selectors/confirmTransaction';
const POLLING_INTERVAL = 30000;
const SLIPPAGE_BUCKETS = {
@@ -2325,8 +2326,7 @@ const mapStateToProps = (state) => ({
state.engine.backgroundState.SwapsController.quoteRefreshSeconds,
gasEstimateType:
state.engine.backgroundState.GasFeeController.gasEstimateType,
- gasFeeEstimates:
- state.engine.backgroundState.GasFeeController.gasFeeEstimates,
+ gasFeeEstimates: selectGasFeeEstimates(state),
usedGasEstimate: state.engine.backgroundState.SwapsController.usedGasEstimate,
usedCustomGas: state.engine.backgroundState.SwapsController.usedCustomGas,
primaryCurrency: state.settings.primaryCurrency,
diff --git a/app/components/UI/Transactions/index.js b/app/components/UI/Transactions/index.js
index d4834c565a4..19d25f14e24 100644
--- a/app/components/UI/Transactions/index.js
+++ b/app/components/UI/Transactions/index.js
@@ -73,6 +73,7 @@ import {
speedUpTransaction,
updateIncomingTransactions,
} from '../../../util/transaction-controller';
+import { selectGasFeeEstimates } from '../../../selectors/confirmTransaction';
const createStyles = (colors, typography) =>
StyleSheet.create({
@@ -867,8 +868,7 @@ const mapStateToProps = (state) => ({
selectedAddress: selectSelectedAddress(state),
networkConfigurations: selectNetworkConfigurations(state),
providerConfig: selectProviderConfig(state),
- gasFeeEstimates:
- state.engine.backgroundState.GasFeeController.gasFeeEstimates,
+ gasFeeEstimates: selectGasFeeEstimates(state),
primaryCurrency: state.settings.primaryCurrency,
tokens: selectTokensByAddress(state),
gasEstimateType:
diff --git a/app/components/Views/AccountActions/AccountActions.tsx b/app/components/Views/AccountActions/AccountActions.tsx
index 8469f1653ef..6a083f36cac 100644
--- a/app/components/Views/AccountActions/AccountActions.tsx
+++ b/app/components/Views/AccountActions/AccountActions.tsx
@@ -20,7 +20,7 @@ import {
getEtherscanAddressUrl,
getEtherscanBaseUrl,
} from '../../../util/etherscan';
-import { Analytics, MetaMetricsEvents } from '../../../core/Analytics';
+import { MetaMetricsEvents } from '../../../core/Analytics';
import { RPC } from '../../../constants/network';
import {
selectNetworkConfigurations,
@@ -33,7 +33,6 @@ import { strings } from '../../../../locales/i18n';
import styleSheet from './AccountActions.styles';
import Logger from '../../../util/Logger';
import { protectWalletModalVisible } from '../../../actions/user';
-import AnalyticsV2 from '../../../util/analyticsV2';
import Routes from '../../../constants/navigation/Routes';
import generateTestId from '../../../../wdio/utils/generateTestId';
import {
@@ -42,12 +41,14 @@ import {
SHOW_PRIVATE_KEY,
VIEW_ETHERSCAN,
} from './AccountActions.constants';
+import { useMetrics } from '../../../components/hooks/useMetrics';
const AccountActions = () => {
const { styles } = useStyles(styleSheet, {});
const sheetRef = useRef(null);
const { navigate } = useNavigation();
const dispatch = useDispatch();
+ const { trackEvent } = useMetrics();
const providerConfig = useSelector(selectProviderConfig);
@@ -94,7 +95,7 @@ const AccountActions = () => {
goToBrowserUrl(url, etherscan_url);
}
- Analytics.trackEvent(MetaMetricsEvents.NAVIGATION_TAPS_VIEW_ETHERSCAN);
+ trackEvent(MetaMetricsEvents.NAVIGATION_TAPS_VIEW_ETHERSCAN);
});
};
@@ -110,18 +111,13 @@ const AccountActions = () => {
Logger.log('Error while trying to share address', err);
});
- Analytics.trackEvent(
- MetaMetricsEvents.NAVIGATION_TAPS_SHARE_PUBLIC_ADDRESS,
- );
+ trackEvent(MetaMetricsEvents.NAVIGATION_TAPS_SHARE_PUBLIC_ADDRESS);
});
};
const goToExportPrivateKey = () => {
sheetRef.current?.onCloseBottomSheet(() => {
- AnalyticsV2.trackEvent(
- MetaMetricsEvents.REVEAL_PRIVATE_KEY_INITIATED,
- {},
- );
+ trackEvent(MetaMetricsEvents.REVEAL_PRIVATE_KEY_INITIATED);
navigate(Routes.SETTINGS.REVEAL_PRIVATE_CREDENTIAL, {
credentialName: 'private_key',
diff --git a/app/components/Views/AccountBackupStep1/index.js b/app/components/Views/AccountBackupStep1/index.js
index 99804c6ab18..0c492426f48 100644
--- a/app/components/Views/AccountBackupStep1/index.js
+++ b/app/components/Views/AccountBackupStep1/index.js
@@ -29,9 +29,8 @@ import { MetaMetricsEvents } from '../../../core/Analytics';
import DefaultPreference from 'react-native-default-preference';
import { useTheme } from '../../../util/theme';
-import trackAfterInteractions from '../../../util/metrics/TrackAfterInteraction/trackAfterInteractions';
-import Logger from '../../../util/Logger';
import { ManualBackUpStepsSelectorsIDs } from '../../../../e2e/selectors/Onboarding/ManualBackUpSteps.selectors';
+import trackOnboarding from '../../../util/metrics/TrackOnboarding/trackOnboarding';
const createStyles = (colors) =>
StyleSheet.create({
mainWrapper: {
@@ -128,9 +127,7 @@ const AccountBackupStep1 = (props) => {
const styles = createStyles(colors);
const track = (event, properties) => {
- trackAfterInteractions(event, properties).catch(() => {
- Logger.log('AccountBackupStep1', `Failed to track ${event}`);
- });
+ trackOnboarding(event, properties);
};
useEffect(() => {
diff --git a/app/components/Views/AccountBackupStep1B/index.js b/app/components/Views/AccountBackupStep1B/index.js
index 61dbbab92dd..7693a2881d4 100644
--- a/app/components/Views/AccountBackupStep1B/index.js
+++ b/app/components/Views/AccountBackupStep1B/index.js
@@ -24,9 +24,8 @@ import { CHOOSE_PASSWORD_STEPS } from '../../../constants/onboarding';
import { MetaMetricsEvents } from '../../../core/Analytics';
import { useTheme } from '../../../util/theme';
-import trackAfterInteractions from '../../../util/metrics/TrackAfterInteraction/trackAfterInteractions';
-import Logger from '../../../util/Logger';
import { ManualBackUpStepsSelectorsIDs } from '../../../../e2e/selectors/Onboarding/ManualBackUpSteps.selectors';
+import trackOnboarding from '../../../util/metrics/TrackOnboarding/trackOnboarding';
const explain_backup_seedphrase = require('../../../images/explain-backup-seedphrase.png'); // eslint-disable-line
@@ -207,9 +206,7 @@ const AccountBackupStep1B = (props) => {
const styles = createStyles(colors);
const track = (event, properties) => {
- trackAfterInteractions(event, properties).catch(() => {
- Logger.log('AccountBackupStep1B', `Failed to track ${event}`);
- });
+ trackOnboarding(event, properties);
};
useEffect(() => {
diff --git a/app/components/Views/AccountConnect/AccountConnect.tsx b/app/components/Views/AccountConnect/AccountConnect.tsx
index c99a0f926a5..4990423e22a 100644
--- a/app/components/Views/AccountConnect/AccountConnect.tsx
+++ b/app/components/Views/AccountConnect/AccountConnect.tsx
@@ -18,7 +18,6 @@ import BottomSheet, {
import UntypedEngine from '../../../core/Engine';
import { isDefaultAccountName } from '../../../util/ENSUtils';
import Logger from '../../../util/Logger';
-import AnalyticsV2 from '../../../util/analyticsV2';
import { MetaMetricsEvents } from '../../../core/Analytics';
import { SelectedAccount } from '../../../components/UI/AccountSelectorList/AccountSelectorList.types';
import {
@@ -54,13 +53,15 @@ import AccountConnectSingleSelector from './AccountConnectSingleSelector';
import AccountConnectMultiSelector from './AccountConnectMultiSelector';
import useFavicon from '../../hooks/useFavicon/useFavicon';
import URLParse from 'url-parse';
-import { trackDappVisitedEvent } from '../../../analytics';
+import { trackDappVisitedEvent } from '../../../util/metrics';
+import { useMetrics } from '../../../components/hooks/useMetrics';
const AccountConnect = (props: AccountConnectProps) => {
const Engine = UntypedEngine as any;
const { hostInfo, permissionRequestId } = props.route.params;
const [isLoading, setIsLoading] = useState(false);
const navigation = useNavigation();
+ const { trackEvent } = useMetrics();
const selectedWalletAddress = useSelector(selectSelectedAddress);
const [selectedAddresses, setSelectedAddresses] = useState([
selectedWalletAddress,
@@ -117,12 +118,12 @@ const AccountConnect = (props: AccountConnectProps) => {
(requestId) => {
Engine.context.PermissionController.rejectPermissionsRequest(requestId);
- AnalyticsV2.trackEvent(MetaMetricsEvents.CONNECT_REQUEST_CANCELLED, {
+ trackEvent(MetaMetricsEvents.CONNECT_REQUEST_CANCELLED, {
number_of_accounts: accountsLength,
source: 'permission system',
});
},
- [Engine.context.PermissionController, accountsLength],
+ [Engine.context.PermissionController, accountsLength, trackEvent],
);
const triggerDappVisitedEvent = useCallback(
@@ -160,7 +161,7 @@ const AccountConnect = (props: AccountConnectProps) => {
triggerDappVisitedEvent(connectedAccountLength);
- AnalyticsV2.trackEvent(MetaMetricsEvents.CONNECT_REQUEST_COMPLETED, {
+ trackEvent(MetaMetricsEvents.CONNECT_REQUEST_COMPLETED, {
number_of_accounts: accountsLength,
number_of_accounts_connected: connectedAccountLength,
account_type: getAddressAccountType(activeAddress),
@@ -204,6 +205,7 @@ const AccountConnect = (props: AccountConnectProps) => {
toastRef,
accountsLength,
triggerDappVisitedEvent,
+ trackEvent,
]);
const handleCreateAccount = useCallback(
@@ -216,17 +218,14 @@ const AccountConnect = (props: AccountConnectProps) => {
addedAccountAddress,
) as string;
!isMultiSelect && setSelectedAddresses([checksummedAddress]);
- AnalyticsV2.trackEvent(
- MetaMetricsEvents.ACCOUNTS_ADDED_NEW_ACCOUNT,
- {},
- );
+ trackEvent(MetaMetricsEvents.ACCOUNTS_ADDED_NEW_ACCOUNT);
} catch (e: any) {
Logger.error(e, 'error while trying to add a new account');
} finally {
setIsLoading(false);
}
},
- [Engine.context],
+ [Engine.context, trackEvent],
);
const hideSheet = (callback?: () => void) =>
@@ -266,16 +265,13 @@ const AccountConnect = (props: AccountConnectProps) => {
case USER_INTENT.Import: {
navigation.navigate('ImportPrivateKeyView');
// TODO: Confirm if this is where we want to track importing an account or within ImportPrivateKeyView screen.
- AnalyticsV2.trackEvent(
- MetaMetricsEvents.ACCOUNTS_IMPORTED_NEW_ACCOUNT,
- {},
- );
+ trackEvent(MetaMetricsEvents.ACCOUNTS_IMPORTED_NEW_ACCOUNT);
break;
}
case USER_INTENT.ConnectHW: {
navigation.navigate('ConnectQRHardwareFlow');
// TODO: Confirm if this is where we want to track connecting a hardware wallet or within ConnectQRHardwareFlow screen.
- AnalyticsV2.trackEvent(MetaMetricsEvents.CONNECT_HARDWARE_WALLET, {});
+ trackEvent(MetaMetricsEvents.CONNECT_HARDWARE_WALLET);
break;
}
@@ -293,6 +289,7 @@ const AccountConnect = (props: AccountConnectProps) => {
permissionRequestId,
handleCreateAccount,
handleConnect,
+ trackEvent,
]);
const handleSheetDismiss = () => {
diff --git a/app/components/Views/AccountPermissions/AccountPermissions.tsx b/app/components/Views/AccountPermissions/AccountPermissions.tsx
index f98ff3e13fa..75e131a49c5 100755
--- a/app/components/Views/AccountPermissions/AccountPermissions.tsx
+++ b/app/components/Views/AccountPermissions/AccountPermissions.tsx
@@ -27,7 +27,6 @@ import {
ToastVariants,
} from '../../../component-library/components/Toast';
import { ToastOptions } from '../../../component-library/components/Toast/Toast.types';
-import AnalyticsV2 from '../../../util/analyticsV2';
import { MetaMetricsEvents } from '../../../core/Analytics';
import { useAccounts, Account } from '../../hooks/useAccounts';
import getAccountNameWithENS from '../../../util/accounts';
@@ -50,9 +49,11 @@ import AccountPermissionsRevoke from './AccountPermissionsRevoke';
import { USER_INTENT } from '../../../constants/permissions';
import useFavicon from '../../hooks/useFavicon/useFavicon';
import URLParse from 'url-parse';
+import { useMetrics } from '../../../components/hooks/useMetrics';
const AccountPermissions = (props: AccountPermissionsProps) => {
const navigation = useNavigation();
+ const { trackEvent } = useMetrics();
const Engine = UntypedEngine as any;
const {
hostInfo: {
@@ -170,11 +171,8 @@ const AccountPermissions = (props: AccountPermissionsProps) => {
try {
setIsLoading(true);
await KeyringController.addNewAccount();
- AnalyticsV2.trackEvent(
- MetaMetricsEvents.ACCOUNTS_ADDED_NEW_ACCOUNT,
- {},
- );
- AnalyticsV2.trackEvent(MetaMetricsEvents.SWITCHED_ACCOUNT, {
+ trackEvent(MetaMetricsEvents.ACCOUNTS_ADDED_NEW_ACCOUNT);
+ trackEvent(MetaMetricsEvents.SWITCHED_ACCOUNT, {
source: metricsSource,
number_of_accounts: accounts?.length,
});
@@ -226,7 +224,7 @@ const AccountPermissions = (props: AccountPermissionsProps) => {
const totalAccounts = accountsLength;
// TODO: confirm this value is the newly added accounts or total connected accounts
const connectedAccounts = connectedAccountLength;
- AnalyticsV2.trackEvent(MetaMetricsEvents.ADD_ACCOUNT_DAPP_PERMISSIONS, {
+ trackEvent(MetaMetricsEvents.ADD_ACCOUNT_DAPP_PERMISSIONS, {
number_of_accounts: totalAccounts,
number_of_accounts_connected: connectedAccounts,
number_of_networks: nonTestnetNetworks,
@@ -246,6 +244,7 @@ const AccountPermissions = (props: AccountPermissionsProps) => {
accountAvatarType,
accountsLength,
nonTestnetNetworks,
+ trackEvent,
]);
useEffect(() => {
@@ -256,7 +255,7 @@ const AccountPermissions = (props: AccountPermissionsProps) => {
case USER_INTENT.Confirm: {
handleConnect();
hideSheet(() => {
- AnalyticsV2.trackEvent(MetaMetricsEvents.SWITCHED_ACCOUNT, {
+ trackEvent(MetaMetricsEvents.SWITCHED_ACCOUNT, {
source: metricsSource,
number_of_accounts: accounts?.length,
});
@@ -275,17 +274,14 @@ const AccountPermissions = (props: AccountPermissionsProps) => {
case USER_INTENT.Import: {
navigation.navigate('ImportPrivateKeyView');
// Is this where we want to track importing an account or within ImportPrivateKeyView screen?
- AnalyticsV2.trackEvent(
- MetaMetricsEvents.ACCOUNTS_IMPORTED_NEW_ACCOUNT,
- {},
- );
+ trackEvent(MetaMetricsEvents.ACCOUNTS_IMPORTED_NEW_ACCOUNT);
break;
}
case USER_INTENT.ConnectHW: {
navigation.navigate('ConnectQRHardwareFlow');
// Is this where we want to track connecting a hardware wallet or within ConnectQRHardwareFlow screen?
- AnalyticsV2.trackEvent(MetaMetricsEvents.CONNECT_HARDWARE_WALLET, {});
+ trackEvent(MetaMetricsEvents.CONNECT_HARDWARE_WALLET);
break;
}
@@ -303,6 +299,7 @@ const AccountPermissions = (props: AccountPermissionsProps) => {
handleCreateAccount,
handleConnect,
accounts?.length,
+ trackEvent,
]);
const renderConnectedScreen = useCallback(
diff --git a/app/components/Views/AccountPermissions/AccountPermissionsConnected/AccountPermissionsConnected.tsx b/app/components/Views/AccountPermissions/AccountPermissionsConnected/AccountPermissionsConnected.tsx
index a727f51378e..ca2112a893c 100644
--- a/app/components/Views/AccountPermissions/AccountPermissionsConnected/AccountPermissionsConnected.tsx
+++ b/app/components/Views/AccountPermissions/AccountPermissionsConnected/AccountPermissionsConnected.tsx
@@ -24,7 +24,6 @@ import {
ToastVariants,
} from '../../../../component-library/components/Toast';
import getAccountNameWithENS from '../../../../util/accounts';
-import AnalyticsV2 from '../../../../util/analyticsV2';
import { MetaMetricsEvents } from '../../../../core/Analytics';
import Routes from '../../../../constants/navigation/Routes';
import { selectProviderConfig } from '../../../../selectors/networkController';
@@ -33,6 +32,7 @@ import { ConnectedAccountsSelectorsIDs } from '../../../../../e2e/selectors/Moda
// Internal dependencies.
import { AccountPermissionsConnectedProps } from './AccountPermissionsConnected.types';
import styles from './AccountPermissionsConnected.styles';
+import { useMetrics } from '../../../../components/hooks/useMetrics';
const AccountPermissionsConnected = ({
ensByAccountAddress,
@@ -49,6 +49,7 @@ const AccountPermissionsConnected = ({
urlWithProtocol,
}: AccountPermissionsConnectedProps) => {
const { navigate } = useNavigation();
+ const { trackEvent } = useMetrics();
const providerConfig: ProviderConfig = useSelector(selectProviderConfig);
@@ -112,10 +113,10 @@ const AccountPermissionsConnected = ({
screen: Routes.SHEET.NETWORK_SELECTOR,
});
- AnalyticsV2.trackEvent(MetaMetricsEvents.NETWORK_SELECTOR_PRESSED, {
+ trackEvent(MetaMetricsEvents.NETWORK_SELECTOR_PRESSED, {
chain_id: getDecimalChainId(providerConfig.chainId),
});
- }, [providerConfig.chainId, navigate]);
+ }, [providerConfig.chainId, navigate, trackEvent]);
const renderSheetAction = useCallback(
() => (
diff --git a/app/components/Views/AccountPermissions/AccountPermissionsRevoke/AccountPermissionsRevoke.tsx b/app/components/Views/AccountPermissions/AccountPermissionsRevoke/AccountPermissionsRevoke.tsx
index 21261f0f44b..1a7bad87f57 100644
--- a/app/components/Views/AccountPermissions/AccountPermissionsRevoke/AccountPermissionsRevoke.tsx
+++ b/app/components/Views/AccountPermissions/AccountPermissionsRevoke/AccountPermissionsRevoke.tsx
@@ -25,7 +25,6 @@ import { ToastOptions } from '../../../../component-library/components/Toast/Toa
import { AccountPermissionsScreens } from '../AccountPermissions.types';
import getAccountNameWithENS from '../../../../util/accounts';
import { MetaMetricsEvents } from '../../../../core/Analytics';
-import AnalyticsV2 from '../../../../util/analyticsV2';
import { selectAccountsLength } from '../../../../selectors/accountTrackerController';
// Internal dependencies.
@@ -37,6 +36,7 @@ import Avatar from '../../../../component-library/components/Avatars/Avatar/Avat
import { AvatarVariant } from '../../../../component-library/components/Avatars/Avatar';
import { selectNetworkConfigurations } from '../../../../selectors/networkController';
import { ConnectedAccountsSelectorsIDs } from '../../../../../e2e/selectors/Modals/ConnectedAccountModal.selectors';
+import { useMetrics } from '../../../../components/hooks/useMetrics';
const AccountPermissionsRevoke = ({
ensByAccountAddress,
@@ -52,6 +52,7 @@ const AccountPermissionsRevoke = ({
}: AccountPermissionsRevokeProps) => {
const Engine = UntypedEngine as any;
const { styles } = useStyles(styleSheet, {});
+ const { trackEvent } = useMetrics();
const activeAddress = permittedAddresses[0];
const { toastRef } = useContext(ToastContext);
@@ -67,20 +68,17 @@ const AccountPermissionsRevoke = ({
await Engine.context.PermissionController.revokeAllPermissions(
hostname,
);
- AnalyticsV2.trackEvent(
- MetaMetricsEvents.REVOKE_ACCOUNT_DAPP_PERMISSIONS,
- {
- number_of_accounts: accountsLength,
- number_of_accounts_connected: permittedAddresses.length,
- number_of_networks: nonTestnetNetworks,
- },
- );
+ trackEvent(MetaMetricsEvents.REVOKE_ACCOUNT_DAPP_PERMISSIONS, {
+ number_of_accounts: accountsLength,
+ number_of_accounts_connected: permittedAddresses.length,
+ number_of_networks: nonTestnetNetworks,
+ });
} catch (e) {
Logger.log(`Failed to revoke all accounts for ${hostname}`, e);
}
},
/* eslint-disable-next-line */
- [hostname],
+ [hostname, trackEvent],
);
const renderSheetAction = useCallback(
@@ -178,14 +176,11 @@ const AccountPermissionsRevoke = ({
labelOptions,
});
}
- AnalyticsV2.trackEvent(
- MetaMetricsEvents.REVOKE_ACCOUNT_DAPP_PERMISSIONS,
- {
- number_of_accounts: accountsLength,
- number_of_accounts_connected: permittedAddresses.length,
- number_of_networks: nonTestnetNetworks,
- },
- );
+ trackEvent(MetaMetricsEvents.REVOKE_ACCOUNT_DAPP_PERMISSIONS, {
+ number_of_accounts: accountsLength,
+ number_of_accounts_connected: permittedAddresses.length,
+ number_of_networks: nonTestnetNetworks,
+ });
}
}}
label={strings('accounts.disconnect')}
diff --git a/app/components/Views/AccountSelector/AccountSelector.tsx b/app/components/Views/AccountSelector/AccountSelector.tsx
index 26ee3bdccbd..c911bdcc759 100644
--- a/app/components/Views/AccountSelector/AccountSelector.tsx
+++ b/app/components/Views/AccountSelector/AccountSelector.tsx
@@ -6,7 +6,7 @@ import React, {
useRef,
useState,
} from 'react';
-import { InteractionManager, Platform, View } from 'react-native';
+import { Platform, View } from 'react-native';
// External dependencies.
import AccountSelectorList from '../../UI/AccountSelectorList';
@@ -15,7 +15,6 @@ import BottomSheet, {
} from '../../../component-library/components/BottomSheets/BottomSheet';
import SheetHeader from '../../../component-library/components/Sheet/SheetHeader';
import UntypedEngine from '../../../core/Engine';
-import AnalyticsV2 from '../../../util/analyticsV2';
import { MetaMetricsEvents } from '../../../core/Analytics';
import { strings } from '../../../../locales/i18n';
import { useAccounts } from '../../hooks/useAccounts';
@@ -40,9 +39,11 @@ import styles from './AccountSelector.styles';
import { useDispatch, useSelector } from 'react-redux';
import { setReloadAccounts } from '../../../actions/accounts';
import { RootState } from '../../../reducers';
+import { useMetrics } from '../../../components/hooks/useMetrics';
const AccountSelector = ({ route }: AccountSelectorProps) => {
const dispatch = useDispatch();
+ const { trackEvent } = useMetrics();
const { onSelectAccount, checkBalanceError } = route.params || {};
const { reloadAccounts } = useSelector((state: RootState) => state.accounts);
@@ -68,15 +69,14 @@ const AccountSelector = ({ route }: AccountSelectorProps) => {
PreferencesController.setSelectedAddress(address);
sheetRef.current?.onCloseBottomSheet();
onSelectAccount?.(address);
- InteractionManager.runAfterInteractions(() => {
- // Track Event: "Switched Account"
- AnalyticsV2.trackEvent(MetaMetricsEvents.SWITCHED_ACCOUNT, {
- source: 'Wallet Tab',
- number_of_accounts: accounts?.length,
- });
+
+ // Track Event: "Switched Account"
+ trackEvent(MetaMetricsEvents.SWITCHED_ACCOUNT, {
+ source: 'Wallet Tab',
+ number_of_accounts: accounts?.length,
});
},
- [Engine.context, accounts?.length, onSelectAccount],
+ [Engine.context, accounts?.length, onSelectAccount, trackEvent],
);
const onRemoveImportedAccount = useCallback(
diff --git a/app/components/Views/ActivityView/index.js b/app/components/Views/ActivityView/index.js
index 31ce6f0a665..777a69d6abe 100644
--- a/app/components/Views/ActivityView/index.js
+++ b/app/components/Views/ActivityView/index.js
@@ -12,10 +12,10 @@ import RampOrdersList from '../../UI/Ramp/Views/OrdersList';
import ErrorBoundary from '../ErrorBoundary';
import { useTheme } from '../../../util/theme';
import Routes from '../../../constants/navigation/Routes';
-import AnalyticsV2 from '../../../util/analyticsV2';
import { MetaMetricsEvents } from '../../../core/Analytics';
import { selectAccountsByChainId } from '../../../selectors/accountTrackerController';
import { selectSelectedAddress } from '../../../selectors/preferencesController';
+import { useMetrics } from '../../../components/hooks/useMetrics';
const styles = StyleSheet.create({
wrapper: {
@@ -25,6 +25,7 @@ const styles = StyleSheet.create({
const ActivityView = () => {
const { colors } = useTheme();
+ const { trackEvent } = useMetrics();
const navigation = useNavigation();
const selectedAddress = useSelector(selectSelectedAddress);
const hasOrders = useSelector((state) => getHasOrders(state) || false);
@@ -35,11 +36,11 @@ const ActivityView = () => {
screen: Routes.SHEET.ACCOUNT_SELECTOR,
});
// Track Event: "Opened Acount Switcher"
- AnalyticsV2.trackEvent(MetaMetricsEvents.BROWSER_OPEN_ACCOUNT_SWITCH, {
+ trackEvent(MetaMetricsEvents.BROWSER_OPEN_ACCOUNT_SWITCH, {
number_of_accounts: Object.keys(accountsByChainId[selectedAddress] ?? {})
.length,
});
- }, [navigation, accountsByChainId, selectedAddress]);
+ }, [navigation, accountsByChainId, selectedAddress, trackEvent]);
useEffect(
() => {
diff --git a/app/components/Views/AddAccountActions/AddAccountActions.tsx b/app/components/Views/AddAccountActions/AddAccountActions.tsx
index 0dca0e554ad..d64547b9861 100644
--- a/app/components/Views/AddAccountActions/AddAccountActions.tsx
+++ b/app/components/Views/AddAccountActions/AddAccountActions.tsx
@@ -8,7 +8,6 @@ import SheetHeader from '../../../component-library/components/Sheet/SheetHeader
import AccountAction from '../AccountAction/AccountAction';
import { IconName } from '../../../component-library/components/Icons/Icon';
import { strings } from '../../../../locales/i18n';
-import AnalyticsV2 from '../../../util/analyticsV2';
import { MetaMetricsEvents } from '../../../core/Analytics';
import Logger from '../../../util/Logger';
import Engine from '../../../core/Engine';
@@ -17,22 +16,24 @@ import Engine from '../../../core/Engine';
import { AddAccountActionsProps } from './AddAccountActions.types';
import { AddAccountModalSelectorsIDs } from '../../../../e2e/selectors/Modals/AddAccountModal.selectors';
import Routes from '../../../constants/navigation/Routes';
+import { useMetrics } from '../../../components/hooks/useMetrics';
const AddAccountActions = ({ onBack }: AddAccountActionsProps) => {
const { navigate } = useNavigation();
+ const { trackEvent } = useMetrics();
const [isLoading, setIsLoading] = useState(false);
const openImportAccount = useCallback(() => {
navigate('ImportPrivateKeyView');
onBack();
- AnalyticsV2.trackEvent(MetaMetricsEvents.ACCOUNTS_IMPORTED_NEW_ACCOUNT, {});
- }, [navigate, onBack]);
+ trackEvent(MetaMetricsEvents.ACCOUNTS_IMPORTED_NEW_ACCOUNT, {});
+ }, [navigate, onBack, trackEvent]);
const openConnectHardwareWallet = useCallback(() => {
navigate(Routes.HW.CONNECT);
onBack();
- AnalyticsV2.trackEvent(MetaMetricsEvents.CONNECT_HARDWARE_WALLET, {});
- }, [onBack, navigate]);
+ trackEvent(MetaMetricsEvents.CONNECT_HARDWARE_WALLET, {});
+ }, [onBack, navigate, trackEvent]);
const createNewAccount = useCallback(async () => {
const { KeyringController, PreferencesController } = Engine.context;
@@ -41,7 +42,7 @@ const AddAccountActions = ({ onBack }: AddAccountActionsProps) => {
const { addedAccountAddress } = await KeyringController.addNewAccount();
PreferencesController.setSelectedAddress(addedAccountAddress);
- AnalyticsV2.trackEvent(MetaMetricsEvents.ACCOUNTS_ADDED_NEW_ACCOUNT, {});
+ trackEvent(MetaMetricsEvents.ACCOUNTS_ADDED_NEW_ACCOUNT, {});
} catch (e: any) {
Logger.error(e, 'error while trying to add a new account');
} finally {
@@ -49,7 +50,7 @@ const AddAccountActions = ({ onBack }: AddAccountActionsProps) => {
setIsLoading(false);
}
- }, [onBack, setIsLoading]);
+ }, [onBack, setIsLoading, trackEvent]);
return (
diff --git a/app/components/Views/Asset/index.js b/app/components/Views/Asset/index.js
index b68c7a1f7bf..fa5ba2ecc57 100644
--- a/app/components/Views/Asset/index.js
+++ b/app/components/Views/Asset/index.js
@@ -22,7 +22,6 @@ import {
TX_UNAPPROVED,
} from '../../../constants/transaction';
import { MetaMetricsEvents } from '../../../core/Analytics';
-import Analytics from '../../../core/Analytics/Analytics';
import AppConstants from '../../../core/AppConstants';
import {
swapsLivenessSelector,
@@ -65,6 +64,7 @@ import {
TOKEN_OVERVIEW_SWAP_BUTTON,
} from '../../../../wdio/screen-objects/testIDs/Screens/TokenOverviewScreen.testIds';
import { updateIncomingTransactions } from '../../../util/transaction-controller';
+import { withMetricsAwareness } from '../../../components/hooks/useMetrics';
const createStyles = (colors) =>
StyleSheet.create({
@@ -176,6 +176,10 @@ class Asset extends PureComponent {
* Boolean that indicates if native token is supported to buy
*/
isNetworkBuyNativeTokenSupported: PropTypes.bool,
+ /**
+ * Metrics injected by withMetricsAwareness HOC
+ */
+ metrics: PropTypes.object,
};
state = {
@@ -474,15 +478,11 @@ class Asset extends PureComponent {
const onBuy = () => {
navigation.navigate(Routes.RAMP.BUY);
- InteractionManager.runAfterInteractions(() => {
- Analytics.trackEventWithParameters(
- MetaMetricsEvents.BUY_BUTTON_CLICKED,
- {
- text: 'Buy',
- location: 'Token Screen',
- chain_id_destination: chainId,
- },
- );
+
+ this.props.metrics.trackEvent(MetaMetricsEvents.BUY_BUTTON_CLICKED, {
+ text: 'Buy',
+ location: 'Token Screen',
+ chain_id_destination: chainId,
});
};
@@ -595,4 +595,4 @@ const mapStateToProps = (state) => ({
),
});
-export default connect(mapStateToProps)(Asset);
+export default connect(mapStateToProps)(withMetricsAwareness(Asset));
diff --git a/app/components/Views/AssetDetails/index.tsx b/app/components/Views/AssetDetails/index.tsx
index 58a233599da..5af5ba85310 100644
--- a/app/components/Views/AssetDetails/index.tsx
+++ b/app/components/Views/AssetDetails/index.tsx
@@ -30,7 +30,6 @@ import {
import WarningMessage from '../confirmations/SendFlow/WarningMessage';
import { useTheme } from '../../../util/theme';
import { MetaMetricsEvents } from '../../../core/Analytics';
-import AnalyticsV2 from '../../../util/analyticsV2';
import Routes from '../../../constants/navigation/Routes';
import {
selectChainId,
@@ -43,6 +42,7 @@ import {
import { selectTokens } from '../../../selectors/tokensController';
import { selectContractExchangeRates } from '../../../selectors/tokenRatesController';
import { selectContractBalances } from '../../../selectors/tokenBalancesController';
+import { useMetrics } from '../../../components/hooks/useMetrics';
const createStyles = (colors: any) =>
StyleSheet.create({
@@ -104,6 +104,7 @@ interface Props {
const AssetDetails = (props: Props) => {
const { address } = props.route.params;
const { colors } = useTheme();
+ const { trackEvent } = useMetrics();
const styles = createStyles(colors);
const navigation = useNavigation();
const dispatch = useDispatch();
@@ -178,7 +179,7 @@ const AssetDetails = (props: Props) => {
tokenSymbol: symbol,
}),
});
- AnalyticsV2.trackEvent(MetaMetricsEvents.TOKENS_HIDDEN, {
+ trackEvent(MetaMetricsEvents.TOKENS_HIDDEN, {
location: 'token_details',
token_standard: 'ERC20',
asset_type: 'token',
diff --git a/app/components/Views/Browser/index.js b/app/components/Views/Browser/index.js
index 6420d4d4b1e..499b65840c3 100644
--- a/app/components/Views/Browser/index.js
+++ b/app/components/Views/Browser/index.js
@@ -18,7 +18,6 @@ import BrowserTab from '../BrowserTab';
import AppConstants from '../../../core/AppConstants';
import { baseStyles } from '../../../styles/common';
import { useTheme } from '../../../util/theme';
-import AnalyticsV2 from '../../../util/analyticsV2';
import { MetaMetricsEvents } from '../../../core/Analytics';
import {
getPermittedAccounts,
@@ -39,6 +38,7 @@ import { selectAccountsLength } from '../../../selectors/accountTrackerControlle
import URL from 'url-parse';
import { isEqual } from 'lodash';
import { selectNetworkConfigurations } from '../../../selectors/networkController';
+import { useMetrics } from '../../../components/hooks/useMetrics';
const margin = 16;
const THUMB_WIDTH = Dimensions.get('window').width / 2 - margin * 2;
@@ -63,6 +63,7 @@ const Browser = (props) => {
} = props;
const previousTabs = useRef(null);
const { colors } = useTheme();
+ const { trackEvent } = useMetrics();
const { toastRef } = useContext(ToastContext);
const browserUrl = props.route?.params?.url;
const prevSiteHostname = useRef(browserUrl);
@@ -92,7 +93,7 @@ const Browser = (props) => {
}, isEqual);
const handleRightTopButtonAnalyticsEvent = () => {
- AnalyticsV2.trackEvent(MetaMetricsEvents.OPEN_DAPP_PERMISSIONS, {
+ trackEvent(MetaMetricsEvents.OPEN_DAPP_PERMISSIONS, {
number_of_accounts: accountsLength,
number_of_accounts_connected: permittedAccountsList.length,
number_of_networks: nonTestnetNetworks,
@@ -131,7 +132,7 @@ const Browser = (props) => {
};
const switchToTab = (tab) => {
- AnalyticsV2.trackEvent(MetaMetricsEvents.BROWSER_SWITCH_TAB, {});
+ trackEvent(MetaMetricsEvents.BROWSER_SWITCH_TAB, {});
setActiveTab(tab.id);
hideTabsAndUpdateUrl(tab.url);
updateTabInfo(tab.url, tab.id);
diff --git a/app/components/Views/BrowserTab/index.js b/app/components/Views/BrowserTab/index.js
index cea53ff6d75..539a36ccdae 100644
--- a/app/components/Views/BrowserTab/index.js
+++ b/app/components/Views/BrowserTab/index.js
@@ -7,7 +7,6 @@ import {
Alert,
Linking,
BackHandler,
- InteractionManager,
Platform,
} from 'react-native';
import { isEqual } from 'lodash';
@@ -49,9 +48,7 @@ import { addToHistory, addToWhitelist } from '../../../actions/browser';
import Device from '../../../util/device';
import AppConstants from '../../../core/AppConstants';
import SearchApi from 'react-native-search-api';
-import Analytics from '../../../core/Analytics/Analytics';
import { MetaMetricsEvents } from '../../../core/Analytics';
-import AnalyticsV2, { trackErrorAsAnalytics } from '../../../util/analyticsV2';
import setOnboardingWizardStep from '../../../actions/wizard';
import OnboardingWizard from '../../UI/OnboardingWizard';
import DrawerStatusTracker from '../../../core/DrawerStatusTracker';
@@ -101,7 +98,9 @@ import { TextVariant } from '../../../component-library/components/Texts/Text';
import { regex } from '../../../../app/util/regex';
import { selectChainId } from '../../../selectors/networkController';
import { BrowserViewSelectorsIDs } from '../../../../e2e/selectors/BrowserView.selectors';
-import { trackDappVisitedEvent } from '../../../analytics';
+import { useMetrics } from '../../../components/hooks/useMetrics';
+import trackDappVisitedEvent from '../../../util/metrics/trackDappVisited';
+import trackErrorAsAnalytics from '../../../util/metrics/TrackError/trackErrorAsAnalytics';
const { HOMEPAGE_URL, NOTIFICATION_NAMES } = AppConstants;
const HOMEPAGE_HOST = new URL(HOMEPAGE_URL)?.hostname;
@@ -290,7 +289,7 @@ export const BrowserTab = (props) => {
const { colors, shadows } = useTheme();
const styles = createStyles(colors, shadows);
const favicon = useFavicon(url.current);
-
+ const { trackEvent, isEnabled, getMetaMetricsId } = useMetrics();
/**
* Is the current tab the active tab
*/
@@ -386,10 +385,9 @@ export const BrowserTab = (props) => {
const toggleOptions = useCallback(() => {
dismissTextSelectionIfNeeded();
setShowOptions(!showOptions);
- InteractionManager.runAfterInteractions(() => {
- Analytics.trackEvent(MetaMetricsEvents.DAPP_BROWSER_OPTIONS);
- });
- }, [dismissTextSelectionIfNeeded, showOptions]);
+
+ trackEvent(MetaMetricsEvents.DAPP_BROWSER_OPTIONS);
+ }, [dismissTextSelectionIfNeeded, showOptions, trackEvent]);
/**
* Show the options menu
@@ -713,8 +711,8 @@ export const BrowserTab = (props) => {
*/
const injectHomePageScripts = async (bookmarks) => {
const { current } = webviewRef;
- const analyticsEnabled = Analytics.checkEnabled();
- const disctinctId = await Analytics.getDistinctId();
+ const analyticsEnabled = isEnabled();
+ const disctinctId = await getMetaMetricsId();
const homepageScripts = `
window.__mmFavorites = ${JSON.stringify(bookmarks || props.bookmarks)};
window.__mmSearchEngine = "${props.searchEngine}";
@@ -842,11 +840,11 @@ export const BrowserTab = (props) => {
);
const trackEventSearchUsed = useCallback(() => {
- AnalyticsV2.trackEvent(MetaMetricsEvents.BROWSER_SEARCH_USED, {
+ trackEvent(MetaMetricsEvents.BROWSER_SEARCH_USED, {
option_chosen: 'Search on URL',
number_of_tabs: undefined,
});
- }, []);
+ }, [trackEvent]);
/**
* Function that allows custom handling of any web view requests.
@@ -963,7 +961,7 @@ export const BrowserTab = (props) => {
toggleOptionsIfNeeded();
if (url.current === HOMEPAGE_URL) return reload();
await go(HOMEPAGE_URL);
- Analytics.trackEvent(MetaMetricsEvents.DAPP_HOME);
+ trackEvent(MetaMetricsEvents.DAPP_HOME);
};
/**
@@ -1108,12 +1106,9 @@ export const BrowserTab = (props) => {
error,
setAccountsPermissionsVisible: () => {
// Track Event: "Opened Acount Switcher"
- AnalyticsV2.trackEvent(
- MetaMetricsEvents.BROWSER_OPEN_ACCOUNT_SWITCH,
- {
- number_of_accounts: accounts?.length,
- },
- );
+ trackEvent(MetaMetricsEvents.BROWSER_OPEN_ACCOUNT_SWITCH, {
+ number_of_accounts: accounts?.length,
+ });
props.navigation.navigate(Routes.MODAL.ROOT_MODAL_FLOW, {
screen: Routes.SHEET.ACCOUNT_PERMISSIONS,
params: {
@@ -1185,7 +1180,7 @@ export const BrowserTab = (props) => {
* Track new tab event
*/
const trackNewTabEvent = () => {
- AnalyticsV2.trackEvent(MetaMetricsEvents.BROWSER_NEW_TAB, {
+ trackEvent(MetaMetricsEvents.BROWSER_NEW_TAB, {
option_chosen: 'Browser Options',
number_of_tabs: undefined,
});
@@ -1195,7 +1190,7 @@ export const BrowserTab = (props) => {
* Track add site to favorites event
*/
const trackAddToFavoritesEvent = () => {
- AnalyticsV2.trackEvent(MetaMetricsEvents.BROWSER_ADD_FAVORITES, {
+ trackEvent(MetaMetricsEvents.BROWSER_ADD_FAVORITES, {
dapp_name: title.current || '',
});
};
@@ -1204,14 +1199,14 @@ export const BrowserTab = (props) => {
* Track share site event
*/
const trackShareEvent = () => {
- AnalyticsV2.trackEvent(MetaMetricsEvents.BROWSER_SHARE_SITE);
+ trackEvent(MetaMetricsEvents.BROWSER_SHARE_SITE);
};
/**
* Track reload site event
*/
const trackReloadEvent = () => {
- AnalyticsV2.trackEvent(MetaMetricsEvents.BROWSER_RELOAD);
+ trackEvent(MetaMetricsEvents.BROWSER_RELOAD);
};
/**
@@ -1246,7 +1241,7 @@ export const BrowserTab = (props) => {
},
});
trackAddToFavoritesEvent();
- Analytics.trackEvent(MetaMetricsEvents.DAPP_ADD_TO_FAVORITE);
+ trackEvent(MetaMetricsEvents.DAPP_ADD_TO_FAVORITE);
};
/**
@@ -1273,7 +1268,7 @@ export const BrowserTab = (props) => {
error,
),
);
- Analytics.trackEvent(MetaMetricsEvents.DAPP_OPEN_IN_BROWSER);
+ trackEvent(MetaMetricsEvents.DAPP_OPEN_IN_BROWSER);
};
/**
diff --git a/app/components/Views/ChoosePassword/index.js b/app/components/Views/ChoosePassword/index.js
index 547b0d1f43c..fc9a6cf9197 100644
--- a/app/components/Views/ChoosePassword/index.js
+++ b/app/components/Views/ChoosePassword/index.js
@@ -62,7 +62,7 @@ import AnimatedFox from 'react-native-animated-fox';
import { LoginOptionsSwitch } from '../../UI/LoginOptionsSwitch';
import navigateTermsOfUse from '../../../util/termsOfUse/termsOfUse';
import { ChoosePasswordSelectorsIDs } from '../../../../e2e/selectors/Onboarding/ChoosePassword.selectors';
-import trackAfterInteractions from '../../../util/metrics/TrackAfterInteraction/trackAfterInteractions';
+import trackOnboarding from '../../../util/metrics/TrackOnboarding/trackOnboarding';
const createStyles = (colors) =>
StyleSheet.create({
@@ -260,9 +260,7 @@ class ChoosePassword extends PureComponent {
keyringControllerPasswordSet = false;
track = (event, properties) => {
- trackAfterInteractions(event, properties).catch(() => {
- Logger.log('ChoosePassword', `Failed to track ${event}`);
- });
+ trackOnboarding(event, properties);
};
updateNavBar = () => {
diff --git a/app/components/Views/confirmations/Approval/components/TransactionEditor/index.js b/app/components/Views/confirmations/Approval/components/TransactionEditor/index.js
index 4c40adcfb53..206df0d6104 100644
--- a/app/components/Views/confirmations/Approval/components/TransactionEditor/index.js
+++ b/app/components/Views/confirmations/Approval/components/TransactionEditor/index.js
@@ -50,6 +50,8 @@ import {
import { selectAccounts } from '../../../../../../selectors/accountTrackerController';
import { selectContractBalances } from '../../../../../../selectors/tokenBalancesController';
import { selectSelectedAddress } from '../../../../../../selectors/preferencesController';
+import { selectGasFeeEstimates } from '../../../../../../selectors/confirmTransaction';
+import { selectGasFeeControllerEstimateType } from '../../../../../../selectors/gasFeeController';
const EDIT = 'edit';
const REVIEW = 'review';
@@ -970,10 +972,8 @@ const mapStateToProps = (state) => ({
ticker: selectTicker(state),
transaction: getNormalizedTxState(state),
activeTabUrl: getActiveTabUrl(state),
- gasFeeEstimates:
- state.engine.backgroundState.GasFeeController.gasFeeEstimates,
- gasEstimateType:
- state.engine.backgroundState.GasFeeController.gasEstimateType,
+ gasFeeEstimates: selectGasFeeEstimates(state),
+ gasEstimateType: selectGasFeeControllerEstimateType(state),
conversionRate: selectConversionRate(state),
currentCurrency: selectCurrentCurrency(state),
nativeCurrency: selectNativeCurrency(state),
diff --git a/app/components/Views/confirmations/Approval/index.js b/app/components/Views/confirmations/Approval/index.js
index d74f4e6f865..03a7ef623a4 100644
--- a/app/components/Views/confirmations/Approval/index.js
+++ b/app/components/Views/confirmations/Approval/index.js
@@ -9,7 +9,6 @@ import { getTransactionOptionsTitle } from '../../../UI/Navbar';
import { resetTransaction } from '../../../../actions/transaction';
import { connect } from 'react-redux';
import NotificationManager from '../../../../core/NotificationManager';
-import Analytics from '../../../../core/Analytics/Analytics';
import AppConstants from '../../../../core/AppConstants';
import { MetaMetricsEvents } from '../../../../core/Analytics';
import {
@@ -26,7 +25,6 @@ import {
} from '../../../../util/address';
import { WALLET_CONNECT_ORIGIN } from '../../../../util/walletconnect';
import Logger from '../../../../util/Logger';
-import AnalyticsV2 from '../../../../util/analyticsV2';
import { GAS_ESTIMATE_TYPES } from '@metamask/gas-fee-controller';
import { KEYSTONE_TX_CANCELED } from '../../../../constants/error';
import { ThemeContext, mockTheme } from '../../../../util/theme';
@@ -50,6 +48,7 @@ import { getBlockaidMetricsParams } from '../../../../util/blockaid';
import { getDecimalChainId } from '../../../../util/networks';
import { updateTransaction } from '../../../../util/transaction-controller';
+import { withMetricsAwareness } from '../../../../components/hooks/useMetrics';
const REVIEW = 'review';
const EDIT = 'edit';
@@ -111,6 +110,10 @@ class Approval extends PureComponent {
* A string representing the network chainId
*/
chainId: PropTypes.string,
+ /**
+ * Metrics injected by withMetricsAwareness HOC
+ */
+ metrics: PropTypes.object,
};
state = {
@@ -220,7 +223,7 @@ class Approval extends PureComponent {
navigation &&
navigation.setParams({ mode: REVIEW, dispatch: this.onModeChange });
- AnalyticsV2.trackEvent(
+ this.props.metrics.trackEvent(
MetaMetricsEvents.DAPP_TRANSACTION_STARTED,
this.getAnalyticsParams(),
);
@@ -230,7 +233,7 @@ class Approval extends PureComponent {
* Call Analytics to track confirm started event for approval screen
*/
trackConfirmScreen = () => {
- Analytics.trackEventWithParameters(
+ this.props.metrics.trackEvent(
MetaMetricsEvents.TRANSACTIONS_CONFIRM_STARTED,
this.getTrackingParams(),
);
@@ -242,7 +245,7 @@ class Approval extends PureComponent {
trackEditScreen = async () => {
const { transaction } = this.props;
const actionKey = await getTransactionReviewActionKey(transaction);
- Analytics.trackEventWithParameters(
+ this.props.metrics.trackEvent(
MetaMetricsEvents.TRANSACTIONS_EDIT_TRANSACTION,
{
...this.getTrackingParams(),
@@ -255,7 +258,7 @@ class Approval extends PureComponent {
* Call Analytics to track cancel pressed
*/
trackOnCancel = () => {
- Analytics.trackEventWithParameters(
+ this.props.metrics.trackEvent(
MetaMetricsEvents.TRANSACTIONS_CANCEL_TRANSACTION,
this.getTrackingParams(),
);
@@ -347,10 +350,13 @@ class Approval extends PureComponent {
this.props.hideModal();
this.state.mode === REVIEW && this.trackOnCancel();
this.showWalletConnectNotification();
- AnalyticsV2.trackEvent(MetaMetricsEvents.DAPP_TRANSACTION_CANCELLED, {
- ...this.getAnalyticsParams(),
- ...this.getBlockaidMetricsParams(),
- });
+ this.props.metrics.trackEvent(
+ MetaMetricsEvents.DAPP_TRANSACTION_CANCELLED,
+ {
+ ...this.getAnalyticsParams(),
+ ...this.getBlockaidMetricsParams(),
+ },
+ );
};
onLedgerConfirmation = (approve, transactionId, gaParams) => {
@@ -368,7 +374,7 @@ class Approval extends PureComponent {
this.showWalletConnectNotification();
- AnalyticsV2.trackEvent(
+ this.props.metrics.trackEvent(
MetaMetricsEvents.DAPP_TRANSACTION_CANCELLED,
gaParams,
);
@@ -376,7 +382,7 @@ class Approval extends PureComponent {
this.showWalletConnectNotification(true);
}
} finally {
- AnalyticsV2.trackEvent(
+ this.props.metrics.trackEvent(
MetaMetricsEvents.DAPP_TRANSACTION_COMPLETED,
gaParams,
);
@@ -491,19 +497,22 @@ class Approval extends PureComponent {
this.setState({ transactionHandled: true });
this.props.hideModal();
} else {
- AnalyticsV2.trackEvent(
+ this.props.metrics.trackEvent(
MetaMetricsEvents.QR_HARDWARE_TRANSACTION_CANCELED,
);
}
this.setState({ transactionHandled: false });
}
- AnalyticsV2.trackEvent(MetaMetricsEvents.DAPP_TRANSACTION_COMPLETED, {
- ...this.getAnalyticsParams({
- gasEstimateType,
- gasSelected,
- }),
- ...this.getBlockaidMetricsParams(),
- });
+ this.props.metrics.trackEvent(
+ MetaMetricsEvents.DAPP_TRANSACTION_COMPLETED,
+ {
+ ...this.getAnalyticsParams({
+ gasEstimateType,
+ gasSelected,
+ }),
+ ...this.getBlockaidMetricsParams(),
+ },
+ );
this.setState({ transactionConfirmed: false });
};
@@ -640,4 +649,7 @@ const mapDispatchToProps = (dispatch) => ({
Approval.contextType = ThemeContext;
-export default connect(mapStateToProps, mapDispatchToProps)(Approval);
+export default connect(
+ mapStateToProps,
+ mapDispatchToProps,
+)(withMetricsAwareness(Approval));
diff --git a/app/components/Views/confirmations/ApproveView/Approve/index.js b/app/components/Views/confirmations/ApproveView/Approve/index.js
index 71617d418a0..879b5bc5266 100644
--- a/app/components/Views/confirmations/ApproveView/Approve/index.js
+++ b/app/components/Views/confirmations/ApproveView/Approve/index.js
@@ -1,5 +1,5 @@
import React, { PureComponent } from 'react';
-import { Alert, InteractionManager, AppState, View } from 'react-native';
+import { Alert, AppState, View } from 'react-native';
import PropTypes from 'prop-types';
import { getApproveNavbar } from '../../../../UI/Navbar';
import { connect } from 'react-redux';
@@ -14,7 +14,7 @@ import AddNickname from '../../components/ApproveTransactionReview/AddNickname';
import Modal from 'react-native-modal';
import { strings } from '../../../../../../locales/i18n';
import { getNetworkNonce } from '../../../../../util/networks';
-import Analytics from '../../../../../core/Analytics/Analytics';
+
import {
setTransactionObject,
setNonce,
@@ -39,7 +39,6 @@ import { MetaMetricsEvents } from '../../../../../core/Analytics';
import Logger from '../../../../../util/Logger';
import EditGasFee1559 from '../../components/EditGasFee1559Update';
import EditGasFeeLegacy from '../../components/EditGasFeeLegacyUpdate';
-import AnalyticsV2 from '../../../../../util/analyticsV2';
import AppConstants from '../../../../../core/AppConstants';
import { shallowEqual } from '../../../../../util/general';
import { KEYSTONE_TX_CANCELED } from '../../../../../constants/error';
@@ -74,6 +73,9 @@ import { ethErrors } from 'eth-rpc-errors';
import { getLedgerKeyring } from '../../../../../core/Ledger/Ledger';
import ExtendedKeyringTypes from '../../../../../constants/keyringTypes';
import { updateTransaction } from '../../../../../util/transaction-controller';
+import { withMetricsAwareness } from '../../../../../components/hooks/useMetrics';
+import { selectGasFeeEstimates } from '../../../../../selectors/confirmTransaction';
+import { selectGasFeeControllerEstimateType } from '../../../../../selectors/gasFeeController';
const EDIT = 'edit';
const REVIEW = 'review';
@@ -166,6 +168,10 @@ class Approve extends PureComponent {
* Object that represents the navigator
*/
navigation: PropTypes.object,
+ /**
+ * Metrics injected by withMetricsAwareness HOC
+ */
+ metrics: PropTypes.object,
};
state = {
@@ -357,15 +363,14 @@ class Approve extends PureComponent {
};
trackApproveEvent = (event) => {
- const { transaction, tokensLength, accountsLength, providerType } =
+ const { transaction, tokensLength, accountsLength, providerType, metrics } =
this.props;
- InteractionManager.runAfterInteractions(() => {
- Analytics.trackEventWithParameters(event, {
- view: transaction.origin,
- numberOfTokens: tokensLength,
- numberOfAccounts: accountsLength,
- network: providerType,
- });
+
+ metrics.trackEvent(event, {
+ view: transaction.origin,
+ numberOfTokens: tokensLength,
+ numberOfAccounts: accountsLength,
+ network: providerType,
});
};
@@ -466,6 +471,7 @@ class Approve extends PureComponent {
};
onLedgerConfirmation = (approve, transactionId, gaParams) => {
+ const { metrics } = this.props;
const { TransactionController } = Engine.context;
try {
//manual cancel from UI when transaction is awaiting from ledger confirmation
@@ -478,7 +484,7 @@ class Approve extends PureComponent {
TransactionController.cancelTransaction(transactionId);
- AnalyticsV2.trackEvent(MetaMetricsEvents.APPROVAL_CANCELLED, gaParams);
+ metrics.trackEvent(MetaMetricsEvents.APPROVAL_CANCELLED, gaParams);
NotificationManager.showSimpleNotification({
status: `simple_notification_rejected`,
@@ -488,14 +494,14 @@ class Approve extends PureComponent {
});
}
} finally {
- AnalyticsV2.trackEvent(MetaMetricsEvents.APPROVAL_COMPLETED, gaParams);
+ metrics.trackEvent(MetaMetricsEvents.APPROVAL_COMPLETED, gaParams);
}
};
onConfirm = async () => {
const { TransactionController, KeyringController, ApprovalController } =
Engine.context;
- const { transactions, gasEstimateType } = this.props;
+ const { transactions, gasEstimateType, metrics } = this.props;
const {
legacyGasTransaction,
transactionConfirmed,
@@ -564,7 +570,7 @@ class Approve extends PureComponent {
waitForResult: true,
});
- AnalyticsV2.trackEvent(
+ metrics.trackEvent(
MetaMetricsEvents.APPROVAL_COMPLETED,
this.getAnalyticsParams(),
);
@@ -577,9 +583,7 @@ class Approve extends PureComponent {
);
Logger.error(error, 'error while trying to send transaction (Approve)');
} else {
- AnalyticsV2.trackEvent(
- MetaMetricsEvents.QR_HARDWARE_TRANSACTION_CANCELED,
- );
+ metrics.trackEvent(MetaMetricsEvents.QR_HARDWARE_TRANSACTION_CANCELED);
}
this.setState({ transactionHandled: false });
}
@@ -587,6 +591,7 @@ class Approve extends PureComponent {
};
onCancel = () => {
+ const { metrics, hideModal } = this.props;
Engine.rejectPendingApproval(
this.props.transaction.id,
ethErrors.provider.userRejectedRequest(),
@@ -595,11 +600,11 @@ class Approve extends PureComponent {
logErrors: false,
},
);
- AnalyticsV2.trackEvent(
+ metrics.trackEvent(
MetaMetricsEvents.APPROVAL_CANCELLED,
this.getAnalyticsParams(),
);
- this.props.hideModal();
+ hideModal();
NotificationManager.showSimpleNotification({
status: `simple_notification_rejected`,
@@ -614,13 +619,10 @@ class Approve extends PureComponent {
};
onModeChange = (mode) => {
+ const { metrics } = this.props;
this.setState({ mode });
if (mode === EDIT) {
- InteractionManager.runAfterInteractions(() => {
- Analytics.trackEvent(
- MetaMetricsEvents.SEND_FLOW_ADJUSTS_TRANSACTION_FEE,
- );
- });
+ metrics.trackEvent(MetaMetricsEvents.SEND_FLOW_ADJUSTS_TRANSACTION_FEE);
}
};
@@ -893,10 +895,8 @@ const mapStateToProps = (state) => ({
accountsLength: selectAccountsLength(state),
primaryCurrency: state.settings.primaryCurrency,
chainId: selectChainId(state),
- gasFeeEstimates:
- state.engine.backgroundState.GasFeeController.gasFeeEstimates,
- gasEstimateType:
- state.engine.backgroundState.GasFeeController.gasEstimateType,
+ gasFeeEstimates: selectGasFeeEstimates(state),
+ gasEstimateType: selectGasFeeControllerEstimateType(state),
conversionRate: selectConversionRate(state),
currentCurrency: selectCurrentCurrency(state),
nativeCurrency: selectNativeCurrency(state),
@@ -916,4 +916,7 @@ const mapDispatchToProps = (dispatch) => ({
Approve.contextType = ThemeContext;
-export default connect(mapStateToProps, mapDispatchToProps)(Approve);
+export default connect(
+ mapStateToProps,
+ mapDispatchToProps,
+)(withMetricsAwareness(Approve));
diff --git a/app/components/Views/confirmations/Send/index.js b/app/components/Views/confirmations/Send/index.js
index 0aeb712218b..01f584c1496 100644
--- a/app/components/Views/confirmations/Send/index.js
+++ b/app/components/Views/confirmations/Send/index.js
@@ -28,7 +28,6 @@ import {
import { toggleDappTransactionModal } from '../../../../actions/modals';
import NotificationManager from '../../../../core/NotificationManager';
import { showAlert } from '../../../../actions/alert';
-import Analytics from '../../../../core/Analytics/Analytics';
import { MetaMetricsEvents } from '../../../../core/Analytics';
import {
getTransactionReviewActionKey,
@@ -42,7 +41,6 @@ import TransactionTypes from '../../../../core/TransactionTypes';
import { MAINNET } from '../../../../constants/network';
import BigNumber from 'bignumber.js';
import { WalletDevice } from '@metamask/transaction-controller';
-import AnalyticsV2 from '../../../../util/analyticsV2';
import {
addTransaction,
estimateGas,
@@ -63,6 +61,7 @@ import {
selectSelectedAddress,
} from '../../../../selectors/preferencesController';
import { ethErrors } from 'eth-rpc-errors';
+import { withMetricsAwareness } from '../../../../components/hooks/useMetrics';
const REVIEW = 'review';
const EDIT = 'edit';
@@ -151,6 +150,10 @@ class Send extends PureComponent {
* Object that represents the current route info like params passed to it
*/
route: PropTypes.object,
+ /**
+ * Metrics injected by withMetricsAwareness HOC
+ */
+ metrics: PropTypes.object,
};
state = {
@@ -618,7 +621,7 @@ class Send extends PureComponent {
);
Logger.error(error, 'error while trying to send transaction (Send)');
} else {
- AnalyticsV2.trackEvent(
+ this.props.metrics.trackEvent(
MetaMetricsEvents.QR_HARDWARE_TRANSACTION_CANCELED,
);
}
@@ -634,7 +637,7 @@ class Send extends PureComponent {
* Call Analytics to track confirm started event for send screen
*/
trackConfirmScreen = () => {
- Analytics.trackEventWithParameters(
+ this.props.metrics.trackEvent(
MetaMetricsEvents.TRANSACTIONS_CONFIRM_STARTED,
this.getTrackingParams(),
);
@@ -646,7 +649,7 @@ class Send extends PureComponent {
trackEditScreen = async () => {
const { transaction } = this.props;
const actionKey = await getTransactionReviewActionKey(transaction);
- Analytics.trackEventWithParameters(
+ this.props.metrics.trackEvent(
MetaMetricsEvents.TRANSACTIONS_EDIT_TRANSACTION,
{
...this.getTrackingParams(),
@@ -659,7 +662,7 @@ class Send extends PureComponent {
* Call Analytics to track cancel pressed
*/
trackOnCancel = () => {
- Analytics.trackEventWithParameters(
+ this.props.metrics.trackEvent(
MetaMetricsEvents.TRANSACTIONS_CANCEL_TRANSACTION,
this.getTrackingParams(),
);
@@ -669,7 +672,7 @@ class Send extends PureComponent {
* Call Analytics to track confirm pressed
*/
trackOnConfirm = () => {
- Analytics.trackEventWithParameters(
+ this.props.metrics.trackEvent(
MetaMetricsEvents.TRANSACTIONS_COMPLETED_TRANSACTION,
this.getTrackingParams(),
);
@@ -782,4 +785,7 @@ const mapDispatchToProps = (dispatch) => ({
Send.contextType = ThemeContext;
-export default connect(mapStateToProps, mapDispatchToProps)(Send);
+export default connect(
+ mapStateToProps,
+ mapDispatchToProps,
+)(withMetricsAwareness(Send));
diff --git a/app/components/Views/confirmations/SendFlow/Amount/index.js b/app/components/Views/confirmations/SendFlow/Amount/index.js
index e5dfcd27d90..79251004053 100644
--- a/app/components/Views/confirmations/SendFlow/Amount/index.js
+++ b/app/components/Views/confirmations/SendFlow/Amount/index.js
@@ -62,7 +62,6 @@ import collectiblesTransferInformation from '../../../../../util/collectibles-tr
import { strings } from '../../../../../../locales/i18n';
import Device from '../../../../../util/device';
import { BN } from 'ethereumjs-util';
-import Analytics from '../../../../../core/Analytics/Analytics';
import { MetaMetricsEvents } from '../../../../../core/Analytics';
import dismissKeyboard from 'react-native/Libraries/Utilities/dismissKeyboard';
import NetworkMainAssetLogo from '../../../../UI/NetworkMainAssetLogo';
@@ -106,6 +105,9 @@ import { swapsUtils } from '@metamask/swaps-controller';
import { regex } from '../../../../../util/regex';
import { AmountViewSelectorsIDs } from '../../../../../../e2e/selectors/SendFlow/AmountView.selectors';
import { isNetworkRampNativeTokenSupported } from '../../../../../components/UI/Ramp/utils';
+import { withMetricsAwareness } from '../../../../../components/hooks/useMetrics';
+import { selectGasFeeEstimates } from '../../../../../selectors/confirmTransaction';
+import { selectGasFeeControllerEstimateType } from '../../../../../selectors/gasFeeController';
const KEYBOARD_OFFSET = Device.isSmallDevice() ? 80 : 120;
@@ -476,6 +478,18 @@ class Amount extends PureComponent {
* String that indicates the current chain id
*/
chainId: PropTypes.string,
+ /**
+ * Metrics injected by withMetricsAwareness HOC
+ */
+ metrics: PropTypes.object,
+ /**
+ * Gas fee estimates for the transaction.
+ */
+ gasFeeEstimates: PropTypes.object,
+ /**
+ * Type of gas fee estimate provided by the gas fee controller.
+ */
+ gasEstimateType: PropTypes.string,
};
state = {
@@ -516,6 +530,8 @@ class Amount extends PureComponent {
providerType,
selectedAsset,
isPaymentRequest,
+ gasEstimateType,
+ gasFeeEstimates,
} = this.props;
// For analytics
this.updateNavBar();
@@ -530,23 +546,19 @@ class Amount extends PureComponent {
this.onInputChange(readableValue);
!selectedAsset.tokenId && this.handleSelectedAssetBalance(selectedAsset);
- const { GasFeeController } = Engine.context;
- const [gasEstimates, gas] = await Promise.all([
- GasFeeController.fetchGasFeeEstimates({ shouldUpdateState: false }),
- this.estimateGasLimit(),
- ]);
+ const [gas] = await Promise.all([this.estimateGasLimit()]);
- if (gasEstimates.gasEstimateType === GAS_ESTIMATE_TYPES.FEE_MARKET) {
- const gasFeeEstimates =
- gasEstimates.gasFeeEstimates[AppConstants.GAS_OPTIONS.MEDIUM];
+ if (gasEstimateType === GAS_ESTIMATE_TYPES.FEE_MARKET) {
+ const mediumGasFeeEstimates =
+ gasFeeEstimates[AppConstants.GAS_OPTIONS.MEDIUM];
const estimatedBaseFeeHex = decGWEIToHexWEI(
- gasEstimates.gasFeeEstimates.estimatedBaseFee,
+ gasFeeEstimates.estimatedBaseFee,
);
const suggestedMaxPriorityFeePerGasHex = decGWEIToHexWEI(
- gasFeeEstimates.suggestedMaxPriorityFeePerGas,
+ mediumGasFeeEstimates.suggestedMaxPriorityFeePerGas,
);
const suggestedMaxFeePerGasHex = decGWEIToHexWEI(
- gasFeeEstimates.suggestedMaxFeePerGas,
+ mediumGasFeeEstimates.suggestedMaxFeePerGas,
);
const gasLimitHex = BNToHex(gas);
const gasHexes = calculateEIP1559GasFeeHexes({
@@ -558,17 +570,13 @@ class Amount extends PureComponent {
this.setState({
estimatedTotalGas: hexToBN(gasHexes.gasFeeMaxHex),
});
- } else if (gasEstimates.gasEstimateType === GAS_ESTIMATE_TYPES.LEGACY) {
+ } else if (gasEstimateType === GAS_ESTIMATE_TYPES.LEGACY) {
const gasPrice = hexToBN(
- decGWEIToHexWEI(
- gasEstimates.gasFeeEstimates[AppConstants.GAS_OPTIONS.MEDIUM],
- ),
+ decGWEIToHexWEI(gasFeeEstimates[AppConstants.GAS_OPTIONS.MEDIUM]),
);
this.setState({ estimatedTotalGas: gas.mul(gasPrice) });
} else {
- const gasPrice = hexToBN(
- decGWEIToHexWEI(gasEstimates.gasFeeEstimates.gasPrice),
- );
+ const gasPrice = hexToBN(decGWEIToHexWEI(gasFeeEstimates.gasPrice));
this.setState({ estimatedTotalGas: gas.mul(gasPrice) });
}
@@ -678,11 +686,9 @@ class Amount extends PureComponent {
} else {
await this.prepareTransaction(value);
}
- InteractionManager.runAfterInteractions(() => {
- Analytics.trackEventWithParameters(
- MetaMetricsEvents.SEND_FLOW_ADDS_AMOUNT,
- { network: providerType },
- );
+
+ this.props.metrics.trackEvent(MetaMetricsEvents.SEND_FLOW_ADDS_AMOUNT, {
+ network: providerType,
});
setSelectedAsset(selectedAsset);
@@ -1260,13 +1266,13 @@ class Amount extends PureComponent {
const navigateToBuyOrSwaps = () => {
if (isSwappable) {
- Analytics.trackEventWithParameters(MetaMetricsEvents.LINK_CLICKED, {
+ this.props.metrics.trackEvent(MetaMetricsEvents.LINK_CLICKED, {
location: 'insufficient_funds_warning',
text: 'swap_tokens',
});
navigateToSwap();
} else if (isNetworkBuyNativeTokenSupported && selectedAsset.isETH) {
- Analytics.trackEventWithParameters(MetaMetricsEvents.LINK_CLICKED, {
+ this.props.metrics.trackEvent(MetaMetricsEvents.LINK_CLICKED, {
location: 'insufficient_funds_warning',
text: 'buy_more',
});
@@ -1512,6 +1518,8 @@ const mapStateToProps = (state, ownProps) => ({
collectibleContracts: collectibleContractsSelector(state),
conversionRate: selectConversionRate(state),
currentCurrency: selectCurrentCurrency(state),
+ gasEstimateType: selectGasFeeControllerEstimateType(state),
+ gasFeeEstimates: selectGasFeeEstimates(state),
providerType: selectProviderType(state),
primaryCurrency: state.settings.primaryCurrency,
selectedAddress: selectSelectedAddress(state),
@@ -1538,4 +1546,7 @@ const mapDispatchToProps = (dispatch) => ({
resetTransaction: () => dispatch(resetTransaction()),
});
-export default connect(mapStateToProps, mapDispatchToProps)(Amount);
+export default connect(
+ mapStateToProps,
+ mapDispatchToProps,
+)(withMetricsAwareness(Amount));
diff --git a/app/components/Views/confirmations/SendFlow/Confirm/components/CustomGasModal/CustomGasModal.tsx b/app/components/Views/confirmations/SendFlow/Confirm/components/CustomGasModal/CustomGasModal.tsx
index 225683325c2..0527864bcb8 100644
--- a/app/components/Views/confirmations/SendFlow/Confirm/components/CustomGasModal/CustomGasModal.tsx
+++ b/app/components/Views/confirmations/SendFlow/Confirm/components/CustomGasModal/CustomGasModal.tsx
@@ -9,6 +9,7 @@ import EditGasFee1559 from '../../../../components/EditGasFee1559Update';
import EditGasFeeLegacy from '../../../../components/EditGasFeeLegacyUpdate';
import createStyles from './CustomGasModal.styles';
import { CustomGasModalProps } from './CustomGasModal.types';
+import { selectGasFeeEstimates } from '../../../../../../../selectors/confirmTransaction';
const CustomGasModal = ({
gasSelected,
@@ -27,9 +28,8 @@ const CustomGasModal = ({
const { colors } = useAppThemeFromContext();
const styles = createStyles();
const transaction = useSelector((state: any) => state.transaction);
- const gasFeeEstimate = useSelector(
- (state: any) =>
- state.engine.backgroundState.GasFeeController.gasFeeEstimates,
+ const gasFeeEstimate = useSelector((state: any) =>
+ selectGasFeeEstimates(state),
);
const primaryCurrency = useSelector(
(state: any) => state.settings.primaryCurrency,
diff --git a/app/components/Views/confirmations/SendFlow/Confirm/index.js b/app/components/Views/confirmations/SendFlow/Confirm/index.js
index 0d0037f4c0f..cda0b937a53 100644
--- a/app/components/Views/confirmations/SendFlow/Confirm/index.js
+++ b/app/components/Views/confirmations/SendFlow/Confirm/index.js
@@ -36,6 +36,7 @@ import {
resetTransaction,
setNonce,
setProposedNonce,
+ setTransactionId,
} from '../../../../../actions/transaction';
import { getGasLimit } from '../../../../../util/custom-gas';
import Engine from '../../../../../core/Engine';
@@ -48,7 +49,6 @@ import CollectibleMedia from '../../../../UI/CollectibleMedia';
import Modal from 'react-native-modal';
import IonicIcon from 'react-native-vector-icons/Ionicons';
import TransactionTypes from '../../../../../core/TransactionTypes';
-import Analytics from '../../../../../core/Analytics/Analytics';
import { MetaMetricsEvents } from '../../../../../core/Analytics';
import { shallowEqual, renderShortText } from '../../../../../util/general';
import {
@@ -62,7 +62,6 @@ import {
getDecimalChainId,
} from '../../../../../util/networks';
import Text from '../../../../Base/Text';
-import AnalyticsV2 from '../../../../../util/analyticsV2';
import { addHexPrefix } from 'ethereumjs-util';
import { removeFavoriteCollectible } from '../../../../../actions/collectibles';
import { SafeAreaView } from 'react-native-safe-area-context';
@@ -116,6 +115,10 @@ import TransactionBlockaidBanner from '../../components/TransactionBlockaidBanne
import { createLedgerTransactionModalNavDetails } from '../../../../../components/UI/LedgerModals/LedgerTransactionModal';
import CustomGasModal from './components/CustomGasModal';
import { ResultType } from '../../components/BlockaidBanner/BlockaidBanner.types';
+import { withMetricsAwareness } from '../../../../../components/hooks/useMetrics';
+import { selectTransactionGasFeeEstimates } from '../../../../../selectors/confirmTransaction';
+import { selectGasFeeControllerEstimateType } from '../../../../../selectors/gasFeeController';
+import { updateTransaction } from '../../../../../util/transaction-controller';
const EDIT = 'edit';
const EDIT_NONCE = 'edit_nonce';
@@ -229,6 +232,14 @@ class Confirm extends PureComponent {
* Boolean that indicates if the network supports buy
*/
isNativeTokenBuySupported: PropTypes.bool,
+ /**
+ * Metrics injected by withMetricsAwareness HOC
+ */
+ metrics: PropTypes.object,
+ /**
+ * Set transaction ID
+ */
+ setTransactionId: PropTypes.func,
};
state = {
@@ -251,7 +262,6 @@ class Confirm extends PureComponent {
multiLayerL1FeeTotal: '0x0',
result: {},
transactionMeta: {},
- preparedTransaction: {},
};
originIsWalletConnect = this.props.transaction.origin?.startsWith(
@@ -326,10 +336,17 @@ class Confirm extends PureComponent {
contractBalances,
transactionState: { selectedAsset },
} = this.props;
+
+ const { transactionMeta } = this.state;
const { TokensController } = Engine.context;
await stopGasPolling(this.state.pollToken);
clearInterval(intervalIdForEstimatedL1Fee);
+ Engine.rejectPendingApproval(transactionMeta.id, undefined, {
+ ignoreMissing: true,
+ logErrors: false,
+ });
+
/**
* Remove token that was added to the account temporarily
* Ref.: https://github.com/MetaMask/metamask-mobile/pull/3989#issuecomment-1367558394
@@ -375,8 +392,16 @@ class Confirm extends PureComponent {
navigation,
providerType,
isPaymentRequest,
+ setTransactionId,
} = this.props;
+ const {
+ from,
+ transactionTo: to,
+ transactionValue: value,
+ data,
+ } = this.props.transaction;
+
this.updateNavBar();
this.getGasLimit();
@@ -385,7 +410,7 @@ class Confirm extends PureComponent {
pollToken,
});
// For analytics
- AnalyticsV2.trackEvent(
+ this.props.metrics.trackEvent(
MetaMetricsEvents.SEND_TRANSACTION_STARTED,
this.getAnalyticsParams(),
);
@@ -400,9 +425,41 @@ class Confirm extends PureComponent {
POLLING_INTERVAL_ESTIMATED_L1_FEE,
);
}
+ // add transaction
+ const { TransactionController } = Engine.context;
+ const { result, transactionMeta } =
+ await TransactionController.addTransaction(this.props.transaction, {
+ deviceConfirmedOn: WalletDevice.MM_MOBILE,
+ origin: TransactionTypes.MMM,
+ });
+
+ setTransactionId(transactionMeta.id);
+
+ this.setState({ result, transactionMeta });
+
+ if (isBlockaidFeatureEnabled()) {
+ // start validate ppom
+ const id = transactionMeta.id;
+ const reqObject = {
+ id,
+ jsonrpc: '2.0',
+ method: 'eth_sendTransaction',
+ origin: TransactionTypes.MMM,
+ params: [
+ {
+ from,
+ to,
+ value,
+ data,
+ },
+ ],
+ };
+
+ ppomUtil.validateRequest(reqObject, id);
+ }
};
- componentDidUpdate = async (prevProps, prevState) => {
+ componentDidUpdate = (prevProps, prevState) => {
const {
transactionState: {
transactionTo,
@@ -438,7 +495,8 @@ class Confirm extends PureComponent {
if (
this.props.gasFeeEstimates &&
gas &&
- (!shallowEqual(prevProps.gasFeeEstimates, this.props.gasFeeEstimates) ||
+ (!prevProps.gasFeeEstimates ||
+ !shallowEqual(prevProps.gasFeeEstimates, this.props.gasFeeEstimates) ||
gas !== prevProps?.transactionState?.transaction?.gas)
) {
const gasEstimateTypeChanged =
@@ -480,52 +538,6 @@ class Confirm extends PureComponent {
this.parseTransactionDataHeader();
}
}
-
- const { gasEstimationReady, preparedTransaction } = this.state;
-
- // only add transaction if gasEstimationReady and preparedTransaction has gas
- if (gasEstimationReady && !preparedTransaction.gas) {
- const { TransactionController } = Engine.context;
-
- const preparedTransaction = this.prepareTransactionToSend();
-
- // update state only if preparedTransaction has gas
- if (preparedTransaction.gas) {
- const { from, to, value, data } = preparedTransaction;
-
- // eslint-disable-next-line react/no-did-update-set-state
- this.setState({ preparedTransaction }, async () => {
- const { result, transactionMeta } =
- await TransactionController.addTransaction(preparedTransaction, {
- deviceConfirmedOn: WalletDevice.MM_MOBILE,
- origin: TransactionTypes.MMM,
- });
-
- this.setState({ result, transactionMeta });
-
- if (isBlockaidFeatureEnabled()) {
- // start validate ppom
- const id = transactionMeta.id;
- const reqObject = {
- id,
- jsonrpc: '2.0',
- method: 'eth_sendTransaction',
- origin: TransactionTypes.MMM,
- params: [
- {
- from,
- to,
- value,
- data,
- },
- ],
- };
-
- ppomUtil.validateRequest(reqObject, id);
- }
- });
- }
- }
};
setScrollViewRef = (ref) => {
@@ -540,11 +552,9 @@ class Confirm extends PureComponent {
onModeChange = (mode) => {
this.setState({ mode });
if (mode === EDIT) {
- InteractionManager.runAfterInteractions(() => {
- Analytics.trackEvent(
- MetaMetricsEvents.SEND_FLOW_ADJUSTS_TRANSACTION_FEE,
- );
- });
+ this.props.metrics.trackEvent(
+ MetaMetricsEvents.SEND_FLOW_ADJUSTS_TRANSACTION_FEE,
+ );
}
};
@@ -768,7 +778,7 @@ class Confirm extends PureComponent {
assetType,
});
this.checkRemoveCollectible();
- AnalyticsV2.trackEvent(
+ this.props.metrics.trackEvent(
MetaMetricsEvents.SEND_TRANSACTION_COMPLETED,
gaParams,
);
@@ -799,11 +809,7 @@ class Confirm extends PureComponent {
if (transactionConfirmed) return;
this.setState({ transactionConfirmed: true, stopUpdateGas: true });
try {
- const {
- result,
- transactionMeta,
- preparedTransaction: transaction,
- } = this.state;
+ const transaction = this.prepareTransactionToSend();
let error;
if (gasEstimateType === GAS_ESTIMATE_TYPES.FEE_MARKET) {
@@ -823,6 +829,8 @@ class Confirm extends PureComponent {
return;
}
+ const { result, transactionMeta } = this.state;
+
const isLedgerAccount = isHardwareAccount(transaction.from, [
ExtendedKeyringTypes.ledger,
]);
@@ -852,6 +860,7 @@ class Confirm extends PureComponent {
return;
}
+ await this.persistTransactionParameters(transaction);
await KeyringController.resetQRKeyringState();
await ApprovalController.accept(transactionMeta.id, undefined, {
waitForResult: true,
@@ -868,10 +877,13 @@ class Confirm extends PureComponent {
assetType,
});
this.checkRemoveCollectible();
- AnalyticsV2.trackEvent(MetaMetricsEvents.SEND_TRANSACTION_COMPLETED, {
- ...this.getAnalyticsParams(),
- ...this.withBlockaidMetricsParams(),
- });
+ this.props.metrics.trackEvent(
+ MetaMetricsEvents.SEND_TRANSACTION_COMPLETED,
+ {
+ ...this.getAnalyticsParams(),
+ ...this.withBlockaidMetricsParams(),
+ },
+ );
stopGasPolling();
resetTransaction();
navigation && navigation.dangerouslyGetParent()?.pop();
@@ -885,7 +897,7 @@ class Confirm extends PureComponent {
);
Logger.error(error, 'error while trying to send transaction (Confirm)');
} else {
- AnalyticsV2.trackEvent(
+ this.props.metrics.trackEvent(
MetaMetricsEvents.QR_HARDWARE_TRANSACTION_CANCELED,
);
}
@@ -939,7 +951,6 @@ class Confirm extends PureComponent {
updateTransactionStateWithUpdatedNonce = (nonceValue) => {
this.props.setNonce(nonceValue);
- this.setState({ preparedTransaction: {} });
};
renderCustomNonceModal = () => {
@@ -1019,9 +1030,10 @@ class Confirm extends PureComponent {
} catch (error) {
Logger.error(error, 'Navigation: Error when navigating to buy ETH.');
}
- InteractionManager.runAfterInteractions(() => {
- Analytics.trackEvent(MetaMetricsEvents.RECEIVE_OPTIONS_PAYMENT_REQUEST);
- });
+
+ this.props.metrics.trackEvent(
+ MetaMetricsEvents.RECEIVE_OPTIONS_PAYMENT_REQUEST,
+ );
};
goToFaucet = () => {
@@ -1085,7 +1097,7 @@ class Confirm extends PureComponent {
...this.withBlockaidMetricsParams(),
external_link_clicked: 'security_alert_support_link',
};
- AnalyticsV2.trackEvent(
+ this.props.metrics.trackEvent(
MetaMetricsEvents.CONTRACT_ADDRESS_COPIED,
analyticsParams,
);
@@ -1119,6 +1131,21 @@ class Confirm extends PureComponent {
return confirmButtonStyle;
}
+ async persistTransactionParameters(transactionParams) {
+ const { TransactionController } = Engine.context;
+ const { transactionMeta } = this.state;
+ const { id: transactionId } = transactionMeta;
+
+ const controllerTransactionMeta =
+ TransactionController.state.transactions.find(
+ (tx) => tx.id === transactionId,
+ );
+
+ controllerTransactionMeta.transaction = transactionParams;
+
+ await updateTransaction(controllerTransactionMeta);
+ }
+
render = () => {
const { selectedAsset, paymentRequest } = this.props.transactionState;
const {
@@ -1353,10 +1380,8 @@ const mapStateToProps = (state) => ({
selectedAsset: state.transaction.selectedAsset,
transactionState: state.transaction,
primaryCurrency: state.settings.primaryCurrency,
- gasFeeEstimates:
- state.engine.backgroundState.GasFeeController.gasFeeEstimates,
- gasEstimateType:
- state.engine.backgroundState.GasFeeController.gasEstimateType,
+ gasFeeEstimates: selectTransactionGasFeeEstimates(state),
+ gasEstimateType: selectGasFeeControllerEstimateType(state),
isPaymentRequest: state.transaction.paymentRequest,
securityAlertResponse:
state.transaction.currentTransactionSecurityAlertResponse,
@@ -1370,6 +1395,8 @@ const mapDispatchToProps = (dispatch) => ({
prepareTransaction: (transaction) =>
dispatch(prepareTransaction(transaction)),
resetTransaction: () => dispatch(resetTransaction()),
+ setTransactionId: (transactionId) =>
+ dispatch(setTransactionId(transactionId)),
setNonce: (nonce) => dispatch(setNonce(nonce)),
setProposedNonce: (nonce) => dispatch(setProposedNonce(nonce)),
removeFavoriteCollectible: (selectedAddress, chainId, collectible) =>
@@ -1377,4 +1404,7 @@ const mapDispatchToProps = (dispatch) => ({
showAlert: (config) => dispatch(showAlert(config)),
});
-export default connect(mapStateToProps, mapDispatchToProps)(Confirm);
+export default connect(
+ mapStateToProps,
+ mapDispatchToProps,
+)(withMetricsAwareness(Confirm));
diff --git a/app/components/Views/confirmations/SendFlow/Confirm/index.test.tsx b/app/components/Views/confirmations/SendFlow/Confirm/index.test.tsx
index 63fba6cbb5b..0607097b9ff 100644
--- a/app/components/Views/confirmations/SendFlow/Confirm/index.test.tsx
+++ b/app/components/Views/confirmations/SendFlow/Confirm/index.test.tsx
@@ -5,7 +5,6 @@ import Confirm from '.';
import { renderScreen } from '../../../../../util/test/renderWithProvider';
import Routes from '../../../../../constants/navigation/Routes';
import initialBackgroundState from '../../../../../util/test/initial-background-state.json';
-import analyticsV2 from '../../../../../util/analyticsV2';
import { TESTID_ACCORDION_CONTENT } from '../../../../../component-library/components/Accordions/Accordion/Accordion.constants';
import { FALSE_POSITIVE_REPOST_LINE_TEST_ID } from '../../components/BlockaidBanner/BlockaidBanner.constants';
@@ -97,6 +96,7 @@ jest.mock('../../../../../lib/ppom/ppom-util', () => ({
}));
jest.mock('../../../../../core/Engine', () => ({
+ rejectPendingApproval: jest.fn(),
context: {
TokensController: {
addToken: jest.fn(),
@@ -151,13 +151,6 @@ describe('Confirm', () => {
});
it('displays blockaid banner', async () => {
- const trackEventSypy = jest
- .spyOn(analyticsV2, 'trackEvent')
- .mockImplementation((name, params) => {
- expect(name).toBeDefined();
- expect(params).toBeDefined();
- });
-
const { queryByText, queryByTestId } = render(Confirm);
await waitFor(async () => {
@@ -172,8 +165,6 @@ describe('Confirm', () => {
await queryByTestId(FALSE_POSITIVE_REPOST_LINE_TEST_ID),
).toBeDefined();
expect(await queryByText('Something doesn’t look right?')).toBeDefined();
-
- expect(trackEventSypy).toHaveBeenCalledTimes(1);
});
});
});
diff --git a/app/components/Views/confirmations/SendFlow/SendTo/index.js b/app/components/Views/confirmations/SendFlow/SendTo/index.js
index ba972537fbf..f6155efeae0 100644
--- a/app/components/Views/confirmations/SendFlow/SendTo/index.js
+++ b/app/components/Views/confirmations/SendFlow/SendTo/index.js
@@ -1,25 +1,16 @@
import React, { Fragment, PureComponent } from 'react';
-import {
- View,
- InteractionManager,
- ScrollView,
- Alert,
- Platform,
- BackHandler,
-} from 'react-native';
+import { View, ScrollView, Alert, Platform, BackHandler } from 'react-native';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { toChecksumAddress } from 'ethereumjs-util';
import { SafeAreaView } from 'react-native-safe-area-context';
import Icon from 'react-native-vector-icons/FontAwesome';
-import Analytics from '../../../../../core/Analytics/Analytics';
import AddressList from '../AddressList';
import Text from '../../../../Base/Text';
import WarningMessage from '../WarningMessage';
import { getSendFlowTitle } from '../../../../UI/Navbar';
import StyledButton from '../../../../UI/StyledButton';
import { MetaMetricsEvents } from '../../../../../core/Analytics';
-import AnalyticsV2 from '../../../../../util/analyticsV2';
import {
getDecimalChainId,
handleNetworkSwitch,
@@ -72,6 +63,7 @@ import SendFlowAddressFrom from '../AddressFrom';
import SendFlowAddressTo from '../AddressTo';
import { includes } from 'lodash';
import { SendViewSelectorsIDs } from '../../../../../../e2e/selectors/SendView.selectors';
+import { withMetricsAwareness } from '../../../../../components/hooks/useMetrics';
const dummy = () => true;
@@ -149,6 +141,10 @@ class SendFlow extends PureComponent {
* Object of addresses associated with multiple chains {'id': [address: string]}
*/
ambiguousAddressEntries: PropTypes.object,
+ /**
+ * Metrics injected by withMetricsAwareness HOC
+ */
+ metrics: PropTypes.object,
};
addressToInputRef = React.createRef();
@@ -300,14 +296,10 @@ class SendFlow extends PureComponent {
toEnsName,
toSelectedAddressName,
);
- InteractionManager.runAfterInteractions(() => {
- Analytics.trackEventWithParameters(
- MetaMetricsEvents.SEND_FLOW_ADDS_RECIPIENT,
- {
- network: providerType,
- },
- );
+ this.props.metrics.trackEvent(MetaMetricsEvents.SEND_FLOW_ADDS_RECIPIENT, {
+ network: providerType,
});
+
navigation.navigate('Amount');
};
@@ -318,12 +310,11 @@ class SendFlow extends PureComponent {
goToBuy = () => {
this.props.navigation.navigate(Routes.RAMP.BUY);
- InteractionManager.runAfterInteractions(() => {
- AnalyticsV2.trackEvent(MetaMetricsEvents.BUY_BUTTON_CLICKED, {
- button_location: 'Send Flow warning',
- button_copy: 'Buy Native Token',
- chain_id_destination: this.props.chainId,
- });
+
+ this.props.metrics.trackEvent(MetaMetricsEvents.BUY_BUTTON_CLICKED, {
+ button_location: 'Send Flow warning',
+ button_copy: 'Buy Native Token',
+ chain_id_destination: this.props.chainId,
});
};
@@ -423,7 +414,7 @@ class SendFlow extends PureComponent {
const isAmbiguousAddress = includes(currentChain, toAccount);
if (isAmbiguousAddress) {
this.setState({ showAmbiguousAcountWarning: isAmbiguousAddress });
- AnalyticsV2.trackEvent(
+ this.props.metrics.trackEvent(
MetaMetricsEvents.SEND_FLOW_SELECT_DUPLICATE_ADDRESS,
{
chain_id: getDecimalChainId(this.props.chainId),
@@ -712,4 +703,7 @@ const mapDispatchToProps = (dispatch) => ({
resetTransaction: () => dispatch(resetTransaction()),
});
-export default connect(mapStateToProps, mapDispatchToProps)(SendFlow);
+export default connect(
+ mapStateToProps,
+ mapDispatchToProps,
+)(withMetricsAwareness(SendFlow));
diff --git a/app/components/Views/confirmations/components/ApproveTransactionReview/AddNickname/index.tsx b/app/components/Views/confirmations/components/ApproveTransactionReview/AddNickname/index.tsx
index 39fbb85f0fb..833af76b957 100644
--- a/app/components/Views/confirmations/components/ApproveTransactionReview/AddNickname/index.tsx
+++ b/app/components/Views/confirmations/components/ApproveTransactionReview/AddNickname/index.tsx
@@ -4,7 +4,6 @@ import AntDesignIcon from 'react-native-vector-icons/AntDesign';
import EthereumAddress from '../../../../../UI/EthereumAddress';
import Engine from '../../../../../../core/Engine';
import { MetaMetricsEvents } from '../../../../../../core/Analytics';
-import AnalyticsV2 from '../../../../../../util/analyticsV2';
import { toChecksumAddress } from 'ethereumjs-util';
import { connect } from 'react-redux';
@@ -39,6 +38,7 @@ import {
} from '../../../../../../selectors/networkController';
import { selectIdentities } from '../../../../../../selectors/preferencesController';
import { ContractNickNameViewSelectorsIDs } from '../../../../../../../e2e/selectors/ContractNickNameView.selectors';
+import { useMetrics } from '../../../../../../components/hooks/useMetrics';
const getAnalyticsParams = () => ({});
@@ -64,6 +64,7 @@ const AddNickname = (props: AddNicknameProps) => {
const [showFullAddress, setShowFullAddress] = useState(false);
const [shouldDisableButton, setShouldDisableButton] = useState(true);
const { colors, themeAppearance } = useTheme();
+ const { trackEvent } = useMetrics();
const styles = createStyles(colors);
const chooseToContinue = () => {
@@ -109,10 +110,7 @@ const AddNickname = (props: AddNicknameProps) => {
data: { msg: strings('transactions.address_copied_to_clipboard') },
});
- AnalyticsV2.trackEvent(
- MetaMetricsEvents.CONTRACT_ADDRESS_COPIED,
- getAnalyticsParams(),
- );
+ trackEvent(MetaMetricsEvents.CONTRACT_ADDRESS_COPIED, getAnalyticsParams());
};
const saveTokenNickname = () => {
@@ -124,7 +122,7 @@ const AddNickname = (props: AddNicknameProps) => {
providerChainId,
);
closeModal();
- AnalyticsV2.trackEvent(
+ trackEvent(
MetaMetricsEvents.CONTRACT_ADDRESS_NICKNAME,
getAnalyticsParams(),
);
diff --git a/app/components/Views/confirmations/components/ApproveTransactionReview/index.js b/app/components/Views/confirmations/components/ApproveTransactionReview/index.js
index 967a06da650..0609d11c210 100644
--- a/app/components/Views/confirmations/components/ApproveTransactionReview/index.js
+++ b/app/components/Views/confirmations/components/ApproveTransactionReview/index.js
@@ -45,9 +45,7 @@ import Avatar, {
import Identicon from '../../../../UI/Identicon';
import TransactionTypes from '../../../../../core/TransactionTypes';
import { showAlert } from '../../../../../actions/alert';
-import Analytics from '../../../../../core/Analytics/Analytics';
import { MetaMetricsEvents } from '../../../../../core/Analytics';
-import AnalyticsV2 from '../../../../../util/analyticsV2';
import TransactionHeader from '../../../../UI/TransactionHeader';
import TransactionReviewDetailsCard from '../TransactionReview/TransactionReviewDetailsCard';
import AppConstants from '../../../../../core/AppConstants';
@@ -98,6 +96,7 @@ import InfoModal from '../../../../UI/Swaps/components/InfoModal';
import { ResultType } from '../BlockaidBanner/BlockaidBanner.types';
import TransactionBlockaidBanner from '../TransactionBlockaidBanner/TransactionBlockaidBanner';
import { regex } from '../../../../../util/regex';
+import { withMetricsAwareness } from '../../../../../components/hooks/useMetrics';
const { ORIGIN_DEEPLINK, ORIGIN_QR_CODE } = AppConstants.DEEPLINKS;
const POLLING_INTERVAL_ESTIMATED_L1_FEE = 30000;
@@ -272,6 +271,10 @@ class ApproveTransactionReview extends PureComponent {
* Boolean that indicates gas estimated value is confirmed before approving
*/
isGasEstimateStatusIn: PropTypes.bool,
+ /**
+ * Metrics injected by withMetricsAwareness HOC
+ */
+ metrics: PropTypes.object,
};
state = {
@@ -462,7 +465,7 @@ class ApproveTransactionReview extends PureComponent {
spendLimitCustomValue: minTokenAllowance,
},
() => {
- AnalyticsV2.trackEvent(
+ this.props.metrics.trackEvent(
MetaMetricsEvents.APPROVAL_STARTED,
this.getAnalyticsParams(),
);
@@ -563,13 +566,12 @@ class ApproveTransactionReview extends PureComponent {
trackApproveEvent = (event) => {
const { transaction, tokensLength, accountsLength, providerType } =
this.props;
- InteractionManager.runAfterInteractions(() => {
- Analytics.trackEventWithParameters(event, {
- view: transaction.origin,
- numberOfTokens: tokensLength,
- numberOfAccounts: accountsLength,
- network: providerType,
- });
+
+ this.props.metrics.trackEvent(event, {
+ view: transaction.origin,
+ numberOfTokens: tokensLength,
+ numberOfAccounts: accountsLength,
+ network: providerType,
});
};
@@ -580,7 +582,9 @@ class ApproveTransactionReview extends PureComponent {
toggleViewDetails = () => {
const { viewDetails } = this.state;
- Analytics.trackEvent(MetaMetricsEvents.DAPP_APPROVE_SCREEN_VIEW_DETAILS);
+ this.props.metrics.trackEvent(
+ MetaMetricsEvents.DAPP_APPROVE_SCREEN_VIEW_DETAILS,
+ );
this.setState({ viewDetails: !viewDetails });
};
@@ -592,7 +596,7 @@ class ApproveTransactionReview extends PureComponent {
content: 'clipboard-alert',
data: { msg: strings('transactions.address_copied_to_clipboard') },
});
- AnalyticsV2.trackEvent(
+ this.props.metrics.trackEvent(
MetaMetricsEvents.CONTRACT_ADDRESS_COPIED,
this.getAnalyticsParams(),
);
@@ -611,7 +615,9 @@ class ApproveTransactionReview extends PureComponent {
tokenSpendValue,
originalApproveAmount,
} = this.state;
- Analytics.trackEvent(MetaMetricsEvents.TRANSACTIONS_EDIT_TRANSACTION);
+ this.props.metrics.trackEvent(
+ MetaMetricsEvents.TRANSACTIONS_EDIT_TRANSACTION,
+ );
updateTokenAllowanceState({
tokenStandard,
@@ -696,7 +702,7 @@ class ApproveTransactionReview extends PureComponent {
...this.withBlockaidMetricsParams(),
external_link_clicked: 'security_alert_support_link',
};
- AnalyticsV2.trackEvent(
+ this.props.metrics.trackEvent(
MetaMetricsEvents.CONTRACT_ADDRESS_COPIED,
analyticsParams,
);
@@ -1164,18 +1170,22 @@ class ApproveTransactionReview extends PureComponent {
} catch (error) {
Logger.error(error, 'Navigation: Error when navigating to buy ETH.');
}
- InteractionManager.runAfterInteractions(() => {
- Analytics.trackEvent(MetaMetricsEvents.RECEIVE_OPTIONS_PAYMENT_REQUEST);
- });
+
+ this.props.metrics.trackEvent(
+ MetaMetricsEvents.RECEIVE_OPTIONS_PAYMENT_REQUEST,
+ );
};
onCancelPress = () => {
const { onCancel } = this.props;
onCancel && onCancel();
- AnalyticsV2.trackEvent(MetaMetricsEvents.APPROVAL_PERMISSION_UPDATED, {
- ...this.getAnalyticsParams(),
- ...this.withBlockaidMetricsParams(),
- });
+ this.props.metrics.trackEvent(
+ MetaMetricsEvents.APPROVAL_PERMISSION_UPDATED,
+ {
+ ...this.getAnalyticsParams(),
+ ...this.withBlockaidMetricsParams(),
+ },
+ );
};
onConfirmPress = () => {
@@ -1186,10 +1196,13 @@ class ApproveTransactionReview extends PureComponent {
const { onConfirm } = this.props;
if (tokenStandard === ERC20 && !isReadyToApprove) {
- AnalyticsV2.trackEvent(MetaMetricsEvents.APPROVAL_PERMISSION_UPDATED, {
- ...this.getAnalyticsParams(),
- ...this.withBlockaidMetricsParams(),
- });
+ this.props.metrics.trackEvent(
+ MetaMetricsEvents.APPROVAL_PERMISSION_UPDATED,
+ {
+ ...this.getAnalyticsParams(),
+ ...this.withBlockaidMetricsParams(),
+ },
+ );
return this.setState({ isReadyToApprove: true });
}
@@ -1288,4 +1301,8 @@ ApproveTransactionReview.contextType = ThemeContext;
export default connect(
mapStateToProps,
mapDispatchToProps,
-)(withNavigation(withQRHardwareAwareness(ApproveTransactionReview)));
+)(
+ withNavigation(
+ withQRHardwareAwareness(withMetricsAwareness(ApproveTransactionReview)),
+ ),
+);
diff --git a/app/components/Views/confirmations/components/EditGasFee1559Update/index.tsx b/app/components/Views/confirmations/components/EditGasFee1559Update/index.tsx
index ef4ee040a38..537f7fb9961 100644
--- a/app/components/Views/confirmations/components/EditGasFee1559Update/index.tsx
+++ b/app/components/Views/confirmations/components/EditGasFee1559Update/index.tsx
@@ -22,7 +22,6 @@ import {
import BigNumber from 'bignumber.js';
import FadeAnimationView from '../../../../UI/FadeAnimationView';
import { MetaMetricsEvents } from '../../../../../core/Analytics';
-import AnalyticsV2 from '../../../../../util/analyticsV2';
import TimeEstimateInfoModal from '../../../../UI/TimeEstimateInfoModal';
import useModalHandler from '../../../../Base/hooks/useModalHandler';
@@ -42,6 +41,7 @@ import {
GAS_LIMIT_MIN,
GAS_PRICE_MIN as GAS_MIN,
} from '../../../../../util/gasUtils';
+import { useMetrics } from '../../../../../components/hooks/useMetrics';
const EditGasFee1559Update = ({
selectedGasValue,
@@ -91,6 +91,7 @@ const EditGasFee1559Update = ({
hideTimeEstimateInfoModal,
] = useModalHandler(false);
const { colors } = useAppThemeFromContext() || mockTheme;
+ const { trackEvent } = useMetrics();
const styles = createStyles(colors);
const gasTransaction = useGasTransaction({
@@ -133,13 +134,13 @@ const EditGasFee1559Update = ({
const toggleAdvancedOptions = useCallback(() => {
if (!showAdvancedOptions) {
- AnalyticsV2.trackEvent(
+ trackEvent(
MetaMetricsEvents.GAS_ADVANCED_OPTIONS_CLICKED,
getAnalyticsParams(),
);
}
setShowAdvancedOptions(!showAdvancedOptions);
- }, [getAnalyticsParams, showAdvancedOptions]);
+ }, [getAnalyticsParams, showAdvancedOptions, trackEvent]);
const toggleLearnMoreModal = useCallback(() => {
setShowLearnMoreModal(!showLearnMoreModal);
@@ -153,10 +154,7 @@ const EditGasFee1559Update = ({
);
const save = useCallback(() => {
- AnalyticsV2.trackEvent(
- MetaMetricsEvents.GAS_FEE_CHANGED,
- getAnalyticsParams(),
- );
+ trackEvent(MetaMetricsEvents.GAS_FEE_CHANGED, getAnalyticsParams());
const newGasPriceObject = {
suggestedMaxFeePerGas: gasObject?.suggestedMaxFeePerGas,
@@ -165,7 +163,7 @@ const EditGasFee1559Update = ({
};
onSave(gasTransaction, newGasPriceObject);
- }, [getAnalyticsParams, onSave, gasTransaction, gasObject]);
+ }, [getAnalyticsParams, onSave, gasTransaction, gasObject, trackEvent]);
const changeGas = useCallback(
(gas, option) => {
diff --git a/app/components/Views/confirmations/components/EditGasFeeLegacyUpdate/index.tsx b/app/components/Views/confirmations/components/EditGasFeeLegacyUpdate/index.tsx
index c4a3757c57b..901d351ab33 100644
--- a/app/components/Views/confirmations/components/EditGasFeeLegacyUpdate/index.tsx
+++ b/app/components/Views/confirmations/components/EditGasFeeLegacyUpdate/index.tsx
@@ -21,7 +21,6 @@ import Text, {
import { MetaMetricsEvents } from '../../../../../core/Analytics';
import { useGasTransaction } from '../../../../../core/GasPolling/GasPolling';
import { selectChainId } from '../../../../../selectors/networkController';
-import AnalyticsV2 from '../../../../../util/analyticsV2';
import {
getDecimalChainId,
isMainnetByChainId,
@@ -40,6 +39,8 @@ import {
GAS_LIMIT_MIN,
GAS_PRICE_MIN,
} from '../../../../../util/gasUtils';
+import { useMetrics } from '../../../../../components/hooks/useMetrics';
+import { selectGasFeeEstimates } from '../../../../../selectors/confirmTransaction';
const EditGasFeeLegacy = ({
onCancel,
@@ -56,6 +57,7 @@ const EditGasFeeLegacy = ({
selectedGasObject,
hasDappSuggestedGas,
}: EditGasFeeLegacyUpdateProps) => {
+ const { trackEvent } = useMetrics();
const [showRangeInfoModal, setShowRangeInfoModal] = useState(false);
const [infoText, setInfoText] = useState('');
const [gasPriceError, setGasPriceError] = useState('');
@@ -73,11 +75,7 @@ const EditGasFeeLegacy = ({
const { colors } = useTheme();
const styles = createStyles(colors);
-
- const gasFeeEstimate = useSelector(
- (state: any) =>
- state.engine.backgroundState.GasFeeController.gasFeeEstimates,
- );
+ const gasFeeEstimate = useSelector(selectGasFeeEstimates);
const primaryCurrency = useSelector(
(state: any) => state.settings.primaryCurrency,
@@ -97,7 +95,7 @@ const EditGasFeeLegacy = ({
});
const save = useCallback(() => {
- AnalyticsV2.trackEvent(MetaMetricsEvents.GAS_FEE_CHANGED, {
+ trackEvent(MetaMetricsEvents.GAS_FEE_CHANGED, {
...analyticsParams,
chain_id: getDecimalChainId(chainId),
function_type: view,
@@ -109,7 +107,15 @@ const EditGasFeeLegacy = ({
legacyGasLimit: gasObjectLegacy?.legacyGasLimit,
};
onSave(gasTransaction, newGasPriceObject);
- }, [onSave, gasTransaction, gasObjectLegacy, analyticsParams, chainId, view]);
+ }, [
+ onSave,
+ gasTransaction,
+ gasObjectLegacy,
+ analyticsParams,
+ chainId,
+ view,
+ trackEvent,
+ ]);
const changeGas = useCallback((gas) => {
updateGasObjectLegacy({
diff --git a/app/components/Views/confirmations/components/MessageSign/MessageSign.tsx b/app/components/Views/confirmations/components/MessageSign/MessageSign.tsx
index 2a3ac444079..19156226ebf 100644
--- a/app/components/Views/confirmations/components/MessageSign/MessageSign.tsx
+++ b/app/components/Views/confirmations/components/MessageSign/MessageSign.tsx
@@ -6,7 +6,6 @@ import SignatureRequest from '../SignatureRequest';
import ExpandedMessage from '../SignatureRequest/ExpandedMessage';
import { KEYSTONE_TX_CANCELED } from '../../../../../constants/error';
import { MetaMetricsEvents } from '../../../../../core/Analytics';
-import AnalyticsV2 from '../../../../../util/analyticsV2';
import { useTheme } from '../../../../../util/theme';
import {
getAnalyticsParams,
@@ -19,6 +18,7 @@ import createExternalSignModelNav from '../../../../../util/hardwareWallet/signa
import { SigningModalSelectorsIDs } from '../../../../../../e2e/selectors/Modals/SigningModal.selectors';
import { useNavigation } from '@react-navigation/native';
import Engine from '../../../../../core/Engine';
+import { useMetrics } from '../../../../../components/hooks/useMetrics';
interface MessageSignProps {
/**
@@ -75,6 +75,7 @@ const MessageSign = ({
showExpandedMessage,
}: MessageSignProps) => {
const navigation = useNavigation();
+ const { trackEvent } = useMetrics();
const [truncateMessage, setTruncateMessage] = useState(false);
const { securityAlertResponse } = useSelector(
(reduxState: any) => reduxState.signatureRequest,
@@ -84,14 +85,14 @@ const MessageSign = ({
const styles = createStyles(colors);
useEffect(() => {
- AnalyticsV2.trackEvent(
+ trackEvent(
MetaMetricsEvents.SIGNATURE_REQUESTED,
getAnalyticsParams(messageParams, 'eth_sign'),
);
const onSignatureError = ({ error }: any) => {
if (error?.message.startsWith(KEYSTONE_TX_CANCELED)) {
- AnalyticsV2.trackEvent(
+ trackEvent(
MetaMetricsEvents.QR_HARDWARE_TRANSACTION_CANCELED,
getAnalyticsParams(messageParams, 'eth_sign'),
);
@@ -108,7 +109,7 @@ const MessageSign = ({
onSignatureError,
);
};
- }, [messageParams]);
+ }, [messageParams, trackEvent]);
const shouldTruncateMessage = (e: any) => {
if (e.nativeEvent.lines.length > 5) {
diff --git a/app/components/Views/confirmations/components/MessageSign/index.test.tsx b/app/components/Views/confirmations/components/MessageSign/index.test.tsx
index 208ba1ece0a..14c23daa1f8 100644
--- a/app/components/Views/confirmations/components/MessageSign/index.test.tsx
+++ b/app/components/Views/confirmations/components/MessageSign/index.test.tsx
@@ -7,17 +7,17 @@ import { InteractionManager } from 'react-native';
import AppConstants from '../../../../../core/AppConstants';
import { strings } from '../../../../../../locales/i18n';
import initialBackgroundState from '../../../../../util/test/initial-background-state.json';
-import analyticsV2 from '../../../../../util/analyticsV2';
import renderWithProvider from '../../../../../util/test/renderWithProvider';
import { act, waitFor } from '@testing-library/react-native';
-import { KEYSTONE_TX_CANCELED } from '../../../../../constants/error';
-import { MetaMetricsEvents } from '../../../../../core/Analytics';
// eslint-disable-next-line import/no-namespace
import * as addressUtils from '../../../../../util/address';
import createExternalSignModelNav from '../../../../../util/hardwareWallet/signatureUtils';
+import { useMetrics } from '../../../../../components/hooks/useMetrics';
const fakeAddress = '0xE413f7dB07f9B93936189867588B1440D823e651';
+jest.mock('../../../../../components/hooks/useMetrics');
+
jest.mock('../../../../../core/Engine', () => ({
acceptPendingApproval: jest.fn(),
rejectPendingApproval: jest.fn(),
@@ -36,14 +36,10 @@ jest.mock('../../../../../core/Engine', () => ({
},
}));
-const EngineMock = Engine as jest.Mocked;
-
jest.mock('../../../../../core/NotificationManager', () => ({
showSimpleNotification: jest.fn(),
}));
-jest.mock('../../../../../util/analyticsV2');
-
jest.mock('../../../../../util/address', () => ({
...jest.requireActual('../../../../../util/address'),
isExternalHardwareAccount: jest.fn(),
@@ -129,6 +125,11 @@ function createContainer({
);
}
+const mockTrackEvent = jest.fn();
+(useMetrics as jest.Mock).mockReturnValue({
+ trackEvent: mockTrackEvent,
+});
+
describe('MessageSign', () => {
beforeEach(() => {
jest.clearAllMocks();
@@ -145,7 +146,6 @@ describe('MessageSign', () => {
'TestMessageId:signError',
expect.any(Function),
);
- expect(analyticsV2.trackEvent).toHaveBeenCalledTimes(1);
expect(
Engine.context.SignatureController.hub.removeListener,
).toHaveBeenCalledTimes(0);
@@ -243,46 +243,6 @@ describe('MessageSign', () => {
});
});
- describe('trackEvent', () => {
- it('tracks event for rejected requests', async () => {
- const container = createContainer();
- await container.getByTestId('SignatureRequest').props.onReject();
-
- expect((analyticsV2.trackEvent as jest.Mock).mock.calls[1][0]).toEqual({
- category: 'Signature Rejected',
- });
- expect((analyticsV2.trackEvent as jest.Mock).mock.calls[1][1]).toEqual({
- account_type: 'MetaMask',
- dapp_host_name: undefined,
- chain_id: undefined,
- signature_type: 'eth_sign',
- security_alert_response: 'Benign',
- security_alert_reason: '',
- ppom_eth_chainId_count: 1,
- version: undefined,
- });
- });
-
- it('tracks event for approved requests', async () => {
- const container = createContainer();
- await container.getByTestId('SignatureRequest').props.onConfirm();
-
- expect((analyticsV2.trackEvent as jest.Mock).mock.calls[1][0]).toEqual({
- category: 'Signature Approved',
- });
- expect((analyticsV2.trackEvent as jest.Mock).mock.calls[1][1]).toEqual({
- account_type: 'MetaMask',
- dapp_host_name: undefined,
- chain_id: undefined,
- signature_type: 'eth_sign',
- security_alert_response: 'Benign',
- security_alert_reason: '',
- ppom_eth_chainId_count: 1,
- version: undefined,
- });
- });
- });
-
describe('shouldTruncateMessage', () => {
it('sets truncateMessage to true if message is more then 5 characters', async () => {
const container = createContainer();
@@ -333,7 +293,6 @@ describe('MessageSign', () => {
'TestMessageId:signError',
expect.any(Function),
);
- expect(analyticsV2.trackEvent).toHaveBeenCalledTimes(1);
expect(
Engine.context.SignatureController.hub.removeListener,
).toHaveBeenCalledTimes(0);
@@ -361,30 +320,4 @@ describe('MessageSign', () => {
).toHaveBeenCalledWith('TestMessageId:signError', expect.any(Function));
});
});
-
- describe('onSignatureError', () => {
- let events: any;
- beforeEach(() => {
- events = {};
-
- EngineMock.context.SignatureController.hub.on.mockImplementationOnce(
- (event: any, callback: any) => {
- events[event] = callback;
- },
- );
- });
-
- it('track has been called when error message starts with KeystoneError#Tx_canceled', async () => {
- createContainer();
- events['TestMessageId:signError']({
- error: new Error(KEYSTONE_TX_CANCELED),
- });
- await waitFor(() => {
- expect(analyticsV2.trackEvent).toHaveBeenCalledTimes(2);
- expect((analyticsV2.trackEvent as jest.Mock).mock.calls[1][0]).toEqual(
- MetaMetricsEvents.QR_HARDWARE_TRANSACTION_CANCELED,
- );
- });
- });
- });
});
diff --git a/app/components/Views/confirmations/components/PersonalSign/PersonalSign.tsx b/app/components/Views/confirmations/components/PersonalSign/PersonalSign.tsx
index c0986c88f71..8a516e84aef 100644
--- a/app/components/Views/confirmations/components/PersonalSign/PersonalSign.tsx
+++ b/app/components/Views/confirmations/components/PersonalSign/PersonalSign.tsx
@@ -8,7 +8,6 @@ import NotificationManager from '../../../../../core/NotificationManager';
import { strings } from '../../../../../../locales/i18n';
import { WALLET_CONNECT_ORIGIN } from '../../../../../util/walletconnect';
import { MetaMetricsEvents } from '../../../../../core/Analytics';
-import AnalyticsV2 from '../../../../../util/analyticsV2';
import {
getAddressAccountType,
isExternalHardwareAccount,
@@ -30,6 +29,7 @@ import { SecurityAlertResponse } from '../BlockaidBanner/BlockaidBanner.types';
import { SigningModalSelectorsIDs } from '../../../../../../e2e/selectors/Modals/SigningModal.selectors';
import { getDecimalChainId } from '../../../../../util/networks';
import Logger from '../../../../../util/Logger';
+import { useMetrics } from '../../../../../components/hooks/useMetrics';
/**
* Converts a hexadecimal string to a utf8 string.
@@ -61,6 +61,7 @@ const PersonalSign = ({
showExpandedMessage,
}: PersonalSignProps) => {
const navigation = useNavigation();
+ const { trackEvent } = useMetrics();
const [truncateMessage, setTruncateMessage] = useState(false);
const { securityAlertResponse } = useSelector(
(reduxState: any) => reduxState.signatureRequest,
@@ -107,7 +108,7 @@ const PersonalSign = ({
useEffect(() => {
const onSignatureError = ({ error }: { error: Error }) => {
if (error?.message.startsWith(KEYSTONE_TX_CANCELED)) {
- AnalyticsV2.trackEvent(
+ trackEvent(
MetaMetricsEvents.QR_HARDWARE_TRANSACTION_CANCELED,
getAnalyticsParams(),
);
@@ -123,7 +124,7 @@ const PersonalSign = ({
onSignatureError,
);
};
- }, [getAnalyticsParams, messageParams.metamaskId]);
+ }, [getAnalyticsParams, messageParams.metamaskId, trackEvent]);
const showWalletConnectNotification = (confirmation = false) => {
InteractionManager.runAfterInteractions(() => {
@@ -146,20 +147,14 @@ const PersonalSign = ({
const rejectSignature = async () => {
await onReject();
showWalletConnectNotification(false);
- AnalyticsV2.trackEvent(
- MetaMetricsEvents.SIGNATURE_REJECTED,
- getAnalyticsParams(),
- );
+ trackEvent(MetaMetricsEvents.SIGNATURE_REJECTED, getAnalyticsParams());
};
const confirmSignature = async () => {
if (!isExternalHardwareAccount(messageParams.from)) {
await onConfirm();
showWalletConnectNotification(true);
- AnalyticsV2.trackEvent(
- MetaMetricsEvents.SIGNATURE_APPROVED,
- getAnalyticsParams(),
- );
+ trackEvent(MetaMetricsEvents.SIGNATURE_APPROVED, getAnalyticsParams());
} else {
navigation.navigate(
...(await createExternalSignModelNav(
diff --git a/app/components/Views/confirmations/components/PersonalSign/index.test.tsx b/app/components/Views/confirmations/components/PersonalSign/index.test.tsx
index b2793039d9f..c64557f57c6 100644
--- a/app/components/Views/confirmations/components/PersonalSign/index.test.tsx
+++ b/app/components/Views/confirmations/components/PersonalSign/index.test.tsx
@@ -11,8 +11,9 @@ import { InteractionManager } from 'react-native';
import AppConstants from '../../../../../core/AppConstants';
import { strings } from '../../../../../../locales/i18n';
import initialBackgroundState from '../../../../../util/test/initial-background-state.json';
-import analyticsV2 from '../../../../../util/analyticsV2';
+import { useMetrics } from '../../../../../components/hooks/useMetrics';
+jest.mock('../../../../../components/hooks/useMetrics');
jest.mock('../../../../../core/Engine', () => ({
acceptPendingApproval: jest.fn(),
rejectPendingApproval: jest.fn(),
@@ -69,13 +70,16 @@ jest.mock('react-redux', () => ({
}),
}));
-jest.mock('../../../../../util/analyticsV2');
-
jest.mock('../../../../../util/address', () => ({
getAddressAccountType: jest.fn().mockReturnValue('Metamask'),
isExternalHardwareAccount: jest.fn().mockReturnValue(false),
}));
+const mockTrackEvent = jest.fn();
+(useMetrics as jest.Mock).mockReturnValue({
+ trackEvent: mockTrackEvent,
+});
+
function createWrapper({
origin = messageParamsMock.origin,
mockConfirm = jest.fn(),
@@ -178,9 +182,9 @@ describe('PersonalSign', () => {
const wrapper = createWrapper().dive();
await (wrapper.find(SignatureRequest).props() as any).onReject();
- const rejectedMocks = (
- analyticsV2.trackEvent as jest.Mock
- ).mock.calls.filter((call) => call[0].category === 'Signature Rejected');
+ const rejectedMocks = (mockTrackEvent as jest.Mock).mock.calls.filter(
+ (call) => call[0].category === 'Signature Rejected',
+ );
const mockCallsLength = rejectedMocks.length;
@@ -202,9 +206,9 @@ describe('PersonalSign', () => {
const wrapper = createWrapper().dive();
await (wrapper.find(SignatureRequest).props() as any).onConfirm();
- const signedMocks = (
- analyticsV2.trackEvent as jest.Mock
- ).mock.calls.filter((call) => call[0].category === 'Signature Approved');
+ const signedMocks = (mockTrackEvent as jest.Mock).mock.calls.filter(
+ (call) => call[0].category === 'Signature Approved',
+ );
const mockCallsLength = signedMocks.length;
diff --git a/app/components/Views/confirmations/components/SignatureRequest/index.js b/app/components/Views/confirmations/components/SignatureRequest/index.js
index 27accc98ff6..a52c26d179b 100644
--- a/app/components/Views/confirmations/components/SignatureRequest/index.js
+++ b/app/components/Views/confirmations/components/SignatureRequest/index.js
@@ -7,11 +7,9 @@ import { SigningModalSelectorsIDs } from '../../../../../../e2e/selectors/Modals
import { strings } from '../../../../../../locales/i18n';
import ExtendedKeyringTypes from '../../../../../constants/keyringTypes';
import { MetaMetricsEvents } from '../../../../../core/Analytics';
-import Analytics from '../../../../../core/Analytics/Analytics';
import { selectProviderType } from '../../../../../selectors/networkController';
import { fontStyles } from '../../../../../styles/common';
import { isHardwareAccount } from '../../../../../util/address';
-import AnalyticsV2 from '../../../../../util/analyticsV2';
import { getHost } from '../../../../../util/browser';
import { getAnalyticsParams } from '../../../../../util/confirmation/signatureUtils';
import Device from '../../../../../util/device';
@@ -24,6 +22,7 @@ import QRSigningDetails from '../../../../UI/QRHardware/QRSigningDetails';
import withQRHardwareAwareness from '../../../../UI/QRHardware/withQRHardwareAwareness';
import WebsiteIcon from '../../../../UI/WebsiteIcon';
import { ResultType } from '../BlockaidBanner/BlockaidBanner.types';
+import { withMetricsAwareness } from '../../../../../components/hooks/useMetrics';
const createStyles = (colors) =>
StyleSheet.create({
@@ -172,6 +171,10 @@ class SignatureRequest extends PureComponent {
selectedAddress: PropTypes.string,
testID: PropTypes.string,
securityAlertResponse: PropTypes.object,
+ /**
+ * Metrics injected by withMetricsAwareness HOC
+ */
+ metrics: PropTypes.object,
};
/**
@@ -179,7 +182,7 @@ class SignatureRequest extends PureComponent {
*/
onReject = () => {
this.props.onReject();
- Analytics.trackEventWithParameters(
+ this.props.metrics.trackEvent(
MetaMetricsEvents.TRANSACTIONS_CANCEL_SIGNATURE,
this.getTrackingParams(),
);
@@ -190,7 +193,7 @@ class SignatureRequest extends PureComponent {
*/
onConfirm = () => {
this.props.onConfirm();
- Analytics.trackEventWithParameters(
+ this.props.metrics.trackEvent(
MetaMetricsEvents.TRANSACTIONS_CONFIRM_SIGNATURE,
this.getTrackingParams(),
);
@@ -313,7 +316,7 @@ class SignatureRequest extends PureComponent {
),
external_link_clicked: 'security_alert_support_link',
};
- AnalyticsV2.trackEvent(
+ this.props.metrics.trackEvent(
MetaMetricsEvents.SIGNATURE_REQUESTED,
analyticsParams,
);
@@ -423,5 +426,5 @@ const mapStateToProps = (state) => ({
SignatureRequest.contextType = ThemeContext;
export default connect(mapStateToProps)(
- withQRHardwareAwareness(SignatureRequest),
+ withQRHardwareAwareness(withMetricsAwareness(SignatureRequest)),
);
diff --git a/app/components/Views/confirmations/components/TransactionReview/TransactionReviewInformation/index.js b/app/components/Views/confirmations/components/TransactionReview/TransactionReviewInformation/index.js
index 07c1ed16cb9..6ab721aa806 100644
--- a/app/components/Views/confirmations/components/TransactionReview/TransactionReviewInformation/index.js
+++ b/app/components/Views/confirmations/components/TransactionReview/TransactionReviewInformation/index.js
@@ -29,7 +29,6 @@ import {
calculateERC20EIP1559,
} from '../../../../../../util/transactions';
import { sumHexWEIs } from '../../../../../../util/conversions';
-import Analytics from '../../../../../../core/Analytics/Analytics';
import { MetaMetricsEvents } from '../../../../../../core/Analytics';
import {
TESTNET_FAUCETS,
@@ -63,6 +62,7 @@ import { createBrowserNavDetails } from '../../../../Browser';
import { isNetworkRampNativeTokenSupported } from '../../../../../../components/UI/Ramp/utils';
import { getRampNetworks } from '../../../../../../reducers/fiatOrders';
import Routes from '../../../../../../constants/navigation/Routes';
+import { withMetricsAwareness } from '../../../../../../components/hooks/useMetrics';
const createStyles = (colors) =>
StyleSheet.create({
@@ -234,6 +234,10 @@ class TransactionReviewInformation extends PureComponent {
* Boolean that indicates if the network supports buy
*/
isNativeTokenBuySupported: PropTypes.bool,
+ /**
+ * Metrics injected by withMetricsAwareness HOC
+ */
+ metrics: PropTypes.object,
};
state = {
@@ -300,9 +304,10 @@ class TransactionReviewInformation extends PureComponent {
} catch (error) {
Logger.error(error, 'Navigation: Error when navigating to buy ETH.');
}
- InteractionManager.runAfterInteractions(() => {
- Analytics.trackEvent(MetaMetricsEvents.RECEIVE_OPTIONS_PAYMENT_REQUEST);
- });
+
+ this.props.metrics.trackEvent(
+ MetaMetricsEvents.RECEIVE_OPTIONS_PAYMENT_REQUEST,
+ );
};
edit = () => {
@@ -757,4 +762,4 @@ TransactionReviewInformation.contextType = ThemeContext;
export default connect(
mapStateToProps,
mapDispatchToProps,
-)(TransactionReviewInformation);
+)(withMetricsAwareness(TransactionReviewInformation));
diff --git a/app/components/Views/confirmations/components/TransactionReview/index.js b/app/components/Views/confirmations/components/TransactionReview/index.js
index 2293e2c310a..a0a7e98d608 100644
--- a/app/components/Views/confirmations/components/TransactionReview/index.js
+++ b/app/components/Views/confirmations/components/TransactionReview/index.js
@@ -1,12 +1,6 @@
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
-import {
- StyleSheet,
- View,
- InteractionManager,
- Animated,
- ScrollView,
-} from 'react-native';
+import { StyleSheet, View, Animated, ScrollView } from 'react-native';
import Eth from 'ethjs-query';
import {
isMultiLayerFeeNetwork,
@@ -38,8 +32,6 @@ import { getBlockaidMetricsParams } from '../../../../../util/blockaid';
import TransactionReviewInformation from './TransactionReviewInformation';
import TransactionReviewSummary from './TransactionReviewSummary';
import TransactionReviewData from './TransactionReviewData';
-import Analytics from '../../../../../core/Analytics/Analytics';
-import AnalyticsV2 from '../../../../../util/analyticsV2';
import { MetaMetricsEvents } from '../../../../../core/Analytics';
import TransactionHeader from '../../../../UI/TransactionHeader';
import AccountFromToInfoCard from '../../../../UI/AccountFromToInfoCard';
@@ -64,6 +56,7 @@ import ApproveTransactionHeader from '../ApproveTransactionHeader';
import AppConstants from '../../../../../core/AppConstants';
import TransactionBlockaidBanner from '../TransactionBlockaidBanner/TransactionBlockaidBanner';
import { ResultType } from '../BlockaidBanner/BlockaidBanner.types';
+import { withMetricsAwareness } from '../../../../../components/hooks/useMetrics';
const POLLING_INTERVAL_ESTIMATED_L1_FEE = 30000;
@@ -250,6 +243,10 @@ class TransactionReview extends PureComponent {
* @returns {string}
*/
gasSelected: PropTypes.string,
+ /**
+ * Metrics injected by withMetricsAwareness HOC
+ */
+ metrics: PropTypes.object,
};
state = {
@@ -294,6 +291,7 @@ class TransactionReview extends PureComponent {
tokens,
chainId,
tokenList,
+ metrics,
} = this.props;
let { showHexData } = this.props;
let assetAmount, conversionRate, fiatValue;
@@ -324,9 +322,9 @@ class TransactionReview extends PureComponent {
fiatValue,
approveTransaction,
});
- InteractionManager.runAfterInteractions(() => {
- AnalyticsV2.trackEvent(MetaMetricsEvents.TRANSACTIONS_CONFIRM_STARTED);
- });
+
+ metrics.trackEvent(MetaMetricsEvents.TRANSACTIONS_CONFIRM_STARTED);
+
if (isMultiLayerFeeNetwork(chainId)) {
this.fetchEstimatedL1Fee();
intervalIdForEstimatedL1Fee = setInterval(
@@ -337,14 +335,14 @@ class TransactionReview extends PureComponent {
};
onContactUsClicked = () => {
- const { transaction } = this.props;
+ const { transaction, metrics } = this.props;
const additionalParams = {
...getBlockaidMetricsParams(
transaction?.currentTransactionSecurityAlertResponse,
),
external_link_clicked: 'security_alert_support_link',
};
- AnalyticsV2.trackEvent(
+ metrics.trackEvent(
MetaMetricsEvents.TRANSACTIONS_CONFIRM_STARTED,
additionalParams,
);
@@ -394,8 +392,8 @@ class TransactionReview extends PureComponent {
};
edit = () => {
- const { onModeChange } = this.props;
- Analytics.trackEvent(MetaMetricsEvents.TRANSACTIONS_EDIT_TRANSACTION);
+ const { onModeChange, metrics } = this.props;
+ metrics.trackEvent(MetaMetricsEvents.TRANSACTIONS_EDIT_TRANSACTION);
onModeChange && onModeChange('edit');
};
@@ -663,5 +661,7 @@ const mapStateToProps = (state) => ({
TransactionReview.contextType = ThemeContext;
export default connect(mapStateToProps)(
- withNavigation(withQRHardwareAwareness(TransactionReview)),
+ withNavigation(
+ withQRHardwareAwareness(withMetricsAwareness(TransactionReview)),
+ ),
);
diff --git a/app/components/Views/confirmations/components/TransactionReview/index.test.tsx b/app/components/Views/confirmations/components/TransactionReview/index.test.tsx
index 1ec7f51a0c3..a617618c7b7 100644
--- a/app/components/Views/confirmations/components/TransactionReview/index.test.tsx
+++ b/app/components/Views/confirmations/components/TransactionReview/index.test.tsx
@@ -7,7 +7,6 @@ import { Provider } from 'react-redux';
import * as TransactionUtils from '../../../../../util/transactions';
// eslint-disable-next-line import/no-namespace
import * as BlockaidUtils from '../../../../../util/blockaid';
-import analyticsV2 from '../../../../../util/analyticsV2';
import renderWithProvider from '../../../../../util/test/renderWithProvider';
import initialBackgroundState from '../../../../../util/test/initial-background-state.json';
import { fireEvent } from '@testing-library/react-native';
@@ -185,12 +184,6 @@ describe('TransactionReview', () => {
req: {},
chainId: '0x1',
};
- const trackEventSypy = jest
- .spyOn(analyticsV2, 'trackEvent')
- .mockImplementation((name, params) => {
- expect(name).toBeDefined();
- expect(params).toBeDefined();
- });
const blockaidMetricsParamsSpy = jest
.spyOn(BlockaidUtils, 'getBlockaidMetricsParams')
@@ -239,7 +232,6 @@ describe('TransactionReview', () => {
fireEvent.press(await getByText('Report an issue'));
- expect(trackEventSypy).toHaveBeenCalledTimes(1);
expect(blockaidMetricsParamsSpy).toHaveBeenCalledTimes(1);
expect(blockaidMetricsParamsSpy).toHaveBeenCalledWith({
id: '123',
diff --git a/app/components/Views/confirmations/components/TypedSign/index.js b/app/components/Views/confirmations/components/TypedSign/index.js
index 8f518edae9d..ab992817d54 100644
--- a/app/components/Views/confirmations/components/TypedSign/index.js
+++ b/app/components/Views/confirmations/components/TypedSign/index.js
@@ -7,7 +7,6 @@ import SignatureRequest from '../SignatureRequest';
import ExpandedMessage from '../SignatureRequest/ExpandedMessage';
import Device from '../../../../../util/device';
import { MetaMetricsEvents } from '../../../../../core/Analytics';
-import AnalyticsV2 from '../../../../../util/analyticsV2';
import { KEYSTONE_TX_CANCELED } from '../../../../../constants/error';
import { ThemeContext, mockTheme } from '../../../../../util/theme';
import sanitizeString from '../../../../../util/string';
@@ -24,6 +23,7 @@ import {
import { isExternalHardwareAccount } from '../../../../../util/address';
import createExternalSignModelNav from '../../../../../util/hardwareWallet/signatureUtils';
import { SigningModalSelectorsIDs } from '../../../../../../e2e/selectors/Modals/SigningModal.selectors';
+import { withMetricsAwareness } from '../../../../../components/hooks/useMetrics';
const createStyles = (colors) =>
StyleSheet.create({
@@ -87,6 +87,10 @@ class TypedSign extends PureComponent {
* Security alert response object
*/
securityAlertResponse: PropTypes.object,
+ /**
+ * Metrics injected by withMetricsAwareness HOC
+ */
+ metrics: PropTypes.object,
};
state = {
@@ -97,9 +101,10 @@ class TypedSign extends PureComponent {
const {
messageParams: { metamaskId },
messageParams,
+ metrics,
} = this.props;
- AnalyticsV2.trackEvent(
+ metrics.trackEvent(
MetaMetricsEvents.SIGNATURE_REQUESTED,
getAnalyticsParams(messageParams, 'typed_sign'),
);
@@ -114,8 +119,9 @@ class TypedSign extends PureComponent {
};
onSignatureError = ({ error }) => {
+ const { metrics } = this.props;
if (error?.message.startsWith(KEYSTONE_TX_CANCELED)) {
- AnalyticsV2.trackEvent(
+ metrics.trackEvent(
MetaMetricsEvents.QR_HARDWARE_TRANSACTION_CANCELED,
getAnalyticsParams(),
);
@@ -282,4 +288,4 @@ const mapStateToProps = (state) => ({
securityAlertResponse: state.signatureRequest.securityAlertResponse,
});
-export default connect(mapStateToProps)(TypedSign);
+export default connect(mapStateToProps)(withMetricsAwareness(TypedSign));
diff --git a/app/components/Views/confirmations/components/TypedSign/index.test.tsx b/app/components/Views/confirmations/components/TypedSign/index.test.tsx
index 147e21527df..d743adca9da 100644
--- a/app/components/Views/confirmations/components/TypedSign/index.test.tsx
+++ b/app/components/Views/confirmations/components/TypedSign/index.test.tsx
@@ -12,7 +12,15 @@ import AppConstants from '../../../../../core/AppConstants';
import initialBackgroundState from '../../../../../util/test/initial-background-state.json';
import renderWithProvider from '../../../../../util/test/renderWithProvider';
import { fireEvent, waitFor } from '@testing-library/react-native';
-import analyticsV2 from '../../../../../util/analyticsV2';
+import { MetaMetrics } from '../../../../../core/Analytics';
+
+jest.mock('../../../../../core/Analytics/MetaMetrics');
+
+const mockMetrics = {
+ trackEvent: jest.fn(),
+};
+
+(MetaMetrics.getInstance as jest.Mock).mockReturnValue(mockMetrics);
jest.mock('../../../../../core/Engine', () => ({
acceptPendingApproval: jest.fn(),
@@ -43,8 +51,6 @@ jest.mock('../../../../../util/address', () => ({
getAddressAccountType: jest.fn().mockReturnValue('Metamask'),
}));
-jest.mock('../../../../../util/analyticsV2');
-
const messageParamsMock = {
data: { type: 'string', name: 'Message', value: 'Hi, Alice!' },
origin: 'example.com',
@@ -324,9 +330,9 @@ describe('TypedSign', () => {
expect(mockReject).toHaveBeenCalledTimes(1);
- const rejectedMocks = (
- analyticsV2.trackEvent as jest.Mock
- ).mock.calls.filter((call) => call[0].category === 'Signature Rejected');
+ const rejectedMocks = mockMetrics.trackEvent.mock.calls.filter(
+ (call) => call[0].category === 'Signature Rejected',
+ );
const mockCallsLength = rejectedMocks.length;
@@ -364,9 +370,9 @@ describe('TypedSign', () => {
);
fireEvent.press(signButton);
- const signedMocks = (
- analyticsV2.trackEvent as jest.Mock
- ).mock.calls.filter((call) => call[0].category === 'Signature Approved');
+ const signedMocks = mockMetrics.trackEvent.mock.calls.filter(
+ (call) => call[0].category === 'Signature Approved',
+ );
const mockCallsLength = signedMocks.length;
diff --git a/app/components/Views/confirmations/components/UpdateEIP1559Tx/index.tsx b/app/components/Views/confirmations/components/UpdateEIP1559Tx/index.tsx
index 162cbca019b..ac360427d59 100644
--- a/app/components/Views/confirmations/components/UpdateEIP1559Tx/index.tsx
+++ b/app/components/Views/confirmations/components/UpdateEIP1559Tx/index.tsx
@@ -22,6 +22,8 @@ import {
import { selectAccounts } from '../../../../../selectors/accountTrackerController';
import { selectSelectedAddress } from '../../../../../selectors/preferencesController';
import { getDecimalChainId } from '../../../../../util/networks';
+import { selectGasFeeEstimates } from '../../../../../selectors/confirmTransaction';
+import { selectGasFeeControllerEstimateType } from '../../../../../selectors/gasFeeController';
const UpdateEIP1559Tx = ({
gas,
@@ -255,10 +257,8 @@ const mapStateToProps = (state: any) => ({
accounts: selectAccounts(state),
selectedAddress: selectSelectedAddress(state),
ticker: selectTicker(state),
- gasFeeEstimates:
- state.engine.backgroundState.GasFeeController.gasFeeEstimates,
- gasEstimateType:
- state.engine.backgroundState.GasFeeController.gasEstimateType,
+ gasFeeEstimates: selectGasFeeEstimates(state),
+ gasEstimateType: selectGasFeeControllerEstimateType(state),
primaryCurrency: state.settings.primaryCurrency,
chainId: selectChainId(state),
});
diff --git a/app/components/Views/confirmations/components/WatchAssetRequest/index.js b/app/components/Views/confirmations/components/WatchAssetRequest/index.js
index 83bfc439e0c..e4235b62af9 100644
--- a/app/components/Views/confirmations/components/WatchAssetRequest/index.js
+++ b/app/components/Views/confirmations/components/WatchAssetRequest/index.js
@@ -10,7 +10,6 @@ import { renderFromTokenMinimalUnit } from '../../../../../util/number';
import TokenImage from '../../../../UI/TokenImage';
import Device from '../../../../../util/device';
import { MetaMetricsEvents } from '../../../../../core/Analytics';
-import AnalyticsV2 from '../../../../../util/analyticsV2';
import useTokenBalance from '../../../../hooks/useTokenBalance';
import { useTheme } from '../../../../../util/theme';
@@ -21,6 +20,7 @@ import { getActiveTabUrl } from '../../../../../util/transactions';
import { isEqual } from 'lodash';
import { SigningModalSelectorsIDs } from '../../../../../../e2e/selectors/Modals/SigningModal.selectors';
import { getDecimalChainId } from '../../../../../util/networks';
+import { useMetrics } from '../../../../../components/hooks/useMetrics';
const createStyles = (colors) =>
StyleSheet.create({
@@ -104,6 +104,7 @@ const WatchAssetRequest = ({
const { asset, interactingAddress } = suggestedAssetMeta;
// TODO - Once TokensController is updated, interactingAddress should always be defined
const { colors } = useTheme();
+ const { trackEvent } = useMetrics();
const styles = createStyles(colors);
const [balance, , error] = useTokenBalance(asset.address, interactingAddress);
const chainId = useSelector(selectChainId);
@@ -132,10 +133,7 @@ const WatchAssetRequest = ({
const onConfirmPress = async () => {
await onConfirm();
InteractionManager.runAfterInteractions(() => {
- AnalyticsV2.trackEvent(
- MetaMetricsEvents.TOKEN_ADDED,
- getAnalyticsParams(),
- );
+ trackEvent(MetaMetricsEvents.TOKEN_ADDED, getAnalyticsParams());
NotificationManager.showSimpleNotification({
status: `simple_notification`,
duration: 5000,
diff --git a/app/core/Engine.ts b/app/core/Engine.ts
index 309394444cd..792a6ea3d58 100644
--- a/app/core/Engine.ts
+++ b/app/core/Engine.ts
@@ -934,6 +934,7 @@ class Engine {
// @ts-expect-error at this point in time the provider will be defined by the `networkController.initializeProvider`
blockTracker:
networkController.getProviderAndBlockTracker().blockTracker,
+ getGasFeeEstimates: () => gasFeeController.fetchGasFeeEstimates(),
getNetworkState: () => networkController.state,
getSelectedAddress: () => preferencesController.state.selectedAddress,
incomingTransactions: {
diff --git a/app/core/GasPolling/GasPolling.ts b/app/core/GasPolling/GasPolling.ts
index 6d1455960cb..ddf1866f1d5 100644
--- a/app/core/GasPolling/GasPolling.ts
+++ b/app/core/GasPolling/GasPolling.ts
@@ -23,6 +23,7 @@ import {
LegacyProps,
UseGasTransactionProps,
} from './types';
+import { selectGasFeeEstimates } from '../../selectors/confirmTransaction';
/**
*
@@ -61,7 +62,7 @@ export const useDataStore = () => {
showCustomNonce,
] = useSelector(
(state: any) => [
- state.engine.backgroundState.GasFeeController.gasFeeEstimates,
+ selectGasFeeEstimates(state),
state.engine.backgroundState.GasFeeController.gasEstimateType,
selectContractExchangeRates(state),
selectConversionRate(state),
diff --git a/app/core/redux/slices/engine/index.ts b/app/core/redux/slices/engine/index.ts
index 700f13624cd..522cf5e705a 100644
--- a/app/core/redux/slices/engine/index.ts
+++ b/app/core/redux/slices/engine/index.ts
@@ -1,3 +1,4 @@
+import { cloneDeep } from 'lodash';
import Engine from '../../../Engine';
import { createAction, PayloadAction } from '@reduxjs/toolkit';
@@ -5,6 +6,8 @@ const initialState = {
backgroundState: {} as any,
};
+const legacyControllers = ['TransactionController'];
+
// Create an action to initialize the background state
export const initBgState = createAction('INIT_BG_STATE');
@@ -25,10 +28,24 @@ const engineReducer = (
}
case updateBgState.type: {
const newState = { ...state };
+
if (action.payload) {
- newState.backgroundState[action.payload?.key] =
+ const newControllerState =
Engine.state[action.payload.key as keyof typeof Engine.state];
+
+ // The BaseControllerV1 controllers modify the original state object on update,
+ // rather than replacing it as done in BaseControllerV2.
+ // This introduces two issues:
+ // - Memoized selectors do not fire on nested objects since the references don't change.
+ // - Deep comparison selectors do not fire since the cached objects are references to the original
+ // state object which has been mutated.
+ // This is resolved by doing a deep clone in this scenario to force an entirely new object.
+ newState.backgroundState[action.payload?.key] =
+ legacyControllers.includes(action.payload.key)
+ ? cloneDeep(newControllerState)
+ : newControllerState;
}
+
return newState;
}
default:
diff --git a/app/reducers/transaction/index.js b/app/reducers/transaction/index.js
index fca004b898d..191f9bfe218 100644
--- a/app/reducers/transaction/index.js
+++ b/app/reducers/transaction/index.js
@@ -160,6 +160,13 @@ const transactionReducer = (state = initialState, action) => {
},
};
}
+ case 'SET_TRANSACTION_ID': {
+ const { transactionId } = action;
+ return {
+ ...state,
+ id: transactionId,
+ };
+ }
default:
return state;
}
diff --git a/app/selectors/confirmTransaction.test.ts b/app/selectors/confirmTransaction.test.ts
new file mode 100644
index 00000000000..06ee8c2f2b9
--- /dev/null
+++ b/app/selectors/confirmTransaction.test.ts
@@ -0,0 +1,134 @@
+import { mergeGasFeeEstimates } from '@metamask/transaction-controller';
+import { RootState } from '../reducers';
+import {
+ selectCurrentTransactionGasFeeEstimates,
+ selectCurrentTransactionMetadata,
+ selectGasFeeEstimates,
+} from './confirmTransaction';
+import {
+ GAS_ESTIMATE_TYPES,
+ GasFeeEstimates,
+} from '@metamask/gas-fee-controller';
+
+jest.mock('@metamask/transaction-controller', () => ({
+ ...jest.requireActual('@metamask/transaction-controller'),
+ mergeGasFeeEstimates: jest.fn(),
+}));
+
+const GAS_FEE_ESTIMATES_MOCK = { low: '1', medium: '2', high: '3' };
+
+const TRANSACTION_GAS_FEE_ESTIMATES_MOCK = {
+ low: { suggestedMaxFeePerGas: '0x1', suggestedMaxPriorityFeePerGas: '0x2' },
+ medium: {
+ suggestedMaxFeePerGas: '0x3',
+ suggestedMaxPriorityFeePerGas: '0x4',
+ },
+ high: { suggestedMaxFeePerGas: '0x5', suggestedMaxPriorityFeePerGas: '0x6' },
+};
+
+describe('Confirm Transaction Selectors', () => {
+ const mergeGasFeeEstimatesMock = jest.mocked(mergeGasFeeEstimates);
+
+ describe('selectCurrentTransactionMetadata', () => {
+ it('returns the current transaction metadata', () => {
+ const transactions = [{ id: 1 }, { id: 2 }, { id: 3, chainId: '123' }];
+ const currentTransaction = { id: 3 };
+
+ const state = {
+ transaction: currentTransaction,
+ engine: {
+ backgroundState: { TransactionController: { transactions } },
+ },
+ };
+
+ expect(
+ selectCurrentTransactionMetadata(state as unknown as RootState),
+ ).toStrictEqual(transactions[2]);
+ });
+ });
+
+ describe('selectCurrentTransactionGasFeeEstimates', () => {
+ it('returns the gas fee estimates from current transaction metadata', () => {
+ const transactions = [
+ { id: 1 },
+ { id: 2 },
+ { id: 3, chainId: '123', gasFeeEstimates: GAS_FEE_ESTIMATES_MOCK },
+ ];
+
+ const currentTransaction = { id: 3 };
+
+ const state = {
+ transaction: currentTransaction,
+ engine: {
+ backgroundState: { TransactionController: { transactions } },
+ },
+ };
+
+ expect(
+ selectCurrentTransactionGasFeeEstimates(state as unknown as RootState),
+ ).toStrictEqual(GAS_FEE_ESTIMATES_MOCK);
+ });
+ });
+
+ describe('selectGasFeeEstimates', () => {
+ it('returns GasFeeController estimates if no transaction estimates', () => {
+ const transactions = [{ id: 1 }, { id: 2 }, { id: 3, chainId: '123' }];
+ const currentTransaction = { id: 3 };
+
+ const state = {
+ transaction: currentTransaction,
+ engine: {
+ backgroundState: {
+ GasFeeController: { gasFeeEstimates: GAS_FEE_ESTIMATES_MOCK },
+ TransactionController: { transactions },
+ },
+ },
+ };
+
+ expect(
+ selectGasFeeEstimates(state as unknown as RootState),
+ ).toStrictEqual(GAS_FEE_ESTIMATES_MOCK);
+ });
+
+ it('returns merged estimates if GasFeeController estimates and transaction estimates exist', () => {
+ const transactions = [
+ { id: 1 },
+ { id: 2 },
+ {
+ id: 3,
+ chainId: '123',
+ gasFeeEstimates: TRANSACTION_GAS_FEE_ESTIMATES_MOCK,
+ },
+ ];
+ const currentTransaction = { id: 3 };
+
+ const state = {
+ transaction: currentTransaction,
+ engine: {
+ backgroundState: {
+ GasFeeController: {
+ gasEstimateType: GAS_ESTIMATE_TYPES.FEE_MARKET,
+ gasFeeEstimates: GAS_FEE_ESTIMATES_MOCK,
+ },
+ TransactionController: { transactions },
+ },
+ },
+ };
+
+ mergeGasFeeEstimatesMock.mockReturnValue(
+ TRANSACTION_GAS_FEE_ESTIMATES_MOCK as GasFeeEstimates,
+ );
+
+ expect(
+ selectGasFeeEstimates(state as unknown as RootState),
+ ).toStrictEqual(TRANSACTION_GAS_FEE_ESTIMATES_MOCK);
+
+ expect(mergeGasFeeEstimatesMock).toHaveBeenCalledTimes(1);
+ expect(mergeGasFeeEstimatesMock).toHaveBeenCalledWith({
+ gasFeeControllerEstimateType: GAS_ESTIMATE_TYPES.FEE_MARKET,
+ gasFeeControllerEstimates: GAS_FEE_ESTIMATES_MOCK,
+ transactionGasFeeEstimates: TRANSACTION_GAS_FEE_ESTIMATES_MOCK,
+ });
+ });
+ });
+});
diff --git a/app/selectors/confirmTransaction.ts b/app/selectors/confirmTransaction.ts
new file mode 100644
index 00000000000..366657f3551
--- /dev/null
+++ b/app/selectors/confirmTransaction.ts
@@ -0,0 +1,61 @@
+import { selectTransactions } from './transactionController';
+import { RootState } from '../reducers';
+import {
+ selectGasFeeControllerEstimateType,
+ selectGasFeeControllerEstimates,
+} from './gasFeeController';
+import { mergeGasFeeEstimates } from '@metamask/transaction-controller';
+import { createSelector } from 'reselect';
+import { createDeepEqualSelector } from './util';
+
+const selectCurrentTransactionId = (state: RootState) => state.transaction?.id;
+
+export const selectCurrentTransactionMetadata = createSelector(
+ selectTransactions,
+ selectCurrentTransactionId,
+ (transactions, currentTransactionId) =>
+ transactions.find((tx) => tx.id === currentTransactionId),
+);
+
+const selectCurrentTransactionGasFeeEstimatesStrict = createSelector(
+ selectCurrentTransactionMetadata,
+ (transactionMetadata) => transactionMetadata?.gasFeeEstimates,
+);
+
+const selectCurrentTransactionGasFeeEstimatesLoaded = createSelector(
+ selectCurrentTransactionMetadata,
+ (transactionMetadata) => transactionMetadata?.gasFeeEstimatesLoaded,
+);
+
+export const selectCurrentTransactionGasFeeEstimates = createDeepEqualSelector(
+ selectCurrentTransactionGasFeeEstimatesStrict,
+ (gasFeeEstimates) => gasFeeEstimates,
+);
+
+export const selectGasFeeEstimates = createSelector(
+ selectGasFeeControllerEstimateType,
+ selectGasFeeControllerEstimates,
+ selectCurrentTransactionGasFeeEstimates,
+ (
+ gasFeeControllerEstimateType,
+ gasFeeControllerEstimates,
+ transactionGasFeeEstimates,
+ ) => {
+ if (transactionGasFeeEstimates) {
+ return mergeGasFeeEstimates({
+ gasFeeControllerEstimateType: gasFeeControllerEstimateType as any,
+ gasFeeControllerEstimates: gasFeeControllerEstimates as any,
+ transactionGasFeeEstimates,
+ });
+ }
+
+ return gasFeeControllerEstimates;
+ },
+);
+
+export const selectTransactionGasFeeEstimates = createSelector(
+ selectCurrentTransactionGasFeeEstimatesLoaded,
+ selectGasFeeEstimates,
+ (transactionGasFeeEstimatesLoaded, gasFeeEstimates) =>
+ transactionGasFeeEstimatesLoaded ? gasFeeEstimates : undefined,
+);
diff --git a/app/selectors/gasFeeController.test.ts b/app/selectors/gasFeeController.test.ts
new file mode 100644
index 00000000000..b0159bb4374
--- /dev/null
+++ b/app/selectors/gasFeeController.test.ts
@@ -0,0 +1,48 @@
+import { GAS_ESTIMATE_TYPES } from '@metamask/gas-fee-controller';
+import {
+ selectGasFeeControllerEstimateType,
+ selectGasFeeControllerEstimates,
+} from './gasFeeController';
+import { RootState } from '../reducers';
+
+describe('GasFeeController Selectors', () => {
+ describe('selectGasFeeControllerEstimates', () => {
+ it('returns the gas estimate type from GasFeeController state', () => {
+ const gasFeeEstimates = { low: '1', medium: '2', high: '3' };
+
+ const state = {
+ engine: {
+ backgroundState: {
+ GasFeeController: {
+ gasFeeEstimates,
+ },
+ },
+ },
+ };
+
+ expect(
+ selectGasFeeControllerEstimates(state as unknown as RootState),
+ ).toStrictEqual(gasFeeEstimates);
+ });
+ });
+
+ describe('selectGasFeeControllerEstimateType', () => {
+ it('returns the gas estimate type from GasFeeController state', () => {
+ const gasEstimateType = GAS_ESTIMATE_TYPES.FEE_MARKET;
+
+ const state = {
+ engine: {
+ backgroundState: {
+ GasFeeController: {
+ gasEstimateType,
+ },
+ },
+ },
+ };
+
+ expect(
+ selectGasFeeControllerEstimateType(state as RootState),
+ ).toStrictEqual(gasEstimateType);
+ });
+ });
+});
diff --git a/app/selectors/gasFeeController.ts b/app/selectors/gasFeeController.ts
new file mode 100644
index 00000000000..6e7da2e9bb3
--- /dev/null
+++ b/app/selectors/gasFeeController.ts
@@ -0,0 +1,21 @@
+import { createSelector } from 'reselect';
+import { RootState } from '../reducers';
+import { createDeepEqualSelector } from './util';
+
+const selectGasFeeControllerState = (state: RootState) =>
+ state.engine.backgroundState.GasFeeController;
+
+const selectGasFeeControllerEstimatesStrict = createSelector(
+ selectGasFeeControllerState,
+ (gasFeeControllerState) => gasFeeControllerState.gasFeeEstimates,
+);
+
+export const selectGasFeeControllerEstimates = createDeepEqualSelector(
+ selectGasFeeControllerEstimatesStrict,
+ (gasFeeEstimates) => gasFeeEstimates,
+);
+
+export const selectGasFeeControllerEstimateType = createSelector(
+ selectGasFeeControllerState,
+ (gasFeeControllerState) => gasFeeControllerState.gasEstimateType,
+);
diff --git a/app/selectors/transactionController.test.ts b/app/selectors/transactionController.test.ts
new file mode 100644
index 00000000000..b4b78876f4e
--- /dev/null
+++ b/app/selectors/transactionController.test.ts
@@ -0,0 +1,24 @@
+import { RootState } from '../reducers';
+import { selectTransactions } from './transactionController';
+
+describe('TransactionController Selectors', () => {
+ describe('selectTransactions', () => {
+ it('returns transactions from TransactionController state', () => {
+ const transactions = [{ id: 1 }, { id: 2 }];
+
+ const state = {
+ engine: {
+ backgroundState: {
+ TransactionController: {
+ transactions,
+ },
+ },
+ },
+ };
+
+ expect(selectTransactions(state as unknown as RootState)).toStrictEqual(
+ transactions,
+ );
+ });
+ });
+});
diff --git a/app/selectors/transactionController.ts b/app/selectors/transactionController.ts
new file mode 100644
index 00000000000..a86514680af
--- /dev/null
+++ b/app/selectors/transactionController.ts
@@ -0,0 +1,17 @@
+import { createSelector } from 'reselect';
+import { RootState } from '../reducers';
+import { createDeepEqualSelector } from './util';
+
+const selectTransactionControllerState = (state: RootState) =>
+ state.engine.backgroundState.TransactionController;
+
+const selectTransactionsStrict = createSelector(
+ selectTransactionControllerState,
+ (transactionControllerState) => transactionControllerState.transactions,
+);
+
+// eslint-disable-next-line import/prefer-default-export
+export const selectTransactions = createDeepEqualSelector(
+ selectTransactionsStrict,
+ (transactions) => transactions,
+);
diff --git a/app/selectors/util.ts b/app/selectors/util.ts
new file mode 100644
index 00000000000..ce59abd821c
--- /dev/null
+++ b/app/selectors/util.ts
@@ -0,0 +1,8 @@
+import { isEqual } from 'lodash';
+import { createSelectorCreator, defaultMemoize } from 'reselect';
+
+// eslint-disable-next-line import/prefer-default-export
+export const createDeepEqualSelector = createSelectorCreator(
+ defaultMemoize,
+ isEqual,
+);
diff --git a/app/util/confirmation/signatureUtils.js b/app/util/confirmation/signatureUtils.js
index f6331b044a8..7365b2464df 100644
--- a/app/util/confirmation/signatureUtils.js
+++ b/app/util/confirmation/signatureUtils.js
@@ -1,6 +1,5 @@
import Engine from '../../core/Engine';
-import { MetaMetricsEvents } from '../../core/Analytics';
-import AnalyticsV2 from '../analyticsV2';
+import { MetaMetrics, MetaMetricsEvents } from '../../core/Analytics';
import { getAddressAccountType } from '../address';
import NotificationManager from '../../core/NotificationManager';
import { WALLET_CONNECT_ORIGIN } from '../walletconnect';
@@ -95,7 +94,7 @@ export const handleSignatureAction = async (
) => {
await onAction();
showWalletConnectNotification(messageParams, confirmation);
- AnalyticsV2.trackEvent(
+ MetaMetrics.getInstance().trackEvent(
confirmation
? MetaMetricsEvents.SIGNATURE_APPROVED
: MetaMetricsEvents.SIGNATURE_REJECTED,
diff --git a/app/util/metrics/index.ts b/app/util/metrics/index.ts
index 3bc681cb1c7..6942d325ebc 100644
--- a/app/util/metrics/index.ts
+++ b/app/util/metrics/index.ts
@@ -1,7 +1,9 @@
import DeviceAnalyticsMetaData from './DeviceAnalyticsMetaData/generateDeviceAnalyticsMetaData';
import UserSettingsAnalyticsMetaData from './UserSettingsAnalyticsMetaData/generateUserProfileAnalyticsMetaData';
import TrackAfterInteractions from './TrackAfterInteraction/trackAfterInteractions';
+import trackDappVisitedEvent from './trackDappVisitedEvent';
export default DeviceAnalyticsMetaData;
export { UserSettingsAnalyticsMetaData };
export { TrackAfterInteractions };
+export { trackDappVisitedEvent };
diff --git a/app/analytics/index.test.ts b/app/util/metrics/trackDappVisited/index.test.ts
similarity index 86%
rename from app/analytics/index.test.ts
rename to app/util/metrics/trackDappVisited/index.test.ts
index fc3a9f375de..8c5896d68a6 100644
--- a/app/analytics/index.test.ts
+++ b/app/util/metrics/trackDappVisited/index.test.ts
@@ -1,15 +1,17 @@
-import { trackDappVisitedEvent } from './index';
-import { MetaMetricsEvents } from '../core/Analytics';
-import AnalyticsV2 from '../util/analyticsV2';
+import trackDappVisitedEvent from './index';
+import { MetaMetrics, MetaMetricsEvents } from '../../../core/Analytics';
-// Mock AnalyticsV2
-jest.mock('../util/analyticsV2', () => ({
+jest.mock('../../../core/Analytics/MetaMetrics');
+
+const mockMetrics = {
trackEvent: jest.fn(),
-}));
+};
+
+(MetaMetrics.getInstance as jest.Mock).mockReturnValue(mockMetrics);
// Mock store.getState
let mockGetState: jest.Mock;
-jest.mock('../store', () => {
+jest.mock('../../../store', () => {
mockGetState = jest.fn();
mockGetState.mockImplementation(() => ({
browser: {
@@ -34,7 +36,7 @@ jest.mock('../store', () => {
describe('trackDappVisitedEvent', () => {
afterEach(() => {
- jest.resetAllMocks();
+ jest.clearAllMocks();
});
it('should track with isFirstVisit = true', () => {
@@ -63,7 +65,7 @@ describe('trackDappVisitedEvent', () => {
numberOfConnectedAccounts: 1,
});
- expect(AnalyticsV2.trackEvent).toBeCalledWith(
+ expect(mockMetrics.trackEvent).toBeCalledWith(
MetaMetricsEvents.DAPP_VISITED,
expectedMetrics,
);
@@ -95,7 +97,7 @@ describe('trackDappVisitedEvent', () => {
numberOfConnectedAccounts: 1,
});
- expect(AnalyticsV2.trackEvent).toBeCalledWith(
+ expect(mockMetrics.trackEvent).toBeCalledWith(
MetaMetricsEvents.DAPP_VISITED,
expectedMetrics,
);
@@ -127,7 +129,7 @@ describe('trackDappVisitedEvent', () => {
numberOfConnectedAccounts: 1,
});
- expect(AnalyticsV2.trackEvent).toBeCalledWith(
+ expect(mockMetrics.trackEvent).toBeCalledWith(
MetaMetricsEvents.DAPP_VISITED,
expectedMetrics,
);
@@ -159,7 +161,7 @@ describe('trackDappVisitedEvent', () => {
numberOfConnectedAccounts: 1,
});
- expect(AnalyticsV2.trackEvent).toBeCalledWith(
+ expect(mockMetrics.trackEvent).toBeCalledWith(
MetaMetricsEvents.DAPP_VISITED,
expectedMetrics,
);
diff --git a/app/analytics/index.ts b/app/util/metrics/trackDappVisited/index.ts
similarity index 50%
rename from app/analytics/index.ts
rename to app/util/metrics/trackDappVisited/index.ts
index 10d35787e4d..58993a279af 100644
--- a/app/analytics/index.ts
+++ b/app/util/metrics/trackDappVisited/index.ts
@@ -1,18 +1,18 @@
-import { selectIdentities } from '../selectors/preferencesController';
-import { store } from '../store';
-import { MetaMetricsEvents } from '../core/Analytics';
-import AnalyticsV2 from '../util/analyticsV2';
-import { addToVisitedDapp } from '../actions/browser';
+import { store } from '../../../store';
+import { selectIdentities } from '../../../selectors/preferencesController';
+import { addToVisitedDapp } from '../../../actions/browser';
+import { MetaMetrics, MetaMetricsEvents } from '../../../core/Analytics';
/**
* Tracks Dapp visited event
*
- * @param hostname - Hostname of the Dapp
- * @param numberOfConnectedAccounts - Number of connected accounts that are connected to the Dapp
+ * This is used to track when a user visits a Dapp in the in-app browser
+ *
+ * @param params - The parameter object for the tracking function
+ * @param params.hostname - Hostname of the Dapp
+ * @param params.numberOfConnectedAccounts - Number of connected accounts that are connected to the Dapp
*/
-// This file will export more events in the future.
-// eslint-disable-next-line import/prefer-default-export
-export const trackDappVisitedEvent = ({
+const trackDappVisitedEvent = ({
hostname,
numberOfConnectedAccounts,
}: {
@@ -28,11 +28,12 @@ export const trackDappVisitedEvent = ({
// Add Dapp hostname to visited dapps
store.dispatch(addToVisitedDapp(hostname));
- // Track DAPP_VISITED event
- AnalyticsV2.trackEvent(MetaMetricsEvents.DAPP_VISITED, {
+ MetaMetrics.getInstance().trackEvent(MetaMetricsEvents.DAPP_VISITED, {
is_first_visit: isFirstVisit,
number_of_accounts: numberOfWalletAccounts,
number_of_accounts_connected: numberOfConnectedAccounts,
source: 'in-app browser',
});
};
+
+export default trackDappVisitedEvent;
diff --git a/app/util/metrics/trackDappVisitedEvent/index.test.ts b/app/util/metrics/trackDappVisitedEvent/index.test.ts
new file mode 100644
index 00000000000..8c5896d68a6
--- /dev/null
+++ b/app/util/metrics/trackDappVisitedEvent/index.test.ts
@@ -0,0 +1,169 @@
+import trackDappVisitedEvent from './index';
+import { MetaMetrics, MetaMetricsEvents } from '../../../core/Analytics';
+
+jest.mock('../../../core/Analytics/MetaMetrics');
+
+const mockMetrics = {
+ trackEvent: jest.fn(),
+};
+
+(MetaMetrics.getInstance as jest.Mock).mockReturnValue(mockMetrics);
+
+// Mock store.getState
+let mockGetState: jest.Mock;
+jest.mock('../../../store', () => {
+ mockGetState = jest.fn();
+ mockGetState.mockImplementation(() => ({
+ browser: {
+ visitedDappsByHostname: {},
+ },
+ engine: {
+ backgroundState: {
+ PreferencesController: {
+ identities: { '0x1': true, '0x2': true },
+ },
+ },
+ },
+ }));
+
+ return {
+ store: {
+ getState: mockGetState,
+ dispatch: jest.fn(),
+ },
+ };
+});
+
+describe('trackDappVisitedEvent', () => {
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('should track with isFirstVisit = true', () => {
+ mockGetState.mockImplementation(() => ({
+ browser: {
+ visitedDappsByHostname: {},
+ },
+ engine: {
+ backgroundState: {
+ PreferencesController: {
+ identities: { '0x1': true, '0x2': true },
+ },
+ },
+ },
+ }));
+
+ const expectedMetrics = {
+ is_first_visit: true,
+ number_of_accounts: 2,
+ number_of_accounts_connected: 1,
+ source: 'in-app browser',
+ };
+
+ trackDappVisitedEvent({
+ hostname: 'uniswap.org',
+ numberOfConnectedAccounts: 1,
+ });
+
+ expect(mockMetrics.trackEvent).toBeCalledWith(
+ MetaMetricsEvents.DAPP_VISITED,
+ expectedMetrics,
+ );
+ });
+
+ it('should track with isFirstVisit = false', () => {
+ mockGetState.mockImplementation(() => ({
+ browser: {
+ visitedDappsByHostname: { 'uniswap.org': true },
+ },
+ engine: {
+ backgroundState: {
+ PreferencesController: {
+ identities: { '0x1': true, '0x2': true },
+ },
+ },
+ },
+ }));
+
+ const expectedMetrics = {
+ is_first_visit: false,
+ number_of_accounts: 2,
+ number_of_accounts_connected: 1,
+ source: 'in-app browser',
+ };
+
+ trackDappVisitedEvent({
+ hostname: 'uniswap.org',
+ numberOfConnectedAccounts: 1,
+ });
+
+ expect(mockMetrics.trackEvent).toBeCalledWith(
+ MetaMetricsEvents.DAPP_VISITED,
+ expectedMetrics,
+ );
+ });
+
+ it('should track with the correct number of connected accounts', () => {
+ mockGetState.mockImplementation(() => ({
+ browser: {
+ visitedDappsByHostname: { 'uniswap.org': true },
+ },
+ engine: {
+ backgroundState: {
+ PreferencesController: {
+ identities: { '0x1': true, '0x2': true },
+ },
+ },
+ },
+ }));
+
+ const expectedMetrics = {
+ is_first_visit: false,
+ number_of_accounts: 2,
+ number_of_accounts_connected: 1,
+ source: 'in-app browser',
+ };
+
+ trackDappVisitedEvent({
+ hostname: 'uniswap.org',
+ numberOfConnectedAccounts: 1,
+ });
+
+ expect(mockMetrics.trackEvent).toBeCalledWith(
+ MetaMetricsEvents.DAPP_VISITED,
+ expectedMetrics,
+ );
+ });
+
+ it('should track with the correct number of wallet accounts', () => {
+ mockGetState.mockImplementation(() => ({
+ browser: {
+ visitedDappsByHostname: { 'uniswap.org': true },
+ },
+ engine: {
+ backgroundState: {
+ PreferencesController: {
+ identities: { '0x1': true },
+ },
+ },
+ },
+ }));
+
+ const expectedMetrics = {
+ is_first_visit: false,
+ number_of_accounts: 1,
+ number_of_accounts_connected: 1,
+ source: 'in-app browser',
+ };
+
+ trackDappVisitedEvent({
+ hostname: 'uniswap.org',
+ numberOfConnectedAccounts: 1,
+ });
+
+ expect(mockMetrics.trackEvent).toBeCalledWith(
+ MetaMetricsEvents.DAPP_VISITED,
+ expectedMetrics,
+ );
+ });
+});
diff --git a/app/util/metrics/trackDappVisitedEvent/index.ts b/app/util/metrics/trackDappVisitedEvent/index.ts
new file mode 100644
index 00000000000..58993a279af
--- /dev/null
+++ b/app/util/metrics/trackDappVisitedEvent/index.ts
@@ -0,0 +1,39 @@
+import { store } from '../../../store';
+import { selectIdentities } from '../../../selectors/preferencesController';
+import { addToVisitedDapp } from '../../../actions/browser';
+import { MetaMetrics, MetaMetricsEvents } from '../../../core/Analytics';
+
+/**
+ * Tracks Dapp visited event
+ *
+ * This is used to track when a user visits a Dapp in the in-app browser
+ *
+ * @param params - The parameter object for the tracking function
+ * @param params.hostname - Hostname of the Dapp
+ * @param params.numberOfConnectedAccounts - Number of connected accounts that are connected to the Dapp
+ */
+const trackDappVisitedEvent = ({
+ hostname,
+ numberOfConnectedAccounts,
+}: {
+ hostname: string;
+ numberOfConnectedAccounts: number;
+}) => {
+ const visitedDappsByHostname =
+ store.getState().browser.visitedDappsByHostname;
+ const isFirstVisit = !visitedDappsByHostname[hostname];
+ const accountByAddress = selectIdentities(store.getState());
+ const numberOfWalletAccounts = Object.keys(accountByAddress).length;
+
+ // Add Dapp hostname to visited dapps
+ store.dispatch(addToVisitedDapp(hostname));
+
+ MetaMetrics.getInstance().trackEvent(MetaMetricsEvents.DAPP_VISITED, {
+ is_first_visit: isFirstVisit,
+ number_of_accounts: numberOfWalletAccounts,
+ number_of_accounts_connected: numberOfConnectedAccounts,
+ source: 'in-app browser',
+ });
+};
+
+export default trackDappVisitedEvent;
diff --git a/app/util/middlewares.js b/app/util/middlewares.js
index fe60f6c4f0b..d2378482551 100644
--- a/app/util/middlewares.js
+++ b/app/util/middlewares.js
@@ -1,5 +1,5 @@
import Logger from './Logger';
-import { trackErrorAsAnalytics } from './analyticsV2';
+import trackErrorAsAnalytics from './metrics/TrackError/trackErrorAsAnalytics';
/**
* List of rpc errors caused by the user rejecting a certain action.
diff --git a/app/util/sentry/utils.js b/app/util/sentry/utils.js
index ffcc573e8a8..78d0c48465c 100644
--- a/app/util/sentry/utils.js
+++ b/app/util/sentry/utils.js
@@ -83,8 +83,25 @@ function removeDeviceName(report) {
report.contexts.device.name = null;
}
+/**
+ * Removes SES from the Sentry error event stack trace.
+ * By default, SES is shown as the top level frame, which can obscure errors.
+ * We filter it out by identifying the SES stack trace frame simply by 'filename',
+ * since the 'context_line' is rather verbose.
+ * @param {*} report - the error event
+ */
+function removeSES(report) {
+ const stacktraceFrames = report.exception.values[0].stacktrace.frames;
+ const filteredFrames = stacktraceFrames.filter(
+ (frame) => frame.filename !== 'app:///ses.cjs',
+ );
+ report.exception.values[0].stacktrace.frames = filteredFrames;
+}
+
function rewriteReport(report) {
try {
+ // filter out SES from error stack trace
+ removeSES(report);
// simplify certain complex error messages (e.g. Ethjs)
simplifyErrorMessages(report);
// remove urls from error message
diff --git a/patches/@metamask+transaction-controller+6.1.0.patch b/patches/@metamask+transaction-controller+6.1.0.patch
index 6a42705ee04..6dc9dd5e7cb 100644
--- a/patches/@metamask+transaction-controller+6.1.0.patch
+++ b/patches/@metamask+transaction-controller+6.1.0.patch
@@ -1,6 +1,6 @@
diff --git a/node_modules/@metamask/transaction-controller/dist/.patch.txt b/node_modules/@metamask/transaction-controller/dist/.patch.txt
new file mode 100644
-index 0000000..e8035ba
+index 0000000..550de56
--- /dev/null
+++ b/node_modules/@metamask/transaction-controller/dist/.patch.txt
@@ -0,0 +1,7 @@
@@ -367,10 +367,10 @@ index 0000000..18bc7af
+//# sourceMappingURL=IncomingTransactionHelper.js.map
\ No newline at end of file
diff --git a/node_modules/@metamask/transaction-controller/dist/TransactionController.d.ts b/node_modules/@metamask/transaction-controller/dist/TransactionController.d.ts
-index 2c9675f..46df86c 100644
+index 2c9675f..7aeceec 100644
--- a/node_modules/@metamask/transaction-controller/dist/TransactionController.d.ts
+++ b/node_modules/@metamask/transaction-controller/dist/TransactionController.d.ts
-@@ -2,10 +2,11 @@
+@@ -2,10 +2,12 @@
import { EventEmitter } from 'events';
import Common from '@ethereumjs/common';
import { TypedTransaction } from '@ethereumjs/tx';
@@ -380,11 +380,12 @@ index 2c9675f..46df86c 100644
-import { AcceptRequest as AcceptApprovalRequest, AddApprovalRequest, RejectRequest as RejectApprovalRequest } from '@metamask/approval-controller';
+import { AddApprovalRequest } from '@metamask/approval-controller';
+import { NonceLock } from 'nonce-tracker/dist/NonceTracker';
++import { GasFeeState } from '@metamask/gas-fee-controller';
+import { SecurityAlertResponse, SubmitHistoryEntry, Transaction, TransactionMeta, WalletDevice } from './types';
/**
* @type Result
* @property result - Promise resolving to a new transaction hash
-@@ -15,44 +16,6 @@ export interface Result {
+@@ -15,44 +17,6 @@ export interface Result {
result: Promise;
transactionMeta: TransactionMeta;
}
@@ -429,7 +430,7 @@ index 2c9675f..46df86c 100644
export interface GasPriceValue {
gasPrice: string;
}
-@@ -60,115 +23,6 @@ export interface FeeMarketEIP1559Values {
+@@ -60,115 +24,6 @@ export interface FeeMarketEIP1559Values {
maxFeePerGas: string;
maxPriorityFeePerGas: string;
}
@@ -545,7 +546,7 @@ index 2c9675f..46df86c 100644
/**
* @type TransactionConfig
*
-@@ -205,11 +59,15 @@ export interface TransactionState extends BaseState {
+@@ -205,11 +60,15 @@ export interface TransactionState extends BaseState {
methodData: {
[key: string]: MethodData;
};
@@ -562,7 +563,7 @@ index 2c9675f..46df86c 100644
/**
* Multiplier used to determine a transaction's increased gas fee during speed up
*/
-@@ -221,7 +79,7 @@ declare const controllerName = "TransactionController";
+@@ -221,7 +80,7 @@ declare const controllerName = "TransactionController";
/**
* The external actions available to the {@link TransactionController}.
*/
@@ -571,8 +572,12 @@ index 2c9675f..46df86c 100644
/**
* The messenger of the {@link TransactionController}.
*/
-@@ -238,19 +96,9 @@ export declare class TransactionController extends BaseController Promise;
getNetworkState: () => NetworkState;
+ getSelectedAddress: () => string;
+ incomingTransactions: {
@@ -631,7 +638,7 @@ index 2c9675f..46df86c 100644
}, config?: Partial, state?: Partial);
/**
* Starts a new polling interval.
-@@ -301,11 +162,20 @@ export declare class TransactionController extends BaseController:unapproved` hub event will be emitted once added.
*
* @param transaction - The transaction object to add.
@@ -655,7 +662,7 @@ index 2c9675f..46df86c 100644
prepareUnsignedEthTx(txParams: Record): TypedTransaction;
/**
* `@ethereumjs/tx` uses `@ethereumjs/common` as a configuration tool for
-@@ -317,22 +187,6 @@ export declare class TransactionController extends BaseController:finished` hub event.
-@@ -374,6 +228,13 @@ export declare class TransactionController extends BaseController {
@@ -925,7 +936,7 @@ index 3edd9c2..28a1c63 100644
/**
* EventEmitter instance used to listen to specific transactional events
*/
-@@ -126,6 +84,8 @@ class TransactionController extends base_controller_1.BaseController {
+@@ -126,19 +87,51 @@ class TransactionController extends base_controller_1.BaseController {
this.defaultState = {
methodData: {},
transactions: [],
@@ -934,7 +945,8 @@ index 3edd9c2..28a1c63 100644
};
this.initialize();
this.provider = provider;
-@@ -133,12 +93,29 @@ class TransactionController extends base_controller_1.BaseController {
+ this.messagingSystem = messenger;
++ this.getGasFeeEstimates = getGasFeeEstimates;
this.getNetworkState = getNetworkState;
this.ethQuery = new eth_query_1.default(provider);
this.registry = new eth_method_registry_1.default({ provider });
@@ -960,13 +972,25 @@ index 3edd9c2..28a1c63 100644
+ }),
+ transactionLimit: this.config.txHistoryLimit,
+ updateTransactions: incomingTransactions.updateTransactions,
- });
++ });
+ this.incomingTransactionHelper.hub.on('transactions', this.onIncomingTransactions.bind(this));
+ this.incomingTransactionHelper.hub.on('updatedLastFetchedBlockNumbers', this.onUpdatedLastFetchedBlockNumbers.bind(this));
++ this.gasFeeFlows = this.getGasFeeFlows();
++ const gasFeePoller = new GasFeePoller_1.GasFeePoller({
++ gasFeeFlows: this.gasFeeFlows,
++ getChainIds: () => [this.getNetworkState().providerConfig.chainId],
++ getEthQuery: () => this.ethQuery,
++ getGasFeeControllerEstimates: this.getGasFeeEstimates,
++ getTransactions: () => this.state.transactions,
++ onStateChange: (listener) => {
++ this.subscribe(listener);
++ },
+ });
++ gasFeePoller.hub.on('transaction-updated', this.updateTransaction.bind(this));
onNetworkStateChange(() => {
this.ethQuery = new eth_query_1.default(this.provider);
this.registry = new eth_method_registry_1.default({ provider: this.provider });
-@@ -146,7 +123,7 @@ class TransactionController extends base_controller_1.BaseController {
+@@ -146,7 +139,7 @@ class TransactionController extends base_controller_1.BaseController {
this.poll();
}
failTransaction(transactionMeta, error) {
@@ -975,7 +999,7 @@ index 3edd9c2..28a1c63 100644
this.updateTransaction(newTransactionMeta);
this.hub.emit(`${transactionMeta.id}:finished`, newTransactionMeta);
}
-@@ -157,43 +134,6 @@ class TransactionController extends base_controller_1.BaseController {
+@@ -157,43 +150,6 @@ class TransactionController extends base_controller_1.BaseController {
return { registryMethod, parsedRegistryMethod };
});
}
@@ -1019,7 +1043,7 @@ index 3edd9c2..28a1c63 100644
/**
* Starts a new polling interval.
*
-@@ -241,11 +181,13 @@ class TransactionController extends base_controller_1.BaseController {
+@@ -241,11 +197,13 @@ class TransactionController extends base_controller_1.BaseController {
* if not provided. If A `:unapproved` hub event will be emitted once added.
*
* @param transaction - The transaction object to add.
@@ -1036,7 +1060,7 @@ index 3edd9c2..28a1c63 100644
return __awaiter(this, void 0, void 0, function* () {
const { providerConfig, networkId } = this.getNetworkState();
const { transactions } = this.state;
-@@ -256,11 +198,12 @@ class TransactionController extends base_controller_1.BaseController {
+@@ -256,11 +214,12 @@ class TransactionController extends base_controller_1.BaseController {
networkID: networkId !== null && networkId !== void 0 ? networkId : undefined,
chainId: providerConfig.chainId,
origin,
@@ -1050,7 +1074,7 @@ index 3edd9c2..28a1c63 100644
};
try {
const { gas, estimateGasError } = yield this.estimateGas(transaction);
-@@ -271,28 +214,24 @@ class TransactionController extends base_controller_1.BaseController {
+@@ -271,28 +230,24 @@ class TransactionController extends base_controller_1.BaseController {
this.failTransaction(transactionMeta, error);
return Promise.reject(error);
}
@@ -1094,7 +1118,7 @@ index 3edd9c2..28a1c63 100644
});
}
prepareUnsignedEthTx(txParams) {
-@@ -312,7 +251,9 @@ class TransactionController extends base_controller_1.BaseController {
+@@ -312,7 +267,9 @@ class TransactionController extends base_controller_1.BaseController {
*/
getCommonConfiguration() {
const { networkId, providerConfig: { type: chain, chainId, nickname: name }, } = this.getNetworkState();
@@ -1105,7 +1129,7 @@ index 3edd9c2..28a1c63 100644
return new common_1.default({ chain, hardfork: HARDFORK });
}
const customChainParams = {
-@@ -322,104 +263,6 @@ class TransactionController extends base_controller_1.BaseController {
+@@ -322,104 +279,6 @@ class TransactionController extends base_controller_1.BaseController {
};
return common_1.default.forCustomChain(controller_utils_1.NetworkType.mainnet, customChainParams, HARDFORK);
}
@@ -1210,7 +1234,7 @@ index 3edd9c2..28a1c63 100644
/**
* Attempts to cancel a transaction based on its ID by setting its status to "rejected"
* and emitting a `:finished` hub event.
-@@ -482,10 +325,9 @@ class TransactionController extends base_controller_1.BaseController {
+@@ -482,10 +341,9 @@ class TransactionController extends base_controller_1.BaseController {
const unsignedEthTx = this.prepareUnsignedEthTx(txParams);
const signedTx = yield this.sign(unsignedEthTx, transactionMeta.transaction.from);
const rawTransaction = (0, ethereumjs_util_1.bufferToHex)(signedTx.serialize());
@@ -1223,7 +1247,7 @@ index 3edd9c2..28a1c63 100644
});
}
/**
-@@ -535,9 +377,7 @@ class TransactionController extends base_controller_1.BaseController {
+@@ -535,9 +393,7 @@ class TransactionController extends base_controller_1.BaseController {
const unsignedEthTx = this.prepareUnsignedEthTx(txParams);
const signedTx = yield this.sign(unsignedEthTx, transactionMeta.transaction.from);
const rawTransaction = (0, ethereumjs_util_1.bufferToHex)(signedTx.serialize());
@@ -1234,7 +1258,16 @@ index 3edd9c2..28a1c63 100644
const baseTransactionMeta = Object.assign(Object.assign({}, transactionMeta), { id: (0, uuid_1.v1)(), time: Date.now(), transactionHash });
const newTransactionMeta = newMaxFeePerGas && newMaxPriorityFeePerGas
? Object.assign(Object.assign({}, baseTransactionMeta), { transaction: Object.assign(Object.assign({}, transactionMeta.transaction), { maxFeePerGas: newMaxFeePerGas, maxPriorityFeePerGas: newMaxPriorityFeePerGas }) }) : Object.assign(Object.assign({}, baseTransactionMeta), { transaction: Object.assign(Object.assign({}, transactionMeta.transaction), { gasPrice: newGasPrice }) });
-@@ -661,6 +501,23 @@ class TransactionController extends base_controller_1.BaseController {
+@@ -596,6 +452,8 @@ class TransactionController extends base_controller_1.BaseController {
+ }
+ catch (error) {
+ estimateGasError = utils_1.ESTIMATE_GAS_ERROR;
++ // Fallback to 95% of the block gasLimit.
++ gasHex = estimatedTransaction.gas;
+ }
+ // 4. Pad estimated gas without exceeding the most recent block gasLimit. If the network is a
+ // a custom network then return the eth_estimateGas value.
+@@ -661,6 +519,23 @@ class TransactionController extends base_controller_1.BaseController {
transactions[index] = transactionMeta;
this.update({ transactions: this.trimTransactionsForState(transactions) });
}
@@ -1258,7 +1291,7 @@ index 3edd9c2..28a1c63 100644
/**
* Removes all transactions from state, optionally based on the current network.
*
-@@ -686,67 +543,15 @@ class TransactionController extends base_controller_1.BaseController {
+@@ -686,67 +561,15 @@ class TransactionController extends base_controller_1.BaseController {
});
}
/**
@@ -1332,7 +1365,7 @@ index 3edd9c2..28a1c63 100644
});
}
/**
-@@ -765,7 +570,9 @@ class TransactionController extends base_controller_1.BaseController {
+@@ -765,7 +588,9 @@ class TransactionController extends base_controller_1.BaseController {
*/
trimTransactionsForState(transactions) {
const nonceNetworkSet = new Set();
@@ -1343,7 +1376,7 @@ index 3edd9c2..28a1c63 100644
const { chainId, networkID, status, transaction, time } = tx;
if (transaction) {
const key = `${transaction.nonce}-${chainId ? (0, controller_utils_1.convertHexToDecimal)(chainId) : networkID}-${new Date(time).toDateString()}`;
-@@ -780,7 +587,7 @@ class TransactionController extends base_controller_1.BaseController {
+@@ -780,7 +605,7 @@ class TransactionController extends base_controller_1.BaseController {
}
return false;
});
@@ -1352,7 +1385,7 @@ index 3edd9c2..28a1c63 100644
return txsToKeep;
}
/**
-@@ -790,10 +597,10 @@ class TransactionController extends base_controller_1.BaseController {
+@@ -790,10 +615,10 @@ class TransactionController extends base_controller_1.BaseController {
* @returns Whether the transaction is in a final state.
*/
isFinalState(status) {
@@ -1367,7 +1400,7 @@ index 3edd9c2..28a1c63 100644
}
/**
* Method to verify the state of a transaction using the Blockchain as a source of truth.
-@@ -805,7 +612,7 @@ class TransactionController extends base_controller_1.BaseController {
+@@ -805,7 +630,7 @@ class TransactionController extends base_controller_1.BaseController {
return __awaiter(this, void 0, void 0, function* () {
const { status, transactionHash } = meta;
switch (status) {
@@ -1376,7 +1409,7 @@ index 3edd9c2..28a1c63 100644
const txReceipt = yield (0, controller_utils_1.query)(this.ethQuery, 'getTransactionReceipt', [
transactionHash,
]);
-@@ -822,7 +629,7 @@ class TransactionController extends base_controller_1.BaseController {
+@@ -822,7 +647,7 @@ class TransactionController extends base_controller_1.BaseController {
return [meta, false];
}
return [meta, true];
@@ -1385,7 +1418,7 @@ index 3edd9c2..28a1c63 100644
const txObj = yield (0, controller_utils_1.query)(this.ethQuery, 'getTransactionByHash', [
transactionHash,
]);
-@@ -837,9 +644,17 @@ class TransactionController extends base_controller_1.BaseController {
+@@ -837,9 +662,17 @@ class TransactionController extends base_controller_1.BaseController {
}
/* istanbul ignore next */
if (txObj === null || txObj === void 0 ? void 0 : txObj.blockNumber) {
@@ -1406,7 +1439,7 @@ index 3edd9c2..28a1c63 100644
}
return [meta, false];
default:
-@@ -868,128 +683,233 @@ class TransactionController extends base_controller_1.BaseController {
+@@ -868,128 +701,236 @@ class TransactionController extends base_controller_1.BaseController {
return Number(txReceipt.status) === 0;
});
}
@@ -1735,10 +1768,12 @@ index 3edd9c2..28a1c63 100644
+ if (submitHistory.length > SUBMIT_HISTORY_LIMIT) {
+ submitHistory.pop();
}
-- }
++ this.update({ submitHistory });
+ }
- getApprovalId(txMeta) {
- return String(txMeta.id);
-+ this.update({ submitHistory });
++ getGasFeeFlows() {
++ return [new LineaGasFeeFlow_1.LineaGasFeeFlow()];
}
}
exports.TransactionController = TransactionController;
@@ -2203,14 +2238,496 @@ index 0000000..0757847
+}
+//# sourceMappingURL=etherscan.js.map
\ No newline at end of file
+diff --git a/node_modules/@metamask/transaction-controller/dist/gas-flows/DefaultGasFeeFlow.d.ts b/node_modules/@metamask/transaction-controller/dist/gas-flows/DefaultGasFeeFlow.d.ts
+new file mode 100644
+index 0000000..325802b
+--- /dev/null
++++ b/node_modules/@metamask/transaction-controller/dist/gas-flows/DefaultGasFeeFlow.d.ts
+@@ -0,0 +1,10 @@
++import type { GasFeeFlow, GasFeeFlowRequest, GasFeeFlowResponse, TransactionMeta } from '../types';
++/**
++ * The standard implementation of a gas fee flow that obtains gas fee estimates using only the GasFeeController.
++ */
++export declare class DefaultGasFeeFlow implements GasFeeFlow {
++ #private;
++ matchesTransaction(_transactionMeta: TransactionMeta): boolean;
++ getGasFees(request: GasFeeFlowRequest): Promise;
++}
++//# sourceMappingURL=DefaultGasFeeFlow.d.ts.map
+\ No newline at end of file
+diff --git a/node_modules/@metamask/transaction-controller/dist/gas-flows/DefaultGasFeeFlow.d.ts.map b/node_modules/@metamask/transaction-controller/dist/gas-flows/DefaultGasFeeFlow.d.ts.map
+new file mode 100644
+index 0000000..1a801e5
+--- /dev/null
++++ b/node_modules/@metamask/transaction-controller/dist/gas-flows/DefaultGasFeeFlow.d.ts.map
+@@ -0,0 +1 @@
++{"version":3,"file":"DefaultGasFeeFlow.d.ts","sourceRoot":"","sources":["../../src/gas-flows/DefaultGasFeeFlow.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAGV,UAAU,EACV,iBAAiB,EACjB,kBAAkB,EAClB,eAAe,EAChB,MAAM,UAAU,CAAC;AAiBlB;;GAEG;AACH,qBAAa,iBAAkB,YAAW,UAAU;;IAClD,kBAAkB,CAAC,gBAAgB,EAAE,eAAe,GAAG,OAAO;IAIxD,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC;CA4E1E"}
+\ No newline at end of file
+diff --git a/node_modules/@metamask/transaction-controller/dist/gas-flows/DefaultGasFeeFlow.js b/node_modules/@metamask/transaction-controller/dist/gas-flows/DefaultGasFeeFlow.js
+new file mode 100644
+index 0000000..2b4ffd9
+--- /dev/null
++++ b/node_modules/@metamask/transaction-controller/dist/gas-flows/DefaultGasFeeFlow.js
+@@ -0,0 +1,79 @@
++"use strict";
++var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
++ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
++ return new (P || (P = Promise))(function (resolve, reject) {
++ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
++ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
++ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
++ step((generator = generator.apply(thisArg, _arguments || [])).next());
++ });
++};
++var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
++ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
++ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
++ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
++};
++var _DefaultGasFeeFlow_instances, _DefaultGasFeeFlow_getEstimateLevel, _DefaultGasFeeFlow_getFeeMarketLevel, _DefaultGasFeeFlow_getLegacyLevel, _DefaultGasFeeFlow_gweiDecimalToWeiHex;
++Object.defineProperty(exports, "__esModule", { value: true });
++exports.DefaultGasFeeFlow = void 0;
++const gas_fee_controller_1 = require("@metamask/gas-fee-controller");
++const utils_1 = require("@metamask/utils");
++const controller_utils_1 = require("@metamask/controller-utils");
++const logger_1 = require("../logger");
++const types_1 = require("../types");
++const log = (0, utils_1.createModuleLogger)(logger_1.projectLogger, 'default-gas-fee-flow');
++/**
++ * The standard implementation of a gas fee flow that obtains gas fee estimates using only the GasFeeController.
++ */
++class DefaultGasFeeFlow {
++ constructor() {
++ _DefaultGasFeeFlow_instances.add(this);
++ }
++ matchesTransaction(_transactionMeta) {
++ return true;
++ }
++ getGasFees(request) {
++ return __awaiter(this, void 0, void 0, function* () {
++ const { getGasFeeControllerEstimates } = request;
++ const { gasEstimateType, gasFeeEstimates } = yield getGasFeeControllerEstimates();
++ if (gasEstimateType === gas_fee_controller_1.GAS_ESTIMATE_TYPES.FEE_MARKET) {
++ log('Using fee market estimates', gasFeeEstimates);
++ }
++ else if (gasEstimateType === gas_fee_controller_1.GAS_ESTIMATE_TYPES.LEGACY) {
++ log('Using legacy estimates', gasFeeEstimates);
++ }
++ else {
++ throw new Error(`'No gas fee estimates available`);
++ }
++ const estimates = Object.values(types_1.GasFeeEstimateLevel).reduce((result, level) => (Object.assign(Object.assign({}, result), { [level]: __classPrivateFieldGet(this, _DefaultGasFeeFlow_instances, "m", _DefaultGasFeeFlow_getEstimateLevel).call(this, {
++ gasEstimateType,
++ gasFeeEstimates,
++ level,
++ }) })), {});
++ return { estimates };
++ });
++ }
++}
++exports.DefaultGasFeeFlow = DefaultGasFeeFlow;
++_DefaultGasFeeFlow_instances = new WeakSet(), _DefaultGasFeeFlow_getEstimateLevel = function _DefaultGasFeeFlow_getEstimateLevel({ gasEstimateType, gasFeeEstimates, level, }) {
++ if (gasEstimateType === gas_fee_controller_1.GAS_ESTIMATE_TYPES.FEE_MARKET) {
++ return __classPrivateFieldGet(this, _DefaultGasFeeFlow_instances, "m", _DefaultGasFeeFlow_getFeeMarketLevel).call(this, gasFeeEstimates, level);
++ }
++ return __classPrivateFieldGet(this, _DefaultGasFeeFlow_instances, "m", _DefaultGasFeeFlow_getLegacyLevel).call(this, gasFeeEstimates, level);
++}, _DefaultGasFeeFlow_getFeeMarketLevel = function _DefaultGasFeeFlow_getFeeMarketLevel(gasFeeEstimates, level) {
++ const maxFeePerGas = __classPrivateFieldGet(this, _DefaultGasFeeFlow_instances, "m", _DefaultGasFeeFlow_gweiDecimalToWeiHex).call(this, gasFeeEstimates[level].suggestedMaxFeePerGas);
++ const maxPriorityFeePerGas = __classPrivateFieldGet(this, _DefaultGasFeeFlow_instances, "m", _DefaultGasFeeFlow_gweiDecimalToWeiHex).call(this, gasFeeEstimates[level].suggestedMaxPriorityFeePerGas);
++ return {
++ maxFeePerGas,
++ maxPriorityFeePerGas,
++ };
++}, _DefaultGasFeeFlow_getLegacyLevel = function _DefaultGasFeeFlow_getLegacyLevel(gasFeeEstimates, level) {
++ const gasPrice = __classPrivateFieldGet(this, _DefaultGasFeeFlow_instances, "m", _DefaultGasFeeFlow_gweiDecimalToWeiHex).call(this, gasFeeEstimates[level]);
++ return {
++ maxFeePerGas: gasPrice,
++ maxPriorityFeePerGas: gasPrice,
++ };
++}, _DefaultGasFeeFlow_gweiDecimalToWeiHex = function _DefaultGasFeeFlow_gweiDecimalToWeiHex(gweiDecimal) {
++ return (0, controller_utils_1.toHex)((0, controller_utils_1.gweiDecToWEIBN)(gweiDecimal));
++};
++//# sourceMappingURL=DefaultGasFeeFlow.js.map
+\ No newline at end of file
+diff --git a/node_modules/@metamask/transaction-controller/dist/gas-flows/DefaultGasFeeFlow.js.map b/node_modules/@metamask/transaction-controller/dist/gas-flows/DefaultGasFeeFlow.js.map
+new file mode 100644
+index 0000000..f95c0a5
+--- /dev/null
++++ b/node_modules/@metamask/transaction-controller/dist/gas-flows/DefaultGasFeeFlow.js.map
+@@ -0,0 +1 @@
++{"version":3,"file":"DefaultGasFeeFlow.js","sourceRoot":"","sources":["../../src/gas-flows/DefaultGasFeeFlow.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AAIA,qEAAkE;AAClE,2CAA0D;AAE1D,iEAAmE;AACnE,sCAA0C;AAS1C,oCAA+C;AAE/C,MAAM,GAAG,GAAG,IAAA,0BAAkB,EAAC,sBAAa,EAAE,sBAAsB,CAAC,CAAC;AActE;;GAEG;AACH,MAAa,iBAAiB;IAA9B;;IAiFA,CAAC;IAhFC,kBAAkB,CAAC,gBAAiC;QAClD,OAAO,IAAI,CAAC;IACd,CAAC;IAEK,UAAU,CAAC,OAA0B;;YACzC,MAAM,EAAE,4BAA4B,EAAE,GAAG,OAAO,CAAC;YAEjD,MAAM,EAAE,eAAe,EAAE,eAAe,EAAE,GACxC,MAAM,4BAA4B,EAAE,CAAC;YAEvC,IAAI,eAAe,KAAK,uCAAkB,CAAC,UAAU,EAAE;gBACrD,GAAG,CAAC,4BAA4B,EAAE,eAAe,CAAC,CAAC;aACpD;iBAAM,IAAI,eAAe,KAAK,uCAAkB,CAAC,MAAM,EAAE;gBACxD,GAAG,CAAC,wBAAwB,EAAE,eAAe,CAAC,CAAC;aAChD;iBAAM;gBACL,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;aACpD;YAED,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,2BAAmB,CAAC,CAAC,MAAM,CACzD,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,iCACd,MAAM,KACT,CAAC,KAAK,CAAC,EAAE,uBAAA,IAAI,yEAAkB,MAAtB,IAAI,EAAmB;oBAC9B,eAAe;oBACf,eAAe;oBACf,KAAK;iBAC8D,CAAC,IACtE,EACF,EAAqB,CACtB,CAAC;YAEF,OAAO,EAAE,SAAS,EAAE,CAAC;QACvB,CAAC;KAAA;CAiDF;AAjFD,8CAiFC;iIA/CmB,EAChB,eAAe,EACf,eAAe,EACf,KAAK,GAG0B;IAC/B,IAAI,eAAe,KAAK,uCAAkB,CAAC,UAAU,EAAE;QACrD,OAAO,uBAAA,IAAI,0EAAmB,MAAvB,IAAI,EAAoB,eAAe,EAAE,KAAK,CAAC,CAAC;KACxD;IAED,OAAO,uBAAA,IAAI,uEAAgB,MAApB,IAAI,EAAiB,eAAe,EAAE,KAAK,CAAC,CAAC;AACtD,CAAC,uFAGC,eAA0C,EAC1C,KAA0B;IAE1B,MAAM,YAAY,GAAG,uBAAA,IAAI,4EAAqB,MAAzB,IAAI,EACvB,eAAe,CAAC,KAAK,CAAC,CAAC,qBAAqB,CAC7C,CAAC;IAEF,MAAM,oBAAoB,GAAG,uBAAA,IAAI,4EAAqB,MAAzB,IAAI,EAC/B,eAAe,CAAC,KAAK,CAAC,CAAC,6BAA6B,CACrD,CAAC;IAEF,OAAO;QACL,YAAY;QACZ,oBAAoB;KACrB,CAAC;AACJ,CAAC,iFAGC,eAAuC,EACvC,KAA0B;IAE1B,MAAM,QAAQ,GAAG,uBAAA,IAAI,4EAAqB,MAAzB,IAAI,EAAsB,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC;IAEnE,OAAO;QACL,YAAY,EAAE,QAAQ;QACtB,oBAAoB,EAAE,QAAQ;KAC/B,CAAC;AACJ,CAAC,2FAEoB,WAAmB;IACtC,OAAO,IAAA,wBAAK,EAAC,IAAA,iCAAc,EAAC,WAAW,CAAC,CAAC,CAAC;AAC5C,CAAC","sourcesContent":["import type {\n LegacyGasPriceEstimate,\n GasFeeEstimates as FeeMarketGasPriceEstimate,\n} from '@metamask/gas-fee-controller';\nimport { GAS_ESTIMATE_TYPES } from '@metamask/gas-fee-controller';\nimport { createModuleLogger, Hex } from '@metamask/utils';\n\nimport { gweiDecToWEIBN, toHex } from '@metamask/controller-utils';\nimport { projectLogger } from '../logger';\nimport type {\n GasFeeEstimates,\n GasFeeEstimatesForLevel,\n GasFeeFlow,\n GasFeeFlowRequest,\n GasFeeFlowResponse,\n TransactionMeta,\n} from '../types';\nimport { GasFeeEstimateLevel } from '../types';\n\nconst log = createModuleLogger(projectLogger, 'default-gas-fee-flow');\n\ntype FeeMarketGetEstimateLevelRequest = {\n gasEstimateType: 'fee-market';\n gasFeeEstimates: FeeMarketGasPriceEstimate;\n level: GasFeeEstimateLevel;\n};\n\ntype LegacyGetEstimateLevelRequest = {\n gasEstimateType: 'legacy';\n gasFeeEstimates: LegacyGasPriceEstimate;\n level: GasFeeEstimateLevel;\n};\n\n/**\n * The standard implementation of a gas fee flow that obtains gas fee estimates using only the GasFeeController.\n */\nexport class DefaultGasFeeFlow implements GasFeeFlow {\n matchesTransaction(_transactionMeta: TransactionMeta): boolean {\n return true;\n }\n\n async getGasFees(request: GasFeeFlowRequest): Promise {\n const { getGasFeeControllerEstimates } = request;\n\n const { gasEstimateType, gasFeeEstimates } =\n await getGasFeeControllerEstimates();\n\n if (gasEstimateType === GAS_ESTIMATE_TYPES.FEE_MARKET) {\n log('Using fee market estimates', gasFeeEstimates);\n } else if (gasEstimateType === GAS_ESTIMATE_TYPES.LEGACY) {\n log('Using legacy estimates', gasFeeEstimates);\n } else {\n throw new Error(`'No gas fee estimates available`);\n }\n\n const estimates = Object.values(GasFeeEstimateLevel).reduce(\n (result, level) => ({\n ...result,\n [level]: this.#getEstimateLevel({\n gasEstimateType,\n gasFeeEstimates,\n level,\n } as FeeMarketGetEstimateLevelRequest | LegacyGetEstimateLevelRequest),\n }),\n {} as GasFeeEstimates,\n );\n\n return { estimates };\n }\n\n #getEstimateLevel({\n gasEstimateType,\n gasFeeEstimates,\n level,\n }:\n | FeeMarketGetEstimateLevelRequest\n | LegacyGetEstimateLevelRequest): GasFeeEstimatesForLevel {\n if (gasEstimateType === GAS_ESTIMATE_TYPES.FEE_MARKET) {\n return this.#getFeeMarketLevel(gasFeeEstimates, level);\n }\n\n return this.#getLegacyLevel(gasFeeEstimates, level);\n }\n\n #getFeeMarketLevel(\n gasFeeEstimates: FeeMarketGasPriceEstimate,\n level: GasFeeEstimateLevel,\n ): GasFeeEstimatesForLevel {\n const maxFeePerGas = this.#gweiDecimalToWeiHex(\n gasFeeEstimates[level].suggestedMaxFeePerGas,\n );\n\n const maxPriorityFeePerGas = this.#gweiDecimalToWeiHex(\n gasFeeEstimates[level].suggestedMaxPriorityFeePerGas,\n );\n\n return {\n maxFeePerGas,\n maxPriorityFeePerGas,\n };\n }\n\n #getLegacyLevel(\n gasFeeEstimates: LegacyGasPriceEstimate,\n level: GasFeeEstimateLevel,\n ): GasFeeEstimatesForLevel {\n const gasPrice = this.#gweiDecimalToWeiHex(gasFeeEstimates[level]);\n\n return {\n maxFeePerGas: gasPrice,\n maxPriorityFeePerGas: gasPrice,\n };\n }\n\n #gweiDecimalToWeiHex(gweiDecimal: string): Hex {\n return toHex(gweiDecToWEIBN(gweiDecimal));\n }\n}\n"]}
+\ No newline at end of file
+diff --git a/node_modules/@metamask/transaction-controller/dist/gas-flows/LineaGasFeeFlow.d.ts b/node_modules/@metamask/transaction-controller/dist/gas-flows/LineaGasFeeFlow.d.ts
+new file mode 100644
+index 0000000..ce728db
+--- /dev/null
++++ b/node_modules/@metamask/transaction-controller/dist/gas-flows/LineaGasFeeFlow.d.ts
+@@ -0,0 +1,12 @@
++import type { GasFeeFlow, GasFeeFlowRequest, GasFeeFlowResponse, TransactionMeta } from '../types';
++/**
++ * Implementation of a gas fee flow specific to Linea networks that obtains gas fee estimates using:
++ * - The `linea_estimateGas` RPC method to obtain the base fee and lowest priority fee.
++ * - Static multipliers to increase the base and priority fees.
++ */
++export declare class LineaGasFeeFlow implements GasFeeFlow {
++ #private;
++ matchesTransaction(transactionMeta: TransactionMeta): boolean;
++ getGasFees(request: GasFeeFlowRequest): Promise;
++}
++//# sourceMappingURL=LineaGasFeeFlow.d.ts.map
+\ No newline at end of file
+diff --git a/node_modules/@metamask/transaction-controller/dist/gas-flows/LineaGasFeeFlow.d.ts.map b/node_modules/@metamask/transaction-controller/dist/gas-flows/LineaGasFeeFlow.d.ts.map
+new file mode 100644
+index 0000000..56042e1
+--- /dev/null
++++ b/node_modules/@metamask/transaction-controller/dist/gas-flows/LineaGasFeeFlow.d.ts.map
+@@ -0,0 +1 @@
++{"version":3,"file":"LineaGasFeeFlow.d.ts","sourceRoot":"","sources":["../../src/gas-flows/LineaGasFeeFlow.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAEV,UAAU,EACV,iBAAiB,EACjB,kBAAkB,EAClB,eAAe,EAChB,MAAM,UAAU,CAAC;AAgClB;;;;GAIG;AACH,qBAAa,eAAgB,YAAW,UAAU;;IAChD,kBAAkB,CAAC,eAAe,EAAE,eAAe,GAAG,OAAO;IAIvD,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC;CAqG1E"}
+\ No newline at end of file
+diff --git a/node_modules/@metamask/transaction-controller/dist/gas-flows/LineaGasFeeFlow.js b/node_modules/@metamask/transaction-controller/dist/gas-flows/LineaGasFeeFlow.js
+new file mode 100644
+index 0000000..da048be
+--- /dev/null
++++ b/node_modules/@metamask/transaction-controller/dist/gas-flows/LineaGasFeeFlow.js
+@@ -0,0 +1,111 @@
++"use strict";
++var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
++ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
++ return new (P || (P = Promise))(function (resolve, reject) {
++ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
++ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
++ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
++ step((generator = generator.apply(thisArg, _arguments || [])).next());
++ });
++};
++var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
++ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
++ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
++ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
++};
++var _LineaGasFeeFlow_instances, _LineaGasFeeFlow_getLineaGasFees, _LineaGasFeeFlow_getLineaResponse, _LineaGasFeeFlow_getValuesFromMultipliers, _LineaGasFeeFlow_getMaxFees, _LineaGasFeeFlow_feesToString;
++Object.defineProperty(exports, "__esModule", { value: true });
++exports.LineaGasFeeFlow = void 0;
++const controller_utils_1 = require("@metamask/controller-utils");
++const utils_1 = require("@metamask/utils");
++const logger_1 = require("../logger");
++const types_1 = require("../types");
++const DefaultGasFeeFlow_1 = require("./DefaultGasFeeFlow");
++const log = (0, utils_1.createModuleLogger)(logger_1.projectLogger, 'linea-gas-fee-flow');
++const LINEA_CHAIN_IDS = [
++ controller_utils_1.ChainId['linea-mainnet'],
++ controller_utils_1.ChainId['linea-goerli'],
++];
++const BASE_FEE_MULTIPLIERS = {
++ low: 1,
++ medium: 1.35,
++ high: 1.7,
++};
++const PRIORITY_FEE_MULTIPLIERS = {
++ low: 1,
++ medium: 1.05,
++ high: 1.1,
++};
++/**
++ * Implementation of a gas fee flow specific to Linea networks that obtains gas fee estimates using:
++ * - The `linea_estimateGas` RPC method to obtain the base fee and lowest priority fee.
++ * - Static multipliers to increase the base and priority fees.
++ */
++class LineaGasFeeFlow {
++ constructor() {
++ _LineaGasFeeFlow_instances.add(this);
++ }
++ matchesTransaction(transactionMeta) {
++ return LINEA_CHAIN_IDS.includes(transactionMeta.chainId);
++ }
++ getGasFees(request) {
++ return __awaiter(this, void 0, void 0, function* () {
++ try {
++ return yield __classPrivateFieldGet(this, _LineaGasFeeFlow_instances, "m", _LineaGasFeeFlow_getLineaGasFees).call(this, request);
++ }
++ catch (error) {
++ log('Using default flow as fallback due to error', error);
++ return new DefaultGasFeeFlow_1.DefaultGasFeeFlow().getGasFees(request);
++ }
++ });
++ }
++}
++exports.LineaGasFeeFlow = LineaGasFeeFlow;
++_LineaGasFeeFlow_instances = new WeakSet(), _LineaGasFeeFlow_getLineaGasFees = function _LineaGasFeeFlow_getLineaGasFees(request) {
++ return __awaiter(this, void 0, void 0, function* () {
++ const { ethQuery, transactionMeta } = request;
++ const lineaResponse = yield __classPrivateFieldGet(this, _LineaGasFeeFlow_instances, "m", _LineaGasFeeFlow_getLineaResponse).call(this, transactionMeta, ethQuery);
++ log('Received Linea response', lineaResponse);
++ const baseFees = __classPrivateFieldGet(this, _LineaGasFeeFlow_instances, "m", _LineaGasFeeFlow_getValuesFromMultipliers).call(this, lineaResponse.baseFeePerGas, BASE_FEE_MULTIPLIERS);
++ log('Generated base fees', __classPrivateFieldGet(this, _LineaGasFeeFlow_instances, "m", _LineaGasFeeFlow_feesToString).call(this, baseFees));
++ const priorityFees = __classPrivateFieldGet(this, _LineaGasFeeFlow_instances, "m", _LineaGasFeeFlow_getValuesFromMultipliers).call(this, lineaResponse.priorityFeePerGas, PRIORITY_FEE_MULTIPLIERS);
++ log('Generated priority fees', __classPrivateFieldGet(this, _LineaGasFeeFlow_instances, "m", _LineaGasFeeFlow_feesToString).call(this, priorityFees));
++ const maxFees = __classPrivateFieldGet(this, _LineaGasFeeFlow_instances, "m", _LineaGasFeeFlow_getMaxFees).call(this, baseFees, priorityFees);
++ log('Generated max fees', __classPrivateFieldGet(this, _LineaGasFeeFlow_instances, "m", _LineaGasFeeFlow_feesToString).call(this, maxFees));
++ const estimates = Object.values(types_1.GasFeeEstimateLevel).reduce((result, level) => (Object.assign(Object.assign({}, result), { [level]: {
++ maxFeePerGas: (0, controller_utils_1.toHex)(maxFees[level]),
++ maxPriorityFeePerGas: (0, controller_utils_1.toHex)(priorityFees[level]),
++ } })), {});
++ return { estimates };
++ });
++}, _LineaGasFeeFlow_getLineaResponse = function _LineaGasFeeFlow_getLineaResponse(transactionMeta, ethQuery) {
++ return (0, controller_utils_1.query)(ethQuery, 'linea_estimateGas', [
++ {
++ from: transactionMeta.transaction.from,
++ to: transactionMeta.transaction.to,
++ value: transactionMeta.transaction.value,
++ input: transactionMeta.transaction.data,
++ // Required in request but no impact on response.
++ gasPrice: '0x100000000',
++ },
++ ]);
++}, _LineaGasFeeFlow_getValuesFromMultipliers = function _LineaGasFeeFlow_getValuesFromMultipliers(value, multipliers) {
++ const base = (0, controller_utils_1.hexToBN)(value);
++ const low = base.muln(multipliers.low);
++ const medium = base.muln(multipliers.medium);
++ const high = base.muln(multipliers.high);
++ return {
++ low,
++ medium,
++ high,
++ };
++}, _LineaGasFeeFlow_getMaxFees = function _LineaGasFeeFlow_getMaxFees(baseFees, priorityFees) {
++ return {
++ low: baseFees.low.add(priorityFees.low),
++ medium: baseFees.medium.add(priorityFees.medium),
++ high: baseFees.high.add(priorityFees.high),
++ };
++}, _LineaGasFeeFlow_feesToString = function _LineaGasFeeFlow_feesToString(fees) {
++ return Object.values(types_1.GasFeeEstimateLevel).map((level) => fees[level].toString(10));
++};
++//# sourceMappingURL=LineaGasFeeFlow.js.map
+\ No newline at end of file
+diff --git a/node_modules/@metamask/transaction-controller/dist/gas-flows/LineaGasFeeFlow.js.map b/node_modules/@metamask/transaction-controller/dist/gas-flows/LineaGasFeeFlow.js.map
+new file mode 100644
+index 0000000..4742145
+--- /dev/null
++++ b/node_modules/@metamask/transaction-controller/dist/gas-flows/LineaGasFeeFlow.js.map
+@@ -0,0 +1 @@
++{"version":3,"file":"LineaGasFeeFlow.js","sourceRoot":"","sources":["../../src/gas-flows/LineaGasFeeFlow.ts"],"names":[],"mappings":"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","sourcesContent":["import { ChainId, hexToBN, query, toHex } from '@metamask/controller-utils';\nimport { createModuleLogger, type Hex } from '@metamask/utils';\nimport type { BN } from 'ethereumjs-util';\n\nimport { projectLogger } from '../logger';\nimport type {\n GasFeeEstimates,\n GasFeeFlow,\n GasFeeFlowRequest,\n GasFeeFlowResponse,\n TransactionMeta,\n} from '../types';\nimport { GasFeeEstimateLevel } from '../types';\nimport { DefaultGasFeeFlow } from './DefaultGasFeeFlow';\n\ntype LineaEstimateGasResponse = {\n baseFeePerGas: Hex;\n priorityFeePerGas: Hex;\n};\n\ntype FeesByLevel = {\n [key in GasFeeEstimateLevel]: BN;\n};\n\nconst log = createModuleLogger(projectLogger, 'linea-gas-fee-flow');\n\nconst LINEA_CHAIN_IDS: Hex[] = [\n ChainId['linea-mainnet'],\n ChainId['linea-goerli'],\n];\n\nconst BASE_FEE_MULTIPLIERS = {\n low: 1,\n medium: 1.35,\n high: 1.7,\n};\n\nconst PRIORITY_FEE_MULTIPLIERS = {\n low: 1,\n medium: 1.05,\n high: 1.1,\n};\n\n/**\n * Implementation of a gas fee flow specific to Linea networks that obtains gas fee estimates using:\n * - The `linea_estimateGas` RPC method to obtain the base fee and lowest priority fee.\n * - Static multipliers to increase the base and priority fees.\n */\nexport class LineaGasFeeFlow implements GasFeeFlow {\n matchesTransaction(transactionMeta: TransactionMeta): boolean {\n return LINEA_CHAIN_IDS.includes(transactionMeta.chainId as Hex);\n }\n\n async getGasFees(request: GasFeeFlowRequest): Promise {\n try {\n return await this.#getLineaGasFees(request);\n } catch (error) {\n log('Using default flow as fallback due to error', error);\n return new DefaultGasFeeFlow().getGasFees(request);\n }\n }\n\n async #getLineaGasFees(\n request: GasFeeFlowRequest,\n ): Promise {\n const { ethQuery, transactionMeta } = request;\n\n const lineaResponse = await this.#getLineaResponse(\n transactionMeta,\n ethQuery,\n );\n\n log('Received Linea response', lineaResponse);\n\n const baseFees = this.#getValuesFromMultipliers(\n lineaResponse.baseFeePerGas,\n BASE_FEE_MULTIPLIERS,\n );\n\n log('Generated base fees', this.#feesToString(baseFees));\n\n const priorityFees = this.#getValuesFromMultipliers(\n lineaResponse.priorityFeePerGas,\n PRIORITY_FEE_MULTIPLIERS,\n );\n\n log('Generated priority fees', this.#feesToString(priorityFees));\n\n const maxFees = this.#getMaxFees(baseFees, priorityFees);\n\n log('Generated max fees', this.#feesToString(maxFees));\n\n const estimates = Object.values(GasFeeEstimateLevel).reduce(\n (result, level) => ({\n ...result,\n [level]: {\n maxFeePerGas: toHex(maxFees[level]),\n maxPriorityFeePerGas: toHex(priorityFees[level]),\n },\n }),\n {} as GasFeeEstimates,\n );\n\n return { estimates };\n }\n\n #getLineaResponse(\n transactionMeta: TransactionMeta,\n ethQuery: any,\n ): Promise {\n return query(ethQuery, 'linea_estimateGas', [\n {\n from: transactionMeta.transaction.from,\n to: transactionMeta.transaction.to,\n value: transactionMeta.transaction.value,\n input: transactionMeta.transaction.data,\n // Required in request but no impact on response.\n gasPrice: '0x100000000',\n },\n ]);\n }\n\n #getValuesFromMultipliers(\n value: Hex,\n multipliers: { low: number; medium: number; high: number },\n ): FeesByLevel {\n const base = hexToBN(value);\n const low = base.muln(multipliers.low);\n const medium = base.muln(multipliers.medium);\n const high = base.muln(multipliers.high);\n\n return {\n low,\n medium,\n high,\n };\n }\n\n #getMaxFees(\n baseFees: Record,\n priorityFees: Record,\n ): FeesByLevel {\n return {\n low: baseFees.low.add(priorityFees.low),\n medium: baseFees.medium.add(priorityFees.medium),\n high: baseFees.high.add(priorityFees.high),\n };\n }\n\n #feesToString(fees: FeesByLevel) {\n return Object.values(GasFeeEstimateLevel).map((level) =>\n fees[level].toString(10),\n );\n }\n}\n"]}
+\ No newline at end of file
+diff --git a/node_modules/@metamask/transaction-controller/dist/helpers/GasFeePoller.d.ts b/node_modules/@metamask/transaction-controller/dist/helpers/GasFeePoller.d.ts
+new file mode 100644
+index 0000000..dfaddc5
+--- /dev/null
++++ b/node_modules/@metamask/transaction-controller/dist/helpers/GasFeePoller.d.ts
+@@ -0,0 +1,32 @@
++///
++import EventEmitter from 'events';
++import type { GasFeeState } from '@metamask/gas-fee-controller';
++import type { GasFeeFlow } from '../types';
++import { type TransactionMeta } from '../types';
++/**
++ * Automatically polls and updates suggested gas fees on unapproved transactions.
++ */
++export declare class GasFeePoller {
++ #private;
++ hub: EventEmitter;
++ /**
++ * Constructs a new instance of the GasFeePoller.
++ *
++ * @param options - The options for this instance.
++ * @param options.gasFeeFlows - The gas fee flows to use to obtain suitable gas fees.
++ * @param options.getChainIds - Callback to specify the chain IDs to monitor.
++ * @param options.getEthQuery - Callback to obtain an EthQuery instance.
++ * @param options.getGasFeeControllerEstimates - Callback to obtain the default fee estimates.
++ * @param options.getTransactions - Callback to obtain the transaction data.
++ * @param options.onStateChange - Callback to register a listener for controller state changes.
++ */
++ constructor({ gasFeeFlows, getChainIds, getEthQuery, getGasFeeControllerEstimates, getTransactions, onStateChange, }: {
++ gasFeeFlows: GasFeeFlow[];
++ getChainIds: () => string[];
++ getEthQuery: () => any;
++ getGasFeeControllerEstimates: () => Promise;
++ getTransactions: () => TransactionMeta[];
++ onStateChange: (listener: () => void) => void;
++ });
++}
++//# sourceMappingURL=GasFeePoller.d.ts.map
+\ No newline at end of file
+diff --git a/node_modules/@metamask/transaction-controller/dist/helpers/GasFeePoller.d.ts.map b/node_modules/@metamask/transaction-controller/dist/helpers/GasFeePoller.d.ts.map
+new file mode 100644
+index 0000000..8ccb3d4
+--- /dev/null
++++ b/node_modules/@metamask/transaction-controller/dist/helpers/GasFeePoller.d.ts.map
+@@ -0,0 +1 @@
++{"version":3,"file":"GasFeePoller.d.ts","sourceRoot":"","sources":["../../src/helpers/GasFeePoller.ts"],"names":[],"mappings":";AAAA,OAAO,YAAY,MAAM,QAAQ,CAAC;AAClC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAIhE,OAAO,KAAK,EAAE,UAAU,EAAqB,MAAM,UAAU,CAAC;AAC9D,OAAO,EAAqB,KAAK,eAAe,EAAE,MAAM,UAAU,CAAC;AAOnE;;GAEG;AACH,qBAAa,YAAY;;IACvB,GAAG,EAAE,YAAY,CAAsB;IAgBvC;;;;;;;;;;OAUG;gBACS,EACV,WAAW,EACX,WAAW,EACX,WAAW,EACX,4BAA4B,EAC5B,eAAe,EACf,aAAa,GACd,EAAE;QACD,WAAW,EAAE,UAAU,EAAE,CAAC;QAC1B,WAAW,EAAE,MAAM,MAAM,EAAE,CAAC;QAC5B,WAAW,EAAE,MAAM,GAAG,CAAC;QACvB,4BAA4B,EAAE,MAAM,OAAO,CAAC,WAAW,CAAC,CAAC;QACzD,eAAe,EAAE,MAAM,eAAe,EAAE,CAAC;QACzC,aAAa,EAAE,CAAC,QAAQ,EAAE,MAAM,IAAI,KAAK,IAAI,CAAC;KAC/C;CA+HF"}
+\ No newline at end of file
+diff --git a/node_modules/@metamask/transaction-controller/dist/helpers/GasFeePoller.js b/node_modules/@metamask/transaction-controller/dist/helpers/GasFeePoller.js
+new file mode 100644
+index 0000000..00676ac
+--- /dev/null
++++ b/node_modules/@metamask/transaction-controller/dist/helpers/GasFeePoller.js
+@@ -0,0 +1,147 @@
++"use strict";
++var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
++ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
++ return new (P || (P = Promise))(function (resolve, reject) {
++ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
++ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
++ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
++ step((generator = generator.apply(thisArg, _arguments || [])).next());
++ });
++};
++var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
++ if (kind === "m") throw new TypeError("Private method is not writable");
++ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
++ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
++ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
++};
++var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
++ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
++ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
++ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
++};
++var __importDefault = (this && this.__importDefault) || function (mod) {
++ return (mod && mod.__esModule) ? mod : { "default": mod };
++};
++var _GasFeePoller_instances, _GasFeePoller_gasFeeFlows, _GasFeePoller_getChainIds, _GasFeePoller_getEthQuery, _GasFeePoller_getGasFeeControllerEstimates, _GasFeePoller_getTransactions, _GasFeePoller_timeout, _GasFeePoller_running, _GasFeePoller_start, _GasFeePoller_stop, _GasFeePoller_onTimeout, _GasFeePoller_updateUnapprovedTransactions, _GasFeePoller_updateTransactionSuggestedFees, _GasFeePoller_getUnapprovedTransactions;
++Object.defineProperty(exports, "__esModule", { value: true });
++exports.GasFeePoller = void 0;
++const events_1 = __importDefault(require("events"));
++const utils_1 = require("@metamask/utils");
++const logger_1 = require("../logger");
++const types_1 = require("../types");
++const gas_flow_1 = require("../utils/gas-flow");
++const log = (0, utils_1.createModuleLogger)(logger_1.projectLogger, 'gas-fee-poller');
++const INTERVAL_MILLISECONDS = 10000;
++/**
++ * Automatically polls and updates suggested gas fees on unapproved transactions.
++ */
++class GasFeePoller {
++ /**
++ * Constructs a new instance of the GasFeePoller.
++ *
++ * @param options - The options for this instance.
++ * @param options.gasFeeFlows - The gas fee flows to use to obtain suitable gas fees.
++ * @param options.getChainIds - Callback to specify the chain IDs to monitor.
++ * @param options.getEthQuery - Callback to obtain an EthQuery instance.
++ * @param options.getGasFeeControllerEstimates - Callback to obtain the default fee estimates.
++ * @param options.getTransactions - Callback to obtain the transaction data.
++ * @param options.onStateChange - Callback to register a listener for controller state changes.
++ */
++ constructor({ gasFeeFlows, getChainIds, getEthQuery, getGasFeeControllerEstimates, getTransactions, onStateChange, }) {
++ _GasFeePoller_instances.add(this);
++ this.hub = new events_1.default();
++ _GasFeePoller_gasFeeFlows.set(this, void 0);
++ _GasFeePoller_getChainIds.set(this, void 0);
++ _GasFeePoller_getEthQuery.set(this, void 0);
++ _GasFeePoller_getGasFeeControllerEstimates.set(this, void 0);
++ _GasFeePoller_getTransactions.set(this, void 0);
++ _GasFeePoller_timeout.set(this, void 0);
++ _GasFeePoller_running.set(this, false);
++ __classPrivateFieldSet(this, _GasFeePoller_gasFeeFlows, gasFeeFlows, "f");
++ __classPrivateFieldSet(this, _GasFeePoller_getChainIds, getChainIds, "f");
++ __classPrivateFieldSet(this, _GasFeePoller_getEthQuery, getEthQuery, "f");
++ __classPrivateFieldSet(this, _GasFeePoller_getGasFeeControllerEstimates, getGasFeeControllerEstimates, "f");
++ __classPrivateFieldSet(this, _GasFeePoller_getTransactions, getTransactions, "f");
++ onStateChange(() => {
++ const unapprovedTransactions = __classPrivateFieldGet(this, _GasFeePoller_instances, "m", _GasFeePoller_getUnapprovedTransactions).call(this);
++ if (unapprovedTransactions.length) {
++ __classPrivateFieldGet(this, _GasFeePoller_instances, "m", _GasFeePoller_start).call(this);
++ }
++ else {
++ __classPrivateFieldGet(this, _GasFeePoller_instances, "m", _GasFeePoller_stop).call(this);
++ }
++ });
++ }
++}
++exports.GasFeePoller = GasFeePoller;
++_GasFeePoller_gasFeeFlows = new WeakMap(), _GasFeePoller_getChainIds = new WeakMap(), _GasFeePoller_getEthQuery = new WeakMap(), _GasFeePoller_getGasFeeControllerEstimates = new WeakMap(), _GasFeePoller_getTransactions = new WeakMap(), _GasFeePoller_timeout = new WeakMap(), _GasFeePoller_running = new WeakMap(), _GasFeePoller_instances = new WeakSet(), _GasFeePoller_start = function _GasFeePoller_start() {
++ if (__classPrivateFieldGet(this, _GasFeePoller_running, "f")) {
++ return;
++ }
++ // Intentionally not awaiting since this starts the timeout chain.
++ // eslint-disable-next-line @typescript-eslint/no-floating-promises
++ __classPrivateFieldGet(this, _GasFeePoller_instances, "m", _GasFeePoller_onTimeout).call(this);
++ __classPrivateFieldSet(this, _GasFeePoller_running, true, "f");
++ log('Started polling');
++}, _GasFeePoller_stop = function _GasFeePoller_stop() {
++ if (!__classPrivateFieldGet(this, _GasFeePoller_running, "f")) {
++ return;
++ }
++ clearTimeout(__classPrivateFieldGet(this, _GasFeePoller_timeout, "f"));
++ __classPrivateFieldSet(this, _GasFeePoller_timeout, undefined, "f");
++ __classPrivateFieldSet(this, _GasFeePoller_running, false, "f");
++ log('Stopped polling');
++}, _GasFeePoller_onTimeout = function _GasFeePoller_onTimeout() {
++ return __awaiter(this, void 0, void 0, function* () {
++ yield __classPrivateFieldGet(this, _GasFeePoller_instances, "m", _GasFeePoller_updateUnapprovedTransactions).call(this);
++ // eslint-disable-next-line @typescript-eslint/no-misused-promises
++ __classPrivateFieldSet(this, _GasFeePoller_timeout, setTimeout(() => __classPrivateFieldGet(this, _GasFeePoller_instances, "m", _GasFeePoller_onTimeout).call(this), INTERVAL_MILLISECONDS), "f");
++ });
++}, _GasFeePoller_updateUnapprovedTransactions = function _GasFeePoller_updateUnapprovedTransactions() {
++ return __awaiter(this, void 0, void 0, function* () {
++ const unapprovedTransactions = __classPrivateFieldGet(this, _GasFeePoller_instances, "m", _GasFeePoller_getUnapprovedTransactions).call(this);
++ log('Found unapproved transactions', {
++ count: unapprovedTransactions.length,
++ });
++ const ethQuery = __classPrivateFieldGet(this, _GasFeePoller_getEthQuery, "f").call(this);
++ yield Promise.all(unapprovedTransactions.map((tx) => __classPrivateFieldGet(this, _GasFeePoller_instances, "m", _GasFeePoller_updateTransactionSuggestedFees).call(this, tx, ethQuery)));
++ });
++}, _GasFeePoller_updateTransactionSuggestedFees = function _GasFeePoller_updateTransactionSuggestedFees(transactionMeta, ethQuery) {
++ return __awaiter(this, void 0, void 0, function* () {
++ const gasFeeFlow = (0, gas_flow_1.getGasFeeFlow)(transactionMeta, __classPrivateFieldGet(this, _GasFeePoller_gasFeeFlows, "f"));
++ if (!gasFeeFlow) {
++ log('No gas fee flow found', transactionMeta.id);
++ }
++ else {
++ log('Found gas fee flow', gasFeeFlow.constructor.name, transactionMeta.id);
++ }
++ const request = {
++ ethQuery,
++ getGasFeeControllerEstimates: __classPrivateFieldGet(this, _GasFeePoller_getGasFeeControllerEstimates, "f"),
++ transactionMeta,
++ };
++ if (gasFeeFlow) {
++ try {
++ const response = yield gasFeeFlow.getGasFees(request);
++ transactionMeta.gasFeeEstimates = response.estimates;
++ }
++ catch (error) {
++ log('Failed to get suggested gas fees', transactionMeta.id, error);
++ }
++ }
++ if (!gasFeeFlow && transactionMeta.gasFeeEstimatesLoaded) {
++ return;
++ }
++ transactionMeta.gasFeeEstimatesLoaded = true;
++ this.hub.emit('transaction-updated', transactionMeta, 'GasFeePoller - Suggested gas fees updated');
++ log('Updated suggested gas fees', {
++ gasFeeEstimates: transactionMeta.gasFeeEstimates,
++ transaction: transactionMeta.id,
++ });
++ });
++}, _GasFeePoller_getUnapprovedTransactions = function _GasFeePoller_getUnapprovedTransactions() {
++ const chainIds = __classPrivateFieldGet(this, _GasFeePoller_getChainIds, "f").call(this);
++ return __classPrivateFieldGet(this, _GasFeePoller_getTransactions, "f").call(this).filter((tx) => chainIds.includes(tx.chainId) &&
++ tx.status === types_1.TransactionStatus.unapproved);
++};
++//# sourceMappingURL=GasFeePoller.js.map
+\ No newline at end of file
+diff --git a/node_modules/@metamask/transaction-controller/dist/helpers/GasFeePoller.js.map b/node_modules/@metamask/transaction-controller/dist/helpers/GasFeePoller.js.map
+new file mode 100644
+index 0000000..08b3514
+--- /dev/null
++++ b/node_modules/@metamask/transaction-controller/dist/helpers/GasFeePoller.js.map
+@@ -0,0 +1 @@
++{"version":3,"file":"GasFeePoller.js","sourceRoot":"","sources":["../../src/helpers/GasFeePoller.ts"],"names":[],"mappings":"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","sourcesContent":["import EventEmitter from 'events';\nimport type { GasFeeState } from '@metamask/gas-fee-controller';\nimport { createModuleLogger } from '@metamask/utils';\n\nimport { projectLogger } from '../logger';\nimport type { GasFeeFlow, GasFeeFlowRequest } from '../types';\nimport { TransactionStatus, type TransactionMeta } from '../types';\nimport { getGasFeeFlow } from '../utils/gas-flow';\n\nconst log = createModuleLogger(projectLogger, 'gas-fee-poller');\n\nconst INTERVAL_MILLISECONDS = 10000;\n\n/**\n * Automatically polls and updates suggested gas fees on unapproved transactions.\n */\nexport class GasFeePoller {\n hub: EventEmitter = new EventEmitter();\n\n #gasFeeFlows: GasFeeFlow[];\n\n #getChainIds: () => string[];\n\n #getEthQuery: () => any;\n\n #getGasFeeControllerEstimates: () => Promise;\n\n #getTransactions: () => TransactionMeta[];\n\n #timeout: any;\n\n #running = false;\n\n /**\n * Constructs a new instance of the GasFeePoller.\n *\n * @param options - The options for this instance.\n * @param options.gasFeeFlows - The gas fee flows to use to obtain suitable gas fees.\n * @param options.getChainIds - Callback to specify the chain IDs to monitor.\n * @param options.getEthQuery - Callback to obtain an EthQuery instance.\n * @param options.getGasFeeControllerEstimates - Callback to obtain the default fee estimates.\n * @param options.getTransactions - Callback to obtain the transaction data.\n * @param options.onStateChange - Callback to register a listener for controller state changes.\n */\n constructor({\n gasFeeFlows,\n getChainIds,\n getEthQuery,\n getGasFeeControllerEstimates,\n getTransactions,\n onStateChange,\n }: {\n gasFeeFlows: GasFeeFlow[];\n getChainIds: () => string[];\n getEthQuery: () => any;\n getGasFeeControllerEstimates: () => Promise;\n getTransactions: () => TransactionMeta[];\n onStateChange: (listener: () => void) => void;\n }) {\n this.#gasFeeFlows = gasFeeFlows;\n this.#getChainIds = getChainIds;\n this.#getEthQuery = getEthQuery;\n this.#getGasFeeControllerEstimates = getGasFeeControllerEstimates;\n this.#getTransactions = getTransactions;\n\n onStateChange(() => {\n const unapprovedTransactions = this.#getUnapprovedTransactions();\n\n if (unapprovedTransactions.length) {\n this.#start();\n } else {\n this.#stop();\n }\n });\n }\n\n #start() {\n if (this.#running) {\n return;\n }\n\n // Intentionally not awaiting since this starts the timeout chain.\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this.#onTimeout();\n\n this.#running = true;\n\n log('Started polling');\n }\n\n #stop() {\n if (!this.#running) {\n return;\n }\n\n clearTimeout(this.#timeout);\n\n this.#timeout = undefined;\n this.#running = false;\n\n log('Stopped polling');\n }\n\n async #onTimeout() {\n await this.#updateUnapprovedTransactions();\n\n // eslint-disable-next-line @typescript-eslint/no-misused-promises\n this.#timeout = setTimeout(() => this.#onTimeout(), INTERVAL_MILLISECONDS);\n }\n\n async #updateUnapprovedTransactions() {\n const unapprovedTransactions = this.#getUnapprovedTransactions();\n\n log('Found unapproved transactions', {\n count: unapprovedTransactions.length,\n });\n\n const ethQuery = this.#getEthQuery();\n\n await Promise.all(\n unapprovedTransactions.map((tx) =>\n this.#updateTransactionSuggestedFees(tx, ethQuery),\n ),\n );\n }\n\n async #updateTransactionSuggestedFees(\n transactionMeta: TransactionMeta,\n ethQuery: any,\n ) {\n const gasFeeFlow = getGasFeeFlow(transactionMeta, this.#gasFeeFlows);\n\n if (!gasFeeFlow) {\n log('No gas fee flow found', transactionMeta.id);\n } else {\n log(\n 'Found gas fee flow',\n gasFeeFlow.constructor.name,\n transactionMeta.id,\n );\n }\n\n const request: GasFeeFlowRequest = {\n ethQuery,\n getGasFeeControllerEstimates: this.#getGasFeeControllerEstimates,\n transactionMeta,\n };\n\n if (gasFeeFlow) {\n try {\n const response = await gasFeeFlow.getGasFees(request);\n\n transactionMeta.gasFeeEstimates = response.estimates;\n } catch (error) {\n log('Failed to get suggested gas fees', transactionMeta.id, error);\n }\n }\n\n if (!gasFeeFlow && transactionMeta.gasFeeEstimatesLoaded) {\n return;\n }\n\n transactionMeta.gasFeeEstimatesLoaded = true;\n\n this.hub.emit(\n 'transaction-updated',\n transactionMeta,\n 'GasFeePoller - Suggested gas fees updated',\n );\n\n log('Updated suggested gas fees', {\n gasFeeEstimates: transactionMeta.gasFeeEstimates,\n transaction: transactionMeta.id,\n });\n }\n\n #getUnapprovedTransactions() {\n const chainIds = this.#getChainIds();\n\n return this.#getTransactions().filter(\n (tx) =>\n chainIds.includes(tx.chainId as string) &&\n tx.status === TransactionStatus.unapproved,\n );\n }\n}\n"]}
+\ No newline at end of file
diff --git a/node_modules/@metamask/transaction-controller/dist/index.d.ts b/node_modules/@metamask/transaction-controller/dist/index.d.ts
-index fc7f49b..cd9486a 100644
+index fc7f49b..52e424e 100644
--- a/node_modules/@metamask/transaction-controller/dist/index.d.ts
+++ b/node_modules/@metamask/transaction-controller/dist/index.d.ts
-@@ -1,3 +1,4 @@
+@@ -1,3 +1,5 @@
export * from './TransactionController';
export { isEIP1559Transaction } from './utils';
+export * from './types';
++export { mergeGasFeeEstimates, getGasFeeFlow } from './utils/gas-flow';
//# sourceMappingURL=index.d.ts.map
\ No newline at end of file
diff --git a/node_modules/@metamask/transaction-controller/dist/index.d.ts.map b/node_modules/@metamask/transaction-controller/dist/index.d.ts.map
@@ -2222,14 +2739,22 @@ index 2175387..0000000
-{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,yBAAyB,CAAC;AACxC,OAAO,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC"}
\ No newline at end of file
diff --git a/node_modules/@metamask/transaction-controller/dist/index.js b/node_modules/@metamask/transaction-controller/dist/index.js
-index a1c07c8..602f51c 100644
+index a1c07c8..99cea84 100644
--- a/node_modules/@metamask/transaction-controller/dist/index.js
+++ b/node_modules/@metamask/transaction-controller/dist/index.js
-@@ -18,4 +18,5 @@ exports.isEIP1559Transaction = void 0;
+@@ -14,8 +14,12 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
+ };
+ Object.defineProperty(exports, "__esModule", { value: true });
+-exports.isEIP1559Transaction = void 0;
++exports.getGasFeeFlow = exports.mergeGasFeeEstimates = exports.isEIP1559Transaction = void 0;
__exportStar(require("./TransactionController"), exports);
var utils_1 = require("./utils");
Object.defineProperty(exports, "isEIP1559Transaction", { enumerable: true, get: function () { return utils_1.isEIP1559Transaction; } });
+__exportStar(require("./types"), exports);
++var gas_flow_1 = require("./utils/gas-flow");
++Object.defineProperty(exports, "mergeGasFeeEstimates", { enumerable: true, get: function () { return gas_flow_1.mergeGasFeeEstimates; } });
++Object.defineProperty(exports, "getGasFeeFlow", { enumerable: true, get: function () { return gas_flow_1.getGasFeeFlow; } });
//# sourceMappingURL=index.js.map
\ No newline at end of file
diff --git a/node_modules/@metamask/transaction-controller/dist/index.js.map b/node_modules/@metamask/transaction-controller/dist/index.js.map
@@ -2240,6 +2765,35 @@ index a4460fa..0000000
@@ -1 +0,0 @@
-{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,0DAAwC;AACxC,iCAA+C;AAAtC,6GAAA,oBAAoB,OAAA","sourcesContent":["export * from './TransactionController';\nexport { isEIP1559Transaction } from './utils';\n"]}
\ No newline at end of file
+diff --git a/node_modules/@metamask/transaction-controller/dist/logger.d.ts b/node_modules/@metamask/transaction-controller/dist/logger.d.ts
+new file mode 100644
+index 0000000..43ad63f
+--- /dev/null
++++ b/node_modules/@metamask/transaction-controller/dist/logger.d.ts
+@@ -0,0 +1,6 @@
++///
++import { createModuleLogger } from '@metamask/utils';
++export declare const projectLogger: import("debug").Debugger;
++export declare const incomingTransactionsLogger: import("debug").Debugger;
++export { createModuleLogger };
++//# sourceMappingURL=logger.d.ts.map
+\ No newline at end of file
+diff --git a/node_modules/@metamask/transaction-controller/dist/logger.js b/node_modules/@metamask/transaction-controller/dist/logger.js
+new file mode 100644
+index 0000000..bb55b17
+--- /dev/null
++++ b/node_modules/@metamask/transaction-controller/dist/logger.js
+@@ -0,0 +1,9 @@
++"use strict";
++/* istanbul ignore file */
++Object.defineProperty(exports, "__esModule", { value: true });
++exports.createModuleLogger = exports.incomingTransactionsLogger = exports.projectLogger = void 0;
++const utils_1 = require("@metamask/utils");
++Object.defineProperty(exports, "createModuleLogger", { enumerable: true, get: function () { return utils_1.createModuleLogger; } });
++exports.projectLogger = (0, utils_1.createProjectLogger)('transaction-controller');
++exports.incomingTransactionsLogger = (0, utils_1.createModuleLogger)(exports.projectLogger, 'incoming-transactions');
++//# sourceMappingURL=logger.js.map
+\ No newline at end of file
diff --git a/node_modules/@metamask/transaction-controller/dist/mocks/txsMock.d.ts b/node_modules/@metamask/transaction-controller/dist/mocks/txsMock.d.ts
deleted file mode 100644
index 688af82..0000000
@@ -2852,11 +3406,12 @@ index 99e8f02..0000000
\ No newline at end of file
diff --git a/node_modules/@metamask/transaction-controller/dist/types.d.ts b/node_modules/@metamask/transaction-controller/dist/types.d.ts
new file mode 100644
-index 0000000..3f922df
+index 0000000..39cc136
--- /dev/null
+++ b/node_modules/@metamask/transaction-controller/dist/types.d.ts
-@@ -0,0 +1,174 @@
+@@ -0,0 +1,232 @@
+import { Hex } from '@metamask/utils';
++import { GasFeeState } from '@metamask/gas-fee-controller';
+/**
+ * @type TransactionMeta
+ *
@@ -2899,6 +3454,10 @@ index 0000000..3f922df
+ blockNumber?: string;
+ deviceConfirmedOn?: WalletDevice;
+ verifiedOnBlockchain?: boolean;
++ /** Alternate EIP-1559 gas fee estimates for multiple priority levels. */
++ gasFeeEstimates?: GasFeeEstimates;
++ /** Whether the gas fee estimates have been checked at least once. */
++ gasFeeEstimatesLoaded?: boolean;
+ /**
+ * Response from security validator.
+ */
@@ -3028,18 +3587,71 @@ index 0000000..3f922df
+ /** The transaction parameters that were submitted. */
+ transaction: Record;
+};
++/** Gas fee estimates for a specific priority level. */
++export declare type GasFeeEstimatesForLevel = {
++ /** Maximum amount to pay per gas. */
++ maxFeePerGas: Hex;
++ /** Maximum amount per gas to give to the validator as an incentive. */
++ maxPriorityFeePerGas: Hex;
++};
++/** Alternate priority levels for which values are provided in gas fee estimates. */
++export declare enum GasFeeEstimateLevel {
++ low = "low",
++ medium = "medium",
++ high = "high"
++}
++/** Gas fee estimates for a transaction. */
++export declare type GasFeeEstimates = {
++ /** The gas fee estimate for a low priority transaction. */
++ [GasFeeEstimateLevel.low]: GasFeeEstimatesForLevel;
++ /** The gas fee estimate for a medium priority transaction. */
++ [GasFeeEstimateLevel.medium]: GasFeeEstimatesForLevel;
++ /** The gas fee estimate for a high priority transaction. */
++ [GasFeeEstimateLevel.high]: GasFeeEstimatesForLevel;
++};
++/** Request to a gas fee flow to obtain gas fee estimates. */
++export declare type GasFeeFlowRequest = {
++ /** An EthQuery instance to enable queries to the associated RPC provider. */
++ ethQuery: any;
++ /** Callback to get the GasFeeController estimates. */
++ getGasFeeControllerEstimates: () => Promise;
++ /** The metadata of the transaction to obtain estimates for. */
++ transactionMeta: TransactionMeta;
++};
++/** Response from a gas fee flow containing gas fee estimates. */
++export declare type GasFeeFlowResponse = {
++ /** The gas fee estimates for the transaction. */
++ estimates: GasFeeEstimates;
++};
++/** A method of obtaining gas fee estimates for a specific transaction. */
++export declare type GasFeeFlow = {
++ /**
++ * Determine if the gas fee flow supports the specified transaction.
++ *
++ * @param transactionMeta - The transaction metadata.
++ * @returns Whether the gas fee flow supports the transaction.
++ */
++ matchesTransaction(transactionMeta: TransactionMeta): boolean;
++ /**
++ * Get gas fee estimates for a specific transaction.
++ *
++ * @param request - The gas fee flow request.
++ * @returns The gas fee flow response containing the gas fee estimates.
++ */
++ getGasFees: (request: GasFeeFlowRequest) => Promise;
++};
+export {};
+//# sourceMappingURL=types.d.ts.map
\ No newline at end of file
diff --git a/node_modules/@metamask/transaction-controller/dist/types.js b/node_modules/@metamask/transaction-controller/dist/types.js
new file mode 100644
-index 0000000..1cddb49
+index 0000000..2cc85ee
--- /dev/null
+++ b/node_modules/@metamask/transaction-controller/dist/types.js
-@@ -0,0 +1,29 @@
+@@ -0,0 +1,36 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
-+exports.WalletDevice = exports.TransactionStatus = void 0;
++exports.GasFeeEstimateLevel = exports.WalletDevice = exports.TransactionStatus = void 0;
+/**
+ * The status of the transaction. Each status represents the state of the transaction internally
+ * in the wallet. Some of these correspond with the state of the transaction on the network, but
@@ -3065,6 +3677,13 @@ index 0000000..1cddb49
+ WalletDevice["MM_EXTENSION"] = "metamask_extension";
+ WalletDevice["OTHER"] = "other_device";
+})(WalletDevice = exports.WalletDevice || (exports.WalletDevice = {}));
++/** Alternate priority levels for which values are provided in gas fee estimates. */
++var GasFeeEstimateLevel;
++(function (GasFeeEstimateLevel) {
++ GasFeeEstimateLevel["low"] = "low";
++ GasFeeEstimateLevel["medium"] = "medium";
++ GasFeeEstimateLevel["high"] = "high";
++})(GasFeeEstimateLevel = exports.GasFeeEstimateLevel || (exports.GasFeeEstimateLevel = {}));
+//# sourceMappingURL=types.js.map
\ No newline at end of file
diff --git a/node_modules/@metamask/transaction-controller/dist/utils.d.ts b/node_modules/@metamask/transaction-controller/dist/utils.d.ts
@@ -3231,3 +3850,122 @@ index 8f2f6e3..0000000
@@ -1 +0,0 @@
-{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"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","sourcesContent":["import { addHexPrefix, isHexString } from 'ethereumjs-util';\nimport {\n NetworkType,\n convertHexToDecimal,\n handleFetch,\n isValidHexAddress,\n} from '@metamask/controller-utils';\nimport type { Transaction as NonceTrackerTransaction } from 'nonce-tracker/dist/NonceTracker';\nimport {\n Transaction,\n FetchAllOptions,\n GasPriceValue,\n FeeMarketEIP1559Values,\n TransactionStatus,\n} from './TransactionController';\nimport type { TransactionMeta } from './TransactionController';\n\nexport const ESTIMATE_GAS_ERROR = 'eth_estimateGas rpc method error';\n\nconst NORMALIZERS: { [param in keyof Transaction]: any } = {\n data: (data: string) => addHexPrefix(data),\n from: (from: string) => addHexPrefix(from).toLowerCase(),\n gas: (gas: string) => addHexPrefix(gas),\n gasPrice: (gasPrice: string) => addHexPrefix(gasPrice),\n nonce: (nonce: string) => addHexPrefix(nonce),\n to: (to: string) => addHexPrefix(to).toLowerCase(),\n value: (value: string) => addHexPrefix(value),\n maxFeePerGas: (maxFeePerGas: string) => addHexPrefix(maxFeePerGas),\n maxPriorityFeePerGas: (maxPriorityFeePerGas: string) =>\n addHexPrefix(maxPriorityFeePerGas),\n estimatedBaseFee: (maxPriorityFeePerGas: string) =>\n addHexPrefix(maxPriorityFeePerGas),\n};\n\n/**\n * Return a URL that can be used to fetch ETH transactions.\n *\n * @param networkType - Network type of desired network.\n * @param urlParams - The parameters used to construct the URL.\n * @returns URL to fetch the access the endpoint.\n */\nexport function getEtherscanApiUrl(\n networkType: string,\n urlParams: any,\n): string {\n let etherscanSubdomain = 'api';\n if (networkType !== NetworkType.mainnet) {\n etherscanSubdomain = `api-${networkType}`;\n }\n const apiUrl = `https://${etherscanSubdomain}.etherscan.io`;\n let url = `${apiUrl}/api?`;\n\n for (const paramKey in urlParams) {\n if (urlParams[paramKey]) {\n url += `${paramKey}=${urlParams[paramKey]}&`;\n }\n }\n url += 'tag=latest&page=1';\n return url;\n}\n\n/**\n * Normalizes properties on a Transaction object.\n *\n * @param transaction - Transaction object to normalize.\n * @returns Normalized Transaction object.\n */\nexport function normalizeTransaction(transaction: Transaction) {\n const normalizedTransaction: Transaction = { from: '' };\n let key: keyof Transaction;\n for (key in NORMALIZERS) {\n if (transaction[key as keyof Transaction]) {\n normalizedTransaction[key] = NORMALIZERS[key](transaction[key]) as never;\n }\n }\n return normalizedTransaction;\n}\n\n/**\n * Validates a Transaction object for required properties and throws in\n * the event of any validation error.\n *\n * @param transaction - Transaction object to validate.\n */\nexport function validateTransaction(transaction: Transaction) {\n if (\n !transaction.from ||\n typeof transaction.from !== 'string' ||\n !isValidHexAddress(transaction.from)\n ) {\n throw new Error(\n `Invalid \"from\" address: ${transaction.from} must be a valid string.`,\n );\n }\n\n if (transaction.to === '0x' || transaction.to === undefined) {\n if (transaction.data) {\n delete transaction.to;\n } else {\n throw new Error(\n `Invalid \"to\" address: ${transaction.to} must be a valid string.`,\n );\n }\n } else if (\n transaction.to !== undefined &&\n !isValidHexAddress(transaction.to)\n ) {\n throw new Error(\n `Invalid \"to\" address: ${transaction.to} must be a valid string.`,\n );\n }\n\n if (transaction.value !== undefined) {\n const value = transaction.value.toString();\n if (value.includes('-')) {\n throw new Error(`Invalid \"value\": ${value} is not a positive number.`);\n }\n\n if (value.includes('.')) {\n throw new Error(\n `Invalid \"value\": ${value} number must be denominated in wei.`,\n );\n }\n const intValue = parseInt(transaction.value, 10);\n const isValid =\n Number.isFinite(intValue) &&\n !Number.isNaN(intValue) &&\n !isNaN(Number(value)) &&\n Number.isSafeInteger(intValue);\n if (!isValid) {\n throw new Error(\n `Invalid \"value\": ${value} number must be a valid number.`,\n );\n }\n }\n}\n\n/**\n * Checks if a transaction is EIP-1559 by checking for the existence of\n * maxFeePerGas and maxPriorityFeePerGas within its parameters.\n *\n * @param transaction - Transaction object to add.\n * @returns Boolean that is true if the transaction is EIP-1559 (has maxFeePerGas and maxPriorityFeePerGas), otherwise returns false.\n */\nexport const isEIP1559Transaction = (transaction: Transaction): boolean => {\n const hasOwnProp = (obj: Transaction, key: string) =>\n Object.prototype.hasOwnProperty.call(obj, key);\n return (\n hasOwnProp(transaction, 'maxFeePerGas') &&\n hasOwnProp(transaction, 'maxPriorityFeePerGas')\n );\n};\n\n/**\n * Handles the fetch of incoming transactions.\n *\n * @param networkType - Network type of desired network.\n * @param address - Address to get the transactions from.\n * @param txHistoryLimit - The maximum number of transactions to fetch.\n * @param opt - Object that can contain fromBlock and Etherscan service API key.\n * @returns Responses for both ETH and ERC20 token transactions.\n */\nexport async function handleTransactionFetch(\n networkType: string,\n address: string,\n txHistoryLimit: number,\n opt?: FetchAllOptions,\n): Promise<[{ [result: string]: [] }, { [result: string]: [] }]> {\n // transactions\n const urlParams = {\n module: 'account',\n address,\n startBlock: opt?.fromBlock,\n apikey: opt?.etherscanApiKey,\n offset: txHistoryLimit.toString(),\n order: 'desc',\n };\n const etherscanTxUrl = getEtherscanApiUrl(networkType, {\n ...urlParams,\n action: 'txlist',\n });\n const etherscanTxResponsePromise = handleFetch(etherscanTxUrl);\n\n // tokens\n const etherscanTokenUrl = getEtherscanApiUrl(networkType, {\n ...urlParams,\n action: 'tokentx',\n });\n const etherscanTokenResponsePromise = handleFetch(etherscanTokenUrl);\n\n let [etherscanTxResponse, etherscanTokenResponse] = await Promise.all([\n etherscanTxResponsePromise,\n etherscanTokenResponsePromise,\n ]);\n\n if (\n etherscanTxResponse.status === '0' ||\n etherscanTxResponse.result.length <= 0\n ) {\n etherscanTxResponse = { status: etherscanTxResponse.status, result: [] };\n }\n\n if (\n etherscanTokenResponse.status === '0' ||\n etherscanTokenResponse.result.length <= 0\n ) {\n etherscanTokenResponse = {\n status: etherscanTokenResponse.status,\n result: [],\n };\n }\n\n return [etherscanTxResponse, etherscanTokenResponse];\n}\n\nexport const validateGasValues = (\n gasValues: GasPriceValue | FeeMarketEIP1559Values,\n) => {\n Object.keys(gasValues).forEach((key) => {\n const value = (gasValues as any)[key];\n if (typeof value !== 'string' || !isHexString(value)) {\n throw new TypeError(\n `expected hex string for ${key} but received: ${value}`,\n );\n }\n });\n};\n\nexport const isFeeMarketEIP1559Values = (\n gasValues?: GasPriceValue | FeeMarketEIP1559Values,\n): gasValues is FeeMarketEIP1559Values =>\n (gasValues as FeeMarketEIP1559Values)?.maxFeePerGas !== undefined ||\n (gasValues as FeeMarketEIP1559Values)?.maxPriorityFeePerGas !== undefined;\n\nexport const isGasPriceValue = (\n gasValues?: GasPriceValue | FeeMarketEIP1559Values,\n): gasValues is GasPriceValue =>\n (gasValues as GasPriceValue)?.gasPrice !== undefined;\n\nexport const getIncreasedPriceHex = (value: number, rate: number): string =>\n addHexPrefix(`${parseInt(`${value * rate}`, 10).toString(16)}`);\n\nexport const getIncreasedPriceFromExisting = (\n value: string | undefined,\n rate: number,\n): string => {\n return getIncreasedPriceHex(convertHexToDecimal(value), rate);\n};\n\n/**\n * Validates that the proposed value is greater than or equal to the minimum value.\n *\n * @param proposed - The proposed value.\n * @param min - The minimum value.\n * @returns The proposed value.\n * @throws Will throw if the proposed value is too low.\n */\nexport function validateMinimumIncrease(proposed: string, min: string) {\n const proposedDecimal = convertHexToDecimal(proposed);\n const minDecimal = convertHexToDecimal(min);\n if (proposedDecimal >= minDecimal) {\n return proposed;\n }\n const errorMsg = `The proposed value: ${proposedDecimal} should meet or exceed the minimum value: ${minDecimal}`;\n throw new Error(errorMsg);\n}\n\n/**\n * Helper function to filter and format transactions for the nonce tracker.\n *\n * @param fromAddress - Address of the account from which the transactions to filter from are sent.\n * @param transactionStatus - Status of the transactions for which to filter.\n * @param transactions - Array of transactionMeta objects that have been prefiltered.\n * @returns Array of transactions formatted for the nonce tracker.\n */\nexport function getAndFormatTransactionsForNonceTracker(\n fromAddress: string,\n transactionStatus: TransactionStatus,\n transactions: TransactionMeta[],\n): NonceTrackerTransaction[] {\n return transactions\n .filter(\n ({ status, transaction: { from } }) =>\n status === transactionStatus &&\n from.toLowerCase() === fromAddress.toLowerCase(),\n )\n .map(({ status, transaction: { from, gas, value, nonce } }) => {\n // the only value we care about is the nonce\n // but we need to return the other values to satisfy the type\n // TODO: refactor nonceTracker to not require this\n return {\n status,\n history: [{}],\n txParams: {\n from: from ?? '',\n gas: gas ?? '',\n value: value ?? '',\n nonce: nonce ?? '',\n },\n };\n });\n}\n"]}
\ No newline at end of file
+diff --git a/node_modules/@metamask/transaction-controller/dist/utils/gas-flow.d.ts b/node_modules/@metamask/transaction-controller/dist/utils/gas-flow.d.ts
+new file mode 100644
+index 0000000..59837f9
+--- /dev/null
++++ b/node_modules/@metamask/transaction-controller/dist/utils/gas-flow.d.ts
+@@ -0,0 +1,33 @@
++import type { GasFeeEstimates, LegacyGasPriceEstimate } from '@metamask/gas-fee-controller';
++import { type GasFeeState } from '@metamask/gas-fee-controller';
++import { type GasFeeEstimates as TransactionGasFeeEstimates, type GasFeeFlow, type TransactionMeta } from '../types';
++/**
++ * Returns the first gas fee flow that matches the transaction.
++ *
++ * @param transactionMeta - The transaction metadata to find a gas fee flow for.
++ * @param gasFeeFlows - The gas fee flows to search.
++ * @returns The first gas fee flow that matches the transaction, or undefined if none match.
++ */
++export declare function getGasFeeFlow(transactionMeta: TransactionMeta, gasFeeFlows: GasFeeFlow[]): GasFeeFlow | undefined;
++declare type FeeMarketMergeGasFeeEstimatesRequest = {
++ gasFeeControllerEstimateType: 'fee-market';
++ gasFeeControllerEstimates: GasFeeEstimates;
++ transactionGasFeeEstimates: TransactionGasFeeEstimates;
++};
++declare type LegacyMergeGasFeeEstimatesRequest = {
++ gasFeeControllerEstimateType: 'legacy';
++ gasFeeControllerEstimates: LegacyGasPriceEstimate;
++ transactionGasFeeEstimates: TransactionGasFeeEstimates;
++};
++/**
++ * Merge the gas fee estimates from the gas fee controller with the gas fee estimates from a transaction.
++ *
++ * @param request - Data required to merge gas fee estimates.
++ * @param request.gasFeeControllerEstimateType - Gas fee estimate type from the gas fee controller.
++ * @param request.gasFeeControllerEstimates - Gas fee estimates from the GasFeeController.
++ * @param request.transactionGasFeeEstimates - Gas fee estimates from the transaction.
++ * @returns The merged gas fee estimates.
++ */
++export declare function mergeGasFeeEstimates({ gasFeeControllerEstimateType, gasFeeControllerEstimates, transactionGasFeeEstimates, }: FeeMarketMergeGasFeeEstimatesRequest | LegacyMergeGasFeeEstimatesRequest): GasFeeState['gasFeeEstimates'];
++export {};
++//# sourceMappingURL=gas-flow.d.ts.map
+\ No newline at end of file
+diff --git a/node_modules/@metamask/transaction-controller/dist/utils/gas-flow.d.ts.map b/node_modules/@metamask/transaction-controller/dist/utils/gas-flow.d.ts.map
+new file mode 100644
+index 0000000..46c3e5a
+--- /dev/null
++++ b/node_modules/@metamask/transaction-controller/dist/utils/gas-flow.d.ts.map
+@@ -0,0 +1 @@
++{"version":3,"file":"gas-flow.d.ts","sourceRoot":"","sources":["../../src/utils/gas-flow.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAEV,eAAe,EACf,sBAAsB,EACvB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAEL,KAAK,WAAW,EACjB,MAAM,8BAA8B,CAAC;AAEtC,OAAO,EACL,KAAK,eAAe,IAAI,0BAA0B,EAClD,KAAK,UAAU,EACf,KAAK,eAAe,EAGrB,MAAM,UAAU,CAAC;AAElB;;;;;;GAMG;AACH,wBAAgB,aAAa,CAC3B,eAAe,EAAE,eAAe,EAChC,WAAW,EAAE,UAAU,EAAE,GACxB,UAAU,GAAG,SAAS,CAIxB;AAED,aAAK,oCAAoC,GAAG;IAC1C,4BAA4B,EAAE,YAAY,CAAC;IAC3C,yBAAyB,EAAE,eAAe,CAAC;IAC3C,0BAA0B,EAAE,0BAA0B,CAAC;CACxD,CAAC;AAEF,aAAK,iCAAiC,GAAG;IACvC,4BAA4B,EAAE,QAAQ,CAAC;IACvC,yBAAyB,EAAE,sBAAsB,CAAC;IAClD,0BAA0B,EAAE,0BAA0B,CAAC;CACxD,CAAC;AAEF;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,EACnC,4BAA4B,EAC5B,yBAAyB,EACzB,0BAA0B,GAC3B,EACG,oCAAoC,GACpC,iCAAiC,GAAG,WAAW,CAAC,iBAAiB,CAAC,CAyBrE"}
+\ No newline at end of file
+diff --git a/node_modules/@metamask/transaction-controller/dist/utils/gas-flow.js b/node_modules/@metamask/transaction-controller/dist/utils/gas-flow.js
+new file mode 100644
+index 0000000..7f4e8a9
+--- /dev/null
++++ b/node_modules/@metamask/transaction-controller/dist/utils/gas-flow.js
+@@ -0,0 +1,56 @@
++"use strict";
++Object.defineProperty(exports, "__esModule", { value: true });
++exports.mergeGasFeeEstimates = exports.getGasFeeFlow = void 0;
++const controller_utils_1 = require("@metamask/controller-utils");
++const gas_fee_controller_1 = require("@metamask/gas-fee-controller");
++const types_1 = require("../types");
++/**
++ * Returns the first gas fee flow that matches the transaction.
++ *
++ * @param transactionMeta - The transaction metadata to find a gas fee flow for.
++ * @param gasFeeFlows - The gas fee flows to search.
++ * @returns The first gas fee flow that matches the transaction, or undefined if none match.
++ */
++function getGasFeeFlow(transactionMeta, gasFeeFlows) {
++ return gasFeeFlows.find((gasFeeFlow) => gasFeeFlow.matchesTransaction(transactionMeta));
++}
++exports.getGasFeeFlow = getGasFeeFlow;
++/**
++ * Merge the gas fee estimates from the gas fee controller with the gas fee estimates from a transaction.
++ *
++ * @param request - Data required to merge gas fee estimates.
++ * @param request.gasFeeControllerEstimateType - Gas fee estimate type from the gas fee controller.
++ * @param request.gasFeeControllerEstimates - Gas fee estimates from the GasFeeController.
++ * @param request.transactionGasFeeEstimates - Gas fee estimates from the transaction.
++ * @returns The merged gas fee estimates.
++ */
++function mergeGasFeeEstimates({ gasFeeControllerEstimateType, gasFeeControllerEstimates, transactionGasFeeEstimates, }) {
++ if (gasFeeControllerEstimateType === gas_fee_controller_1.GAS_ESTIMATE_TYPES.FEE_MARKET) {
++ return Object.values(types_1.GasFeeEstimateLevel).reduce((result, level) => (Object.assign(Object.assign({}, result), { [level]: mergeFeeMarketEstimate(gasFeeControllerEstimates[level], transactionGasFeeEstimates[level]) })), Object.assign({}, gasFeeControllerEstimates));
++ }
++ if (gasFeeControllerEstimateType === gas_fee_controller_1.GAS_ESTIMATE_TYPES.LEGACY) {
++ return Object.values(types_1.GasFeeEstimateLevel).reduce((result, level) => (Object.assign(Object.assign({}, result), { [level]: getLegacyEstimate(transactionGasFeeEstimates[level]) })), {});
++ }
++ return gasFeeControllerEstimates;
++}
++exports.mergeGasFeeEstimates = mergeGasFeeEstimates;
++/**
++ * Merge a specific priority level of EIP-1559 gas fee estimates.
++ *
++ * @param gasFeeControllerEstimate - The gas fee estimate from the gas fee controller.
++ * @param transactionGasFeeEstimate - The gas fee estimate from the transaction.
++ * @returns The merged gas fee estimate.
++ */
++function mergeFeeMarketEstimate(gasFeeControllerEstimate, transactionGasFeeEstimate) {
++ return Object.assign(Object.assign({}, gasFeeControllerEstimate), { suggestedMaxFeePerGas: (0, controller_utils_1.weiHexToGweiDec)(transactionGasFeeEstimate.maxFeePerGas), suggestedMaxPriorityFeePerGas: (0, controller_utils_1.weiHexToGweiDec)(transactionGasFeeEstimate.maxPriorityFeePerGas) });
++}
++/**
++ * Generate a specific priority level for a legacy gas fee estimate.
++ *
++ * @param transactionGasFeeEstimate - The gas fee estimate from the transaction.
++ * @returns The legacy gas fee estimate.
++ */
++function getLegacyEstimate(transactionGasFeeEstimate) {
++ return (0, controller_utils_1.weiHexToGweiDec)(transactionGasFeeEstimate.maxFeePerGas);
++}
++//# sourceMappingURL=gas-flow.js.map
+\ No newline at end of file
+diff --git a/node_modules/@metamask/transaction-controller/dist/utils/gas-flow.js.map b/node_modules/@metamask/transaction-controller/dist/utils/gas-flow.js.map
+new file mode 100644
+index 0000000..fe93a4a
+--- /dev/null
++++ b/node_modules/@metamask/transaction-controller/dist/utils/gas-flow.js.map
+@@ -0,0 +1 @@
++{"version":3,"file":"gas-flow.js","sourceRoot":"","sources":["../../src/utils/gas-flow.ts"],"names":[],"mappings":";;;AAAA,iEAA6D;AAM7D,qEAGsC;AAEtC,oCAMkB;AAElB;;;;;;GAMG;AACH,SAAgB,aAAa,CAC3B,eAAgC,EAChC,WAAyB;IAEzB,OAAO,WAAW,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,EAAE,CACrC,UAAU,CAAC,kBAAkB,CAAC,eAAe,CAAC,CAC/C,CAAC;AACJ,CAAC;AAPD,sCAOC;AAcD;;;;;;;;GAQG;AACH,SAAgB,oBAAoB,CAAC,EACnC,4BAA4B,EAC5B,yBAAyB,EACzB,0BAA0B,GAGS;IACnC,IAAI,4BAA4B,KAAK,uCAAkB,CAAC,UAAU,EAAE;QAClE,OAAO,MAAM,CAAC,MAAM,CAAC,2BAAmB,CAAC,CAAC,MAAM,CAC9C,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,iCACd,MAAM,KACT,CAAC,KAAK,CAAC,EAAE,sBAAsB,CAC7B,yBAAyB,CAAC,KAAK,CAAC,EAChC,0BAA0B,CAAC,KAAK,CAAC,CAClC,IACD,EACF,kBAAK,yBAAyB,CAAqB,CACpD,CAAC;KACH;IAED,IAAI,4BAA4B,KAAK,uCAAkB,CAAC,MAAM,EAAE;QAC9D,OAAO,MAAM,CAAC,MAAM,CAAC,2BAAmB,CAAC,CAAC,MAAM,CAC9C,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,iCACd,MAAM,KACT,CAAC,KAAK,CAAC,EAAE,iBAAiB,CAAC,0BAA0B,CAAC,KAAK,CAAC,CAAC,IAC7D,EACF,EAA4B,CAC7B,CAAC;KACH;IAED,OAAO,yBAAyB,CAAC;AACnC,CAAC;AA/BD,oDA+BC;AAED;;;;;;GAMG;AACH,SAAS,sBAAsB,CAC7B,wBAAuC,EACvC,yBAAkD;IAElD,uCACK,wBAAwB,KAC3B,qBAAqB,EAAE,IAAA,kCAAe,EACpC,yBAAyB,CAAC,YAAY,CACvC,EACD,6BAA6B,EAAE,IAAA,kCAAe,EAC5C,yBAAyB,CAAC,oBAAoB,CAC/C,IACD;AACJ,CAAC;AAED;;;;;GAKG;AACH,SAAS,iBAAiB,CACxB,yBAAkD;IAElD,OAAO,IAAA,kCAAe,EAAC,yBAAyB,CAAC,YAAY,CAAC,CAAC;AACjE,CAAC","sourcesContent":["import { weiHexToGweiDec } from '@metamask/controller-utils';\nimport type {\n Eip1559GasFee,\n GasFeeEstimates,\n LegacyGasPriceEstimate,\n} from '@metamask/gas-fee-controller';\nimport {\n GAS_ESTIMATE_TYPES,\n type GasFeeState,\n} from '@metamask/gas-fee-controller';\n\nimport {\n type GasFeeEstimates as TransactionGasFeeEstimates,\n type GasFeeFlow,\n type TransactionMeta,\n type GasFeeEstimatesForLevel,\n GasFeeEstimateLevel,\n} from '../types';\n\n/**\n * Returns the first gas fee flow that matches the transaction.\n *\n * @param transactionMeta - The transaction metadata to find a gas fee flow for.\n * @param gasFeeFlows - The gas fee flows to search.\n * @returns The first gas fee flow that matches the transaction, or undefined if none match.\n */\nexport function getGasFeeFlow(\n transactionMeta: TransactionMeta,\n gasFeeFlows: GasFeeFlow[],\n): GasFeeFlow | undefined {\n return gasFeeFlows.find((gasFeeFlow) =>\n gasFeeFlow.matchesTransaction(transactionMeta),\n );\n}\n\ntype FeeMarketMergeGasFeeEstimatesRequest = {\n gasFeeControllerEstimateType: 'fee-market';\n gasFeeControllerEstimates: GasFeeEstimates;\n transactionGasFeeEstimates: TransactionGasFeeEstimates;\n};\n\ntype LegacyMergeGasFeeEstimatesRequest = {\n gasFeeControllerEstimateType: 'legacy';\n gasFeeControllerEstimates: LegacyGasPriceEstimate;\n transactionGasFeeEstimates: TransactionGasFeeEstimates;\n};\n\n/**\n * Merge the gas fee estimates from the gas fee controller with the gas fee estimates from a transaction.\n *\n * @param request - Data required to merge gas fee estimates.\n * @param request.gasFeeControllerEstimateType - Gas fee estimate type from the gas fee controller.\n * @param request.gasFeeControllerEstimates - Gas fee estimates from the GasFeeController.\n * @param request.transactionGasFeeEstimates - Gas fee estimates from the transaction.\n * @returns The merged gas fee estimates.\n */\nexport function mergeGasFeeEstimates({\n gasFeeControllerEstimateType,\n gasFeeControllerEstimates,\n transactionGasFeeEstimates,\n}:\n | FeeMarketMergeGasFeeEstimatesRequest\n | LegacyMergeGasFeeEstimatesRequest): GasFeeState['gasFeeEstimates'] {\n if (gasFeeControllerEstimateType === GAS_ESTIMATE_TYPES.FEE_MARKET) {\n return Object.values(GasFeeEstimateLevel).reduce(\n (result, level) => ({\n ...result,\n [level]: mergeFeeMarketEstimate(\n gasFeeControllerEstimates[level],\n transactionGasFeeEstimates[level],\n ),\n }),\n { ...gasFeeControllerEstimates } as GasFeeEstimates,\n );\n }\n\n if (gasFeeControllerEstimateType === GAS_ESTIMATE_TYPES.LEGACY) {\n return Object.values(GasFeeEstimateLevel).reduce(\n (result, level) => ({\n ...result,\n [level]: getLegacyEstimate(transactionGasFeeEstimates[level]),\n }),\n {} as LegacyGasPriceEstimate,\n );\n }\n\n return gasFeeControllerEstimates;\n}\n\n/**\n * Merge a specific priority level of EIP-1559 gas fee estimates.\n *\n * @param gasFeeControllerEstimate - The gas fee estimate from the gas fee controller.\n * @param transactionGasFeeEstimate - The gas fee estimate from the transaction.\n * @returns The merged gas fee estimate.\n */\nfunction mergeFeeMarketEstimate(\n gasFeeControllerEstimate: Eip1559GasFee,\n transactionGasFeeEstimate: GasFeeEstimatesForLevel,\n): Eip1559GasFee {\n return {\n ...gasFeeControllerEstimate,\n suggestedMaxFeePerGas: weiHexToGweiDec(\n transactionGasFeeEstimate.maxFeePerGas,\n ),\n suggestedMaxPriorityFeePerGas: weiHexToGweiDec(\n transactionGasFeeEstimate.maxPriorityFeePerGas,\n ),\n };\n}\n\n/**\n * Generate a specific priority level for a legacy gas fee estimate.\n *\n * @param transactionGasFeeEstimate - The gas fee estimate from the transaction.\n * @returns The legacy gas fee estimate.\n */\nfunction getLegacyEstimate(\n transactionGasFeeEstimate: GasFeeEstimatesForLevel,\n): string {\n return weiHexToGweiDec(transactionGasFeeEstimate.maxFeePerGas);\n}\n"]}
+\ No newline at end of file