Skip to content

Commit 49259e8

Browse files
authored
release: 7.72.1 (#28679)
# 🚀 v7.72.1 Testing & Release Quality Process Hi Team, As part of our new **MetaMask Release Quality Process**, here’s a quick overview of the key processes, testing strategies, and milestones to ensure a smooth and high-quality deployment. --- ## 📋 Key Processes ### Testing Strategy - **Developer Teams:** Conduct regression and exploratory testing for your functional areas, including automated and manual tests for critical workflows. - **QA Team:** Focus on exploratory testing across the wallet, prioritize high-impact areas, and triage any Sentry errors found during testing. - **Customer Success Team:** Validate new functionalities and provide feedback to support release monitoring. ### GitHub Signoff - Each team must **sign off on the Release Candidate (RC)** via GitHub by the end of the validation timeline (**Tuesday EOD PT**). - Ensure all tests outlined in the Testing Plan are executed, and any identified issues are addressed. ### Issue Resolution - **Resolve all Release Blockers** (Sev0 and Sev1) by **Tuesday EOD PT**. - For unresolved blockers, PRs may be reverted, or feature flags disabled to maintain release quality and timelines. ### Cherry-Picking Criteria - Only **critical fixes** meeting outlined criteria will be cherry-picked. - Developers must ensure these fixes are thoroughly reviewed, tested, and merged by **Tuesday EOD PT**. --- ## 🗓️ Timeline and Milestones 1. **Today (Friday):** Begin Release Candidate validation. 2. **Tuesday EOD PT:** Finalize RC with all fixes and cherry-picks. 3. **Wednesday:** Buffer day for final checks. 4. **Thursday:** Submit release to app stores and begin rollout to 1% of users. 5. **Monday:** Scale deployment to 10%. 6. **Tuesday:** Full rollout to 100%. --- ## ✅ Signoff Checklist Each team is responsible for signing off via GitHub. Use the checkbox below to track signoff completion: # Team sign-off checklist - [ ] Mobile Platform This process is a major step forward in ensuring release stability and quality. Let’s stay aligned and make this release a success! 🚀 Feel free to reach out if you have questions or need clarification. Many thanks in advance # Reference - Testing plan sheet - https://docs.google.com/spreadsheets/d/1tsoodlAlyvEUpkkcNcbZ4PM9HuC9cEM80RZeoVv5OCQ/edit?gid=404070372#gid=404070372
2 parents 470307e + 215db7b commit 49259e8

11 files changed

Lines changed: 213 additions & 37 deletions

File tree

.github/scripts/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
"dependencies": {
1717
"@actions/core": "^1.10.1",
1818
"@actions/github": "^6.0.0",
19-
"axios": "^1.13.5",
19+
"axios": "^1.15.0",
2020
"simple-git": "^3.25.0"
2121
},
2222
"devDependencies": {

.github/scripts/yarn.lock

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ __metadata:
151151
"@lavamoat/allow-scripts": "npm:^3.2.0"
152152
"@lavamoat/preinstall-always-fail": "npm:^2.1.0"
153153
"@types/node": "npm:^20.16.2"
154-
axios: "npm:^1.13.5"
154+
axios: "npm:^1.15.0"
155155
simple-git: "npm:^3.25.0"
156156
ts-node: "npm:^10.5.0"
157157
typescript: "npm:~5.4.5"
@@ -508,14 +508,14 @@ __metadata:
508508
languageName: node
509509
linkType: hard
510510

511-
"axios@npm:^1.13.5":
512-
version: 1.13.5
513-
resolution: "axios@npm:1.13.5"
511+
"axios@npm:^1.15.0":
512+
version: 1.15.0
513+
resolution: "axios@npm:1.15.0"
514514
dependencies:
515515
follow-redirects: "npm:^1.15.11"
516516
form-data: "npm:^4.0.5"
517-
proxy-from-env: "npm:^1.1.0"
518-
checksum: 10/db726d09902565ef9a0632893530028310e2ec2b95b727114eca1b101450b00014133dfc3871cffc87983fb922bca7e4874d7e2826d1550a377a157cdf3f05b6
517+
proxy-from-env: "npm:^2.1.0"
518+
checksum: 10/d39a2c0ebc7ff4739401b282e726cc2673377949d6c46d60eb619458f8d7a2f7eadbcada7097f4dbc7d5c59abb4d3bf6fac33d474412bc3415d3f5aa7ed45530
519519
languageName: node
520520
linkType: hard
521521

@@ -1422,10 +1422,10 @@ __metadata:
14221422
languageName: node
14231423
linkType: hard
14241424

1425-
"proxy-from-env@npm:^1.1.0":
1426-
version: 1.1.0
1427-
resolution: "proxy-from-env@npm:1.1.0"
1428-
checksum: 10/f0bb4a87cfd18f77bc2fba23ae49c3b378fb35143af16cc478171c623eebe181678f09439707ad80081d340d1593cd54a33a0113f3ccb3f4bc9451488780ee23
1425+
"proxy-from-env@npm:^2.1.0":
1426+
version: 2.1.0
1427+
resolution: "proxy-from-env@npm:2.1.0"
1428+
checksum: 10/fbbaf4dab2a6231dc9e394903a5f66f20475e36b734335790b46feb9da07c37d6b32e2c02e3e2ea4d4b23774c53d8562e5b7cc73282cb43f4a597b7eacaee2ee
14291429
languageName: node
14301430
linkType: hard
14311431

.yarnrc.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,4 @@ npmPreapprovedPackages:
3030
- '@metamask-previews/*'
3131
- '@lavamoat/*'
3232
- '@consensys/*'
33+
- 'axios' # TODO: Remove after 2025-04-12 once axios 1.15.0 ages past the 3-day npmMinimalAgeGate

CHANGELOG.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [7.72.1]
11+
12+
### Fixed
13+
14+
- Fixed a bug where users in restricted regions could open perps positions from the Market Insights page (#28688)
15+
1016
## [7.72.0]
1117

1218
### Added
@@ -11156,7 +11162,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1115611162
- [#957](https://github.com/MetaMask/metamask-mobile/pull/957): fix timeouts (#957)
1115711163
- [#954](https://github.com/MetaMask/metamask-mobile/pull/954): Bugfix: onboarding navigation (#954)
1115811164

11159-
[Unreleased]: https://github.com/MetaMask/metamask-mobile/compare/v7.72.0...HEAD
11165+
[Unreleased]: https://github.com/MetaMask/metamask-mobile/compare/v7.72.1...HEAD
11166+
[7.72.1]: https://github.com/MetaMask/metamask-mobile/compare/v7.72.0...v7.72.1
1116011167
[7.72.0]: https://github.com/MetaMask/metamask-mobile/compare/v7.71.1...v7.72.0
1116111168
[7.71.1]: https://github.com/MetaMask/metamask-mobile/compare/v7.71.0...v7.71.1
1116211169
[7.71.0]: https://github.com/MetaMask/metamask-mobile/compare/v7.70.1...v7.71.0

app/components/UI/MarketInsights/Views/MarketInsightsView/MarketInsightsView.test.tsx

Lines changed: 80 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ const mockCreateEventBuilder = jest.fn(
2626
const mockUseSwapBridgeNavigation = jest.fn((_options: unknown) => ({
2727
goToSwaps: mockGoToSwaps,
2828
}));
29+
const mockPerpsTrack = jest.fn();
30+
let mockIsEligible = true;
2931

3032
let mockRouteParams: {
3133
assetSymbol: string;
@@ -228,6 +230,27 @@ jest.mock('../../../../hooks/useAnalytics/useAnalytics', () => ({
228230
}),
229231
}));
230232

233+
jest.mock('../../../Perps/selectors/perpsController', () => ({
234+
selectPerpsEligibility: jest.fn(() => mockIsEligible),
235+
}));
236+
237+
jest.mock('../../../../../selectors/accountsController', () => ({
238+
...jest.requireActual('../../../../../selectors/accountsController'),
239+
selectSelectedInternalAccountAddress: jest.fn(() => '0xMockAddress'),
240+
}));
241+
242+
jest.mock('../../../Perps/components/PerpsBottomSheetTooltip', () => {
243+
const { View: MockView } = jest.requireActual('react-native');
244+
const Tooltip = (props: { testID?: string; onClose?: () => void }) => (
245+
<MockView testID={props.testID ?? 'geo-block-tooltip'} />
246+
);
247+
return { __esModule: true, default: Tooltip };
248+
});
249+
250+
jest.mock('../../../Perps/hooks/usePerpsEventTracking', () => ({
251+
usePerpsEventTracking: () => ({ track: mockPerpsTrack }),
252+
}));
253+
231254
jest.mock('@metamask/design-system-react-native', () => {
232255
const actual = jest.requireActual('@metamask/design-system-react-native');
233256
const { View } = jest.requireActual('react-native');
@@ -259,6 +282,7 @@ describe('MarketInsightsView', () => {
259282
jest.spyOn(Linking, 'openURL').mockResolvedValue(undefined);
260283
jest.clearAllMocks();
261284
resetFeedbackCache();
285+
mockIsEligible = true;
262286
mockRouteParams = {
263287
assetSymbol: 'ETH',
264288
assetIdentifier: 'eip155:1/erc20:0x123',
@@ -729,7 +753,7 @@ describe('MarketInsightsView', () => {
729753
expect(queryByTestId(MarketInsightsSelectorsIDs.BUY_BUTTON)).toBeNull();
730754
});
731755

732-
it('navigates to PerpsOrderRedirect with long direction when Long button is pressed', () => {
756+
it('navigates to PerpsOrderRedirect with long direction when Long button is pressed', async () => {
733757
mockRouteParams = {
734758
assetSymbol: 'ETH',
735759
assetIdentifier: 'ETH',
@@ -751,7 +775,9 @@ describe('MarketInsightsView', () => {
751775

752776
const { getByTestId } = renderWithProvider(<MarketInsightsView />);
753777

754-
fireEvent.press(getByTestId(MarketInsightsSelectorsIDs.LONG_BUTTON));
778+
await act(async () => {
779+
fireEvent.press(getByTestId(MarketInsightsSelectorsIDs.LONG_BUTTON));
780+
});
755781

756782
expect(mockNavigate).toHaveBeenCalledWith(
757783
Routes.PERPS.ROOT,
@@ -763,7 +789,7 @@ describe('MarketInsightsView', () => {
763789
expect(mockGoToSwaps).not.toHaveBeenCalled();
764790
});
765791

766-
it('navigates to PerpsOrderRedirect with short direction when Short button is pressed', () => {
792+
it('navigates to PerpsOrderRedirect with short direction when Short button is pressed', async () => {
767793
mockRouteParams = {
768794
assetSymbol: 'ETH',
769795
assetIdentifier: 'ETH',
@@ -785,7 +811,9 @@ describe('MarketInsightsView', () => {
785811

786812
const { getByTestId } = renderWithProvider(<MarketInsightsView />);
787813

788-
fireEvent.press(getByTestId(MarketInsightsSelectorsIDs.SHORT_BUTTON));
814+
await act(async () => {
815+
fireEvent.press(getByTestId(MarketInsightsSelectorsIDs.SHORT_BUTTON));
816+
});
789817

790818
expect(mockNavigate).toHaveBeenCalledWith(
791819
Routes.PERPS.ROOT,
@@ -797,6 +825,47 @@ describe('MarketInsightsView', () => {
797825
expect(mockGoToSwaps).not.toHaveBeenCalled();
798826
});
799827

828+
it('shows geo-block modal instead of navigating when user is not eligible', async () => {
829+
mockIsEligible = false;
830+
mockRouteParams = {
831+
assetSymbol: 'ETH',
832+
assetIdentifier: 'ETH',
833+
isPerps: true,
834+
};
835+
mockUseMarketInsights.mockReturnValue({
836+
report: {
837+
asset: 'eth',
838+
generatedAt: '2026-02-17T11:55:00.000Z',
839+
headline: 'ETH perps insight',
840+
summary: 'Open interest rises',
841+
trends: [],
842+
sources: [],
843+
},
844+
isLoading: false,
845+
error: null,
846+
timeAgo: '1m ago',
847+
});
848+
849+
const { getByTestId, queryByTestId } = renderWithProvider(
850+
<MarketInsightsView />,
851+
);
852+
853+
expect(
854+
queryByTestId('market-insights-geo-block-tooltip'),
855+
).not.toBeOnTheScreen();
856+
857+
await act(async () => {
858+
fireEvent.press(getByTestId(MarketInsightsSelectorsIDs.LONG_BUTTON));
859+
});
860+
861+
expect(getByTestId('market-insights-geo-block-tooltip')).toBeOnTheScreen();
862+
expect(mockNavigate).not.toHaveBeenCalledWith(
863+
Routes.PERPS.ROOT,
864+
expect.anything(),
865+
);
866+
expect(mockPerpsTrack).toHaveBeenCalled();
867+
});
868+
800869
it('navigates to swaps when swap button is pressed in token context', () => {
801870
const { getByTestId, queryByTestId } = renderWithProvider(
802871
<MarketInsightsView />,
@@ -814,7 +883,7 @@ describe('MarketInsightsView', () => {
814883
);
815884
});
816885

817-
it('sends perps_market analytics property (not caip19) in perps context', () => {
886+
it('sends perps_market analytics property (not caip19) in perps context', async () => {
818887
mockRouteParams = {
819888
assetSymbol: 'ETH',
820889
assetIdentifier: 'ETH',
@@ -864,7 +933,9 @@ describe('MarketInsightsView', () => {
864933
);
865934

866935
// Long button carries perps_market, digest_id, and interaction_type 'long'
867-
fireEvent.press(getByTestId(MarketInsightsSelectorsIDs.LONG_BUTTON));
936+
await act(async () => {
937+
fireEvent.press(getByTestId(MarketInsightsSelectorsIDs.LONG_BUTTON));
938+
});
868939
expect(mockTrackEvent).toHaveBeenCalledWith(
869940
expect.objectContaining({
870941
category: MetaMetricsEvents.MARKET_INSIGHTS_INTERACTION,
@@ -887,7 +958,9 @@ describe('MarketInsightsView', () => {
887958
);
888959

889960
// Short button carries perps_market, digest_id, and interaction_type 'short'
890-
fireEvent.press(getByTestId(MarketInsightsSelectorsIDs.SHORT_BUTTON));
961+
await act(async () => {
962+
fireEvent.press(getByTestId(MarketInsightsSelectorsIDs.SHORT_BUTTON));
963+
});
891964
expect(mockTrackEvent).toHaveBeenCalledWith(
892965
expect.objectContaining({
893966
category: MetaMetricsEvents.MARKET_INSIGHTS_INTERACTION,

app/components/UI/MarketInsights/Views/MarketInsightsView/MarketInsightsView.tsx

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import {
1313
Pressable,
1414
Animated,
1515
Image,
16+
Modal,
17+
View,
1618
useColorScheme,
1719
} from 'react-native';
1820
import Video from 'react-native-video';
@@ -83,6 +85,13 @@ import MarketInsightsFeedbackBottomSheet, {
8385
import { useRampNavigation } from '../../../Ramp/hooks/useRampNavigation';
8486
import parseRampIntent from '../../../Ramp/utils/parseRampIntent';
8587
import { getDecimalChainId } from '../../../../../util/networks';
88+
import { selectPerpsEligibility } from '../../../Perps/selectors/perpsController';
89+
import PerpsBottomSheetTooltip from '../../../Perps/components/PerpsBottomSheetTooltip';
90+
import {
91+
PERPS_EVENT_PROPERTY,
92+
PERPS_EVENT_VALUE,
93+
} from '@metamask/perps-controller';
94+
import { usePerpsEventTracking } from '../../../Perps/hooks/usePerpsEventTracking';
8695

8796
const feedbackByDigest = new Map<string, 'up' | 'down'>();
8897

@@ -212,6 +221,11 @@ const MarketInsightsView: React.FC = () => {
212221
[isDarkMode],
213222
);
214223

224+
const isEligible = useSelector(selectPerpsEligibility);
225+
const [isEligibilityModalVisible, setIsEligibilityModalVisible] =
226+
useState(false);
227+
const { track } = usePerpsEventTracking();
228+
215229
const { trackEvent, createEventBuilder } = useAnalytics();
216230
const { toastRef } = useContext(ToastContext);
217231
const theme = useAppThemeFromContext();
@@ -328,8 +342,23 @@ const MarketInsightsView: React.FC = () => {
328342
assetSymbolProperty,
329343
]);
330344

345+
const closeEligibilityModal = useCallback(() => {
346+
setIsEligibilityModalVisible(false);
347+
}, []);
348+
331349
const handlePerpsDirectionPress = useCallback(
332-
(direction: 'long' | 'short') => {
350+
async (direction: 'long' | 'short') => {
351+
if (!isEligible) {
352+
track(MetaMetricsEvents.PERPS_SCREEN_VIEWED, {
353+
[PERPS_EVENT_PROPERTY.SCREEN_TYPE]:
354+
PERPS_EVENT_VALUE.SCREEN_TYPE.GEO_BLOCK_NOTIF,
355+
[PERPS_EVENT_PROPERTY.SOURCE]:
356+
PERPS_EVENT_VALUE.SOURCE.MARKET_INSIGHTS,
357+
});
358+
setIsEligibilityModalVisible(true);
359+
return;
360+
}
361+
333362
const event = createEventBuilder(
334363
MetaMetricsEvents.MARKET_INSIGHTS_INTERACTION,
335364
)
@@ -347,6 +376,8 @@ const MarketInsightsView: React.FC = () => {
347376
});
348377
},
349378
[
379+
isEligible,
380+
track,
350381
navigation,
351382
trackEvent,
352383
createEventBuilder,
@@ -844,6 +875,20 @@ const MarketInsightsView: React.FC = () => {
844875
onSubmit={handleFeedbackSubmit}
845876
/>
846877
) : null}
878+
879+
{isEligibilityModalVisible && (
880+
// Android Compatibility: Wrap the <Modal> in a plain <View> component to prevent rendering issues and freezing.
881+
<View>
882+
<Modal visible transparent animationType="none" statusBarTranslucent>
883+
<PerpsBottomSheetTooltip
884+
isVisible
885+
onClose={closeEligibilityModal}
886+
contentKey="geo_block"
887+
testID="market-insights-geo-block-tooltip"
888+
/>
889+
</Modal>
890+
</View>
891+
)}
847892
</Box>
848893
);
849894
};

app/constants/ota.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import otaConfig from '../../ota.config.js';
66
* Reset to v0 when releasing a new native build
77
* We keep this OTA_VERSION here to because changes in ota.config.js will affect the fingerprint and break the workflow in Github Actions
88
*/
9-
export const OTA_VERSION: string = 'vX.XX.X';
9+
export const OTA_VERSION: string = 'v7.72.1';
1010
export const RUNTIME_VERSION = otaConfig.RUNTIME_VERSION;
1111
export const PROJECT_ID = otaConfig.PROJECT_ID;
1212
export const UPDATE_URL = otaConfig.UPDATE_URL;

app/controllers/perps/constants/eventNames.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,7 @@ export const PERPS_EVENT_VALUE = {
234234
ADD_FUNDS_ACTION: 'add_funds_action',
235235
CANCEL_ORDER: 'cancel_order',
236236
ASSET_DETAIL_SCREEN: 'asset_detail_screen',
237+
MARKET_INSIGHTS: 'market_insights',
237238
// TAT-2449: Geo-block sources for close/modify actions
238239
CLOSE_POSITION_ACTION: 'close_position_action',
239240
MODIFY_POSITION_ACTION: 'modify_position_action',

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@
160160
"@unrs/resolver-binding-wasm32-wasi": "npm:npm-empty-package@1.0.0",
161161
"d3-color": "3.1.0",
162162
"napi-postinstall": "npm:npm-empty-package@1.0.0",
163-
"axios": "^1.13.5",
163+
"axios": "^1.15.0",
164164
"lodash": "4.18.1",
165165
"redux-persist-filesystem-storage/react-native-blob-util": "^0.19.9",
166166
"@ethersproject/providers/ws": "^7.5.10",
@@ -365,7 +365,7 @@
365365
"@walletconnect/utils": "^2.23.0",
366366
"@xmldom/xmldom": "^0.8.12",
367367
"asyncstorage-down": "4.2.0",
368-
"axios": "^1.13.5",
368+
"axios": "^1.15.0",
369369
"bignumber.js": "^9.0.1",
370370
"bitcoin-address-validation": "2.2.3",
371371
"bnjs4": "npm:bn.js@^4.12.3",

0 commit comments

Comments
 (0)