Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
007c36b
refactor: delete `EvmAccountSelectorList` and `TurnOnBackupAndSync` (…
gantunesr Mar 5, 2026
32bd5c5
refactor: replace BuildQuote navbar with HeaderCompact standard (#27021)
amitabh94 Mar 5, 2026
b1dcb14
feat: Add the "refundTo" param for postQuote transactions (#27065)
dan437 Mar 5, 2026
a6f7d4f
feat: add Tempo Mainnet network logo (#26904)
maxime-oe Mar 5, 2026
54365c8
feat: MUSD-352 replace boost with bonus globally for conversion flows…
Matt561 Mar 5, 2026
2b288d1
feat(predict): always use Permit2 fee auth and attach allowancesTx vi…
matallui Mar 5, 2026
950a7e6
test: color-no-hex predict (#27008)
georgewrmarshall Mar 5, 2026
1a763e6
fix: MUSD-404 missing received musd link in transaction details for l…
Matt561 Mar 5, 2026
903489d
feat: MUSD-394 add generic transaction fee row to conversion confirma…
Matt561 Mar 5, 2026
6d91b75
fix: MUSD-345 Harden mUSD Quick Convert Loading and Navigation (#26608)
Matt561 Mar 5, 2026
de668f2
feat: MUSD-363 refactored token-conversion-asset-header to stack asse…
Matt561 Mar 5, 2026
e345e4e
feat(rewards): show off-device linked accounts banner on settings pag…
VGR-GIT Mar 5, 2026
098426e
fix(predict): make fee exemption logic feature-flag-driven in Predict…
matallui Mar 5, 2026
ba7d28d
test: delete offramp.failing e2e test and add sell-mode unit test cov…
chrisleewilcox Mar 5, 2026
4ccf7f9
chore: upgrade design-system-react-native to 0.10.0 and deprecate leg…
georgewrmarshall Mar 5, 2026
5271380
fix(ramps): remove title from sharedheader (#27024)
imyugioh Mar 5, 2026
d8064c1
chore(release): Bump main version to 7.70.0 (#27088)
metamaskbot Mar 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,12 @@ module.exports = {
'@metamask/design-tokens/color-no-hex': 'error',
},
},
{
files: ['app/components/UI/Predict/**/*.{js,jsx,ts,tsx}'],
rules: {
'@metamask/design-tokens/color-no-hex': 'error',
},
},
{
files: [
'app/components/UI/Name/**/*.{js,ts,tsx}',
Expand Down
1 change: 0 additions & 1 deletion .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,6 @@ app/core/SnapKeyring @MetaMask/accounts-e

# Co-owned by accounts and mobile-core-ux
app/components/Views/AccountSelector @MetaMask/accounts-engineers @MetaMask/mobile-core-ux
app/components/UI/EvmAccountSelectorList @MetaMask/accounts-engineers @MetaMask/mobile-core-ux

# Multichain Accounts
**/multichain-accounts/** @MetaMask/accounts-engineers
Expand Down
2 changes: 1 addition & 1 deletion android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ android {
applicationId "io.metamask"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionName "7.69.0"
versionName "7.70.0"
versionCode 3607
testBuildType System.getProperty('testBuildType', 'debug')
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ import { useTailwind } from '@metamask/design-system-twrnc-preset';
// Internal dependencies
import { ActionListItemProps } from './ActionListItem.types';

/**
* @deprecated Please update your code to use `ActionListItem` from `@metamask/design-system-react-native`.
* The API may have changed - compare props before migrating.
* @see {@link https://github.com/MetaMask/metamask-design-system/blob/main/packages/design-system-react-native/src/components/ActionListItem/README.md}
*/
const ActionListItem: React.FC<ActionListItemProps> = ({
label,
description,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ const ButtonHeroInner = ({
* The useTailwind hook needs to be called inside the ThemeProvider context to get the locked theme.
* By splitting into two components, we ensure the hook gets the correct theme context for all color calculations.
*/
/**
* @deprecated Please update your code to use `ButtonHero` from `@metamask/design-system-react-native`.
* The API may have changed - compare props before migrating.
* @see {@link https://github.com/MetaMask/metamask-design-system/blob/main/packages/design-system-react-native/src/components/ButtonHero/README.md}
*/
const ButtonHero = (props: ButtonBaseProps) => (
<ThemeProvider theme={Theme.Light}>
<ButtonHeroInner {...props} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ import {
ButtonSemanticSeverity,
} from './ButtonSemantic.types';

/**
* @deprecated Please update your code to use `ButtonSemantic` from `@metamask/design-system-react-native`.
* The API may have changed - compare props before migrating.
* @see {@link https://github.com/MetaMask/metamask-design-system/blob/main/packages/design-system-react-native/src/components/ButtonSemantic/README.md}
*/
const ButtonSemantic: React.FC<ButtonSemanticProps> = ({
severity,
size = ButtonSize.Lg,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ import {
BottomSheetHeaderVariant,
} from './BottomSheetHeader.types';

/**
* @deprecated Please update your code to use `BottomSheetHeader` from `@metamask/design-system-react-native`.
* The API may have changed - compare props before migrating.
* @see {@link https://github.com/MetaMask/metamask-design-system/blob/main/packages/design-system-react-native/src/components/BottomSheetHeader/README.md}
*/
const BottomSheetHeader: React.FC<BottomSheetHeaderProps> = ({
style,
children,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ import {
DEFAULT_BUTTONBASE_LABEL_TEXTVARIANT,
} from './ButtonBase.constants';

/**
* @deprecated Please update your code to use `ButtonBase` from `@metamask/design-system-react-native`.
* The API may have changed - compare props before migrating.
* @see {@link https://github.com/MetaMask/metamask-design-system/blob/main/packages/design-system-react-native/src/components/ButtonBase/README.md}
*/
const ButtonBase = ({
label,
labelColor = DEFAULT_BUTTONBASE_LABEL_COLOR,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
* @deprecated Please update your code to use `TextField` from `@metamask/design-system-react-native`.
* The API may have changed — compare props before migrating.
* @see {@link https://github.com/MetaMask/metamask-design-system/blob/main/packages/design-system-react-native/src/components/TextField/README.md}
* @since @metamask/design-system-react-native@0.9.0
*/
const TextField = React.forwardRef<TextInput | null, TextFieldProps>(
(
Expand Down
6 changes: 0 additions & 6 deletions app/components/Nav/Main/MainNavigator.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,6 @@ import { useAccountMenuEnabled } from '../../../selectors/featureFlagController/
import PerpsPositionTransactionView from '../../UI/Perps/Views/PerpsTransactionsView/PerpsPositionTransactionView';
import PerpsOrderTransactionView from '../../UI/Perps/Views/PerpsTransactionsView/PerpsOrderTransactionView';
import PerpsFundingTransactionView from '../../UI/Perps/Views/PerpsTransactionsView/PerpsFundingTransactionView';
import TurnOnBackupAndSync from '../../Views/Identity/TurnOnBackupAndSync/TurnOnBackupAndSync';
import DeFiProtocolPositionDetails from '../../UI/DeFiPositions/DeFiProtocolPositionDetails';
import UnmountOnBlur from '../../Views/UnmountOnBlur';
///: BEGIN:ONLY_INCLUDE_IF(sample-feature)
Expand Down Expand Up @@ -1207,11 +1206,6 @@ const MainNavigator = () => {
component={NotificationsOptInStack}
options={NotificationsOptInStack.navigationOptions}
/>
<Stack.Screen
name={Routes.IDENTITY.TURN_ON_BACKUP_AND_SYNC}
component={TurnOnBackupAndSync}
options={TurnOnBackupAndSync.navigationOptions}
/>
<Stack.Screen
name="DeFiProtocolPositionDetails"
component={DeFiProtocolPositionDetails}
Expand Down
12 changes: 0 additions & 12 deletions app/components/Nav/Main/__snapshots__/MainNavigator.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -358,10 +358,6 @@ exports[`MainNavigator Tab Bar Visibility hides tab bar when browser is active 1
component={[Function]}
name="OptInStack"
/>
<Screen
component={[Function]}
name="TurnOnBackupAndSync"
/>
<Screen
component={[Function]}
name="DeFiProtocolPositionDetails"
Expand Down Expand Up @@ -756,10 +752,6 @@ exports[`MainNavigator Tab Bar Visibility shows tab bar when not in browser 1`]
component={[Function]}
name="OptInStack"
/>
<Screen
component={[Function]}
name="TurnOnBackupAndSync"
/>
<Screen
component={[Function]}
name="DeFiProtocolPositionDetails"
Expand Down Expand Up @@ -1154,10 +1146,6 @@ exports[`MainNavigator matches rendered snapshot 1`] = `
component={[Function]}
name="OptInStack"
/>
<Screen
component={[Function]}
name="TurnOnBackupAndSync"
/>
<Screen
component={[Function]}
name="DeFiProtocolPositionDetails"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { selectMusdQuickConvertEnabledFlag } from '../../selectors/featureFlags'
import {
createTokenChainKey,
selectHasInFlightMusdConversion,
selectHasUnapprovedMusdConversion,
selectMusdConversionStatuses,
} from '../../selectors/musdConversionStatus';
import { MUSD_CONVERSION_APY } from '../../constants/musd';
Expand Down Expand Up @@ -42,6 +43,7 @@ jest.mock('../../selectors/featureFlags', () => ({
jest.mock('../../selectors/musdConversionStatus', () => ({
...jest.requireActual('../../selectors/musdConversionStatus'),
selectHasInFlightMusdConversion: jest.fn(),
selectHasUnapprovedMusdConversion: jest.fn(),
selectMusdConversionStatuses: jest.fn(),
}));
const mockGetStakingNavbar = jest.fn<object, unknown[]>(() => ({}));
Expand Down Expand Up @@ -90,6 +92,10 @@ const mockUseMusdConversion = useMusdConversion as jest.MockedFunction<
const mockUseMusdBalance = useMusdBalance as jest.MockedFunction<
typeof useMusdBalance
>;
const mockSelectHasUnapprovedMusdConversion =
selectHasUnapprovedMusdConversion as jest.MockedFunction<
typeof selectHasUnapprovedMusdConversion
>;
const mockSelectHasInFlightMusdConversion =
selectHasInFlightMusdConversion as jest.MockedFunction<
typeof selectHasInFlightMusdConversion
Expand Down Expand Up @@ -171,6 +177,7 @@ describe('MusdQuickConvertView', () => {
fiatBalanceAggregatedFormatted: '$0.00',
});
mockSelectHasInFlightMusdConversion.mockReturnValue(false);
mockSelectHasUnapprovedMusdConversion.mockReturnValue(false);
mockSelectMusdConversionStatuses.mockReturnValue({});
});

Expand Down Expand Up @@ -461,8 +468,8 @@ describe('MusdQuickConvertView', () => {
});
});

describe('in-flight conversion', () => {
it('does not call initiateMaxConversion or initiateCustomConversion when Max or Edit is pressed and hasInFlightMusdConversion is true', async () => {
describe('unapproved conversion', () => {
it('does not call initiateMaxConversion or initiateCustomConversion when Max or Edit is pressed and hasUnapprovedMusdConversion is true', async () => {
const token = createMockToken();
mockUseMusdConversionTokens.mockReturnValue({
tokens: [token],
Expand All @@ -471,7 +478,7 @@ describe('MusdQuickConvertView', () => {
isMusdSupportedOnChain: jest.fn(),
hasConvertibleTokensByChainId: jest.fn(),
});
mockSelectHasInFlightMusdConversion.mockReturnValue(true);
mockSelectHasUnapprovedMusdConversion.mockReturnValue(true);

const { getAllByTestId } = renderWithProvider(<MusdQuickConvertView />, {
state: initialRootState,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,15 @@ describe('MusdBalanceCard', () => {
expect(getByText(MUSD_TOKEN.symbol)).toBeOnTheScreen();
});

it('displays percentage boost text from localization', () => {
it('displays percentage bonus text from localization', () => {
const { getByText } = renderWithProvider(
<MusdBalanceCard chainId={CHAIN_IDS.MAINNET} balance="$100.00" />,
{
state: initialRootState,
},
);

expect(getByText('3% boost')).toBeOnTheScreen();
expect(getByText('3% bonus')).toBeOnTheScreen();
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ const MusdBalanceCard = ({ chainId, balance }: MusdBalanceCardProps) => {

<View style={styles.right}>
<Text variant={TextVariant.BodyMDMedium} color={TextColor.Success}>
{strings('earn.musd_conversion.percentage_boost', {
{strings('earn.musd_conversion.percentage_bonus', {
percentage: MUSD_CONVERSION_APY,
})}
</Text>
Expand Down
9 changes: 8 additions & 1 deletion app/components/UI/Earn/Views/MusdQuickConvertView/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { selectMusdQuickConvertEnabledFlag } from '../../selectors/featureFlags'
import {
createTokenChainKey,
selectHasInFlightMusdConversion,
selectHasUnapprovedMusdConversion,
selectMusdConversionStatuses,
} from '../../selectors/musdConversionStatus';
import ConvertTokenRow from '../../components/Musd/ConvertTokenRow';
Expand Down Expand Up @@ -81,6 +82,9 @@ const MusdQuickConvertView = () => {
// Get convertible tokens
const { tokens: conversionTokens } = useMusdConversionTokens();

const hasUnapprovedMusdConversion = useSelector(
selectHasUnapprovedMusdConversion,
);
const hasInFlightMusdConversion = useSelector(
selectHasInFlightMusdConversion,
);
Expand Down Expand Up @@ -200,7 +204,9 @@ const MusdQuickConvertView = () => {
token={item}
onMaxPress={handleMaxPress}
onEditPress={handleEditPress}
areActionsDisabled={hasInFlightMusdConversion}
areActionsDisabled={
hasUnapprovedMusdConversion || hasInFlightMusdConversion
}
isConversionPending={Boolean(txStatusInfo?.isPending)}
/>
);
Expand All @@ -210,6 +216,7 @@ const MusdQuickConvertView = () => {
handleEditPress,
handleMaxPress,
hasInFlightMusdConversion,
hasUnapprovedMusdConversion,
],
);

Expand Down
11 changes: 11 additions & 0 deletions app/components/UI/Earn/components/EarnTransactionMonitor.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,18 @@ import React from 'react';
import { render } from '@testing-library/react-native';
import EarnTransactionMonitor from './EarnTransactionMonitor';
import { useMusdConversionStatus } from '../hooks/useMusdConversionStatus';
import { useMusdConversionStaleApprovalCleanup } from '../hooks/useMusdConversionStaleApprovalCleanup';
import { useMerklClaimStatus } from '../hooks/useMerklClaimStatus';

jest.mock('../hooks/useMusdConversionStatus');
jest.mock('../hooks/useMusdConversionStaleApprovalCleanup');
jest.mock('../hooks/useMerklClaimStatus');

describe('EarnTransactionMonitor', () => {
const mockUseMusdConversionStatus = jest.mocked(useMusdConversionStatus);
const mockUseMusdConversionStaleApprovalCleanup = jest.mocked(
useMusdConversionStaleApprovalCleanup,
);
const mockUseMerklClaimStatus = jest.mocked(useMerklClaimStatus);

beforeEach(() => {
Expand All @@ -31,6 +36,12 @@ describe('EarnTransactionMonitor', () => {
expect(mockUseMusdConversionStatus).toHaveBeenCalledTimes(1);
});

it('calls useMusdConversionStaleApprovalCleanup hook', () => {
render(<EarnTransactionMonitor />);

expect(mockUseMusdConversionStaleApprovalCleanup).toHaveBeenCalledTimes(1);
});

it('calls useMerklClaimStatus hook', () => {
render(<EarnTransactionMonitor />);

Expand Down
7 changes: 7 additions & 0 deletions app/components/UI/Earn/components/EarnTransactionMonitor.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react';
import { useMusdConversionStatus } from '../hooks/useMusdConversionStatus';
import { useMusdConversionStaleApprovalCleanup } from '../hooks/useMusdConversionStaleApprovalCleanup';
import { useMerklClaimStatus } from '../hooks/useMerklClaimStatus';

/**
Expand All @@ -9,6 +10,12 @@ import { useMerklClaimStatus } from '../hooks/useMerklClaimStatus';
* allowing them to remain active even when navigating away from Earn screens.
*/
const EarnTransactionMonitor: React.FC = () => {
/**
* Reject stale mUSD pending approvals on app foreground.
* For example, resuming via notification or deeplink can bypass
* the normal confirmation rejection path.
*/
useMusdConversionStaleApprovalCleanup();
// Enable mUSD conversion status monitoring and toasts
useMusdConversionStatus();
// Enable Merkl bonus claim status monitoring and toasts
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,7 @@ describe('MusdConversionAssetOverviewCta', () => {
});

// Assert
const expectedCtaText = strings('earn.musd_conversion.boost_title', {
const expectedCtaText = strings('earn.musd_conversion.bonus_title', {
percentage: MUSD_CONVERSION_APY,
});

Expand Down Expand Up @@ -484,7 +484,7 @@ describe('MusdConversionAssetOverviewCta', () => {
});

// Assert
const expectedCtaText = strings('earn.musd_conversion.boost_title', {
const expectedCtaText = strings('earn.musd_conversion.bonus_title', {
percentage: MUSD_CONVERSION_APY,
});

Expand Down Expand Up @@ -534,7 +534,7 @@ describe('MusdConversionAssetOverviewCta', () => {
});

// Assert
const expectedCtaText = strings('earn.musd_conversion.boost_title', {
const expectedCtaText = strings('earn.musd_conversion.bonus_title', {
percentage: MUSD_CONVERSION_APY,
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -474,7 +474,7 @@ describeForPlatforms('MusdConversionAssetOverviewCta', () => {
).toBeOnTheScreen();
});

it('renders CTA with correct boost title text', () => {
it('renders CTA with correct bonus title text', () => {
// Arrange
const state = initialStateWallet()
.withMinimalMultichainAssets()
Expand Down Expand Up @@ -507,7 +507,7 @@ describeForPlatforms('MusdConversionAssetOverviewCta', () => {
).toBeOnTheScreen();
});

it('renders CTA with correct boost description text', () => {
it('renders CTA with correct bonus description text', () => {
// Arrange
const state = initialStateWallet()
.withMinimalMultichainAssets()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ const MusdConversionAssetOverviewCta = ({
const submitCtaPressedEvent = () => {
const { EVENT_LOCATIONS, MUSD_CTA_TYPES } = MUSD_EVENTS_CONSTANTS;

const ctaText = strings('earn.musd_conversion.boost_title', {
const ctaText = strings('earn.musd_conversion.bonus_title', {
percentage: MUSD_CONVERSION_APY,
});

Expand Down Expand Up @@ -116,12 +116,12 @@ const MusdConversionAssetOverviewCta = ({
{/* Text content in the center */}
<View style={styles.textContainer}>
<Text variant={TextVariant.BodySMMedium} style={styles.title}>
{strings('earn.musd_conversion.boost_title', {
{strings('earn.musd_conversion.bonus_title', {
percentage: MUSD_CONVERSION_APY,
})}
</Text>
<Text variant={TextVariant.BodySMMedium} color={TextColor.Alternative}>
{strings('earn.musd_conversion.boost_description', {
{strings('earn.musd_conversion.bonus_description', {
percentage: MUSD_CONVERSION_APY,
})}
</Text>
Expand Down
2 changes: 1 addition & 1 deletion app/components/UI/Earn/hooks/useEarnToasts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ const useEarnToasts = (): {
<Icon
name={IconName.CircleX}
color={theme.colors.error.default}
size={IconSize.Xl}
size={IconSize.Lg}
/>
</View>
),
Expand Down
Loading
Loading