diff --git a/.yarn/patches/@metamask-assets-controllers-npm-89.0.1-02fa7acd54.patch b/.yarn/patches/@metamask-assets-controllers-npm-89.0.1-02fa7acd54.patch
new file mode 100644
index 00000000000..21be6c30339
--- /dev/null
+++ b/.yarn/patches/@metamask-assets-controllers-npm-89.0.1-02fa7acd54.patch
@@ -0,0 +1,48 @@
+diff --git a/dist/TokenBalancesController.cjs b/dist/TokenBalancesController.cjs
+index 4918812dde60b8d0e24a7bded27d88f233968858..4e8018bce92b9e5d47fc40784409e16db22be615 100644
+--- a/dist/TokenBalancesController.cjs
++++ b/dist/TokenBalancesController.cjs
+@@ -535,14 +535,16 @@ class TokenBalancesController extends (0, polling_controller_1.StaticIntervalPol
+ }
+ // Update with actual fetched balances only if the value has changed
+ aggregated.forEach(({ success, value, account, token, chainId }) => {
+- var _a, _b, _c;
++ var _a, _b;
+ if (success && value !== undefined) {
++ // Ensure all accounts we add/update are in lower-case
++ const lowerCaseAccount = account.toLowerCase();
+ const newBalance = (0, controller_utils_1.toHex)(value);
+ const tokenAddress = checksum(token);
+- const currentBalance = d.tokenBalances[account]?.[chainId]?.[tokenAddress];
++ const currentBalance = d.tokenBalances[lowerCaseAccount]?.[chainId]?.[tokenAddress];
+ // Only update if the balance has actually changed
+ if (currentBalance !== newBalance) {
+- ((_c = ((_a = d.tokenBalances)[_b = account] ?? (_a[_b] = {})))[chainId] ?? (_c[chainId] = {}))[tokenAddress] = newBalance;
++ ((_b = ((_a = d.tokenBalances)[lowerCaseAccount] ?? (_a[lowerCaseAccount] = {})))[chainId] ?? (_b[chainId] = {}))[tokenAddress] = newBalance;
+ }
+ }
+ });
+diff --git a/dist/TokenBalancesController.mjs b/dist/TokenBalancesController.mjs
+index f64d13f8de56631345a44e6ebb025e62e03f51bc..99aa7f27c574c94b26daa56091ac50d15281dd30 100644
+--- a/dist/TokenBalancesController.mjs
++++ b/dist/TokenBalancesController.mjs
+@@ -531,14 +531,16 @@ export class TokenBalancesController extends StaticIntervalPollingController() {
+ }
+ // Update with actual fetched balances only if the value has changed
+ aggregated.forEach(({ success, value, account, token, chainId }) => {
+- var _a, _b, _c;
++ var _a, _b;
+ if (success && value !== undefined) {
++ // Ensure all accounts we add/update are in lower-case
++ const lowerCaseAccount = account.toLowerCase();
+ const newBalance = toHex(value);
+ const tokenAddress = checksum(token);
+- const currentBalance = d.tokenBalances[account]?.[chainId]?.[tokenAddress];
++ const currentBalance = d.tokenBalances[lowerCaseAccount]?.[chainId]?.[tokenAddress];
+ // Only update if the balance has actually changed
+ if (currentBalance !== newBalance) {
+- ((_c = ((_a = d.tokenBalances)[_b = account] ?? (_a[_b] = {})))[chainId] ?? (_c[chainId] = {}))[tokenAddress] = newBalance;
++ ((_b = ((_a = d.tokenBalances)[lowerCaseAccount] ?? (_a[lowerCaseAccount] = {})))[chainId] ?? (_b[chainId] = {}))[tokenAddress] = newBalance;
+ }
+ }
+ });
diff --git a/README.md b/README.md
index 940055df985..9e950686925 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
-
+
-# MetaMask
+# MetaMask Mobile
[](https://github.com/MetaMask/metamask-mobile/actions/workflows/ci.yml) [](https://github.com/MetaMask/metamask-mobile/actions/workflows/cla.yml)
diff --git a/app/components/UI/AccountRightButton/index.tsx b/app/components/UI/AccountRightButton/index.tsx
index ba656a961ce..b8fb4a038f0 100644
--- a/app/components/UI/AccountRightButton/index.tsx
+++ b/app/components/UI/AccountRightButton/index.tsx
@@ -1,4 +1,10 @@
-import React, { useCallback, useEffect, useRef, useState } from 'react';
+import React, {
+ useCallback,
+ useEffect,
+ useMemo,
+ useRef,
+ useState,
+} from 'react';
import { useSelector } from 'react-redux';
import {
TouchableOpacity,
@@ -9,7 +15,6 @@ import {
EmitterSubscription,
} from 'react-native';
import { useNavigation, useRoute, RouteProp } from '@react-navigation/native';
-import images from 'images/image-icons';
import Device from '../../../util/device';
import AvatarAccount from '../../../component-library/components/Avatars/Avatar/variants/AvatarAccount';
import { AccountRightButtonProps } from './AccountRightButton.types';
@@ -17,7 +22,10 @@ import Avatar, {
AvatarVariant,
AvatarSize,
} from '../../../component-library/components/Avatars/Avatar';
-import { getDecimalChainId } from '../../../util/networks';
+import {
+ getDecimalChainId,
+ getNetworkImageSource,
+} from '../../../util/networks';
import Routes from '../../../constants/navigation/Routes';
import { MetaMetricsEvents } from '../../../core/Analytics';
import { AccountOverviewSelectorsIDs } from '../../../../e2e/selectors/Browser/AccountOverview.selectors';
@@ -156,6 +164,12 @@ const AccountRightButton = ({
const { networkName, networkImageSource } = useNetworkInfo(hostname);
+ const nonEvmNetworkImageSource = useMemo(() => {
+ if (!isEvmSelected && selectedNonEvmNetworkChainId) {
+ return getNetworkImageSource({ chainId: selectedNonEvmNetworkChainId });
+ }
+ }, [isEvmSelected, selectedNonEvmNetworkChainId]);
+
const renderAvatarAccount = () => (
);
@@ -179,7 +193,9 @@ const AccountRightButton = ({
: nonEvmNetworkConfigurations?.[selectedNonEvmNetworkChainId]
?.name
}
- imageSource={isEvmSelected ? networkImageSource : images.SOLANA}
+ imageSource={
+ isEvmSelected ? networkImageSource : nonEvmNetworkImageSource
+ }
/>
)}
diff --git a/app/components/UI/Perps/Views/PerpsTabView/PerpsTabView.styles.ts b/app/components/UI/Perps/Views/PerpsTabView/PerpsTabView.styles.ts
index 8dd9f7eb6e1..ee6b89faff0 100644
--- a/app/components/UI/Perps/Views/PerpsTabView/PerpsTabView.styles.ts
+++ b/app/components/UI/Perps/Views/PerpsTabView/PerpsTabView.styles.ts
@@ -7,7 +7,10 @@ const styleSheet = (params: { theme: Theme }) => {
return StyleSheet.create({
tradeInfoContainer: {
- paddingBottom: 12,
+ paddingBottom: 30,
+ },
+ emptyStateContainer: {
+ paddingBottom: 30,
},
wrapper: {
flex: 1,
diff --git a/app/components/UI/Perps/Views/PerpsTabView/PerpsTabView.tsx b/app/components/UI/Perps/Views/PerpsTabView/PerpsTabView.tsx
index b2b4d868f11..54296afcb6d 100644
--- a/app/components/UI/Perps/Views/PerpsTabView/PerpsTabView.tsx
+++ b/app/components/UI/Perps/Views/PerpsTabView/PerpsTabView.tsx
@@ -255,11 +255,13 @@ const PerpsTabView: React.FC = () => {
}}
>
{!isInitialLoading && hasNoPositionsOrOrders ? (
-
+
+
+
) : (
{renderPositionsSection()}
diff --git a/app/components/UI/Perps/Views/PerpsTransactionsView/PerpsTransactionsView.tsx b/app/components/UI/Perps/Views/PerpsTransactionsView/PerpsTransactionsView.tsx
index 68e871b5f18..b4fa29dec3f 100644
--- a/app/components/UI/Perps/Views/PerpsTransactionsView/PerpsTransactionsView.tsx
+++ b/app/components/UI/Perps/Views/PerpsTransactionsView/PerpsTransactionsView.tsx
@@ -343,15 +343,7 @@ const PerpsTransactionsView: React.FC = () => {
);
- const filterTabs: FilterTab[] = useMemo(
- () => [
- strings('perps.transactions.tabs.trades'),
- strings('perps.transactions.tabs.orders'),
- strings('perps.transactions.tabs.funding'),
- strings('perps.transactions.tabs.deposits'),
- ],
- [],
- );
+ const filterTabs: FilterTab[] = ['Trades', 'Orders', 'Funding', 'Deposits'];
const filterTabDescription = useMemo(() => {
if (activeFilter === 'Funding') {
diff --git a/app/components/UI/Perps/controllers/PerpsController.test.ts b/app/components/UI/Perps/controllers/PerpsController.test.ts
index 26b06e7bff5..9d80caa108c 100644
--- a/app/components/UI/Perps/controllers/PerpsController.test.ts
+++ b/app/components/UI/Perps/controllers/PerpsController.test.ts
@@ -2166,6 +2166,7 @@ describe('PerpsController', () => {
networkClientId: mockNetworkClientId,
origin: 'metamask',
type: 'perpsDeposit',
+ skipInitialGasEstimate: true,
});
});
diff --git a/app/components/UI/Perps/controllers/PerpsController.ts b/app/components/UI/Perps/controllers/PerpsController.ts
index cb978580726..0bbf5f1c6f0 100644
--- a/app/components/UI/Perps/controllers/PerpsController.ts
+++ b/app/components/UI/Perps/controllers/PerpsController.ts
@@ -1279,6 +1279,7 @@ export class PerpsController extends BaseController<
networkClientId,
origin: 'metamask',
type: TransactionType.perpsDeposit,
+ skipInitialGasEstimate: true,
});
// Store the transaction ID and try to get amount from transaction
diff --git a/app/components/UI/Predict/controllers/PredictController.test.ts b/app/components/UI/Predict/controllers/PredictController.test.ts
index d635084a50e..3f30178f3f2 100644
--- a/app/components/UI/Predict/controllers/PredictController.test.ts
+++ b/app/components/UI/Predict/controllers/PredictController.test.ts
@@ -2605,6 +2605,8 @@ describe('PredictController', () => {
networkClientId: 'mainnet',
disableHook: true,
disableSequential: true,
+ disableUpgrade: true,
+ skipInitialGasEstimate: true,
transactions: mockTransactions,
});
});
diff --git a/app/components/UI/Predict/controllers/PredictController.ts b/app/components/UI/Predict/controllers/PredictController.ts
index b6aff6f6e06..ca3612fe691 100644
--- a/app/components/UI/Predict/controllers/PredictController.ts
+++ b/app/components/UI/Predict/controllers/PredictController.ts
@@ -1760,6 +1760,8 @@ export class PredictController extends BaseController<
networkClientId,
disableHook: true,
disableSequential: true,
+ disableUpgrade: true,
+ skipInitialGasEstimate: true,
transactions,
});
diff --git a/app/components/UI/Predict/providers/polymarket/safe/utils.ts b/app/components/UI/Predict/providers/polymarket/safe/utils.ts
index 511258898e9..dde34579b94 100644
--- a/app/components/UI/Predict/providers/polymarket/safe/utils.ts
+++ b/app/components/UI/Predict/providers/polymarket/safe/utils.ts
@@ -389,6 +389,7 @@ export const getDeployProxyWalletTransaction = async ({
to: SAFE_FACTORY_ADDRESS as Hex,
data: calldata,
},
+ type: TransactionType.contractInteraction,
};
} catch (error) {
console.error('Error creating proxy wallet', error);
@@ -580,6 +581,7 @@ export const getProxyWalletAllowancesTransaction = async ({
to: safeAddress as Hex,
data: callData as Hex,
},
+ type: TransactionType.contractInteraction,
};
};
diff --git a/app/components/UI/Ramp/Aggregator/Views/BuildQuote/__snapshots__/BuildQuote.test.tsx.snap b/app/components/UI/Ramp/Aggregator/Views/BuildQuote/__snapshots__/BuildQuote.test.tsx.snap
index 64c2c8aaaff..bdbe4ab2c19 100644
--- a/app/components/UI/Ramp/Aggregator/Views/BuildQuote/__snapshots__/BuildQuote.test.tsx.snap
+++ b/app/components/UI/Ramp/Aggregator/Views/BuildQuote/__snapshots__/BuildQuote.test.tsx.snap
@@ -231,7 +231,7 @@ exports[`BuildQuote View Balance display displays balance from useBalance for no
}
}
>
- Amount to buy
+ Buy
@@ -3090,7 +3090,7 @@ exports[`BuildQuote View Crypto Currency Data renders a special error page if cr
}
}
>
- Amount to buy
+ Buy
@@ -3753,7 +3753,7 @@ exports[`BuildQuote View Crypto Currency Data renders a special error page if cr
}
}
>
- Amount to sell
+ Sell
@@ -4513,7 +4513,7 @@ exports[`BuildQuote View Crypto Currency Data renders an error page when there i
}
}
>
- Amount to buy
+ Buy
@@ -5273,7 +5273,7 @@ exports[`BuildQuote View Crypto Currency Data renders the loading page when cryp
}
}
>
- Amount to buy
+ Buy
@@ -7836,7 +7836,7 @@ exports[`BuildQuote View Fiat Currency Data renders an error page when there is
}
}
>
- Amount to buy
+ Buy
@@ -8596,7 +8596,7 @@ exports[`BuildQuote View Fiat Currency Data renders the loading page when fiats
}
}
>
- Amount to buy
+ Buy
@@ -11068,7 +11068,7 @@ exports[`BuildQuote View Payment Method Data renders an error page when there is
}
}
>
- Amount to buy
+ Buy
@@ -11828,7 +11828,7 @@ exports[`BuildQuote View Payment Method Data renders no icons if there are no pa
}
}
>
- Amount to buy
+ Buy
@@ -14644,7 +14644,7 @@ exports[`BuildQuote View Payment Method Data renders the loading page when payme
}
}
>
- Amount to buy
+ Buy
@@ -17382,7 +17382,7 @@ exports[`BuildQuote View Regions data renders an error page when there is a regi
}
}
>
- Amount to buy
+ Buy
@@ -18142,7 +18142,7 @@ exports[`BuildQuote View Regions data renders the loading page when regions are
}
}
>
- Amount to buy
+ Buy
@@ -20578,7 +20578,7 @@ exports[`BuildQuote View renders correctly 1`] = `
}
}
>
- Amount to buy
+ Buy
@@ -23340,7 +23340,7 @@ exports[`BuildQuote View renders correctly 2`] = `
}
}
>
- Amount to sell
+ Sell
@@ -26179,7 +26179,7 @@ exports[`BuildQuote View renders correctly when sdkError is present 1`] = `
}
}
>
- Amount to buy
+ Buy
@@ -26842,7 +26842,7 @@ exports[`BuildQuote View renders correctly when sdkError is present 2`] = `
}
}
>
- Amount to sell
+ Sell
diff --git a/app/components/UI/Ramp/Deposit/Views/BuildQuote/__snapshots__/BuildQuote.test.tsx.snap b/app/components/UI/Ramp/Deposit/Views/BuildQuote/__snapshots__/BuildQuote.test.tsx.snap
index 4bdcce5f13d..fdbf13a0d2e 100644
--- a/app/components/UI/Ramp/Deposit/Views/BuildQuote/__snapshots__/BuildQuote.test.tsx.snap
+++ b/app/components/UI/Ramp/Deposit/Views/BuildQuote/__snapshots__/BuildQuote.test.tsx.snap
@@ -231,7 +231,7 @@ exports[`BuildQuote Component Continue button functionality displays error when
}
}
>
- Deposit
+ Buy
@@ -2105,7 +2105,7 @@ exports[`BuildQuote Component Continue button functionality displays error when
}
}
>
- Deposit
+ Buy
@@ -3979,7 +3979,7 @@ exports[`BuildQuote Component Continue button functionality displays error when
}
}
>
- Deposit
+ Buy
@@ -5853,7 +5853,7 @@ exports[`BuildQuote Component Keypad Functionality displays converted token amou
}
}
>
- Deposit
+ Buy
@@ -7666,7 +7666,7 @@ exports[`BuildQuote Component Keypad Functionality updates amount when keypad is
}
}
>
- Deposit
+ Buy
@@ -9478,7 +9478,7 @@ exports[`BuildQuote Component Payment Method Selection does not open payment met
}
}
>
- Deposit
+ Buy
@@ -11291,7 +11291,7 @@ exports[`BuildQuote Component Payment Method Selection does not open payment met
}
}
>
- Deposit
+ Buy
@@ -13197,7 +13197,7 @@ exports[`BuildQuote Component Payment Method Selection does not show the duratio
}
}
>
- Deposit
+ Buy
@@ -14965,7 +14965,7 @@ exports[`BuildQuote Component Payment Method Selection shows the right duration
}
}
>
- Deposit
+ Buy
@@ -16778,7 +16778,7 @@ exports[`BuildQuote Component Region Selection displays EUR currency when select
}
}
>
- Deposit
+ Buy
@@ -18591,7 +18591,7 @@ exports[`BuildQuote Component Region Selection displays default US region on ini
}
}
>
- Deposit
+ Buy
@@ -20404,7 +20404,7 @@ exports[`BuildQuote Component Region Selection does not open region modal when r
}
}
>
- Deposit
+ Buy
@@ -22217,7 +22217,7 @@ exports[`BuildQuote Component Region Selection does not open region modal when r
}
}
>
- Deposit
+ Buy
@@ -24123,7 +24123,7 @@ exports[`BuildQuote Component Token Selection does not open token modal when cry
}
}
>
- Deposit
+ Buy
@@ -25934,7 +25934,7 @@ exports[`BuildQuote Component Token Selection does not open token modal when cry
}
}
>
- Deposit
+ Buy
@@ -27838,7 +27838,7 @@ exports[`BuildQuote Component User Details Error displays user details error ale
}
}
>
- Deposit
+ Buy
@@ -29744,7 +29744,7 @@ exports[`BuildQuote Component render matches snapshot 1`] = `
}
}
>
- Deposit
+ Buy
diff --git a/app/components/UI/Ramp/hooks/useRampNavigation.test.ts b/app/components/UI/Ramp/hooks/useRampNavigation.test.ts
index f0fb9ef60cc..8d8330afff7 100644
--- a/app/components/UI/Ramp/hooks/useRampNavigation.test.ts
+++ b/app/components/UI/Ramp/hooks/useRampNavigation.test.ts
@@ -11,6 +11,8 @@ import {
getRampRoutingDecision,
UnifiedRampRoutingType,
} from '../../../../reducers/fiatOrders';
+import { createEligibilityFailedModalNavigationDetails } from '../components/EligibilityFailedModal/EligibilityFailedModal';
+import { createRampUnsupportedModalNavigationDetails } from '../components/RampUnsupportedModal/RampUnsupportedModal';
jest.mock('@react-navigation/native');
jest.mock('@react-navigation/compat', () => ({
@@ -122,6 +124,82 @@ describe('useRampNavigation', () => {
mockUseRampsUnifiedV1Enabled.mockReturnValue(true);
});
+ describe('error and unsupported routing', () => {
+ it('navigates to eligibility failed modal when routing decision is ERROR', () => {
+ mockGetRampRoutingDecision.mockReturnValue(
+ UnifiedRampRoutingType.ERROR,
+ );
+ const navDetails = createEligibilityFailedModalNavigationDetails();
+
+ const { result } = renderHookWithProvider(() => useRampNavigation());
+
+ result.current.goToBuy();
+
+ expect(mockNavigate).toHaveBeenCalledWith(...navDetails);
+ expect(mockCreateRampNavigationDetails).not.toHaveBeenCalled();
+ expect(mockCreateDepositNavigationDetails).not.toHaveBeenCalled();
+ expect(
+ mockCreateTokenSelectionNavigationDetails,
+ ).not.toHaveBeenCalled();
+ });
+
+ it('navigates to eligibility failed modal when routing decision is ERROR with intent', () => {
+ mockGetRampRoutingDecision.mockReturnValue(
+ UnifiedRampRoutingType.ERROR,
+ );
+ const intent = { assetId: 'eip155:1/erc20:0x123' };
+ const navDetails = createEligibilityFailedModalNavigationDetails();
+
+ const { result } = renderHookWithProvider(() => useRampNavigation());
+
+ result.current.goToBuy(intent);
+
+ expect(mockNavigate).toHaveBeenCalledWith(...navDetails);
+ expect(mockCreateRampNavigationDetails).not.toHaveBeenCalled();
+ expect(mockCreateDepositNavigationDetails).not.toHaveBeenCalled();
+ expect(
+ mockCreateTokenSelectionNavigationDetails,
+ ).not.toHaveBeenCalled();
+ });
+
+ it('navigates to unsupported modal when routing decision is UNSUPPORTED', () => {
+ mockGetRampRoutingDecision.mockReturnValue(
+ UnifiedRampRoutingType.UNSUPPORTED,
+ );
+ const navDetails = createRampUnsupportedModalNavigationDetails();
+
+ const { result } = renderHookWithProvider(() => useRampNavigation());
+
+ result.current.goToBuy();
+
+ expect(mockNavigate).toHaveBeenCalledWith(...navDetails);
+ expect(mockCreateRampNavigationDetails).not.toHaveBeenCalled();
+ expect(mockCreateDepositNavigationDetails).not.toHaveBeenCalled();
+ expect(
+ mockCreateTokenSelectionNavigationDetails,
+ ).not.toHaveBeenCalled();
+ });
+
+ it('navigates to unsupported modal when routing decision is UNSUPPORTED with intent', () => {
+ mockGetRampRoutingDecision.mockReturnValue(
+ UnifiedRampRoutingType.UNSUPPORTED,
+ );
+ const intent = { assetId: 'eip155:1/erc20:0x123' };
+ const navDetails = createRampUnsupportedModalNavigationDetails();
+
+ const { result } = renderHookWithProvider(() => useRampNavigation());
+
+ result.current.goToBuy(intent);
+
+ expect(mockNavigate).toHaveBeenCalledWith(...navDetails);
+ expect(mockCreateRampNavigationDetails).not.toHaveBeenCalled();
+ expect(mockCreateDepositNavigationDetails).not.toHaveBeenCalled();
+ expect(
+ mockCreateTokenSelectionNavigationDetails,
+ ).not.toHaveBeenCalled();
+ });
+ });
+
describe('token selection routing', () => {
it('navigates to TokenSelection when no assetId is provided', () => {
const mockNavDetails = [
diff --git a/app/components/UI/Ramp/hooks/useRampNavigation.ts b/app/components/UI/Ramp/hooks/useRampNavigation.ts
index ac603fea154..dc829c1c3a7 100644
--- a/app/components/UI/Ramp/hooks/useRampNavigation.ts
+++ b/app/components/UI/Ramp/hooks/useRampNavigation.ts
@@ -13,6 +13,8 @@ import {
getRampRoutingDecision,
UnifiedRampRoutingType,
} from '../../../../reducers/fiatOrders';
+import { createRampUnsupportedModalNavigationDetails } from '../components/RampUnsupportedModal/RampUnsupportedModal';
+import { createEligibilityFailedModalNavigationDetails } from '../components/EligibilityFailedModal/EligibilityFailedModal';
enum RampMode {
AGGREGATOR = 'AGGREGATOR',
@@ -45,6 +47,18 @@ export const useRampNavigation = () => {
options || {};
if (isRampsUnifiedV1Enabled && !overrideUnifiedRouting) {
+ if (rampRoutingDecision === UnifiedRampRoutingType.ERROR) {
+ navigation.navigate(
+ ...createEligibilityFailedModalNavigationDetails(),
+ );
+ return;
+ }
+
+ if (rampRoutingDecision === UnifiedRampRoutingType.UNSUPPORTED) {
+ navigation.navigate(...createRampUnsupportedModalNavigationDetails());
+ return;
+ }
+
// If no assetId is provided, route to TokenSelection
if (!intent?.assetId) {
navigation.navigate(...createTokenSelectionNavDetails());
diff --git a/app/components/Views/confirmations/components/pay-token-amount/pay-token-amount.tsx b/app/components/Views/confirmations/components/pay-token-amount/pay-token-amount.tsx
index d310c2e9b00..2f2324ac8e5 100644
--- a/app/components/Views/confirmations/components/pay-token-amount/pay-token-amount.tsx
+++ b/app/components/Views/confirmations/components/pay-token-amount/pay-token-amount.tsx
@@ -44,8 +44,24 @@ export function PayTokenAmount({ amountHuman, disabled }: PayTokenAmountProps) {
const fiatRates = useTokenFiatRates(fiatRequests);
- const payTokenFiatRate = fiatRates[0];
- const assetFiatRate = fiatRates[1];
+ const formattedAmount = useMemo(() => {
+ const payTokenFiatRate = fiatRates[0];
+ const assetFiatRate = fiatRates[1];
+
+ if (disabled || !payToken || !payTokenFiatRate || !assetFiatRate) {
+ return undefined;
+ }
+
+ const assetToPayTokenRate = new BigNumber(assetFiatRate).dividedBy(
+ payTokenFiatRate,
+ );
+
+ const payTokenAmount = new BigNumber(amountHuman || '0').multipliedBy(
+ assetToPayTokenRate,
+ );
+
+ return formatAmount(I18n.locale, payTokenAmount);
+ }, [amountHuman, disabled, payToken, fiatRates]);
if (disabled) {
return (
@@ -55,18 +71,7 @@ export function PayTokenAmount({ amountHuman, disabled }: PayTokenAmountProps) {
);
}
- if (!payToken || !payTokenFiatRate || !assetFiatRate)
- return ;
-
- const assetToPayTokenRate = new BigNumber(assetFiatRate).dividedBy(
- payTokenFiatRate,
- );
-
- const payTokenAmount = new BigNumber(amountHuman || '0').multipliedBy(
- assetToPayTokenRate,
- );
-
- const formattedAmount = formatAmount(I18n.locale, payTokenAmount);
+ if (!formattedAmount) return ;
return (
diff --git a/app/components/Views/confirmations/hooks/alerts/useInsufficientPayTokenBalanceAlert.test.ts b/app/components/Views/confirmations/hooks/alerts/useInsufficientPayTokenBalanceAlert.test.ts
index 1182a79a70b..320ddc6b500 100644
--- a/app/components/Views/confirmations/hooks/alerts/useInsufficientPayTokenBalanceAlert.test.ts
+++ b/app/components/Views/confirmations/hooks/alerts/useInsufficientPayTokenBalanceAlert.test.ts
@@ -159,6 +159,39 @@ describe('useInsufficientPayTokenBalanceAlert', () => {
]);
});
+ it('returns alert if pay token balance shortfall is equal to total amount', () => {
+ useTransactionPayTokenMock.mockReturnValue({
+ payToken: {
+ ...PAY_TOKEN_MOCK,
+ balanceRaw: '999',
+ },
+ setPayToken: jest.fn(),
+ });
+
+ useTransactionPayRequiredTokensMock.mockReturnValue([
+ {
+ ...REQUIRED_TOKEN_MOCK,
+ amountUsd: '0.02',
+ },
+ ]);
+
+ const { result } = runHook();
+
+ expect(result.current).toStrictEqual([
+ {
+ key: AlertKeys.InsufficientPayTokenFees,
+ field: RowAlertKey.Amount,
+ isBlocking: true,
+ title: strings('alert_system.insufficient_pay_token_balance.message'),
+ message: strings(
+ 'alert_system.insufficient_pay_token_balance_fees_no_target.message',
+ { amount: '$1.21' },
+ ),
+ severity: Severity.Danger,
+ },
+ ]);
+ });
+
it('returns alert if pay token balance is less than source amount plus source network', () => {
useTransactionPayTokenMock.mockReturnValue({
payToken: {
diff --git a/app/components/Views/confirmations/hooks/alerts/useInsufficientPayTokenBalanceAlert.ts b/app/components/Views/confirmations/hooks/alerts/useInsufficientPayTokenBalanceAlert.ts
index 3d5ec92493f..e700dc4ae46 100644
--- a/app/components/Views/confirmations/hooks/alerts/useInsufficientPayTokenBalanceAlert.ts
+++ b/app/components/Views/confirmations/hooks/alerts/useInsufficientPayTokenBalanceAlert.ts
@@ -83,7 +83,10 @@ export function useInsufficientPayTokenBalanceAlert({
const targetAmountUsd = useMemo(() => {
const shortfall = totalSourceAmountUsd.minus(balanceUsd ?? '0');
- return formatFiat(totalAmountUsd.minus(shortfall));
+ const targetUsdValue = totalAmountUsd.minus(shortfall);
+ const targetUsd = formatFiat(targetUsdValue);
+
+ return targetUsdValue.isLessThanOrEqualTo(0) ? undefined : targetUsd;
}, [balanceUsd, formatFiat, totalAmountUsd, totalSourceAmountUsd]);
const totalSourceNetworkFeeRaw = useMemo(
@@ -141,10 +144,14 @@ export function useInsufficientPayTokenBalanceAlert({
...baseAlert,
key: AlertKeys.InsufficientPayTokenFees,
title: strings('alert_system.insufficient_pay_token_balance.message'),
- message: strings(
- 'alert_system.insufficient_pay_token_balance_fees.message',
- { amount: targetAmountUsd },
- ),
+ message: targetAmountUsd
+ ? strings(
+ 'alert_system.insufficient_pay_token_balance_fees.message',
+ { amount: targetAmountUsd },
+ )
+ : strings(
+ 'alert_system.insufficient_pay_token_balance_fees_no_target.message',
+ ),
},
];
}
diff --git a/app/components/Views/confirmations/hooks/gas/useGasFeeToken.ts b/app/components/Views/confirmations/hooks/gas/useGasFeeToken.ts
index 65a5336325f..da10ad725f6 100644
--- a/app/components/Views/confirmations/hooks/gas/useGasFeeToken.ts
+++ b/app/components/Views/confirmations/hooks/gas/useGasFeeToken.ts
@@ -17,6 +17,7 @@ import { selectNetworkConfigurationByChainId } from '../../../../../selectors/ne
import { RootState } from '../../../../../reducers';
import { useEthFiatAmount } from '../useEthFiatAmount';
import { useAccountNativeBalance } from '../useAccountNativeBalance';
+import { useMemo } from 'react';
export const RATE_WEI_NATIVE = '0xDE0B6B3A7640000'; // 1x10^18
@@ -44,9 +45,9 @@ export function useGasFeeToken({ tokenAddress }: { tokenAddress?: Hex }) {
decimals: 0,
};
- const amountFormatted = formatAmount(
- locale,
- new BigNumber(amount).shiftedBy(-decimals),
+ const amountFormatted = useMemo(
+ () => formatAmount(locale, new BigNumber(amount).shiftedBy(-decimals)),
+ [amount, decimals, locale],
);
const amountFiat = useFiatTokenValue(
@@ -61,20 +62,34 @@ export function useGasFeeToken({ tokenAddress }: { tokenAddress?: Hex }) {
);
const metamaskFeeFiat = useFiatTokenValue(gasFeeToken, metaMaskFee, chainId);
- const transferTransaction =
- tokenAddress === NATIVE_TOKEN_ADDRESS
- ? getNativeTransferTransaction(gasFeeToken)
- : getTokenTransferTransaction(gasFeeToken);
+ const transferTransaction = useMemo(
+ () =>
+ tokenAddress === NATIVE_TOKEN_ADDRESS
+ ? getNativeTransferTransaction(gasFeeToken)
+ : getTokenTransferTransaction(gasFeeToken),
+ [gasFeeToken, tokenAddress],
+ );
- return {
- ...gasFeeToken,
- amountFormatted,
- amountFiat,
- balanceFiat,
- metaMaskFee,
- metamaskFeeFiat,
- transferTransaction,
- };
+ return useMemo(
+ () => ({
+ ...gasFeeToken,
+ amountFormatted,
+ amountFiat,
+ balanceFiat,
+ metaMaskFee,
+ metamaskFeeFiat,
+ transferTransaction,
+ }),
+ [
+ gasFeeToken,
+ amountFormatted,
+ amountFiat,
+ balanceFiat,
+ metaMaskFee,
+ metamaskFeeFiat,
+ transferTransaction,
+ ],
+ );
}
export function useSelectedGasFeeToken() {
@@ -109,19 +124,29 @@ function useNativeGasFeeToken(): GasFeeToken {
const { nativeCurrency } = networkConfiguration ?? {};
const { gas, maxFeePerGas, maxPriorityFeePerGas } = txParams ?? {};
- return {
- amount: (estimatedFeeNativeHex as Hex) ?? '0x0',
- balance,
- decimals: 18,
- gas: gas as Hex,
- gasTransfer: '0x0',
- maxFeePerGas: maxFeePerGas as Hex,
- maxPriorityFeePerGas: maxPriorityFeePerGas as Hex,
- rateWei: RATE_WEI_NATIVE,
- recipient: NATIVE_TOKEN_ADDRESS,
- symbol: nativeCurrency,
- tokenAddress: NATIVE_TOKEN_ADDRESS,
- };
+ return useMemo(
+ () => ({
+ amount: (estimatedFeeNativeHex as Hex) ?? '0x0',
+ balance,
+ decimals: 18,
+ gas: gas as Hex,
+ gasTransfer: '0x0',
+ maxFeePerGas: maxFeePerGas as Hex,
+ maxPriorityFeePerGas: maxPriorityFeePerGas as Hex,
+ rateWei: RATE_WEI_NATIVE,
+ recipient: NATIVE_TOKEN_ADDRESS,
+ symbol: nativeCurrency,
+ tokenAddress: NATIVE_TOKEN_ADDRESS,
+ }),
+ [
+ estimatedFeeNativeHex,
+ balance,
+ gas,
+ maxFeePerGas,
+ maxPriorityFeePerGas,
+ nativeCurrency,
+ ],
+ );
}
function useFiatTokenValue(
diff --git a/app/components/Views/confirmations/hooks/pay/useAutomaticTransactionPayToken.test.ts b/app/components/Views/confirmations/hooks/pay/useAutomaticTransactionPayToken.test.ts
index 15ad960a94d..753717f2bc1 100644
--- a/app/components/Views/confirmations/hooks/pay/useAutomaticTransactionPayToken.test.ts
+++ b/app/components/Views/confirmations/hooks/pay/useAutomaticTransactionPayToken.test.ts
@@ -1,33 +1,26 @@
import { merge } from 'lodash';
import { renderHookWithProvider } from '../../../../../util/test/renderWithProvider';
-import { useTokensWithBalance } from '../../../../UI/Bridge/hooks/useTokensWithBalance';
import { useAutomaticTransactionPayToken } from './useAutomaticTransactionPayToken';
import { useTransactionPayToken } from './useTransactionPayToken';
import { simpleSendTransactionControllerMock } from '../../__mocks__/controllers/transaction-controller-mock';
import { transactionApprovalControllerMock } from '../../__mocks__/controllers/approval-controller-mock';
-import { selectEnabledSourceChains } from '../../../../../core/redux/slices/bridge';
-import { NATIVE_TOKEN_ADDRESS } from '../../constants/tokens';
import { isHardwareAccount } from '../../../../../util/address';
import { TransactionType } from '@metamask/transaction-controller';
import { TransactionPayRequiredToken } from '@metamask/transaction-pay-controller';
import { Hex } from '@metamask/utils';
import { useTransactionPayRequiredTokens } from './useTransactionPayData';
+import { useTransactionPayAvailableTokens } from './useTransactionPayAvailableTokens';
+import { AssetType } from '../../types/token';
jest.mock('./useTransactionPayToken');
-jest.mock('../../../../UI/Bridge/hooks/useTokensWithBalance');
jest.mock('../../../../../util/address');
jest.mock('../../../../../selectors/transactionPayController');
jest.mock('./useTransactionPayData');
-
-jest.mock('../../../../../core/redux/slices/bridge', () => ({
- ...jest.requireActual('../../../../../core/redux/slices/bridge'),
- selectEnabledSourceChains: jest.fn(),
-}));
+jest.mock('./useTransactionPayAvailableTokens');
const TOKEN_ADDRESS_1_MOCK = '0x1234567890abcdef1234567890abcdef12345678';
const TOKEN_ADDRESS_2_MOCK = '0xabcdefabcdefabcdefabcdefabcdefabcdefabcd';
const TOKEN_ADDRESS_3_MOCK = '0xabc1234567890abcdef1234567890abcdef12345678';
-const REQUIRED_BALANCE_MOCK = 10;
const CHAIN_ID_1_MOCK = '0x1';
const CHAIN_ID_2_MOCK = '0x2';
@@ -61,8 +54,9 @@ function runHook({ disable = false } = {}) {
describe('useAutomaticTransactionPayToken', () => {
const useTransactionPayTokenMock = jest.mocked(useTransactionPayToken);
- const useTokensWithBalanceMock = jest.mocked(useTokensWithBalance);
- const selectEnabledSourceChainsMock = jest.mocked(selectEnabledSourceChains);
+ const useTransactionPayAvailableTokensMock = jest.mocked(
+ useTransactionPayAvailableTokens,
+ );
const isHardwareAccountMock = jest.mocked(isHardwareAccount);
const useTransactionPayRequiredTokensMock = jest.mocked(
useTransactionPayRequiredTokens,
@@ -75,8 +69,6 @@ describe('useAutomaticTransactionPayToken', () => {
beforeEach(() => {
jest.resetAllMocks();
- selectEnabledSourceChainsMock.mockReturnValue([]);
-
useTransactionPayTokenMock.mockReturnValue({
payToken: undefined,
setPayToken: setPayTokenMock,
@@ -85,6 +77,7 @@ describe('useAutomaticTransactionPayToken', () => {
useTransactionPayRequiredTokensMock.mockReturnValue([
{
address: TOKEN_ADDRESS_1_MOCK as Hex,
+ chainId: CHAIN_ID_1_MOCK as Hex,
} as TransactionPayRequiredToken,
]);
@@ -92,23 +85,20 @@ describe('useAutomaticTransactionPayToken', () => {
});
it('selects target token if has balance', () => {
- useTokensWithBalanceMock.mockReturnValue([
- {
- address: TOKEN_ADDRESS_1_MOCK,
- chainId: CHAIN_ID_1_MOCK,
- tokenFiatAmount: REQUIRED_BALANCE_MOCK,
- },
+ useTransactionPayAvailableTokensMock.mockReturnValue([
{
address: TOKEN_ADDRESS_2_MOCK,
chainId: CHAIN_ID_1_MOCK,
- tokenFiatAmount: REQUIRED_BALANCE_MOCK + 10,
},
{
address: TOKEN_ADDRESS_3_MOCK,
chainId: CHAIN_ID_2_MOCK,
- tokenFiatAmount: REQUIRED_BALANCE_MOCK + 20,
},
- ] as unknown as ReturnType);
+ {
+ address: TOKEN_ADDRESS_1_MOCK,
+ chainId: CHAIN_ID_1_MOCK,
+ },
+ ] as AssetType[]);
runHook();
@@ -119,33 +109,20 @@ describe('useAutomaticTransactionPayToken', () => {
});
it('selects token with highest balance on same chain if insufficient balance on target token', () => {
- useTokensWithBalanceMock.mockReturnValue([
- {
- address: TOKEN_ADDRESS_1_MOCK,
- chainId: CHAIN_ID_1_MOCK,
- tokenFiatAmount: 0,
- },
- {
- address: TOKEN_ADDRESS_2_MOCK,
- chainId: CHAIN_ID_1_MOCK,
- tokenFiatAmount: REQUIRED_BALANCE_MOCK + 5,
- },
+ useTransactionPayAvailableTokensMock.mockReturnValue([
{
address: TOKEN_ADDRESS_3_MOCK,
- chainId: CHAIN_ID_1_MOCK,
- tokenFiatAmount: REQUIRED_BALANCE_MOCK + 10,
+ chainId: CHAIN_ID_2_MOCK,
},
{
- address: TOKEN_ADDRESS_3_MOCK,
+ address: TOKEN_ADDRESS_2_MOCK,
chainId: CHAIN_ID_2_MOCK,
- tokenFiatAmount: REQUIRED_BALANCE_MOCK + 20,
},
{
- address: NATIVE_TOKEN_ADDRESS,
+ address: TOKEN_ADDRESS_3_MOCK,
chainId: CHAIN_ID_1_MOCK,
- tokenFiatAmount: 1,
},
- ] as unknown as ReturnType);
+ ] as AssetType[]);
runHook();
@@ -156,38 +133,16 @@ describe('useAutomaticTransactionPayToken', () => {
});
it('selects token with highest balance on alternate chain if insufficient balance on same chain', () => {
- useTokensWithBalanceMock.mockReturnValue([
- {
- address: TOKEN_ADDRESS_1_MOCK,
- chainId: CHAIN_ID_1_MOCK,
- tokenFiatAmount: 0,
- },
- {
- address: TOKEN_ADDRESS_2_MOCK,
- chainId: CHAIN_ID_1_MOCK,
- tokenFiatAmount: 0,
- },
- {
- address: TOKEN_ADDRESS_1_MOCK,
- chainId: CHAIN_ID_2_MOCK,
- tokenFiatAmount: REQUIRED_BALANCE_MOCK + 10,
- },
+ useTransactionPayAvailableTokensMock.mockReturnValue([
{
address: TOKEN_ADDRESS_3_MOCK,
chainId: CHAIN_ID_2_MOCK,
- tokenFiatAmount: REQUIRED_BALANCE_MOCK + 20,
- },
- {
- address: NATIVE_TOKEN_ADDRESS,
- chainId: CHAIN_ID_1_MOCK,
- tokenFiatAmount: 0,
},
{
- address: NATIVE_TOKEN_ADDRESS,
+ address: TOKEN_ADDRESS_2_MOCK,
chainId: CHAIN_ID_2_MOCK,
- tokenFiatAmount: 1,
},
- ] as unknown as ReturnType);
+ ] as AssetType[]);
runHook();
@@ -198,28 +153,7 @@ describe('useAutomaticTransactionPayToken', () => {
});
it('selects target token if insufficient balance on all chains', () => {
- useTokensWithBalanceMock.mockReturnValue([
- {
- address: TOKEN_ADDRESS_1_MOCK,
- chainId: CHAIN_ID_1_MOCK,
- tokenFiatAmount: REQUIRED_BALANCE_MOCK - 1,
- },
- {
- address: TOKEN_ADDRESS_2_MOCK,
- chainId: CHAIN_ID_2_MOCK,
- tokenFiatAmount: REQUIRED_BALANCE_MOCK - 1,
- },
- {
- address: NATIVE_TOKEN_ADDRESS,
- chainId: CHAIN_ID_1_MOCK,
- tokenFiatAmount: 1,
- },
- {
- address: NATIVE_TOKEN_ADDRESS,
- chainId: CHAIN_ID_2_MOCK,
- tokenFiatAmount: 1,
- },
- ] as unknown as ReturnType);
+ useTransactionPayAvailableTokensMock.mockReturnValue([]);
runHook();
@@ -230,19 +164,7 @@ describe('useAutomaticTransactionPayToken', () => {
});
it('does nothing if no required tokens', () => {
- useTokensWithBalanceMock.mockReturnValue([
- {
- address: TOKEN_ADDRESS_1_MOCK,
- chainId: CHAIN_ID_1_MOCK,
- tokenFiatAmount: REQUIRED_BALANCE_MOCK,
- },
- {
- address: TOKEN_ADDRESS_2_MOCK,
- chainId: CHAIN_ID_2_MOCK,
- tokenFiatAmount: REQUIRED_BALANCE_MOCK,
- },
- ] as unknown as ReturnType);
-
+ useTransactionPayAvailableTokensMock.mockReturnValue([]);
useTransactionPayRequiredTokensMock.mockReturnValue([]);
runHook();
@@ -250,81 +172,21 @@ describe('useAutomaticTransactionPayToken', () => {
expect(setPayTokenMock).not.toHaveBeenCalled();
});
- it('does not select token if no native balance on chain', () => {
- useTokensWithBalanceMock.mockReturnValue([
- {
- address: TOKEN_ADDRESS_1_MOCK,
- chainId: CHAIN_ID_1_MOCK,
- tokenFiatAmount: REQUIRED_BALANCE_MOCK - 1,
- },
- {
- address: TOKEN_ADDRESS_2_MOCK,
- chainId: CHAIN_ID_1_MOCK,
- tokenFiatAmount: REQUIRED_BALANCE_MOCK + 5,
- },
- {
- address: TOKEN_ADDRESS_3_MOCK,
- chainId: CHAIN_ID_1_MOCK,
- tokenFiatAmount: REQUIRED_BALANCE_MOCK + 10,
- },
- {
- address: TOKEN_ADDRESS_3_MOCK,
- chainId: CHAIN_ID_2_MOCK,
- tokenFiatAmount: REQUIRED_BALANCE_MOCK + 20,
- },
- {
- address: NATIVE_TOKEN_ADDRESS,
- chainId: CHAIN_ID_1_MOCK,
- tokenFiatAmount: 0,
- },
- {
- address: NATIVE_TOKEN_ADDRESS,
- chainId: CHAIN_ID_2_MOCK,
- tokenFiatAmount: 1,
- },
- ] as unknown as ReturnType);
-
- runHook();
-
- expect(setPayTokenMock).toHaveBeenCalledWith({
- address: TOKEN_ADDRESS_3_MOCK,
- chainId: CHAIN_ID_2_MOCK,
- });
- });
-
- it('always selects target token if hardware wallet', () => {
- useTokensWithBalanceMock.mockReturnValue([
- {
- address: TOKEN_ADDRESS_1_MOCK,
- chainId: CHAIN_ID_1_MOCK,
- tokenFiatAmount: REQUIRED_BALANCE_MOCK - 1,
- },
+ it('selects target token if hardware wallet', () => {
+ useTransactionPayAvailableTokensMock.mockReturnValue([
{
address: TOKEN_ADDRESS_2_MOCK,
chainId: CHAIN_ID_1_MOCK,
- tokenFiatAmount: REQUIRED_BALANCE_MOCK - 2,
},
{
address: TOKEN_ADDRESS_1_MOCK,
chainId: CHAIN_ID_2_MOCK,
- tokenFiatAmount: REQUIRED_BALANCE_MOCK + 10,
},
{
address: TOKEN_ADDRESS_3_MOCK,
chainId: CHAIN_ID_2_MOCK,
- tokenFiatAmount: REQUIRED_BALANCE_MOCK + 20,
},
- {
- address: NATIVE_TOKEN_ADDRESS,
- chainId: CHAIN_ID_1_MOCK,
- tokenFiatAmount: 1,
- },
- {
- address: NATIVE_TOKEN_ADDRESS,
- chainId: CHAIN_ID_2_MOCK,
- tokenFiatAmount: 1,
- },
- ] as unknown as ReturnType);
+ ] as AssetType[]);
isHardwareAccountMock.mockReturnValue(true);
@@ -336,53 +198,13 @@ describe('useAutomaticTransactionPayToken', () => {
});
});
- it('returns number of tokens with balance', () => {
- useTokensWithBalanceMock.mockReturnValue([
- {
- address: TOKEN_ADDRESS_1_MOCK,
- chainId: CHAIN_ID_1_MOCK,
- tokenFiatAmount: REQUIRED_BALANCE_MOCK - 1,
- },
- {
- address: TOKEN_ADDRESS_2_MOCK,
- chainId: CHAIN_ID_1_MOCK,
- tokenFiatAmount: REQUIRED_BALANCE_MOCK - 2,
- },
- {
- address: TOKEN_ADDRESS_1_MOCK,
- chainId: CHAIN_ID_2_MOCK,
- tokenFiatAmount: REQUIRED_BALANCE_MOCK + 10,
- },
- {
- address: TOKEN_ADDRESS_3_MOCK,
- chainId: CHAIN_ID_2_MOCK,
- tokenFiatAmount: REQUIRED_BALANCE_MOCK + 20,
- },
- {
- address: NATIVE_TOKEN_ADDRESS,
- chainId: CHAIN_ID_1_MOCK,
- tokenFiatAmount: 1,
- },
- {
- address: NATIVE_TOKEN_ADDRESS,
- chainId: CHAIN_ID_2_MOCK,
- tokenFiatAmount: 1,
- },
- ] as unknown as ReturnType);
-
- const { result } = runHook();
-
- expect(result.current.count).toBe(6);
- });
-
it('selected nothing if disabled', () => {
- useTokensWithBalanceMock.mockReturnValue([
+ useTransactionPayAvailableTokensMock.mockReturnValue([
{
address: TOKEN_ADDRESS_1_MOCK,
chainId: CHAIN_ID_1_MOCK,
- tokenFiatAmount: REQUIRED_BALANCE_MOCK,
},
- ] as unknown as ReturnType);
+ ] as AssetType[]);
runHook({ disable: true });
diff --git a/app/components/Views/confirmations/hooks/pay/useAutomaticTransactionPayToken.ts b/app/components/Views/confirmations/hooks/pay/useAutomaticTransactionPayToken.ts
index c8fed86f528..b53456f0eba 100644
--- a/app/components/Views/confirmations/hooks/pay/useAutomaticTransactionPayToken.ts
+++ b/app/components/Views/confirmations/hooks/pay/useAutomaticTransactionPayToken.ts
@@ -1,149 +1,136 @@
-import { useSelector } from 'react-redux';
-import { useTokensWithBalance } from '../../../../UI/Bridge/hooks/useTokensWithBalance';
-import { selectEnabledSourceChains } from '../../../../../core/redux/slices/bridge';
import { useTransactionMetadataRequest } from '../transactions/useTransactionMetadataRequest';
-import { orderBy } from 'lodash';
import { useEffect, useMemo, useRef } from 'react';
import { Hex } from 'viem';
import { createProjectLogger } from '@metamask/utils';
import { useTransactionPayToken } from './useTransactionPayToken';
-import { BridgeToken } from '../../../../UI/Bridge/types';
import { isHardwareAccount } from '../../../../../util/address';
import { TransactionMeta } from '@metamask/transaction-controller';
-import { getRequiredBalance } from '../../utils/transaction-pay';
-import { getNativeTokenAddress } from '../../utils/asset';
import { useTransactionPayRequiredTokens } from './useTransactionPayData';
+import { useTransactionPayAvailableTokens } from './useTransactionPayAvailableTokens';
+import { AssetType } from '../../types/token';
const log = createProjectLogger('transaction-pay');
-export interface BalanceOverride {
- address: Hex;
- balance: number;
- chainId: Hex;
-}
-
export function useAutomaticTransactionPayToken({
- countOnly = false,
disable = false,
}: {
- countOnly?: boolean;
disable?: boolean;
} = {}) {
const isUpdated = useRef(false);
- const supportedChains = useSelector(selectEnabledSourceChains);
const { setPayToken } = useTransactionPayToken();
const requiredTokens = useTransactionPayRequiredTokens();
+ const tokens = useTransactionPayAvailableTokens();
+
+ const tokensWithBalance = useMemo(
+ () => tokens.filter((t) => !t.disabled),
+ [tokens],
+ );
const transactionMeta =
useTransactionMetadataRequest() ?? ({ txParams: {} } as TransactionMeta);
const {
- chainId,
txParams: { from },
} = transactionMeta;
- const chainIds = useMemo(
- () => (!isUpdated.current ? supportedChains.map((c) => c.chainId) : []),
- [supportedChains],
+ const isHardwareWallet = useMemo(
+ () => isHardwareAccount(from ?? '') ?? false,
+ [from],
);
- const tokens = useTokensWithBalance({ chainIds });
- const isHardwareWallet = isHardwareAccount(from ?? '');
- const requiredBalance = getRequiredBalance(transactionMeta);
-
- let automaticToken: { address: string; chainId?: string } | undefined;
- let count = 0;
-
- const nativeTokenAddress = getNativeTokenAddress(chainId as Hex);
-
- if (!disable && (!isUpdated.current || countOnly)) {
- const targetToken =
- requiredTokens.find((token) => token.address !== nativeTokenAddress) ??
- requiredTokens[0];
-
- const sufficientBalanceTokens = orderBy(
- tokens.filter((token) =>
- isTokenSupported(token, tokens, requiredBalance),
- ),
- (token) => token?.tokenFiatAmount ?? 0,
- 'desc',
- );
-
- count = sufficientBalanceTokens.length;
-
- const requiredToken = sufficientBalanceTokens.find(
- (token) =>
- token.address === targetToken?.address && token.chainId === chainId,
- );
-
- const sameChainHighestBalanceToken = sufficientBalanceTokens?.find(
- (token) => token.chainId === chainId,
- );
-
- const alternateChainHighestBalanceToken = sufficientBalanceTokens?.find(
- (token) => token.chainId !== chainId,
- );
-
- const targetTokenFallback = targetToken
- ? {
- address: targetToken.address,
- chainId,
- }
- : undefined;
-
- automaticToken =
- requiredToken ??
- sameChainHighestBalanceToken ??
- alternateChainHighestBalanceToken ??
- targetTokenFallback;
-
- if (isHardwareWallet) {
- automaticToken = targetTokenFallback;
- }
- }
+ const targetToken = useMemo(
+ () => requiredTokens.find((token) => !token.allowUnderMinimum),
+ [requiredTokens],
+ );
useEffect(() => {
- if (
- isUpdated.current ||
- !automaticToken ||
- !requiredTokens?.length ||
- countOnly
- ) {
+ if (disable || isUpdated.current) {
+ return;
+ }
+
+ const automaticToken = getBestToken({
+ isHardwareWallet,
+ targetToken,
+ tokens: tokensWithBalance,
+ });
+
+ if (!automaticToken) {
+ log('No automatic pay token found');
return;
}
setPayToken({
- address: automaticToken.address as Hex,
- chainId: automaticToken.chainId as Hex,
+ address: automaticToken.address,
+ chainId: automaticToken.chainId,
});
isUpdated.current = true;
log('Automatically selected pay token', automaticToken);
- }, [automaticToken, countOnly, isUpdated, requiredTokens, setPayToken]);
-
- return { count };
+ }, [
+ disable,
+ isHardwareWallet,
+ requiredTokens,
+ setPayToken,
+ targetToken,
+ tokensWithBalance,
+ ]);
}
-function isTokenSupported(
- token: BridgeToken,
- tokens: BridgeToken[],
- requiredBalance: number | undefined,
-): boolean {
- const nativeTokenAddress = getNativeTokenAddress(token.chainId as Hex);
+function getBestToken({
+ isHardwareWallet,
+ targetToken,
+ tokens,
+}: {
+ isHardwareWallet: boolean;
+ targetToken?: { address: Hex; chainId: Hex };
+ tokens: AssetType[];
+}): { address: Hex; chainId: Hex } | undefined {
+ const targetTokenFallback = targetToken
+ ? {
+ address: targetToken.address,
+ chainId: targetToken.chainId,
+ }
+ : undefined;
+
+ if (isHardwareWallet) {
+ return targetTokenFallback;
+ }
- const nativeToken = tokens.find(
- (t) => t.address === nativeTokenAddress && t.chainId === token.chainId,
+ const requiredToken = tokens.find(
+ (t) =>
+ t.address.toLowerCase() === targetToken?.address.toLowerCase() &&
+ t.chainId === targetToken?.chainId,
);
- const tokenAmount = token?.tokenFiatAmount ?? 0;
+ if (requiredToken) {
+ return {
+ address: requiredToken.address as Hex,
+ chainId: requiredToken.chainId as Hex,
+ };
+ }
- const isTokenBalanceSufficient =
- requiredBalance === undefined
- ? tokenAmount > 0
- : tokenAmount >= requiredBalance;
+ const sameChainHighestBalanceToken = tokens.find(
+ (t) => t.chainId === targetToken?.chainId,
+ );
- const hasNativeBalance = (nativeToken?.tokenFiatAmount ?? 0) > 0;
+ if (sameChainHighestBalanceToken) {
+ return {
+ address: sameChainHighestBalanceToken.address as Hex,
+ chainId: sameChainHighestBalanceToken.chainId as Hex,
+ };
+ }
+
+ const alternateChainHighestBalanceToken = tokens.find(
+ (t) => t.chainId !== targetToken?.chainId,
+ );
+
+ if (alternateChainHighestBalanceToken) {
+ return {
+ address: alternateChainHighestBalanceToken.address as Hex,
+ chainId: alternateChainHighestBalanceToken.chainId as Hex,
+ };
+ }
- return isTokenBalanceSufficient && hasNativeBalance;
+ return targetTokenFallback;
}
diff --git a/app/components/Views/confirmations/hooks/pay/useTransactionPayMetrics.test.ts b/app/components/Views/confirmations/hooks/pay/useTransactionPayMetrics.test.ts
index 191da8fe91a..dfe782e10c0 100644
--- a/app/components/Views/confirmations/hooks/pay/useTransactionPayMetrics.test.ts
+++ b/app/components/Views/confirmations/hooks/pay/useTransactionPayMetrics.test.ts
@@ -14,7 +14,6 @@ import { useTransactionPayToken } from './useTransactionPayToken';
import { act } from '@testing-library/react-native';
import { updateConfirmationMetric } from '../../../../../core/redux/slices/confirmationMetrics';
import { TransactionType } from '@metamask/transaction-controller';
-import { useAutomaticTransactionPayToken } from './useAutomaticTransactionPayToken';
import {
TransactionPayQuote,
TransactionPayRequiredToken,
@@ -26,12 +25,14 @@ import {
useTransactionPayRequiredTokens,
useTransactionPayTotals,
} from './useTransactionPayData';
+import { useTransactionPayAvailableTokens } from './useTransactionPayAvailableTokens';
+import { AssetType } from '../../types/token';
jest.mock('./useTransactionPayToken');
-jest.mock('./useAutomaticTransactionPayToken');
jest.mock('../useTokenAmount');
jest.mock('../../../../../selectors/transactionPayController');
jest.mock('../pay/useTransactionPayData');
+jest.mock('./useTransactionPayAvailableTokens');
jest.mock('../../../../../core/redux/slices/confirmationMetrics', () => ({
...jest.requireActual('../../../../../core/redux/slices/confirmationMetrics'),
@@ -76,12 +77,13 @@ describe('useTransactionPayMetrics', () => {
const updateConfirmationMetricMock = jest.mocked(updateConfirmationMetric);
const useTransactionPayQuotesMock = jest.mocked(useTransactionPayQuotes);
const useTransactionPayTotalsMock = jest.mocked(useTransactionPayTotals);
+
const useTransactionPayRequiredTokensMock = jest.mocked(
useTransactionPayRequiredTokens,
);
- const useAutomaticTransactionPayTokenMock = jest.mocked(
- useAutomaticTransactionPayToken,
+ const useTransactionPayAvailableTokensMock = jest.mocked(
+ useTransactionPayAvailableTokens,
);
beforeEach(() => {
@@ -104,9 +106,13 @@ describe('useTransactionPayMetrics', () => {
useTransactionPayQuotesMock.mockReturnValue([]);
- useAutomaticTransactionPayTokenMock.mockReturnValue({
- count: 5,
- });
+ useTransactionPayAvailableTokensMock.mockReturnValue([
+ {},
+ {},
+ {},
+ {},
+ {},
+ ] as AssetType[]);
});
it('does not update metrics if no pay token selected', async () => {
diff --git a/app/components/Views/confirmations/hooks/pay/useTransactionPayMetrics.ts b/app/components/Views/confirmations/hooks/pay/useTransactionPayMetrics.ts
index 3f7ecf2e34d..e1e2ef0c33f 100644
--- a/app/components/Views/confirmations/hooks/pay/useTransactionPayMetrics.ts
+++ b/app/components/Views/confirmations/hooks/pay/useTransactionPayMetrics.ts
@@ -1,4 +1,4 @@
-import { useEffect, useRef } from 'react';
+import { useEffect, useMemo, useRef } from 'react';
import { useDispatch } from 'react-redux';
import { updateConfirmationMetric } from '../../../../../core/redux/slices/confirmationMetrics';
import { useTransactionMetadataRequest } from '../transactions/useTransactionMetadataRequest';
@@ -7,7 +7,6 @@ import { Hex, Json } from '@metamask/utils';
import { TransactionType } from '@metamask/transaction-controller';
import { useTransactionPayToken } from './useTransactionPayToken';
import { BridgeToken } from '../../../../UI/Bridge/types';
-import { useAutomaticTransactionPayToken } from './useAutomaticTransactionPayToken';
import { getNativeTokenAddress } from '../../utils/asset';
import { hasTransactionType } from '../../utils/transaction';
import {
@@ -17,6 +16,7 @@ import {
} from './useTransactionPayData';
import { TransactionPayStrategy } from '@metamask/transaction-pay-controller';
import { BigNumber } from 'bignumber.js';
+import { useTransactionPayAvailableTokens } from './useTransactionPayAvailableTokens';
export function useTransactionPayMetrics() {
const dispatch = useDispatch();
@@ -26,10 +26,12 @@ export function useTransactionPayMetrics() {
const automaticPayToken = useRef();
const quotes = useTransactionPayQuotes();
const totals = useTransactionPayTotals();
+ const tokens = useTransactionPayAvailableTokens();
- const { count: availableTokenCount } = useAutomaticTransactionPayToken({
- countOnly: true,
- });
+ const availableTokens = useMemo(
+ () => tokens.filter((t) => !t.disabled),
+ [tokens],
+ );
const transactionId = transactionMeta?.id ?? '';
const { chainId, type } = transactionMeta ?? {};
@@ -58,7 +60,7 @@ export function useTransactionPayMetrics() {
properties.mm_pay_chain_presented =
automaticPayToken.current?.chainId ?? null;
- properties.mm_pay_payment_token_list_size = availableTokenCount;
+ properties.mm_pay_payment_token_list_size = availableTokens.length;
}
if (payToken && type === TransactionType.perpsDeposit) {
diff --git a/app/components/Views/confirmations/hooks/pay/useTransactionPayToken.ts b/app/components/Views/confirmations/hooks/pay/useTransactionPayToken.ts
index 605da0ba0ba..a5bb24ec246 100644
--- a/app/components/Views/confirmations/hooks/pay/useTransactionPayToken.ts
+++ b/app/components/Views/confirmations/hooks/pay/useTransactionPayToken.ts
@@ -5,6 +5,8 @@ import { RootState } from '../../../../../reducers';
import Engine from '../../../../../core/Engine';
import { selectTransactionPaymentTokenByTransactionId } from '../../../../../selectors/transactionPayController';
import { Hex } from '@metamask/utils';
+import { noop } from 'lodash';
+import EngineService from '../../../../../core/EngineService';
export function useTransactionPayToken() {
const { id: transactionId } = useTransactionMetadataRequest() || { id: '' };
@@ -14,7 +16,7 @@ export function useTransactionPayToken() {
);
const setPayToken = useCallback(
- async (newPayToken: { address: Hex; chainId: Hex }) => {
+ (newPayToken: { address: Hex; chainId: Hex }) => {
const { GasFeeController, NetworkController, TransactionPayController } =
Engine.context;
@@ -22,9 +24,9 @@ export function useTransactionPayToken() {
newPayToken.chainId,
);
- await GasFeeController.fetchGasFeeEstimates({
+ GasFeeController.fetchGasFeeEstimates({
networkClientId,
- });
+ }).catch(noop);
try {
TransactionPayController.updatePaymentToken({
@@ -32,6 +34,8 @@ export function useTransactionPayToken() {
tokenAddress: newPayToken.address,
chainId: newPayToken.chainId,
});
+
+ EngineService.flushState();
} catch (e) {
console.error('Error updating payment token', e);
}
diff --git a/app/components/Views/confirmations/hooks/transactions/useTransferRecipient.ts b/app/components/Views/confirmations/hooks/transactions/useTransferRecipient.ts
index 03b428ab63b..bf95906dce8 100644
--- a/app/components/Views/confirmations/hooks/transactions/useTransferRecipient.ts
+++ b/app/components/Views/confirmations/hooks/transactions/useTransferRecipient.ts
@@ -51,10 +51,15 @@ function getRecipientByType(
data: string,
transactionTo: string,
): string | undefined {
- const dataRecipient = getTransactionDataRecipient(data);
- const paramsRecipient = transactionTo;
-
- return type === TransactionType.simpleSend ? paramsRecipient : dataRecipient;
+ switch (type) {
+ case TransactionType.simpleSend:
+ return transactionTo;
+ case TransactionType.tokenMethodTransfer:
+ case TransactionType.tokenMethodTransferFrom:
+ return getTransactionDataRecipient(data);
+ default:
+ return undefined;
+ }
}
function getTransactionDataRecipient(data: string): string | undefined {
diff --git a/app/components/Views/confirmations/hooks/useTokenAmount.ts b/app/components/Views/confirmations/hooks/useTokenAmount.ts
index 1787ad5eda1..7f8de624582 100644
--- a/app/components/Views/confirmations/hooks/useTokenAmount.ts
+++ b/app/components/Views/confirmations/hooks/useTokenAmount.ts
@@ -32,7 +32,7 @@ import { ERC20_DEFAULT_DECIMALS, fetchErc20Decimals } from '../utils/token';
import { parseStandardTokenTransactionData } from '../utils/transaction';
import { useTransactionMetadataOrThrow } from './transactions/useTransactionMetadataRequest';
import useNetworkInfo from './useNetworkInfo';
-import { useCallback } from 'react';
+import { useCallback, useMemo } from 'react';
import { updateEditableParams } from '../../../../util/transaction-controller';
import { selectTokensByChainIdAndAddress } from '../../../../selectors/tokensController';
import { getTokenTransferData } from '../utils/transaction-pay';
@@ -130,7 +130,11 @@ export const useTokenAmount = ({
networkClientId,
);
- const transactionData = parseStandardTokenTransactionData(tokenData?.data);
+ const transactionData = useMemo(
+ () => parseStandardTokenTransactionData(tokenData?.data),
+ [tokenData?.data],
+ );
+
const recipient = transactionData?.args?._to;
const updateTokenAmount = useCallback(
diff --git a/app/core/EngineService/EngineService.ts b/app/core/EngineService/EngineService.ts
index a602f55abd9..feca935df65 100644
--- a/app/core/EngineService/EngineService.ts
+++ b/app/core/EngineService/EngineService.ts
@@ -83,6 +83,10 @@ export class EngineService {
Logger.log('keyringController vault missing for UPDATE_BG_STATE_KEY');
}
this.updateBatcher.add(controllerName);
+
+ if (controllerName === 'ApprovalController') {
+ this.updateBatcher.flush();
+ }
};
BACKGROUND_STATE_CHANGE_EVENT_NAMES.forEach((eventName) => {
@@ -177,6 +181,14 @@ export class EngineService {
endTrace({ name: TraceName.EngineInitialization });
};
+ /**
+ * Flush any pending controller state updates.
+ * Only necessary in rare cases where immediate state consistency is required.
+ */
+ flushState() {
+ this.updateBatcher.flush();
+ }
+
/**
* Sets up persistence subscriptions for all engine controllers.
*
diff --git a/locales/languages/en.json b/locales/languages/en.json
index b7dcfafe4bf..886138d8fc9 100644
--- a/locales/languages/en.json
+++ b/locales/languages/en.json
@@ -86,6 +86,9 @@
"message": "Add less than {{amount}} or use a different token.",
"title": "Insufficient funds"
},
+ "insufficient_pay_token_balance_fees_no_target": {
+ "message": "Add less or use a different token."
+ },
"insufficient_pay_token_native": {
"message": "Not enough {{ticker}} to cover fees. Use a token on another network or add more {{ticker}} to continue.",
"title": "Insufficient funds"
@@ -628,7 +631,7 @@
"unexpectedError": "An unexpected error occurred.",
"quoteFetchError": "Failed to fetch quote.",
"kycFormsFetchError": "Failed to fetch KYC forms.",
- "title": "Deposit",
+ "title": "Buy",
"limitExceeded": "This deposit would exceed your {{period}} limit. Your deposit including fees must be {{remaining}} or less.",
"limitError": "Failed to check your deposit limits. Please try again later."
},
@@ -3025,14 +3028,14 @@
"buy_description": "Good for buying a specific token",
"buy_unified": "Buy",
"buy_unified_description": "Buy crypto with cash",
- "sell": "Withdraw",
+ "sell": "Sell",
"sell_description": "Sell crypto for cash"
},
"asset_overview": {
"send_button": "Send",
"buy_button": "Buy",
"token_marketplace": "Token marketplace",
- "sell_button": "Withdraw",
+ "sell_button": "Sell",
"receive_button": "Receive",
"portfolio_button": "Portfolio",
"deposit_button": "Deposit",
@@ -4539,8 +4542,8 @@
"quotes_timeout": "Quotes timeout",
"request_new_quotes": "Please request new quotes to get the latest best rate.",
"terms_of_service": "Terms of Service",
- "amount_to_buy": "Amount to buy",
- "amount_to_sell": "Amount to sell",
+ "amount_to_buy": "Buy",
+ "amount_to_sell": "Sell",
"want_to_buy": "You want to buy",
"want_to_sell": "You want to sell",
"current_balance": "Current balance",
diff --git a/logo.png b/logo.png
index a28627a26b0..5622df2bb4c 100644
Binary files a/logo.png and b/logo.png differ
diff --git a/package.json b/package.json
index 0627f3f7ac6..920d4cc0d1f 100644
--- a/package.json
+++ b/package.json
@@ -176,7 +176,7 @@
"@scure/bip32": "1.7.0",
"@metamask/snaps-sdk": "^10.0.0",
"react-native@0.76.9": "patch:react-native@npm%3A0.76.9#./.yarn/patches/react-native-npm-0.76.9-1c25352097.patch",
- "@metamask/transaction-controller@npm:^62.0.0": "patch:@metamask/transaction-controller@npm%3A62.0.0#~/.yarn/patches/@metamask-transaction-controller-npm-61.0.0-cccac388c7.patch"
+ "@metamask/transaction-controller@npm:^62.1.0": "patch:@metamask/transaction-controller@npm%3A62.1.0#~/.yarn/patches/@metamask-transaction-controller-npm-61.0.0-cccac388c7.patch"
},
"dependencies": {
"@config-plugins/detox": "^9.0.0",
@@ -197,7 +197,7 @@
"@metamask/address-book-controller": "^7.0.0",
"@metamask/app-metadata-controller": "^2.0.0",
"@metamask/approval-controller": "^8.0.0",
- "@metamask/assets-controllers": "^89.0.1",
+ "@metamask/assets-controllers": "patch:@metamask/assets-controllers@npm%3A89.0.1#~/.yarn/patches/@metamask-assets-controllers-npm-89.0.1-02fa7acd54.patch",
"@metamask/base-controller": "^9.0.0",
"@metamask/bitcoin-wallet-snap": "^1.6.0",
"@metamask/bridge-controller": "^61.0.0",
@@ -286,9 +286,9 @@
"@metamask/swappable-obj-proxy": "^2.1.0",
"@metamask/swaps-controller": "^15.0.0",
"@metamask/token-search-discovery-controller": "^4.0.0",
- "@metamask/transaction-controller": "patch:@metamask/transaction-controller@npm%3A62.0.0#~/.yarn/patches/@metamask-transaction-controller-npm-61.0.0-cccac388c7.patch",
+ "@metamask/transaction-controller": "patch:@metamask/transaction-controller@npm%3A62.1.0#~/.yarn/patches/@metamask-transaction-controller-npm-61.0.0-cccac388c7.patch",
"@metamask/transaction-pay-controller": "^10.0.0",
- "@metamask/tron-wallet-snap": "^1.9.1",
+ "@metamask/tron-wallet-snap": "^1.10.0",
"@metamask/utils": "^11.8.1",
"@ngraveio/bc-ur": "^1.1.6",
"@nktkas/hyperliquid": "^0.25.9",
diff --git a/yarn.lock b/yarn.lock
index 9eab6b6d2ba..1c2a7db6bd4 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -7326,7 +7326,7 @@ __metadata:
languageName: node
linkType: hard
-"@metamask/assets-controllers@npm:^89.0.1":
+"@metamask/assets-controllers@npm:89.0.1":
version: 89.0.1
resolution: "@metamask/assets-controllers@npm:89.0.1"
dependencies:
@@ -7378,6 +7378,58 @@ __metadata:
languageName: node
linkType: hard
+"@metamask/assets-controllers@patch:@metamask/assets-controllers@npm%3A89.0.1#~/.yarn/patches/@metamask-assets-controllers-npm-89.0.1-02fa7acd54.patch":
+ version: 89.0.1
+ resolution: "@metamask/assets-controllers@patch:@metamask/assets-controllers@npm%3A89.0.1#~/.yarn/patches/@metamask-assets-controllers-npm-89.0.1-02fa7acd54.patch::version=89.0.1&hash=6be0d3"
+ dependencies:
+ "@ethereumjs/util": "npm:^9.1.0"
+ "@ethersproject/abi": "npm:^5.7.0"
+ "@ethersproject/address": "npm:^5.7.0"
+ "@ethersproject/bignumber": "npm:^5.7.0"
+ "@ethersproject/contracts": "npm:^5.7.0"
+ "@ethersproject/providers": "npm:^5.7.0"
+ "@metamask/abi-utils": "npm:^2.0.3"
+ "@metamask/base-controller": "npm:^9.0.0"
+ "@metamask/contract-metadata": "npm:^2.4.0"
+ "@metamask/controller-utils": "npm:^11.15.0"
+ "@metamask/eth-query": "npm:^4.0.0"
+ "@metamask/keyring-api": "npm:^21.0.0"
+ "@metamask/messenger": "npm:^0.3.0"
+ "@metamask/metamask-eth-abis": "npm:^3.1.1"
+ "@metamask/polling-controller": "npm:^15.0.0"
+ "@metamask/rpc-errors": "npm:^7.0.2"
+ "@metamask/snaps-sdk": "npm:^9.0.0"
+ "@metamask/snaps-utils": "npm:^11.0.0"
+ "@metamask/utils": "npm:^11.8.1"
+ "@types/bn.js": "npm:^5.1.5"
+ "@types/uuid": "npm:^8.3.0"
+ async-mutex: "npm:^0.5.0"
+ bitcoin-address-validation: "npm:^2.2.3"
+ bn.js: "npm:^5.2.1"
+ immer: "npm:^9.0.6"
+ lodash: "npm:^4.17.21"
+ multiformats: "npm:^9.9.0"
+ reselect: "npm:^5.1.1"
+ single-call-balance-checker-abi: "npm:^1.0.0"
+ uuid: "npm:^8.3.2"
+ peerDependencies:
+ "@metamask/account-tree-controller": ^3.0.0
+ "@metamask/accounts-controller": ^34.0.0
+ "@metamask/approval-controller": ^8.0.0
+ "@metamask/core-backend": ^4.1.0
+ "@metamask/keyring-controller": ^24.0.0
+ "@metamask/network-controller": ^25.0.0
+ "@metamask/permission-controller": ^12.0.0
+ "@metamask/phishing-controller": ^15.0.0
+ "@metamask/preferences-controller": ^21.0.0
+ "@metamask/providers": ^22.0.0
+ "@metamask/snaps-controllers": ^14.0.0
+ "@metamask/transaction-controller": ^61.0.0
+ webextension-polyfill: ^0.10.0 || ^0.11.0 || ^0.12.0
+ checksum: 10/b936b09bc22944626b3332844070c0fab559b7e3973873cc96c8321618e6879c1ee1215a588bb1f8f38029e4a69f796d01141ad1cb0726fd590df54ca111355b
+ languageName: node
+ linkType: hard
+
"@metamask/auth-network-utils@npm:^0.3.0":
version: 0.3.1
resolution: "@metamask/auth-network-utils@npm:0.3.1"
@@ -9227,9 +9279,9 @@ __metadata:
languageName: node
linkType: hard
-"@metamask/transaction-controller@npm:62.0.0":
- version: 62.0.0
- resolution: "@metamask/transaction-controller@npm:62.0.0"
+"@metamask/transaction-controller@npm:62.1.0":
+ version: 62.1.0
+ resolution: "@metamask/transaction-controller@npm:62.1.0"
dependencies:
"@ethereumjs/common": "npm:^4.4.0"
"@ethereumjs/tx": "npm:^5.4.0"
@@ -9261,7 +9313,7 @@ __metadata:
"@metamask/gas-fee-controller": ^26.0.0
"@metamask/network-controller": ^26.0.0
"@metamask/remote-feature-flag-controller": ^2.0.0
- checksum: 10/885217c920c29e953aec06c5d9e21ecd58847d5593e9e1f60a3e8e52f7de7869a087797cdf60c5022f12f0c87822b19c860c4ec7a9df1b2a0f140c7dfdaa25e3
+ checksum: 10/2de5d4f36e2b5ddf5cee14a17484d0694fc7dd81bb568a51c712fde9eb0ab8395cae49efa66d5a9daefa86a2429e09561445e6dddc0281e9e645364a9aeeffed
languageName: node
linkType: hard
@@ -9303,9 +9355,9 @@ __metadata:
languageName: node
linkType: hard
-"@metamask/transaction-controller@patch:@metamask/transaction-controller@npm%3A62.0.0#~/.yarn/patches/@metamask-transaction-controller-npm-61.0.0-cccac388c7.patch":
- version: 62.0.0
- resolution: "@metamask/transaction-controller@patch:@metamask/transaction-controller@npm%3A62.0.0#~/.yarn/patches/@metamask-transaction-controller-npm-61.0.0-cccac388c7.patch::version=62.0.0&hash=1a3342"
+"@metamask/transaction-controller@patch:@metamask/transaction-controller@npm%3A62.1.0#~/.yarn/patches/@metamask-transaction-controller-npm-61.0.0-cccac388c7.patch":
+ version: 62.1.0
+ resolution: "@metamask/transaction-controller@patch:@metamask/transaction-controller@npm%3A62.1.0#~/.yarn/patches/@metamask-transaction-controller-npm-61.0.0-cccac388c7.patch::version=62.1.0&hash=1a3342"
dependencies:
"@ethereumjs/common": "npm:^4.4.0"
"@ethereumjs/tx": "npm:^5.4.0"
@@ -9337,7 +9389,7 @@ __metadata:
"@metamask/gas-fee-controller": ^26.0.0
"@metamask/network-controller": ^26.0.0
"@metamask/remote-feature-flag-controller": ^2.0.0
- checksum: 10/9caf3dfa6d88dded658f7902e42c8c20b6916c21804a8c7f593cf37b88764732738e6379443a1faefed81ea0d58f4fbac269c85fc240fa98a61f7551ec7465c9
+ checksum: 10/f21f02550da1230a7e0f51ebd5a828d7cbbdfbb5d5d036ecb3e5f7d903b8dff15fb5627b675fd477f158a52722ab1daa23a103bef47be08f4a96391a7fa4dcda
languageName: node
linkType: hard
@@ -9368,10 +9420,10 @@ __metadata:
languageName: node
linkType: hard
-"@metamask/tron-wallet-snap@npm:^1.9.1":
- version: 1.9.1
- resolution: "@metamask/tron-wallet-snap@npm:1.9.1"
- checksum: 10/9880fce865211ed2d58b162f1ba0e41a7aa43c0180319d008b0a8a247a678284009592aeef50c25c8d4eb6393fdc9663d48a2261d7d03658e0850366052d6719
+"@metamask/tron-wallet-snap@npm:^1.10.0":
+ version: 1.10.0
+ resolution: "@metamask/tron-wallet-snap@npm:1.10.0"
+ checksum: 10/c9b2f9cae0a2f9dcfd43934bd4321ed029e395122b61f1f3a8c3780dd4b44ac8669139b382da8b9230123639bf7a7554206223e3127d3e3317dfb802190cda46
languageName: node
linkType: hard
@@ -35291,7 +35343,7 @@ __metadata:
"@metamask/address-book-controller": "npm:^7.0.0"
"@metamask/app-metadata-controller": "npm:^2.0.0"
"@metamask/approval-controller": "npm:^8.0.0"
- "@metamask/assets-controllers": "npm:^89.0.1"
+ "@metamask/assets-controllers": "patch:@metamask/assets-controllers@npm%3A89.0.1#~/.yarn/patches/@metamask-assets-controllers-npm-89.0.1-02fa7acd54.patch"
"@metamask/auto-changelog": "npm:^5.1.0"
"@metamask/base-controller": "npm:^9.0.0"
"@metamask/bitcoin-wallet-snap": "npm:^1.6.0"
@@ -35392,9 +35444,9 @@ __metadata:
"@metamask/test-dapp-multichain": "npm:^0.17.1"
"@metamask/test-dapp-solana": "npm:^0.3.0"
"@metamask/token-search-discovery-controller": "npm:^4.0.0"
- "@metamask/transaction-controller": "patch:@metamask/transaction-controller@npm%3A62.0.0#~/.yarn/patches/@metamask-transaction-controller-npm-61.0.0-cccac388c7.patch"
+ "@metamask/transaction-controller": "patch:@metamask/transaction-controller@npm%3A62.1.0#~/.yarn/patches/@metamask-transaction-controller-npm-61.0.0-cccac388c7.patch"
"@metamask/transaction-pay-controller": "npm:^10.0.0"
- "@metamask/tron-wallet-snap": "npm:^1.9.1"
+ "@metamask/tron-wallet-snap": "npm:^1.10.0"
"@metamask/utils": "npm:^11.8.1"
"@ngraveio/bc-ur": "npm:^1.1.6"
"@nktkas/hyperliquid": "npm:^0.25.9"