Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
6ee3bae
bump semvar version to 7.70.1 && build version to 4152
metamaskbot Mar 23, 2026
0ae0e30
Revert "bump semvar version to 7.70.1 && build version to 4152"
joaoloureirop Mar 23, 2026
dff0679
chore(runway): cherry-pick fix(perps): fix stale data and missing pri…
runway-github[bot] Mar 23, 2026
8c73c19
bump ota version
joaoloureirop Mar 23, 2026
0e0cb25
chore(runway): cherry-pick fix(perps): fix HIP-3 asset ID lookup fail…
runway-github[bot] Mar 24, 2026
c6515db
chore(release): release-changelog/7.70.1 (#27841)
chloeYue Mar 25, 2026
484d661
chore(release): merge stable into release/7.70.1; resolve CHANGELOG (…
chloeYue Mar 25, 2026
b2ebb6d
feat: add access restricted modal and compliance UI infrastructure (#…
michalconsensys Mar 25, 2026
dbff111
feat: bump seedless onboarding controller (#27859)
grvgoel81 Mar 25, 2026
31e0b69
release: 7.70.1 (#27824)
chloeYue Mar 25, 2026
91fc0b0
Merge origin/main into stable-main-7.72.0
metamaskbot Mar 25, 2026
48892de
chore(release): sync stable to main for version 7.70.1 (#27913)
metamaskbotv2[bot] Mar 25, 2026
7ce45e9
feat(activity): Enhance incoming native transfer handling with poison…
PatrykLucka Mar 25, 2026
17260ce
test: color-no-hex small-owners batch (#27154)
georgewrmarshall Mar 25, 2026
602b027
test: add empty mocks for digest API (#27911)
christopherferreira9 Mar 25, 2026
ad3b7a5
test: add playwright assertions class (#27897)
cortisiko Mar 25, 2026
8a7bced
fix: hides perps buttons in ai insights when user has a position cp-7…
joaosantos15 Mar 25, 2026
5e444bd
feat: add test app universal link support (#27811)
baptiste-marchand Mar 25, 2026
ef5e684
feat(earn): gate Tron unstaked claim button behind remote flag (#27908)
ulissesferreira Mar 25, 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
10 changes: 9 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [7.70.1]

### Fixed

- Fixed stale perpetuals data and missing 24h price change after returning from background (#27530)
- Fixed a bug where closing positions on HIP-3 markets (e.g., xyz:BRENTOIL) failed with "Asset ID not found" when navigating via the Perps tab (#27854)

## [7.70.0]

### Added
Expand Down Expand Up @@ -11008,7 +11015,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- [#957](https://github.com/MetaMask/metamask-mobile/pull/957): fix timeouts (#957)
- [#954](https://github.com/MetaMask/metamask-mobile/pull/954): Bugfix: onboarding navigation (#954)

[Unreleased]: https://github.com/MetaMask/metamask-mobile/compare/v7.70.0...HEAD
[Unreleased]: https://github.com/MetaMask/metamask-mobile/compare/v7.70.1...HEAD
[7.70.1]: https://github.com/MetaMask/metamask-mobile/compare/v7.70.0...v7.70.1
[7.70.0]: https://github.com/MetaMask/metamask-mobile/compare/v7.69.1...v7.70.0
[7.69.1]: https://github.com/MetaMask/metamask-mobile/compare/v7.69.0...v7.69.1
[7.69.0]: https://github.com/MetaMask/metamask-mobile/compare/v7.68.3...v7.69.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { fireEvent, render } from '@testing-library/react-native';

// External dependencies.
import { IconName } from '../../../../Icons/Icon';
import { TextVariant } from '../../../../Texts/Text';
import { TextColor, TextVariant } from '../../../../Texts/Text';

// Internal dependencies.
import ButtonBase from './ButtonBase';
Expand Down Expand Up @@ -180,7 +180,7 @@ describe('ButtonBase', () => {
<ButtonBase
label="Click me!"
onPress={() => null}
labelColor="#ff0000"
labelColor={TextColor.Alternative}
size={ButtonSize.Md}
/>,
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import React from 'react';
import { View } from 'react-native';
import { brandColor } from '@metamask/design-tokens';

// External dependencies
import { ThemeContext } from '../../../util/theme';
import renderWithProvider from '../../../util/test/renderWithProvider';

// Internal dependencies
import ThemeProvider from './ThemeProvider';
import { brandColor } from '@metamask/design-tokens';

describe('ThemeProvider', () => {
it('renders children correctly', () => {
Expand Down
32 changes: 18 additions & 14 deletions app/components/Nav/App/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ import { useOTAUpdates } from '../../hooks/useOTAUpdates';
import MultichainTransactionDetailsSheet from '../../UI/MultichainTransactionDetailsModal/MultichainTransactionDetailsSheet';
import TransactionDetailsSheet from '../../UI/TransactionElement/TransactionDetailsSheet';
import ImportWalletTipBottomSheet from '../../UI/TransactionElement/ImportWalletTipBottomSheet';
import { AccessRestrictedProvider } from '../../UI/Compliance';

const clearStackNavigatorOptions = {
headerShown: false,
Expand Down Expand Up @@ -1133,16 +1134,17 @@ const App: React.FC = () => {
useInterval(
async () => {
if (isSeedlessOnboardingLoginFlow) {
await Authentication.checkIsSeedlessPasswordOutdated(
firstLoad.current,
).catch((error) => {
await Authentication.checkIsSeedlessPasswordOutdated({
skipCache: firstLoad.current,
captureSentryError: false,
}).catch((error) => {
Logger.error(error, 'App: Error in checkIsSeedlessPasswordOutdated');
});
firstLoad.current = false;
}
},
{
delay: Duration.Minute * 5,
delay: Duration.Minute * 10,
immediate: true,
},
);
Expand Down Expand Up @@ -1182,16 +1184,18 @@ const App: React.FC = () => {
}, []);

return (
<WebSocketHealthToastProvider>
{/* TODO: Temporary fix for non-V2 Buy token selection; remove RampsBootstrap once V2 flag is on for all users. */}
<RampsBootstrap />
<AppFlow />
<Toast ref={toastRef} />
<PerpsWebSocketHealthToast />
{__DEV__ && <AgentStepHud />}
<ControllerEventToastBridge registrations={predictRegistrations} />
<ProfilerManager />
</WebSocketHealthToastProvider>
<AccessRestrictedProvider>
<WebSocketHealthToastProvider>
{/* TODO: Temporary fix for non-V2 Buy token selection; remove RampsBootstrap once V2 flag is on for all users. */}
<RampsBootstrap />
<AppFlow />
<Toast ref={toastRef} />
<PerpsWebSocketHealthToast />
{__DEV__ && <AgentStepHud />}
<ControllerEventToastBridge registrations={predictRegistrations} />
<ProfilerManager />
</WebSocketHealthToastProvider>
</AccessRestrictedProvider>
);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,12 @@ jest.mock('react-redux', () => ({
useDispatch: jest.fn(),
}));

jest.mock('../../../util/theme', () => ({
useTheme: jest.fn().mockReturnValue({
colors: {
background: {
default: '#FFFFFF',
},
},
}),
}));
jest.mock('../../../util/theme', () => {
const { mockTheme } = jest.requireActual('../../../util/theme');
return {
useTheme: jest.fn(() => mockTheme),
};
});

describe('NavigationProvider', () => {
const mockDispatch = jest.fn();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ describeForPlatforms('BridgeView', () => {

describe('Swap team regression (bug matrix team-swaps-and-bridge)', () => {
/** Issues covered: #24744, #24865, #24802, #25256 */
// eslint-disable-next-line @metamask/design-tokens/color-no-hex -- "#24744" style references are GitHub issue IDs (e.g. "#2342"), not color literals
it('displays gas included label and enables confirm when quote has gas included (#24744)', async () => {
const now = Date.now();
const quoteWithGasIncluded = {
Expand Down Expand Up @@ -238,6 +239,7 @@ describeForPlatforms('BridgeView', () => {
});

// Regression for #25256: two USDT tokens on Linea must both appear in search results.
// eslint-disable-next-line @metamask/design-tokens/color-no-hex -- "#25256" style references are GitHub issue IDs (e.g. "#2342"), not color literals
it('shows two USDT when search API returns two USDT on Linea (#25256)', async () => {
jest
.spyOn(Engine.context.AuthenticationController, 'getBearerToken')
Expand Down Expand Up @@ -407,6 +409,7 @@ describeForPlatforms('BridgeView', () => {
fetchSpy.mockRestore();
}, 25000);

// eslint-disable-next-line @metamask/design-tokens/color-no-hex -- "#24865" style references are GitHub issue IDs (e.g. "#2342"), not color literals
it('shows native token in source area when source is native token from token details (#24865)', () => {
const bnbChainId = '0x38';
const nativeBnbAddress = '0x0000000000000000000000000000000000000000';
Expand Down Expand Up @@ -434,6 +437,7 @@ describeForPlatforms('BridgeView', () => {
expect(within(sourceArea).getByText('BNB')).toBeOnTheScreen();
});

// eslint-disable-next-line @metamask/design-tokens/color-no-hex -- "#24802" style references are GitHub issue IDs (e.g. "#2342"), not color literals
it('renders USDC to BNB swap setup without crash and hides confirm when no quote (#24802)', () => {
const bnbChainIdHex = '0x38';

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import React from 'react';
import { render, fireEvent } from '@testing-library/react-native';
import AccessRestrictedModal from './AccessRestrictedModal';
import { AccessRestrictedModalSelectorsIDs } from './AccessRestrictedModal.testIds';

jest.mock(
'../../../../component-library/components/BottomSheets/BottomSheet',
() => {
// eslint-disable-next-line @typescript-eslint/no-require-imports
const { View } = require('react-native');
return {
__esModule: true,
default: jest.fn(
({
children,
testID,
}: {
children: React.ReactNode;
testID?: string;
}) => <View testID={testID}>{children}</View>,
),
};
},
);

describe('AccessRestrictedModal', () => {
const defaultProps = {
isVisible: true,
onClose: jest.fn(),
onContactSupport: jest.fn(),
};

beforeEach(() => {
jest.clearAllMocks();
});

it('renders nothing when isVisible is false', () => {
const { queryByTestId } = render(
<AccessRestrictedModal {...defaultProps} isVisible={false} />,
);

expect(
queryByTestId(AccessRestrictedModalSelectorsIDs.BOTTOM_SHEET),
).toBeNull();
});

it('renders the modal with title and description when visible', () => {
const { getByTestId } = render(<AccessRestrictedModal {...defaultProps} />);

expect(
getByTestId(AccessRestrictedModalSelectorsIDs.TITLE),
).toBeOnTheScreen();
expect(
getByTestId(AccessRestrictedModalSelectorsIDs.DESCRIPTION),
).toBeOnTheScreen();
expect(
getByTestId(AccessRestrictedModalSelectorsIDs.CONTACT_SUPPORT_BUTTON),
).toBeOnTheScreen();
});

it('calls onContactSupport when pressing the contact support button', () => {
const { getByTestId } = render(<AccessRestrictedModal {...defaultProps} />);

fireEvent.press(
getByTestId(AccessRestrictedModalSelectorsIDs.CONTACT_SUPPORT_BUTTON),
);

expect(defaultProps.onContactSupport).toHaveBeenCalledTimes(1);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export const AccessRestrictedModalSelectorsIDs = {
BOTTOM_SHEET: 'access-restricted-modal',
TITLE: 'access-restricted-modal-title',
DESCRIPTION: 'access-restricted-modal-description',
CONTACT_SUPPORT_BUTTON: 'access-restricted-modal-contact-support',
} as const;
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import React from 'react';
import {
Box,
Text,
TextVariant,
TextColor,
FontWeight,
ButtonBase,
ButtonBaseSize,
BottomSheetHeader,
} from '@metamask/design-system-react-native';
import BottomSheet from '../../../../component-library/components/BottomSheets/BottomSheet';
import { strings } from '../../../../../locales/i18n';
import { AccessRestrictedModalProps } from './AccessRestrictedModal.types';
import { AccessRestrictedModalSelectorsIDs } from './AccessRestrictedModal.testIds';

const AccessRestrictedModal: React.FC<AccessRestrictedModalProps> = ({
isVisible,
onClose,
onContactSupport,
}) => {
if (!isVisible) return null;

return (
<BottomSheet
shouldNavigateBack={false}
onClose={onClose}
testID={AccessRestrictedModalSelectorsIDs.BOTTOM_SHEET}
>
<BottomSheetHeader
onClose={onClose}
titleTestID={AccessRestrictedModalSelectorsIDs.TITLE}
>
{strings('access_restricted.title')}
</BottomSheetHeader>

<Box twClassName="px-4 pb-6">
<Text
variant={TextVariant.BodyMd}
color={TextColor.TextAlternative}
testID={AccessRestrictedModalSelectorsIDs.DESCRIPTION}
>
{strings('access_restricted.description_line1')}
{'\n\n'}
{strings('access_restricted.description_line2')}
</Text>

<ButtonBase
onPress={onContactSupport}
testID={AccessRestrictedModalSelectorsIDs.CONTACT_SUPPORT_BUTTON}
size={ButtonBaseSize.Lg}
twClassName="w-full rounded-xl bg-muted mt-6"
>
<Text variant={TextVariant.BodyMd} fontWeight={FontWeight.Medium}>
{strings('access_restricted.contact_support')}
</Text>
</ButtonBase>
</Box>
</BottomSheet>
);
};

export default AccessRestrictedModal;
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export interface AccessRestrictedModalProps {
isVisible: boolean;
onClose: () => void;
onContactSupport: () => void;
}
3 changes: 3 additions & 0 deletions app/components/UI/Compliance/AccessRestrictedModal/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { default } from './AccessRestrictedModal';
export type { AccessRestrictedModalProps } from './AccessRestrictedModal.types';
export { AccessRestrictedModalSelectorsIDs } from './AccessRestrictedModal.testIds';
Loading
Loading