From fd52726fec6a4116ce5ad5c2e58e4071312c87a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patryk=20=C5=81ucka?= <5708018+PatrykLucka@users.noreply.github.com> Date: Mon, 24 Nov 2025 10:05:15 +0100 Subject: [PATCH 1/8] feat(segment): add isFullView prop to TokenList and TokenListItem components (#23109) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** ## **Description** **What is the reason for the change?** The "Token List Item Clicked" Segment event currently sends the same `source` property value (`mobile-token-list`) regardless of whether users click a token from the wallet home page or from the dedicated full token list page. This makes it impossible to differentiate user behavior between these two contexts in analytics. **What is the improvement/solution?** This PR adds differentiation to the token click tracking by introducing a new `source` value: - `mobile-token-list` - sent when tokens are clicked from the wallet home page (existing behavior, maintains backward compatibility) - `mobile-token-list-page` - sent when tokens are clicked from the full token list page (accessed via "View all tokens") The implementation adds an `isFullView` prop that flows through the component hierarchy (`TokensFullView` → `Tokens` → `TokenList` → `TokenListItem/TokenListItemBip44`) and conditionally sets the source property in the Segment tracking event. ## **Changelog** CHANGELOG entry: null ## **Related issues** Fixes: [TMCU-207](https://consensyssoftware.atlassian.net/browse/TMCU-207) ## **Manual testing steps** ```gherkin Feature: Token List Item Click Tracking Differentiation Scenario: user clicks token from wallet home page Given the user is on the wallet home page And tokens are visible in the token list When user taps on any token in the list Then a "Token List Item Clicked" event is sent to Segment And the event contains property source="mobile-token-list" And the user navigates to the token details page Scenario: user clicks token from full token list page Given the user is on the wallet home page And tokens are visible in the token list When user taps "View all tokens" button And user is navigated to the full token list page And user taps on any token in the list Then a "Token List Item Clicked" event is sent to Segment And the event contains property source="mobile-token-list-page" And the user navigates to the token details page ``` ## **Screenshots/Recordings** ### **Before** ### **After** ## **Pre-merge author checklist** - [x] I’ve followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I’ve included tests if applicable - [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. [TMCU-207]: https://consensyssoftware.atlassian.net/browse/TMCU-207?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ --- > [!NOTE] > Adds an isFullView prop plumbed through TokenList -> TokenListItem/TokenListItemBip44 to set analytics source to 'mobile-token-list-page' vs 'mobile-token-list', with minor full-view layout padding. > > - **Analytics/Tracking**: > - `TOKEN_DETAILS_OPENED` event now sets `source` based on `isFullView` in `TokenListItem` and `TokenListItemBip44` (`mobile-token-list-page` vs `mobile-token-list`). > - **UI/Props flow**: > - Introduces optional `isFullView` prop on `TokenList`, `TokenListItem`, and `TokenListItemBip44` and passes it through both FlashList rendering and homepage list mapping. > - Applies horizontal padding (`contentContainerStyle`) to `FlashList` when `isFullView` is true. > > Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit a75e04b63fd4229219b6a1a13fef9dfa8577fbc7. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot). --- .../UI/Tokens/TokenList/TokenListItem/TokenListItemBip44.tsx | 4 +++- app/components/UI/Tokens/TokenList/TokenListItem/index.tsx | 4 +++- app/components/UI/Tokens/TokenList/index.tsx | 3 +++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/app/components/UI/Tokens/TokenList/TokenListItem/TokenListItemBip44.tsx b/app/components/UI/Tokens/TokenList/TokenListItem/TokenListItemBip44.tsx index faff8e6ae39..a2cfb8af49b 100644 --- a/app/components/UI/Tokens/TokenList/TokenListItem/TokenListItemBip44.tsx +++ b/app/components/UI/Tokens/TokenList/TokenListItem/TokenListItemBip44.tsx @@ -51,6 +51,7 @@ interface TokenListItemProps { setShowScamWarningModal: (arg: boolean) => void; privacyMode: boolean; showPercentageChange?: boolean; + isFullView?: boolean; } export const TokenListItemBip44 = React.memo( @@ -60,6 +61,7 @@ export const TokenListItemBip44 = React.memo( setShowScamWarningModal, privacyMode, showPercentageChange = true, + isFullView = false, }: TokenListItemProps) => { const { trackEvent, createEventBuilder } = useMetrics(); const navigation = useNavigation(); @@ -148,7 +150,7 @@ export const TokenListItemBip44 = React.memo( trackEvent( createEventBuilder(MetaMetricsEvents.TOKEN_DETAILS_OPENED) .addProperties({ - source: 'mobile-token-list', + source: isFullView ? 'mobile-token-list-page' : 'mobile-token-list', chain_id: token.chainId, token_symbol: token.symbol, }) diff --git a/app/components/UI/Tokens/TokenList/TokenListItem/index.tsx b/app/components/UI/Tokens/TokenList/TokenListItem/index.tsx index 06dd50998a5..073c05b9944 100644 --- a/app/components/UI/Tokens/TokenList/TokenListItem/index.tsx +++ b/app/components/UI/Tokens/TokenList/TokenListItem/index.tsx @@ -89,6 +89,7 @@ interface TokenListItemProps { setShowScamWarningModal: (arg: boolean) => void; privacyMode: boolean; showPercentageChange?: boolean; + isFullView?: boolean; } export const TokenListItem = React.memo( @@ -98,6 +99,7 @@ export const TokenListItem = React.memo( setShowScamWarningModal, privacyMode, showPercentageChange = true, + isFullView = false, }: TokenListItemProps) => { const { trackEvent, createEventBuilder } = useMetrics(); const navigation = useNavigation(); @@ -346,7 +348,7 @@ export const TokenListItem = React.memo( trackEvent( createEventBuilder(MetaMetricsEvents.TOKEN_DETAILS_OPENED) .addProperties({ - source: 'mobile-token-list', + source: isFullView ? 'mobile-token-list-page' : 'mobile-token-list', chain_id: token.chainId, token_symbol: token.symbol, }) diff --git a/app/components/UI/Tokens/TokenList/index.tsx b/app/components/UI/Tokens/TokenList/index.tsx index 08f547a700f..f794a244359 100644 --- a/app/components/UI/Tokens/TokenList/index.tsx +++ b/app/components/UI/Tokens/TokenList/index.tsx @@ -100,6 +100,7 @@ const TokenListComponent = ({ setShowScamWarningModal={setShowScamWarningModal} privacyMode={privacyMode} showPercentageChange={showPercentageChange} + isFullView={isFullView} /> ), [ @@ -108,6 +109,7 @@ const TokenListComponent = ({ privacyMode, showPercentageChange, TokenListItemComponent, + isFullView, ], ); @@ -125,6 +127,7 @@ const TokenListComponent = ({ setShowScamWarningModal={setShowScamWarningModal} privacyMode={privacyMode} showPercentageChange={showPercentageChange} + isFullView={isFullView} /> ))} {shouldShowViewAllButton && ( From ad7263b1d2b11383221502c4a73d15a5329ea9c8 Mon Sep 17 00:00:00 2001 From: Matthew Grainger <46547583+Matt561@users.noreply.github.com> Date: Mon, 24 Nov 2025 04:20:00 -0500 Subject: [PATCH 2/8] feat: musd conversion optimizations (#23146) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** This is a follow-up to address feedback from https://github.com/MetaMask/metamask-mobile/pull/23060. ### Changes - Bumped @metamask/transaction-controller to version ^62.2.0 - Replaced `MUSD_CONVERSION_TRANSACTION_TYPE` with `TransactionType.musdConversion` from transaction-controller - Added `skipInitialGasEstimate: true` when creating mUSD conversion transaction to improve performance. Gas estimate is now calculate asynchronously - Fixed case where user could be redirected to previous page prematurely when calling `initiateConversion` from `useMusdConversion` hook. - Removed transition header from mUSD conversion confirmation screen. We no longer see the default blue back arrow and screen title while navigating to the confirmation screen. ## **Changelog** CHANGELOG entry: follow up musd conversion optimizations based on #23060 feedback ## **Related issues** ## **Pre-merge author checklist** - [x] I’ve followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I’ve included tests if applicable - [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --- > [!NOTE] > Switch to TransactionType.musdConversion with async gas estimation, UX tweaks, and controller bump to ^62.2.0. > > - **mUSD conversion flow (Earn)**: > - Use `TransactionType.musdConversion` across hooks, components, and tests (replacing custom `MUSD_CONVERSION_TRANSACTION_TYPE`). > - Create transactions with `skipInitialGasEstimate: true` for faster first paint; add nested transactions; improve error handling to avoid stuck screens. > - Hide header on `REDESIGNED_CONFIRMATIONS` route for a cleaner transition. > - Update confirmation UI to recognize new type: `info-root`, `footer`, `custom-amount-info`, `bridge-fee-row`, and `musd-conversion-info` label/tooltip logic. > - **Controller/engine**: > - Bump `@metamask/transaction-controller` to `^62.2.0` and update Yarn lock; minor init adjustments (registry access, typings). > - **Tests**: > - Update unit tests to reflect new transaction type and async gas estimation behavior. > > Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 865d6a481ebdcfc78f94d9e35d998921fb2dc9cc. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot). --- app/components/UI/Earn/constants/musd.ts | 7 +- .../UI/Earn/hooks/useMusdConversion.test.ts | 5 +- .../UI/Earn/hooks/useMusdConversion.ts | 77 ++++--- .../hooks/useMusdConversionStatus.test.ts | 10 +- .../UI/Earn/hooks/useMusdConversionStatus.ts | 5 +- app/components/UI/Earn/routes/index.tsx | 3 +- .../components/footer/footer.tsx | 3 +- .../components/info-root/info-root.tsx | 3 +- .../custom-amount-info/custom-amount-info.tsx | 3 +- .../musd-conversion-info.tsx | 2 +- .../rows/bridge-fee-row/bridge-fee-row.tsx | 3 +- .../confirmations/constants/confirmations.ts | 7 +- .../transaction-controller-init.ts | 1 + app/util/transactions/index.js | 7 +- package.json | 4 +- yarn.lock | 214 +++++++++++++++--- 16 files changed, 255 insertions(+), 99 deletions(-) diff --git a/app/components/UI/Earn/constants/musd.ts b/app/components/UI/Earn/constants/musd.ts index d19310c1089..a3eaa6b86e2 100644 --- a/app/components/UI/Earn/constants/musd.ts +++ b/app/components/UI/Earn/constants/musd.ts @@ -2,7 +2,7 @@ * mUSD Conversion Constants for Earn namespace */ -import { CHAIN_IDS, TransactionType } from '@metamask/transaction-controller'; +import { CHAIN_IDS } from '@metamask/transaction-controller'; import { Hex } from '@metamask/utils'; import { NETWORKS_CHAIN_ID } from '../../../../constants/network'; @@ -52,8 +52,3 @@ export const CONVERTIBLE_STABLECOINS_BY_CHAIN: Record = (() => { } return result; })(); - -// TODO: Remove this once we add to TransactionType. Requires updating transaction-controller package. -// Similar to a swap except that output token is predetermined (e.g. mUSD) and the user cannot change it. -export const MUSD_CONVERSION_TRANSACTION_TYPE = - 'mUSDConversion' as TransactionType; diff --git a/app/components/UI/Earn/hooks/useMusdConversion.test.ts b/app/components/UI/Earn/hooks/useMusdConversion.test.ts index 2e91d08f675..552b4ba21ec 100644 --- a/app/components/UI/Earn/hooks/useMusdConversion.test.ts +++ b/app/components/UI/Earn/hooks/useMusdConversion.test.ts @@ -6,13 +6,13 @@ import { import Engine from '../../../../core/Engine'; import Logger from '../../../../util/Logger'; import { generateTransferData } from '../../../../util/transactions'; -import { MUSD_CONVERSION_TRANSACTION_TYPE } from '../constants/musd'; import { MMM_ORIGIN } from '../../../Views/confirmations/constants/confirmations'; import Routes from '../../../../constants/navigation/Routes'; import { ConfirmationLoader } from '../../../Views/confirmations/components/confirm/confirm-component'; import { Hex } from '@metamask/utils'; import { useNavigation } from '@react-navigation/native'; import { useSelector } from 'react-redux'; +import { TransactionType } from '@metamask/transaction-controller'; // Mock all external dependencies jest.mock('../../../../core/Engine'); @@ -160,7 +160,8 @@ describe('useMusdConversion', () => { { networkClientId: 'mainnet', origin: MMM_ORIGIN, - type: MUSD_CONVERSION_TRANSACTION_TYPE, + skipInitialGasEstimate: true, + type: TransactionType.musdConversion, nestedTransactions: [ { to: mockConfig.outputToken.address, diff --git a/app/components/UI/Earn/hooks/useMusdConversion.ts b/app/components/UI/Earn/hooks/useMusdConversion.ts index 2c3e5c6a751..183d621e434 100644 --- a/app/components/UI/Earn/hooks/useMusdConversion.ts +++ b/app/components/UI/Earn/hooks/useMusdConversion.ts @@ -4,13 +4,13 @@ import { useSelector } from 'react-redux'; import Engine from '../../../../core/Engine'; import Logger from '../../../../util/Logger'; import { generateTransferData } from '../../../../util/transactions'; -import { MUSD_CONVERSION_TRANSACTION_TYPE } from '../constants/musd'; import { MMM_ORIGIN } from '../../../Views/confirmations/constants/confirmations'; import { useNavigation } from '@react-navigation/native'; import Routes from '../../../../constants/navigation/Routes'; import { ConfirmationLoader } from '../../../Views/confirmations/components/confirm/confirm-component'; import { EVM_SCOPE } from '../constants/networks'; import { selectSelectedInternalAccountByScope } from '../../../../selectors/multichainAccounts/accounts'; +import { TransactionType } from '@metamask/transaction-controller'; /** * Type guard to validate allowedPaymentTokens structure. @@ -161,45 +161,57 @@ export const useMusdConversion = () => { }, }); - const ZERO_HEX_VALUE = '0x0'; + try { + const ZERO_HEX_VALUE = '0x0'; - /** - * Create minimal transfer data with amount = 0 - * The actual amount will be set by the user on the confirmation screen - */ - const transferData = generateTransferData('transfer', { - toAddress: selectedAddress, - amount: ZERO_HEX_VALUE, - }); + /** + * Create minimal transfer data with amount = 0 + * The actual amount will be set by the user on the confirmation screen + */ + const transferData = generateTransferData('transfer', { + toAddress: selectedAddress, + amount: ZERO_HEX_VALUE, + }); - const { TransactionController } = Engine.context; + const { TransactionController } = Engine.context; - const { transactionMeta } = await TransactionController.addTransaction( - { - to: outputToken.address, - from: selectedAddress, - data: transferData, - value: ZERO_HEX_VALUE, - chainId: outputToken.chainId, - }, - { - networkClientId, - origin: MMM_ORIGIN, - type: MUSD_CONVERSION_TRANSACTION_TYPE, - // Important: Nested transaction is required for Relay to work. This will be fixed in a future iteration. - nestedTransactions: [ + const { transactionMeta } = + await TransactionController.addTransaction( { to: outputToken.address, - data: transferData as Hex, + from: selectedAddress, + data: transferData, value: ZERO_HEX_VALUE, + chainId: outputToken.chainId, }, - ], - }, - ); + { + /** + * Calculate gas estimate asynchronously. + * Enabling this reduces our first paint time on the mUSD conversion screen by ~500ms. + */ + skipInitialGasEstimate: true, + networkClientId, + origin: MMM_ORIGIN, + type: TransactionType.musdConversion, + // Important: Nested transaction is required for Relay to work. This will be fixed in a future iteration. + nestedTransactions: [ + { + to: outputToken.address, + data: transferData as Hex, + value: ZERO_HEX_VALUE, + }, + ], + }, + ); - const newTransactionId = transactionMeta.id; + const newTransactionId = transactionMeta.id; - return newTransactionId; + return newTransactionId; + } catch (err) { + // Prevent the user from being stuck on the confirmation screen without a transaction. + navigation.goBack(); + throw err; + } } catch (err) { const errorMessage = err instanceof Error @@ -213,9 +225,6 @@ export const useMusdConversion = () => { setError(errorMessage); - // Prevent user from being stuck on confirmation screen without a transaction. - navigation.goBack(); - throw err; } }, diff --git a/app/components/UI/Earn/hooks/useMusdConversionStatus.test.ts b/app/components/UI/Earn/hooks/useMusdConversionStatus.test.ts index 4f0cbf703ef..39b32092c19 100644 --- a/app/components/UI/Earn/hooks/useMusdConversionStatus.test.ts +++ b/app/components/UI/Earn/hooks/useMusdConversionStatus.test.ts @@ -1,12 +1,12 @@ import { TransactionMeta, TransactionStatus, + TransactionType, } from '@metamask/transaction-controller'; import { renderHook } from '@testing-library/react-hooks'; import Engine from '../../../../core/Engine'; import { useMusdConversionStatus } from './useMusdConversionStatus'; import useEarnToasts, { EarnToastOptionsConfig } from './useEarnToasts'; -import { MUSD_CONVERSION_TRANSACTION_TYPE } from '../constants/musd'; import { ToastVariants } from '../../../../component-library/components/Toast/Toast.types'; import { IconName } from '../../../../component-library/components/Icons/Icon'; import { NotificationFeedbackType } from 'expo-haptics'; @@ -90,7 +90,7 @@ describe('useMusdConversionStatus', () => { const createTransactionMeta = ( status: TransactionStatus, transactionId = 'test-transaction-1', - type = MUSD_CONVERSION_TRANSACTION_TYPE, + type = TransactionType.musdConversion, ): TransactionMeta => ({ id: transactionId, status, @@ -356,7 +356,7 @@ describe('useMusdConversionStatus', () => { const transactionMeta = createTransactionMeta( TransactionStatus.submitted, 'test-transaction-5', - 'contractInteraction' as typeof MUSD_CONVERSION_TRANSACTION_TYPE, + 'contractInteraction' as typeof TransactionType.musdConversion, ); handler({ transactionMeta }); @@ -371,7 +371,7 @@ describe('useMusdConversionStatus', () => { const transactionMeta = createTransactionMeta( TransactionStatus.confirmed, 'test-transaction-6', - 'swap' as typeof MUSD_CONVERSION_TRANSACTION_TYPE, + 'swap' as typeof TransactionType.musdConversion, ); handler({ transactionMeta }); @@ -386,7 +386,7 @@ describe('useMusdConversionStatus', () => { const transactionMeta = createTransactionMeta( TransactionStatus.failed, 'test-transaction-7', - 'simpleSend' as typeof MUSD_CONVERSION_TRANSACTION_TYPE, + 'simpleSend' as typeof TransactionType.musdConversion, ); handler({ transactionMeta }); diff --git a/app/components/UI/Earn/hooks/useMusdConversionStatus.ts b/app/components/UI/Earn/hooks/useMusdConversionStatus.ts index af5ae338224..e313c3b4bee 100644 --- a/app/components/UI/Earn/hooks/useMusdConversionStatus.ts +++ b/app/components/UI/Earn/hooks/useMusdConversionStatus.ts @@ -1,12 +1,11 @@ import { TransactionMeta, TransactionStatus, + TransactionType, } from '@metamask/transaction-controller'; import { useEffect, useRef } from 'react'; import Engine from '../../../../core/Engine'; import useEarnToasts from './useEarnToasts'; -import { MUSD_CONVERSION_TRANSACTION_TYPE } from '../constants/musd'; - /** * Hook to monitor mUSD conversion transaction status and show appropriate toasts * @@ -33,7 +32,7 @@ export const useMusdConversionStatus = () => { }: { transactionMeta: TransactionMeta; }) => { - if (transactionMeta.type !== MUSD_CONVERSION_TRANSACTION_TYPE) { + if (transactionMeta.type !== TransactionType.musdConversion) { return; } diff --git a/app/components/UI/Earn/routes/index.tsx b/app/components/UI/Earn/routes/index.tsx index fc0d81938c2..a29ff8e1d9b 100644 --- a/app/components/UI/Earn/routes/index.tsx +++ b/app/components/UI/Earn/routes/index.tsx @@ -32,8 +32,7 @@ const EarnScreenStack = () => ( name={Routes.FULL_SCREEN_CONFIRMATIONS.REDESIGNED_CONFIRMATIONS} component={Confirm} options={{ - title: '', - headerShown: true, + headerShown: false, }} /> diff --git a/app/components/Views/confirmations/components/footer/footer.tsx b/app/components/Views/confirmations/components/footer/footer.tsx index c65d5e63322..ffc932a9caf 100644 --- a/app/components/Views/confirmations/components/footer/footer.tsx +++ b/app/components/Views/confirmations/components/footer/footer.tsx @@ -38,14 +38,13 @@ import { import { hasTransactionType } from '../../utils/transaction'; import { PredictClaimFooter } from '../predict-confirmations/predict-claim-footer/predict-claim-footer'; import { useIsTransactionPayLoading } from '../../hooks/pay/useTransactionPayData'; -import { MUSD_CONVERSION_TRANSACTION_TYPE } from '../../../../UI/Earn/constants/musd'; import { Skeleton } from '../../../../../component-library/components/Skeleton'; const HIDE_FOOTER_BY_DEFAULT_TYPES = [ TransactionType.perpsDeposit, TransactionType.predictDeposit, TransactionType.predictWithdraw, - MUSD_CONVERSION_TRANSACTION_TYPE, + TransactionType.musdConversion, ]; export const Footer = () => { diff --git a/app/components/Views/confirmations/components/info-root/info-root.tsx b/app/components/Views/confirmations/components/info-root/info-root.tsx index 4d0d5bbee62..85cd6e2d29f 100644 --- a/app/components/Views/confirmations/components/info-root/info-root.tsx +++ b/app/components/Views/confirmations/components/info-root/info-root.tsx @@ -25,7 +25,6 @@ import { hasTransactionType } from '../../utils/transaction'; import { PredictClaimInfo } from '../info/predict-claim-info'; import { PredictWithdrawInfo } from '../info/predict-withdraw-info'; import { MusdConversionInfo } from '../info/musd-conversion-info'; -import { MUSD_CONVERSION_TRANSACTION_TYPE } from '../../../../UI/Earn/constants/musd'; interface ConfirmationInfoComponentRequest { signatureRequestVersion?: string; @@ -95,7 +94,7 @@ const Info = ({ route }: InfoProps) => { if ( transactionMetadata && - hasTransactionType(transactionMetadata, [MUSD_CONVERSION_TRANSACTION_TYPE]) + hasTransactionType(transactionMetadata, [TransactionType.musdConversion]) ) { return ; } diff --git a/app/components/Views/confirmations/components/info/custom-amount-info/custom-amount-info.tsx b/app/components/Views/confirmations/components/info/custom-amount-info/custom-amount-info.tsx index 7b23424cc36..572648507f0 100644 --- a/app/components/Views/confirmations/components/info/custom-amount-info/custom-amount-info.tsx +++ b/app/components/Views/confirmations/components/info/custom-amount-info/custom-amount-info.tsx @@ -48,7 +48,6 @@ import Button, { } from '../../../../../../component-library/components/Buttons/Button'; import { useAlerts } from '../../../context/alert-system-context'; import { useTransactionConfirm } from '../../../hooks/transactions/useTransactionConfirm'; -import { MUSD_CONVERSION_TRANSACTION_TYPE } from '../../../../../UI/Earn/constants/musd'; export interface CustomAmountInfoProps { children?: ReactNode; @@ -266,7 +265,7 @@ function useButtonLabel() { return strings('confirm.deposit_edit_amount_predict_withdraw'); } - if (hasTransactionType(transaction, [MUSD_CONVERSION_TRANSACTION_TYPE])) { + if (hasTransactionType(transaction, [TransactionType.musdConversion])) { return strings('earn.musd_conversion.confirmation_button'); } diff --git a/app/components/Views/confirmations/components/info/musd-conversion-info/musd-conversion-info.tsx b/app/components/Views/confirmations/components/info/musd-conversion-info/musd-conversion-info.tsx index 7b920c832ec..a1fbe0247ba 100644 --- a/app/components/Views/confirmations/components/info/musd-conversion-info/musd-conversion-info.tsx +++ b/app/components/Views/confirmations/components/info/musd-conversion-info/musd-conversion-info.tsx @@ -21,7 +21,7 @@ export const MusdConversionInfo = () => { useEffect(() => { if (!outputTokenInfo) { console.error( - '[Token Conversion] outputToken is required but was not provided in route params. Navigating back.', + '[mUSD Conversion] outputToken is required but was not provided in route params. Navigating back.', ); navigation.goBack(); } diff --git a/app/components/Views/confirmations/components/rows/bridge-fee-row/bridge-fee-row.tsx b/app/components/Views/confirmations/components/rows/bridge-fee-row/bridge-fee-row.tsx index e869ccf4232..9e0fd397d2e 100644 --- a/app/components/Views/confirmations/components/rows/bridge-fee-row/bridge-fee-row.tsx +++ b/app/components/Views/confirmations/components/rows/bridge-fee-row/bridge-fee-row.tsx @@ -24,7 +24,6 @@ import { InfoRowSkeleton, InfoRowVariant } from '../../UI/info-row/info-row'; import AlertRow from '../../UI/info-row/alert-row'; import { RowAlertKey } from '../../UI/info-row/alert-row/constants'; import { useAlerts } from '../../../context/alert-system-context'; -import { MUSD_CONVERSION_TRANSACTION_TYPE } from '../../../../../UI/Earn/constants/musd'; import useFiatFormatter from '../../../../../UI/SimulationDetails/FiatDisplay/useFiatFormatter'; export function BridgeFeeRow() { @@ -111,7 +110,7 @@ function Tooltip({ message = strings('confirm.tooltip.predict_deposit.transaction_fee'); } - if (hasTransactionType(transactionMeta, [MUSD_CONVERSION_TRANSACTION_TYPE])) { + if (hasTransactionType(transactionMeta, [TransactionType.musdConversion])) { message = strings('confirm.tooltip.musd_conversion.transaction_fee'); } diff --git a/app/components/Views/confirmations/constants/confirmations.ts b/app/components/Views/confirmations/constants/confirmations.ts index 23f528c0a89..3c86654d019 100644 --- a/app/components/Views/confirmations/constants/confirmations.ts +++ b/app/components/Views/confirmations/constants/confirmations.ts @@ -1,6 +1,5 @@ import { ApprovalType } from '@metamask/controller-utils'; import { TransactionType } from '@metamask/transaction-controller'; -import { MUSD_CONVERSION_TRANSACTION_TYPE } from '../../../UI/Earn/constants/musd'; export const MMM_ORIGIN = 'metamask'; export const MM_MOBILE_ORIGIN = 'Metamask Mobile'; @@ -16,7 +15,7 @@ export const REDESIGNED_TRANSACTION_TYPES = [ TransactionType.deployContract, TransactionType.lendingDeposit, TransactionType.lendingWithdraw, - MUSD_CONVERSION_TRANSACTION_TYPE, + TransactionType.musdConversion, TransactionType.perpsDeposit, TransactionType.revokeDelegation, TransactionType.simpleSend, @@ -48,12 +47,12 @@ export const REDESIGNED_CONTRACT_INTERACTION_TYPES = [ TransactionType.contractInteraction, TransactionType.lendingDeposit, TransactionType.lendingWithdraw, - MUSD_CONVERSION_TRANSACTION_TYPE, + TransactionType.musdConversion, TransactionType.perpsDeposit, ]; export const FULL_SCREEN_CONFIRMATIONS = [ - MUSD_CONVERSION_TRANSACTION_TYPE, + TransactionType.musdConversion, TransactionType.perpsDeposit, TransactionType.predictDeposit, TransactionType.predictClaim, diff --git a/app/core/Engine/controllers/transaction-controller/transaction-controller-init.ts b/app/core/Engine/controllers/transaction-controller/transaction-controller-init.ts index 8756c1c1c30..5a28459ab86 100644 --- a/app/core/Engine/controllers/transaction-controller/transaction-controller-init.ts +++ b/app/core/Engine/controllers/transaction-controller/transaction-controller-init.ts @@ -95,6 +95,7 @@ export const TransactionControllerInit: ControllerInitFunction< getGasFeeEstimates: (...args) => gasFeeController.fetchGasFeeEstimates(...args), getNetworkClientRegistry: (...args) => + // @ts-expect-error - NetworkController registry type mismatch between peer dependencies networkController.getNetworkClientRegistry(...args), getNetworkState: () => networkController.state, hooks: { diff --git a/app/util/transactions/index.js b/app/util/transactions/index.js index c9f7da13f34..58d200ac8df 100644 --- a/app/util/transactions/index.js +++ b/app/util/transactions/index.js @@ -69,7 +69,6 @@ import { handleMethodData } from '../../util/transaction-controller'; import EthQuery from '@metamask/eth-query'; import { EIP_7702_REVOKE_ADDRESS } from '../../components/Views/confirmations/hooks/7702/useEIP7702Accounts'; import { hasTransactionType } from '../../components/Views/confirmations/utils/transaction'; -import { MUSD_CONVERSION_TRANSACTION_TYPE } from '../../components/UI/Earn/constants/musd'; const { SAI_ADDRESS } = AppConstants; @@ -164,7 +163,7 @@ const reviewActionKeys = { [TransactionType.lendingWithdraw]: strings( 'transactions.tx_review_lending_withdraw', ), - [MUSD_CONVERSION_TRANSACTION_TYPE]: strings( + [TransactionType.musdConversion]: strings( 'transactions.tx_review_musd_conversion', ), }; @@ -219,7 +218,7 @@ const actionKeys = { [TransactionType.predictWithdraw]: strings( 'transactions.tx_review_predict_withdraw', ), - [MUSD_CONVERSION_TRANSACTION_TYPE]: strings( + [TransactionType.musdConversion]: strings( 'transactions.tx_review_musd_conversion', ), }; @@ -551,7 +550,7 @@ export async function getTransactionActionKey(transaction, chainId) { TransactionType.lendingDeposit, TransactionType.lendingWithdraw, TransactionType.perpsDeposit, - MUSD_CONVERSION_TRANSACTION_TYPE, + TransactionType.musdConversion, ].includes(type) ) { return type; diff --git a/package.json b/package.json index fbb5f4e6b33..0a872263b17 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.1.0": "patch:@metamask/transaction-controller@npm%3A62.1.0#~/.yarn/patches/@metamask-transaction-controller-npm-61.0.0-cccac388c7.patch" + "@metamask/transaction-controller@npm:^62.2.0": "patch:@metamask/transaction-controller@npm%3A62.2.0#~/.yarn/patches/@metamask-transaction-controller-npm-61.0.0-cccac388c7.patch" }, "dependencies": { "@config-plugins/detox": "^9.0.0", @@ -286,7 +286,7 @@ "@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.1.0#~/.yarn/patches/@metamask-transaction-controller-npm-61.0.0-cccac388c7.patch", + "@metamask/transaction-controller": "patch:@metamask/transaction-controller@npm%3A62.2.0#~/.yarn/patches/@metamask-transaction-controller-npm-61.0.0-cccac388c7.patch", "@metamask/transaction-pay-controller": "^10.0.0", "@metamask/tron-wallet-snap": "^1.10.0", "@metamask/utils": "^11.8.1", diff --git a/yarn.lock b/yarn.lock index 109251e5769..d7ac51a651b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7284,6 +7284,36 @@ __metadata: languageName: node linkType: hard +"@metamask/accounts-controller@npm:^35.0.0": + version: 35.0.0 + resolution: "@metamask/accounts-controller@npm:35.0.0" + dependencies: + "@ethereumjs/util": "npm:^9.1.0" + "@metamask/base-controller": "npm:^9.0.0" + "@metamask/eth-snap-keyring": "npm:^18.0.0" + "@metamask/keyring-api": "npm:^21.0.0" + "@metamask/keyring-internal-api": "npm:^9.0.0" + "@metamask/keyring-utils": "npm:^3.1.0" + "@metamask/messenger": "npm:^0.3.0" + "@metamask/snaps-sdk": "npm:^9.0.0" + "@metamask/snaps-utils": "npm:^11.0.0" + "@metamask/superstruct": "npm:^3.1.0" + "@metamask/utils": "npm:^11.8.1" + deepmerge: "npm:^4.2.2" + ethereum-cryptography: "npm:^2.1.2" + immer: "npm:^9.0.6" + lodash: "npm:^4.17.21" + uuid: "npm:^8.3.2" + peerDependencies: + "@metamask/keyring-controller": ^25.0.0 + "@metamask/network-controller": ^26.0.0 + "@metamask/providers": ^22.0.0 + "@metamask/snaps-controllers": ^14.0.0 + webextension-polyfill: ^0.10.0 || ^0.11.0 || ^0.12.0 + checksum: 10/eb87e1d0d054dfea4511922643c0ed0d2699cd253a3dfde38d4340c4a321e56e41a34a9a533c91dd36ab68777cf448ee30db416023b95376f9bfd37912d20451 + languageName: node + linkType: hard + "@metamask/address-book-controller@npm:^7.0.0": version: 7.0.0 resolution: "@metamask/address-book-controller@npm:7.0.0" @@ -7827,6 +7857,18 @@ __metadata: languageName: node linkType: hard +"@metamask/eth-block-tracker@npm:^15.0.0": + version: 15.0.0 + resolution: "@metamask/eth-block-tracker@npm:15.0.0" + dependencies: + "@metamask/eth-json-rpc-provider": "npm:^6.0.0" + "@metamask/safe-event-emitter": "npm:^3.0.0" + "@metamask/utils": "npm:^11.8.1" + json-rpc-random-id: "npm:^1.0.1" + checksum: 10/fd2b014507af10b6d6da8196fde5c5527e64e5d2990604fe0ef132ca38755f893019b59507fccce96ed30a694f348de5fcfcb911e442aaff97e116af6ae9a07f + languageName: node + linkType: hard + "@metamask/eth-hd-keyring@npm:^13.0.0": version: 13.0.0 resolution: "@metamask/eth-hd-keyring@npm:13.0.0" @@ -7885,6 +7927,25 @@ __metadata: languageName: node linkType: hard +"@metamask/eth-json-rpc-middleware@npm:^22.0.0": + version: 22.0.0 + resolution: "@metamask/eth-json-rpc-middleware@npm:22.0.0" + dependencies: + "@metamask/eth-block-tracker": "npm:^15.0.0" + "@metamask/eth-json-rpc-provider": "npm:^6.0.0" + "@metamask/eth-sig-util": "npm:^8.2.0" + "@metamask/json-rpc-engine": "npm:^10.2.0" + "@metamask/message-manager": "npm:^14.1.0" + "@metamask/rpc-errors": "npm:^7.0.2" + "@metamask/superstruct": "npm:^3.1.0" + "@metamask/utils": "npm:^11.8.1" + klona: "npm:^2.0.6" + pify: "npm:^5.0.0" + safe-stable-stringify: "npm:^2.4.3" + checksum: 10/c83ef0c45d1d2f00c0ed1c9e56bd0a7ea359d3db8d53f81b099167eccaecce4cbb88b10e4daa17db3e2c777be03681dc352f1581e759c58c6a728f266843c39d + languageName: node + linkType: hard + "@metamask/eth-json-rpc-provider@npm:^4.1.6": version: 4.1.8 resolution: "@metamask/eth-json-rpc-provider@npm:4.1.8" @@ -7911,6 +7972,18 @@ __metadata: languageName: node linkType: hard +"@metamask/eth-json-rpc-provider@npm:^6.0.0": + version: 6.0.0 + resolution: "@metamask/eth-json-rpc-provider@npm:6.0.0" + dependencies: + "@metamask/json-rpc-engine": "npm:^10.2.0" + "@metamask/rpc-errors": "npm:^7.0.2" + "@metamask/utils": "npm:^11.8.1" + nanoid: "npm:^3.3.8" + checksum: 10/ab7cf6139af7e5d2f26406c22651d4eb103a1fbc95f7274307a35878ae7ad26d51440b56575401286d5d4e57f4f39690c44d31b4088b64cf87ccf6c2d9322436 + languageName: node + linkType: hard + "@metamask/eth-ledger-bridge-keyring@npm:11.1.0": version: 11.1.0 resolution: "@metamask/eth-ledger-bridge-keyring@npm:11.1.0" @@ -8134,6 +8207,27 @@ __metadata: languageName: node linkType: hard +"@metamask/gas-fee-controller@npm:^26.0.0": + version: 26.0.0 + resolution: "@metamask/gas-fee-controller@npm:26.0.0" + dependencies: + "@metamask/base-controller": "npm:^9.0.0" + "@metamask/controller-utils": "npm:^11.16.0" + "@metamask/eth-query": "npm:^4.0.0" + "@metamask/ethjs-unit": "npm:^0.3.0" + "@metamask/polling-controller": "npm:^16.0.0" + "@metamask/utils": "npm:^11.8.1" + "@types/bn.js": "npm:^5.1.5" + "@types/uuid": "npm:^8.3.0" + bn.js: "npm:^5.2.1" + uuid: "npm:^8.3.2" + peerDependencies: + "@babel/runtime": ^7.0.0 + "@metamask/network-controller": ^26.0.0 + checksum: 10/9afa0fcc3f4bf9eca493b3799a714c8f814c735705faa611720deb09f095d181dbc98b50650104350c671cb9bb240fa21120dd8ea90aa0f23926dda95d44fe75 + languageName: node + linkType: hard + "@metamask/gator-permissions-controller@npm:^0.3.0": version: 0.3.0 resolution: "@metamask/gator-permissions-controller@npm:0.3.0" @@ -8152,14 +8246,17 @@ __metadata: languageName: node linkType: hard -"@metamask/json-rpc-engine@npm:^10.0.0, @metamask/json-rpc-engine@npm:^10.0.2, @metamask/json-rpc-engine@npm:^10.0.3, @metamask/json-rpc-engine@npm:^10.1.0, @metamask/json-rpc-engine@npm:^10.1.1": - version: 10.1.1 - resolution: "@metamask/json-rpc-engine@npm:10.1.1" +"@metamask/json-rpc-engine@npm:^10.0.0, @metamask/json-rpc-engine@npm:^10.0.2, @metamask/json-rpc-engine@npm:^10.0.3, @metamask/json-rpc-engine@npm:^10.1.0, @metamask/json-rpc-engine@npm:^10.1.1, @metamask/json-rpc-engine@npm:^10.2.0": + version: 10.2.0 + resolution: "@metamask/json-rpc-engine@npm:10.2.0" dependencies: "@metamask/rpc-errors": "npm:^7.0.2" "@metamask/safe-event-emitter": "npm:^3.0.0" "@metamask/utils": "npm:^11.8.1" - checksum: 10/ad28e430543ca93d4487ff3f9c2d2247013ed9bc1d73867b06f2cea09b0e864207c856cc90ef945e7323e3dacc03205ffdbaddce34fc6c9165cd96bd55dc7bd0 + "@types/deep-freeze-strict": "npm:^1.1.0" + deep-freeze-strict: "npm:^1.1.1" + klona: "npm:^2.0.6" + checksum: 10/c4d588ee2a27c5cd3b4634dbafebebb193092364e1eec2bf1fef2bd2f2352ab7fa59758067ab53440d8d93b812270bdf2c0e508640614b51e5d06d68a76a37c1 languageName: node linkType: hard @@ -8303,6 +8400,22 @@ __metadata: languageName: node linkType: hard +"@metamask/message-manager@npm:^14.1.0": + version: 14.1.0 + resolution: "@metamask/message-manager@npm:14.1.0" + dependencies: + "@metamask/base-controller": "npm:^9.0.0" + "@metamask/controller-utils": "npm:^11.16.0" + "@metamask/eth-sig-util": "npm:^8.2.0" + "@metamask/messenger": "npm:^0.3.0" + "@metamask/utils": "npm:^11.8.1" + "@types/uuid": "npm:^8.3.0" + jsonschema: "npm:^1.4.1" + uuid: "npm:^8.3.2" + checksum: 10/0bbea914096b9213fc16283dfe7e79436f2ea21946bcd717440071b7faf36d19a08fef122d5e91f000c586e7e5e909de99d1d2d0e76c1b1b3d42e45ff78474f3 + languageName: node + linkType: hard + "@metamask/message-signing-snap@npm:^1.1.2": version: 1.1.2 resolution: "@metamask/message-signing-snap@npm:1.1.2" @@ -8503,6 +8616,35 @@ __metadata: languageName: node linkType: hard +"@metamask/network-controller@npm:^26.0.0": + version: 26.0.0 + resolution: "@metamask/network-controller@npm:26.0.0" + dependencies: + "@metamask/base-controller": "npm:^9.0.0" + "@metamask/controller-utils": "npm:^11.16.0" + "@metamask/eth-block-tracker": "npm:^15.0.0" + "@metamask/eth-json-rpc-infura": "npm:^10.3.0" + "@metamask/eth-json-rpc-middleware": "npm:^22.0.0" + "@metamask/eth-json-rpc-provider": "npm:^6.0.0" + "@metamask/eth-query": "npm:^4.0.0" + "@metamask/json-rpc-engine": "npm:^10.2.0" + "@metamask/messenger": "npm:^0.3.0" + "@metamask/rpc-errors": "npm:^7.0.2" + "@metamask/swappable-obj-proxy": "npm:^2.3.0" + "@metamask/utils": "npm:^11.8.1" + async-mutex: "npm:^0.5.0" + fast-deep-equal: "npm:^3.1.3" + immer: "npm:^9.0.6" + loglevel: "npm:^1.8.1" + reselect: "npm:^5.1.1" + uri-js: "npm:^4.4.1" + uuid: "npm:^8.3.2" + peerDependencies: + "@metamask/error-reporting-service": ^3.0.0 + checksum: 10/f66c9bda2b88efbbd23144ed3d6503ceb26025df54de86195485185827272d0f7364e59b633946933c3045d24ccd1a46ce9a852d534d5b3ea58392524dd9f3e3 + languageName: node + linkType: hard + "@metamask/network-enablement-controller@npm:3.1.0": version: 3.1.0 resolution: "@metamask/network-enablement-controller@npm:3.1.0" @@ -8657,6 +8799,22 @@ __metadata: languageName: node linkType: hard +"@metamask/polling-controller@npm:^16.0.0": + version: 16.0.0 + resolution: "@metamask/polling-controller@npm:16.0.0" + dependencies: + "@metamask/base-controller": "npm:^9.0.0" + "@metamask/controller-utils": "npm:^11.16.0" + "@metamask/utils": "npm:^11.8.1" + "@types/uuid": "npm:^8.3.0" + fast-json-stable-stringify: "npm:^2.1.0" + uuid: "npm:^8.3.2" + peerDependencies: + "@metamask/network-controller": ^26.0.0 + checksum: 10/b65d6879f7b8c996148eb816402b5544ca849ad8e59890eeeeb35dda18f9e8efe07c86ab5b3acf6a0cf9320b12ae311e2346c03bf74116331579d8460f556961 + languageName: node + linkType: hard + "@metamask/post-message-stream@npm:^10.0.0": version: 10.0.0 resolution: "@metamask/post-message-stream@npm:10.0.0" @@ -8834,16 +8992,16 @@ __metadata: languageName: node linkType: hard -"@metamask/remote-feature-flag-controller@npm:^2.0.0": - version: 2.0.0 - resolution: "@metamask/remote-feature-flag-controller@npm:2.0.0" +"@metamask/remote-feature-flag-controller@npm:^2.0.0, @metamask/remote-feature-flag-controller@npm:^2.0.1": + version: 2.0.1 + resolution: "@metamask/remote-feature-flag-controller@npm:2.0.1" dependencies: "@metamask/base-controller": "npm:^9.0.0" - "@metamask/controller-utils": "npm:^11.14.1" + "@metamask/controller-utils": "npm:^11.16.0" "@metamask/messenger": "npm:^0.3.0" "@metamask/utils": "npm:^11.8.1" uuid: "npm:^8.3.2" - checksum: 10/a5d72c8532dbb93ae3039d4231cf9e76e67ea5c587749b00a72a48e6014af6f68620967c88bcaef7a80cf85bff65d9b4281a32d03b32508fc54a6c08b2a9df6a + checksum: 10/4815f68b368331a6876b339b6204d965725938e459036f486af5b4403604285a864617649f8877d85e9461ddac3cded921b0cc55004cd86ef03224b3fd26a74d languageName: node linkType: hard @@ -9279,9 +9437,9 @@ __metadata: languageName: node linkType: hard -"@metamask/transaction-controller@npm:62.1.0": - version: 62.1.0 - resolution: "@metamask/transaction-controller@npm:62.1.0" +"@metamask/transaction-controller@npm:62.2.0": + version: 62.2.0 + resolution: "@metamask/transaction-controller@npm:62.2.0" dependencies: "@ethereumjs/common": "npm:^4.4.0" "@ethereumjs/tx": "npm:^5.4.0" @@ -9290,12 +9448,17 @@ __metadata: "@ethersproject/contracts": "npm:^5.7.0" "@ethersproject/providers": "npm:^5.7.0" "@ethersproject/wallet": "npm:^5.7.0" + "@metamask/accounts-controller": "npm:^35.0.0" + "@metamask/approval-controller": "npm:^8.0.0" "@metamask/base-controller": "npm:^9.0.0" "@metamask/controller-utils": "npm:^11.16.0" "@metamask/eth-query": "npm:^4.0.0" + "@metamask/gas-fee-controller": "npm:^26.0.0" "@metamask/messenger": "npm:^0.3.0" "@metamask/metamask-eth-abis": "npm:^3.1.1" + "@metamask/network-controller": "npm:^26.0.0" "@metamask/nonce-tracker": "npm:^6.0.0" + "@metamask/remote-feature-flag-controller": "npm:^2.0.1" "@metamask/rpc-errors": "npm:^7.0.2" "@metamask/utils": "npm:^11.8.1" async-mutex: "npm:^0.5.0" @@ -9307,13 +9470,8 @@ __metadata: uuid: "npm:^8.3.2" peerDependencies: "@babel/runtime": ^7.0.0 - "@metamask/accounts-controller": ^35.0.0 - "@metamask/approval-controller": ^8.0.0 "@metamask/eth-block-tracker": ">=9" - "@metamask/gas-fee-controller": ^26.0.0 - "@metamask/network-controller": ^26.0.0 - "@metamask/remote-feature-flag-controller": ^2.0.0 - checksum: 10/2de5d4f36e2b5ddf5cee14a17484d0694fc7dd81bb568a51c712fde9eb0ab8395cae49efa66d5a9daefa86a2429e09561445e6dddc0281e9e645364a9aeeffed + checksum: 10/978884a159300253960443c331422da9355d26f118b6caa59a4924a99ccadae677a41330bf94497f1cb686cc68bea30431220621e4a9d1576652cb5a3489977a languageName: node linkType: hard @@ -9355,9 +9513,9 @@ __metadata: languageName: node linkType: hard -"@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" +"@metamask/transaction-controller@patch:@metamask/transaction-controller@npm%3A62.2.0#~/.yarn/patches/@metamask-transaction-controller-npm-61.0.0-cccac388c7.patch": + version: 62.2.0 + resolution: "@metamask/transaction-controller@patch:@metamask/transaction-controller@npm%3A62.2.0#~/.yarn/patches/@metamask-transaction-controller-npm-61.0.0-cccac388c7.patch::version=62.2.0&hash=1a3342" dependencies: "@ethereumjs/common": "npm:^4.4.0" "@ethereumjs/tx": "npm:^5.4.0" @@ -9366,12 +9524,17 @@ __metadata: "@ethersproject/contracts": "npm:^5.7.0" "@ethersproject/providers": "npm:^5.7.0" "@ethersproject/wallet": "npm:^5.7.0" + "@metamask/accounts-controller": "npm:^35.0.0" + "@metamask/approval-controller": "npm:^8.0.0" "@metamask/base-controller": "npm:^9.0.0" "@metamask/controller-utils": "npm:^11.16.0" "@metamask/eth-query": "npm:^4.0.0" + "@metamask/gas-fee-controller": "npm:^26.0.0" "@metamask/messenger": "npm:^0.3.0" "@metamask/metamask-eth-abis": "npm:^3.1.1" + "@metamask/network-controller": "npm:^26.0.0" "@metamask/nonce-tracker": "npm:^6.0.0" + "@metamask/remote-feature-flag-controller": "npm:^2.0.1" "@metamask/rpc-errors": "npm:^7.0.2" "@metamask/utils": "npm:^11.8.1" async-mutex: "npm:^0.5.0" @@ -9383,13 +9546,8 @@ __metadata: uuid: "npm:^8.3.2" peerDependencies: "@babel/runtime": ^7.0.0 - "@metamask/accounts-controller": ^35.0.0 - "@metamask/approval-controller": ^8.0.0 "@metamask/eth-block-tracker": ">=9" - "@metamask/gas-fee-controller": ^26.0.0 - "@metamask/network-controller": ^26.0.0 - "@metamask/remote-feature-flag-controller": ^2.0.0 - checksum: 10/f21f02550da1230a7e0f51ebd5a828d7cbbdfbb5d5d036ecb3e5f7d903b8dff15fb5627b675fd477f158a52722ab1daa23a103bef47be08f4a96391a7fa4dcda + checksum: 10/aeac45f777786d040d4860cd49fc2665a632cd834eef3f5ef73e1a7fad95ead6bfb02a597a0b60f9bcc7036c0a88fa0affe27a5d9f11f595cb3cffdc13a446d4 languageName: node linkType: hard @@ -35454,7 +35612,7 @@ __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.1.0#~/.yarn/patches/@metamask-transaction-controller-npm-61.0.0-cccac388c7.patch" + "@metamask/transaction-controller": "patch:@metamask/transaction-controller@npm%3A62.2.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.10.0" "@metamask/utils": "npm:^11.8.1" From 2658023f7f50a7de10abcbc3e7ba87c724ffe9cb Mon Sep 17 00:00:00 2001 From: Matthew Walsh Date: Mon, 24 Nov 2025 09:39:08 +0000 Subject: [PATCH 3/8] fix: cp-7.60.0 no quotes alert message (#23120) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** Prevent no quotes alert message being displayed inside button. ## **Changelog** CHANGELOG entry: null ## **Related issues** Fixes: #23098 ## **Manual testing steps** ## **Screenshots/Recordings** ### **Before** ### **After** ## **Pre-merge author checklist** - [x] I’ve followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I’ve included tests if applicable - [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --- > [!NOTE] > Adds a `title` to the `NoPayTokenQuotes` alert and updates tests and English locale strings accordingly. > > - **Alerts** > - Add `title: strings('alert_system.no_pay_token_quotes.title')` to `useNoPayTokenQuotesAlert` in `app/components/Views/confirmations/hooks/alerts/useNoPayTokenQuotesAlert.ts`. > - **Tests** > - Update expectations in `useNoPayTokenQuotesAlert.test.ts` to include `title`. > - **i18n** > - Add `alert_system.no_pay_token_quotes.title` ("No quotes") in `locales/languages/en.json` and keep existing `message`. > > Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 06504210865cf8ab3eab1e4d96b34d62a142c69c. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot). --- .../hooks/alerts/useNoPayTokenQuotesAlert.test.ts | 1 + .../confirmations/hooks/alerts/useNoPayTokenQuotesAlert.ts | 1 + locales/languages/en.json | 3 ++- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/app/components/Views/confirmations/hooks/alerts/useNoPayTokenQuotesAlert.test.ts b/app/components/Views/confirmations/hooks/alerts/useNoPayTokenQuotesAlert.test.ts index 2997e414062..42e474d9fc7 100644 --- a/app/components/Views/confirmations/hooks/alerts/useNoPayTokenQuotesAlert.test.ts +++ b/app/components/Views/confirmations/hooks/alerts/useNoPayTokenQuotesAlert.test.ts @@ -75,6 +75,7 @@ describe('useNoPayTokenQuotesAlert', () => { key: AlertKeys.NoPayTokenQuotes, field: RowAlertKey.PayWith, message: strings('alert_system.no_pay_token_quotes.message'), + title: strings('alert_system.no_pay_token_quotes.title'), severity: Severity.Danger, isBlocking: true, }, diff --git a/app/components/Views/confirmations/hooks/alerts/useNoPayTokenQuotesAlert.ts b/app/components/Views/confirmations/hooks/alerts/useNoPayTokenQuotesAlert.ts index 9fff2d03cf7..1ae25dad991 100644 --- a/app/components/Views/confirmations/hooks/alerts/useNoPayTokenQuotesAlert.ts +++ b/app/components/Views/confirmations/hooks/alerts/useNoPayTokenQuotesAlert.ts @@ -41,6 +41,7 @@ export function useNoPayTokenQuotesAlert() { key: AlertKeys.NoPayTokenQuotes, field: RowAlertKey.PayWith, message: strings('alert_system.no_pay_token_quotes.message'), + title: strings('alert_system.no_pay_token_quotes.title'), severity: Severity.Danger, isBlocking: true, }, diff --git a/locales/languages/en.json b/locales/languages/en.json index 02cb1c185cb..22e35965a38 100644 --- a/locales/languages/en.json +++ b/locales/languages/en.json @@ -94,7 +94,8 @@ "title": "Insufficient funds" }, "no_pay_token_quotes": { - "message": "This payment route isn't available right now. Try changing the amount, network, or token and we'll find the best option." + "message": "This payment route isn't available right now. Try changing the amount, network, or token and we'll find the best option.", + "title": "No quotes" }, "perps_hardware_account": { "title": "Wallet not supported", From e5ce24ee4ab0d5557cfe69c76f2184ff78e28d72 Mon Sep 17 00:00:00 2001 From: Matthew Walsh Date: Mon, 24 Nov 2025 09:41:47 +0000 Subject: [PATCH 4/8] fix: cp-7.60.0 keyboard in metamask pay asset picker (#23148) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** Ensure keyboard is shown on top of modal. ## **Changelog** CHANGELOG entry: null ## **Related issues** Fixes: #23139 ## **Manual testing steps** ## **Screenshots/Recordings** ### **Before** ### **After** Keyboard ## **Pre-merge author checklist** - [x] I’ve followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I’ve included tests if applicable - [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --- > [!NOTE] > Sets `keyboardAvoidingViewEnabled={false}` on `BottomSheet` in `PayWithModal` to change keyboard behavior. > > - **UI**: > - `app/components/Views/confirmations/components/modals/pay-with-modal/pay-with-modal.tsx` > - Pass `keyboardAvoidingViewEnabled={false}` to `BottomSheet` in `PayWithModal`. > > Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 52f6beb216da87136bf2d1f0c2c6765ad087ba3b. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot). --- .../components/modals/pay-with-modal/pay-with-modal.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/components/Views/confirmations/components/modals/pay-with-modal/pay-with-modal.tsx b/app/components/Views/confirmations/components/modals/pay-with-modal/pay-with-modal.tsx index 26526c7ed5b..360f92b9ecb 100644 --- a/app/components/Views/confirmations/components/modals/pay-with-modal/pay-with-modal.tsx +++ b/app/components/Views/confirmations/components/modals/pay-with-modal/pay-with-modal.tsx @@ -43,7 +43,11 @@ export function PayWithModal() { ); return ( - + {strings('pay_with_modal.title')} From e3c18f48dc58f4e0cf2b0f8c2f89024224316fab Mon Sep 17 00:00:00 2001 From: Salim TOUBAL Date: Mon, 24 Nov 2025 10:44:48 +0100 Subject: [PATCH 5/8] fix: fix unknown chain icon display issue in permissions (#21914) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** When displaying network permissions in the `MultichainPermissionsSummary` component, network avatars without an `imageSource` property were being passed to the `AvatarGroup` component, which could result in `undefined` values in the avatars array. This caused potential rendering issues and inconsistent UI behavior when displaying network permissions. ## **Changelog** CHANGELOG entry: Fixed network avatar display issue in permissions when network images are unavailable ## **Related issues** Fixes: ## **Manual testing steps** ```gherkin Feature: my feature name Scenario: user [verb for user action] Given [describe expected initial app state] When user [verb for user action] Then [describe expected outcome] ``` ## **Screenshots/Recordings** ### **Before** https://github.com/user-attachments/assets/860f2212-4930-4f5f-91c9-245d37c31841 ### **After** Screenshot 2025-10-30 at 11 51 44 ## **Pre-merge author checklist** - [ ] I’ve followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [ ] I've completed the PR template to the best of my ability - [ ] I’ve included tests if applicable - [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --- > [!NOTE] > Filters out non-CAIP wallet scopes when generating network avatars and adds tests to validate image source calls and chain ID formats. > > - **Permissions UI**: > - In `MultichainAccountPermissions.tsx`, validate and filter `selectedChainIds` to include only valid CAIP chain IDs and exclude `wallet:` scopes before creating `networkAvatars` (uses `isCaipChainId`). > - **Tests**: > - Update `MultichainAccountPermissions.test.tsx` to mock `getNetworkImageSource` and add a `networkAvatars` test ensuring no `wallet:` scopes are passed and only valid CAIP chain IDs are used. > > Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit fce0c16f9ecd07de326d114183f767e764a05d6c. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot). --- .../MultichainAccountPermissions.test.tsx | 42 +++++++++++++++++++ .../MultichainAccountPermissions.tsx | 24 +++++++---- 2 files changed, 59 insertions(+), 7 deletions(-) diff --git a/app/components/Views/MultichainAccounts/MultichainAccountPermissions/MultichainAccountPermissions.test.tsx b/app/components/Views/MultichainAccounts/MultichainAccountPermissions/MultichainAccountPermissions.test.tsx index 468c8277352..f8a07544ac8 100644 --- a/app/components/Views/MultichainAccounts/MultichainAccountPermissions/MultichainAccountPermissions.test.tsx +++ b/app/components/Views/MultichainAccounts/MultichainAccountPermissions/MultichainAccountPermissions.test.tsx @@ -8,6 +8,7 @@ import { RootState } from '../../../../reducers'; import { MultichainAccountPermissions } from './MultichainAccountPermissions'; import Engine from '../../../../core/Engine'; import { MAINNET_DISPLAY_NAME } from '../../../../core/Engine/constants'; +import { getNetworkImageSource } from '../../../../util/networks'; const mockedNavigate = jest.fn(); const mockedGoBack = jest.fn(); @@ -466,6 +467,47 @@ describe('MultichainAccountPermissions', () => { }); }); + describe('networkAvatars', () => { + it('renders successfully and filters wallet scopes when creating network avatars', () => { + // Arrange + const mockGetNetworkImageSource = + getNetworkImageSource as jest.MockedFunction< + typeof getNetworkImageSource + >; + mockGetNetworkImageSource.mockClear(); + + // Act - Render the component + const { getByTestId } = renderWithProvider( + , + { state: mockInitialState() }, + ); + + // Assert - Component renders successfully without crashing + expect(getByTestId('cancel-button')).toBeDefined(); + expect( + getByTestId('navigate_to_edit_networks_permissions_button'), + ).toBeDefined(); + + // If getNetworkImageSource was called, verify no wallet scopes were passed + const calls = mockGetNetworkImageSource.mock.calls; + calls.forEach((call) => { + const params = call[0]; + if (params?.chainId) { + // The filter should exclude wallet scopes like 'wallet:eip155' + expect(params.chainId).not.toMatch(/^wallet:/); + // Should only be valid CAIP chain IDs + expect(params.chainId).toMatch(/^[a-z]+:\d+$/); + } + }); + }); + }); + describe('screen navigation', () => { it('should render correct screen based on current state', () => { const { getByTestId } = renderWithProvider( diff --git a/app/components/Views/MultichainAccounts/MultichainAccountPermissions/MultichainAccountPermissions.tsx b/app/components/Views/MultichainAccounts/MultichainAccountPermissions/MultichainAccountPermissions.tsx index c118c7716ad..3b291d6ea27 100644 --- a/app/components/Views/MultichainAccounts/MultichainAccountPermissions/MultichainAccountPermissions.tsx +++ b/app/components/Views/MultichainAccounts/MultichainAccountPermissions/MultichainAccountPermissions.tsx @@ -25,6 +25,7 @@ import { parseCaipAccountId, hasProperty, KnownCaipNamespace, + isCaipChainId, } from '@metamask/utils'; import { parseChainId } from '@walletconnect/utils'; import { NetworkConfiguration } from '@metamask/network-controller'; @@ -122,13 +123,22 @@ export const MultichainAccountPermissions = ( const networkAvatars: NetworkAvatarProps[] = useMemo( () => - selectedChainIds.map((selectedChainId) => ({ - size: AvatarSize.Xs, - name: networkConfigurations[selectedChainId]?.name || '', - imageSource: getNetworkImageSource({ chainId: selectedChainId }), - variant: AvatarVariant.Network, - caipChainId: selectedChainId, - })), + selectedChainIds + // TODO: Remove this filter once upstream issue is fixed + // selectedChainIds is populated by getAllScopesFromCaip25CaveatValue() from + // @metamask/chain-agnostic-permission, which incorrectly includes wallet scopes + // like 'wallet:eip155' that are not valid chain IDs. + // For now, filter to only include valid CAIP chain IDs, excluding wallet scopes. + .filter( + (chainId) => isCaipChainId(chainId) && !chainId.startsWith('wallet:'), + ) + .map((selectedChainId) => ({ + size: AvatarSize.Xs, + name: networkConfigurations[selectedChainId]?.name || '', + imageSource: getNetworkImageSource({ chainId: selectedChainId }), + variant: AvatarVariant.Network, + caipChainId: selectedChainId, + })), [networkConfigurations, selectedChainIds], ); From b58ddba86e5a6d1077e2edf0cc405cf57e449af5 Mon Sep 17 00:00:00 2001 From: Matthew Walsh Date: Mon, 24 Nov 2025 09:59:58 +0000 Subject: [PATCH 6/8] fix: cp-7.60.0 infinite loader in metamask pay (#23150) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** Prevent infinite loader when updating amount with same value in metamask pay. ## **Changelog** CHANGELOG entry: null ## **Related issues** Fixes: #23132 #22785 ## **Manual testing steps** ## **Screenshots/Recordings** ### **Before** ### **After** ## **Pre-merge author checklist** - [x] I’ve followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I’ve included tests if applicable - [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --- > [!NOTE] > Avoids unnecessary updates by early-returning when the new token amount equals the current amount, with tests added. > > - **Hooks**: > - Update `useUpdateTokenAmount` to early-return if `newAmountRaw` equals current `amountRaw`, preventing transaction data updates in `useUpdateTokenAmount.ts`. > - **Tests**: > - Add test in `useUpdateTokenAmount.test.ts` asserting no calls to `updateEditableParams` or `updateAtomicBatchData` when amount is unchanged. > > Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit be987719831923b3c33df823daf5c46fed096983. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot). --- .../transactions/useUpdateTokenAmount.test.ts | 17 +++++++++++++++++ .../hooks/transactions/useUpdateTokenAmount.ts | 4 ++++ 2 files changed, 21 insertions(+) diff --git a/app/components/Views/confirmations/hooks/transactions/useUpdateTokenAmount.test.ts b/app/components/Views/confirmations/hooks/transactions/useUpdateTokenAmount.test.ts index 5cbed38b359..4f1b944bd24 100644 --- a/app/components/Views/confirmations/hooks/transactions/useUpdateTokenAmount.test.ts +++ b/app/components/Views/confirmations/hooks/transactions/useUpdateTokenAmount.test.ts @@ -112,4 +112,21 @@ describe('useUpdateTokenAmount', () => { ) + toHex(15000).substring(2), }); }); + + it('does not update amount if new amount is equal to current amount', () => { + const { result } = runHook({ + transactionMeta: { + txParams: { + data: TOKEN_TRANSFER_DATA_MOCK, + from: '0x13', + to: tokenAddress1Mock, + }, + }, + }); + + result.current.updateTokenAmount('0.000000000000000001'); + + expect(updateEditableParamsMock).not.toHaveBeenCalled(); + expect(updateAtomicBatchDataMock).not.toHaveBeenCalled(); + }); }); diff --git a/app/components/Views/confirmations/hooks/transactions/useUpdateTokenAmount.ts b/app/components/Views/confirmations/hooks/transactions/useUpdateTokenAmount.ts index aba39fb6f1e..cf245cc8f4f 100644 --- a/app/components/Views/confirmations/hooks/transactions/useUpdateTokenAmount.ts +++ b/app/components/Views/confirmations/hooks/transactions/useUpdateTokenAmount.ts @@ -59,6 +59,10 @@ export function useUpdateTokenAmount() { decimals ?? 18, ).decimalPlaces(0, BigNumber.ROUND_UP); + if (newAmountRaw.isEqualTo(amountRaw)) { + return; + } + const transactionData = parseStandardTokenTransactionData(data); const recipient = transactionData?.args?._to; From 95b96cae2cb74ceb0a606503b354a69d5a80e3eb Mon Sep 17 00:00:00 2001 From: Matthew Walsh Date: Mon, 24 Nov 2025 10:05:14 +0000 Subject: [PATCH 7/8] fix: cp-7.60.0 automatic highest balance token in metamask pay (#23152) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** Automatically select the highest balance token on any chain in Perps and Predict deposits. ## **Changelog** CHANGELOG entry: null ## **Related issues** Fixes: #23131 ## **Manual testing steps** ## **Screenshots/Recordings** ### **Before** ### **After** ## **Pre-merge author checklist** - [x] I’ve followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I’ve included tests if applicable - [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --- > [!NOTE] > Simplifies automatic pay token selection to the first available token with balance, falling back to target token when none or on hardware wallets, and updates tests accordingly. > > - **Logic (useAutomaticTransactionPayToken.ts)** > - Replace multi-step selection (target match → highest balance same chain → highest balance alt chain) with simple rule: pick `tokens[0]` if any; otherwise fall back to required target token; always fall back for hardware wallets. > - **Tests (useAutomaticTransactionPayToken.test.ts)** > - Update expectations to select the first available token. > - Remove scenarios around highest-balance and cross-chain selection. > - Keep fallbacks: target token when no tokens, hardware wallets, and disabled state does nothing. > > Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 6907490c07813dcaf8185fc8c8c37168f0b556e8. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot). --- .../useAutomaticTransactionPayToken.test.ts | 54 ++----------------- .../pay/useAutomaticTransactionPayToken.ts | 34 ++---------- 2 files changed, 8 insertions(+), 80 deletions(-) diff --git a/app/components/Views/confirmations/hooks/pay/useAutomaticTransactionPayToken.test.ts b/app/components/Views/confirmations/hooks/pay/useAutomaticTransactionPayToken.test.ts index 753717f2bc1..67d0705db66 100644 --- a/app/components/Views/confirmations/hooks/pay/useAutomaticTransactionPayToken.test.ts +++ b/app/components/Views/confirmations/hooks/pay/useAutomaticTransactionPayToken.test.ts @@ -84,42 +84,18 @@ describe('useAutomaticTransactionPayToken', () => { isHardwareAccountMock.mockReturnValue(false); }); - it('selects target token if has balance', () => { + it('selects first token', () => { useTransactionPayAvailableTokensMock.mockReturnValue([ { address: TOKEN_ADDRESS_2_MOCK, - chainId: CHAIN_ID_1_MOCK, - }, - { - address: TOKEN_ADDRESS_3_MOCK, chainId: CHAIN_ID_2_MOCK, }, - { - address: TOKEN_ADDRESS_1_MOCK, - chainId: CHAIN_ID_1_MOCK, - }, - ] as AssetType[]); - - runHook(); - - expect(setPayTokenMock).toHaveBeenCalledWith({ - address: TOKEN_ADDRESS_1_MOCK, - chainId: CHAIN_ID_1_MOCK, - }); - }); - - it('selects token with highest balance on same chain if insufficient balance on target token', () => { - useTransactionPayAvailableTokensMock.mockReturnValue([ { address: TOKEN_ADDRESS_3_MOCK, chainId: CHAIN_ID_2_MOCK, }, { - address: TOKEN_ADDRESS_2_MOCK, - chainId: CHAIN_ID_2_MOCK, - }, - { - address: TOKEN_ADDRESS_3_MOCK, + address: TOKEN_ADDRESS_1_MOCK, chainId: CHAIN_ID_1_MOCK, }, ] as AssetType[]); @@ -127,33 +103,13 @@ describe('useAutomaticTransactionPayToken', () => { runHook(); expect(setPayTokenMock).toHaveBeenCalledWith({ - address: TOKEN_ADDRESS_3_MOCK, - chainId: CHAIN_ID_1_MOCK, - }); - }); - - it('selects token with highest balance on alternate chain if insufficient balance on same chain', () => { - useTransactionPayAvailableTokensMock.mockReturnValue([ - { - address: TOKEN_ADDRESS_3_MOCK, - chainId: CHAIN_ID_2_MOCK, - }, - { - address: TOKEN_ADDRESS_2_MOCK, - chainId: CHAIN_ID_2_MOCK, - }, - ] as AssetType[]); - - runHook(); - - expect(setPayTokenMock).toHaveBeenCalledWith({ - address: TOKEN_ADDRESS_3_MOCK, + address: TOKEN_ADDRESS_2_MOCK, chainId: CHAIN_ID_2_MOCK, }); }); - it('selects target token if insufficient balance on all chains', () => { - useTransactionPayAvailableTokensMock.mockReturnValue([]); + it('selects target token if no tokens with balance', () => { + useTransactionPayAvailableTokensMock.mockReturnValue([] as AssetType[]); runHook(); diff --git a/app/components/Views/confirmations/hooks/pay/useAutomaticTransactionPayToken.ts b/app/components/Views/confirmations/hooks/pay/useAutomaticTransactionPayToken.ts index b53456f0eba..9217fd32e8e 100644 --- a/app/components/Views/confirmations/hooks/pay/useAutomaticTransactionPayToken.ts +++ b/app/components/Views/confirmations/hooks/pay/useAutomaticTransactionPayToken.ts @@ -97,38 +97,10 @@ function getBestToken({ return targetTokenFallback; } - const requiredToken = tokens.find( - (t) => - t.address.toLowerCase() === targetToken?.address.toLowerCase() && - t.chainId === targetToken?.chainId, - ); - - if (requiredToken) { - return { - address: requiredToken.address as Hex, - chainId: requiredToken.chainId as Hex, - }; - } - - const sameChainHighestBalanceToken = tokens.find( - (t) => t.chainId === targetToken?.chainId, - ); - - if (sameChainHighestBalanceToken) { - return { - address: sameChainHighestBalanceToken.address as Hex, - chainId: sameChainHighestBalanceToken.chainId as Hex, - }; - } - - const alternateChainHighestBalanceToken = tokens.find( - (t) => t.chainId !== targetToken?.chainId, - ); - - if (alternateChainHighestBalanceToken) { + if (tokens?.length) { return { - address: alternateChainHighestBalanceToken.address as Hex, - chainId: alternateChainHighestBalanceToken.chainId as Hex, + address: tokens[0].address as Hex, + chainId: tokens[0].chainId as Hex, }; } From eb8968648f58d907e40a750ff5250eceb3c2c245 Mon Sep 17 00:00:00 2001 From: Matthew Walsh Date: Mon, 24 Nov 2025 10:28:29 +0000 Subject: [PATCH 8/8] fix: cp-7.60.0 predict claim button style (#23153) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** Update text colour of Predict claim button in light theme. ## **Changelog** CHANGELOG entry: null ## **Related issues** Fixes: #23096 ## **Manual testing steps** ## **Screenshots/Recordings** ### **Before** ### **After** Claim ## **Pre-merge author checklist** - [x] I’ve followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I’ve included tests if applicable - [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --- > [!NOTE] > Replace Predict claim CTA with ButtonHero and drop light-theme-specific button styling. > > - **Predict Claim Footer (`predict-claim-footer.tsx`)**: > - Replace `Button` with `ButtonHero`; move label to children, remove variant/label props. > - Set `size` to `ButtonBaseSize.Lg` and `isFullWidth`. > - Add imports for `ButtonHero` and `ButtonBaseSize`. > - **Styles (`predict-claim-footer.styles.ts`)**: > - Remove hardcoded `button` style and `lightTheme` dependency; keep container/top/bottom styles. > > Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 44a2213ea866f9082dfcfcda5df7c71e0fefec5a. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot). --- .../predict-claim-footer.styles.ts | 7 ------- .../predict-claim-footer.tsx | 20 +++++++++---------- 2 files changed, 9 insertions(+), 18 deletions(-) diff --git a/app/components/Views/confirmations/components/predict-confirmations/predict-claim-footer/predict-claim-footer.styles.ts b/app/components/Views/confirmations/components/predict-confirmations/predict-claim-footer/predict-claim-footer.styles.ts index bcf46f8d070..2bd46b0a5f1 100644 --- a/app/components/Views/confirmations/components/predict-confirmations/predict-claim-footer/predict-claim-footer.styles.ts +++ b/app/components/Views/confirmations/components/predict-confirmations/predict-claim-footer/predict-claim-footer.styles.ts @@ -1,6 +1,5 @@ import { StyleSheet } from 'react-native'; import { Theme } from '../../../../../../util/theme/models'; -import { lightTheme } from '@metamask/design-tokens'; const styleSheet = (params: { theme: Theme }) => StyleSheet.create({ @@ -23,12 +22,6 @@ const styleSheet = (params: { theme: Theme }) => bottom: { textAlign: 'center', }, - - button: { - width: '100%', - height: 48, - backgroundColor: lightTheme.colors.primary.default, - }, }); export default styleSheet; diff --git a/app/components/Views/confirmations/components/predict-confirmations/predict-claim-footer/predict-claim-footer.tsx b/app/components/Views/confirmations/components/predict-confirmations/predict-claim-footer/predict-claim-footer.tsx index 95dd0ed8e6b..0a23df76696 100644 --- a/app/components/Views/confirmations/components/predict-confirmations/predict-claim-footer/predict-claim-footer.tsx +++ b/app/components/Views/confirmations/components/predict-confirmations/predict-claim-footer/predict-claim-footer.tsx @@ -6,9 +6,6 @@ import Avatar, { AvatarVariant, } from '../../../../../../component-library/components/Avatars/Avatar'; import AvatarGroup from '../../../../../../component-library/components/Avatars/AvatarGroup'; -import Button, { - ButtonVariants, -} from '../../../../../../component-library/components/Buttons/Button'; import Text, { TextColor, TextVariant, @@ -23,6 +20,8 @@ import { PredictPosition } from '../../../../../UI/Predict'; import { AlignItems, FlexDirection } from '../../../../../UI/Box/box.types'; import useFiatFormatter from '../../../../../UI/SimulationDetails/FiatDisplay/useFiatFormatter'; import { BigNumber } from 'bignumber.js'; +import ButtonHero from '../../../../../../component-library/components-temp/Buttons/ButtonHero'; +import { ButtonBaseSize } from '@metamask/design-system-react-native'; export interface PredictClaimFooterProps { onPress: () => void; @@ -51,15 +50,14 @@ export function PredictClaimFooter({ onPress }: PredictClaimFooterProps) { ) : ( )} -