Skip to content

Commit be78e5d

Browse files
authored
Merge branch 'main' into fix/perps/flip-position-tat-2123
2 parents 393b57a + 6684df3 commit be78e5d

66 files changed

Lines changed: 1532 additions & 325 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/actions/smart-e2e-selection/action.yml

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ inputs:
2727
required: false
2828
default: 'false'
2929
base-ref:
30-
description: 'PR base branch ref passed to the AI analysis script for diff comparison'
30+
description: 'PR base branch ref passed to the AI analysis script for diff comparison. When release/*, AI selection is skipped and the full E2E suite is selected.'
3131
required: false
3232
default: ''
3333
outputs:
@@ -57,21 +57,32 @@ runs:
5757
echo "⏭️ SKIP=true due to 'skip-smart-e2e-selection' label on PR"
5858
fi
5959
60+
- name: Check release target branch (full E2E, no AI selection)
61+
id: check-release-target
62+
shell: bash
63+
run: |
64+
echo "SKIP=false" >> "$GITHUB_OUTPUT"
65+
BASE='${{ inputs.base-ref }}'
66+
if [[ -n "$BASE" && "$BASE" == release/* ]]; then
67+
echo "SKIP=true" >> "$GITHUB_OUTPUT"
68+
echo "⏭️ Base branch is release/* — skipping AI E2E selection; full E2E suite will run"
69+
fi
70+
6071
- name: Checkout for PR analysis
61-
if: steps.check-skip-label.outputs.SKIP != 'true'
72+
if: steps.check-skip-label.outputs.SKIP != 'true' && steps.check-release-target.outputs.SKIP != 'true'
6273
uses: actions/checkout@v6
6374
with:
6475
fetch-depth: 1 # Shallow clone for speed; unshallowed below for diff comparison
6576

6677
- name: Disable sparse checkout and restore all files
67-
if: steps.check-skip-label.outputs.SKIP != 'true'
78+
if: steps.check-skip-label.outputs.SKIP != 'true' && steps.check-release-target.outputs.SKIP != 'true'
6879
shell: bash
6980
run: |
7081
git sparse-checkout disable
7182
git checkout HEAD -- .
7283
7384
- name: Fetch base branch for comparison
74-
if: steps.check-skip-label.outputs.SKIP != 'true'
85+
if: steps.check-skip-label.outputs.SKIP != 'true' && steps.check-release-target.outputs.SKIP != 'true'
7586
shell: bash
7687
run: |
7788
# Unshallow the repository first (if it's shallow)
@@ -80,13 +91,13 @@ runs:
8091
git fetch origin "${{ inputs.base-ref || 'main' }}" 2>/dev/null || true
8192
8293
- name: Setup Node.js
83-
if: steps.check-skip-label.outputs.SKIP != 'true'
94+
if: steps.check-skip-label.outputs.SKIP != 'true' && steps.check-release-target.outputs.SKIP != 'true'
8495
uses: actions/setup-node@v6
8596
with:
8697
node-version-file: '.nvmrc'
8798

8899
- name: Install minimal dependencies for AI analysis
89-
if: steps.check-skip-label.outputs.SKIP != 'true'
100+
if: steps.check-skip-label.outputs.SKIP != 'true' && steps.check-release-target.outputs.SKIP != 'true'
90101
shell: bash
91102
run: |
92103
echo "📦 Installing only required packages for AI analysis..."
@@ -98,7 +109,7 @@ runs:
98109
echo "✅ AI analysis dependencies installed in /tmp/ai-deps"
99110
100111
- name: Copy AI dependencies to workspace
101-
if: steps.check-skip-label.outputs.SKIP != 'true'
112+
if: steps.check-skip-label.outputs.SKIP != 'true' && steps.check-release-target.outputs.SKIP != 'true'
102113
shell: bash
103114
run: |
104115
echo "📋 Copying AI dependencies to workspace..."
@@ -130,6 +141,11 @@ runs:
130141
echo "SKIPPED=true" >> "$GITHUB_OUTPUT"
131142
echo "SKIP_REASON=skip-smart-e2e-selection label found" >> "$GITHUB_OUTPUT"
132143
echo "ai_confidence=100" >> "$GITHUB_OUTPUT"
144+
elif [[ "${{ steps.check-release-target.outputs.SKIP }}" == "true" ]]; then
145+
echo "⏭️ Skipping AI analysis - PR targets a release branch (release/*)"
146+
echo "SKIPPED=true" >> "$GITHUB_OUTPUT"
147+
echo "SKIP_REASON=PR targets a release branch (release/*)" >> "$GITHUB_OUTPUT"
148+
echo "ai_confidence=100" >> "$GITHUB_OUTPUT"
133149
else
134150
echo "✅ Running AI analysis for PR #$PR_NUMBER"
135151
# The script will generate the GH output variables - don't fail if script errors

.js.env.example

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,9 +100,6 @@ export MM_SAMPLE_FEATURE_COUNTER_ENABLED="true"
100100
# The endpoint used to submit errors and tracing data to Sentry for dev environment.
101101
# export MM_SENTRY_DSN_DEV=
102102

103-
# Permissions Settings feature flag specific to UI changes
104-
export MM_PERMISSIONS_SETTINGS_V1_ENABLED=""
105-
106103
# Earn Variables
107104
## Stablecoin Lending
108105
export MM_STABLECOIN_LENDING_UI_ENABLED="true"

CHANGELOG.md

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

88
## [Unreleased]
99

10+
## [7.75.0]
11+
12+
### Added
13+
14+
- Added support for ENS v2 (#29258)
15+
- Added a Rive-based animated fox splash screen in place of the static loading spinner for a smoother app launch experience (#29003)
16+
- Hid multichain Activity entries involving tokens flagged as malicious by security scanning, consistent with how malicious tokens are handled on the assets overview (#29239)
17+
- Added Suspicious and Malicious security badges to tokens in the Swaps and Bridge asset pickers (#29070)
18+
- Added security badges to the trending tokens list (#29112)
19+
- Added live mUSD and Veda vault USD balance display on the Money account home page (#28889)
20+
- Added a new hardware connection page (#28019)
21+
- Added an A/B-tested homepage layout that separates trending sections and tracks swap analytics attribution for actions launched from homepage trending sections (#28085)
22+
- Added follow/unfollow functionality for traders (#28843)
23+
- Added hold time to the trader profile view (#28873)
24+
- Extended notification account toggles to all wallet keyrings (#27254)
25+
- Enabled MetaMask Card Cashback for US users (#29138)
26+
- Added an Ondo campaign rewards stats page (#28734)
27+
- Added a migration that resets the native balance to 0 on Tempo chains (#28869)
28+
29+
### Changed
30+
31+
- Hid token warnings in the Swaps asset picker temporarily (#29278)
32+
- Improved MetaMask Card login fields to use the design-system `TextField` with correct username/password autocomplete hints for accessibility and password managers (#29215)
33+
- Moved the Cashback action on Card Home directly under Change asset; the Change asset row no longer shows a trailing chevron in the bottom sheet (#29219)
34+
- Updated the Tempo native token logo (#29105)
35+
- Only show the Transak verify-identity policy screen once on Unified Buy; later visits skip to email entry when not logged in to Transak (#28952)
36+
- Updated token avatars to use only asset image URLs, without a curated token list fallback (#28552)
37+
- Updated the perps section on the Explore screen to display horizontal tile cards with sparkline charts (#28512)
38+
- Stopped using the token list cache for asset overview token details; decimals, aggregators, and market data now come from the asset and token rates (#28533)
39+
- Improved scroll-back user experience in the advanced chart integration on the token details page (#28451)
40+
- Updated app typography and font assets to align with the latest MetaMask design system semibold bold-weight migration (#28363)
41+
- Updated perps chart volume bars to use 30% opacity, matching the transparency style of the spot token details chart (#29132)
42+
- Updated Settings copy so the IPFS gateway is described under Security and privacy instead of Advanced (#29045)
43+
44+
### Fixed
45+
46+
- Fixed featured carousel showing secondary markets instead of the match winner for sports events (#29001)
47+
- Fixed featured carousel showing sports games that had already ended (#29000)
48+
- Fixed Tempo transactions to fall back to a classic transaction when contract deployment is required (#29078)
49+
- Fixed the Confirmation button state by adding a gasless-loading guard, consistent with Extension (#29188)
50+
- Fixed rapid market switching triggering Hyperliquid rate-limit errors (#29056)
51+
- Fixed max mUSD conversion displaying an inflated receive amount (#29175)
52+
- Fixed the mUSD logo not displayed when opening mUSD swap from Rewards (#29194)
53+
- Fixed MetaMask Card home showing zero token balances when the user is not authenticated with the card provider (#29146)
54+
- Fixed a Perps position size formatting bug that stripped valid trailing zeros on whole-unit assets (szDecimals=0), e.g. displaying "1" instead of "100" (#29016)
55+
- Fixed SRP reveal QR code styling to display consistently across light and dark themes (#28969)
56+
- Fixed a visual alignment issue in the Top Traders list where double-digit ranks appeared to be missing their trailing dot (#29099)
57+
- Fixed an iOS-only bug where the header and web content overlapped in the in-app web view screen (#29020)
58+
- Fixed quote polling so the timer stops and interactions are blocked while a quote is being processed (#28862)
59+
- Fixed `wallet_watchAsset` failing with `"Expected a value of type JSON, but received: [object Object]"` when a dapp requested the wallet to watch a token (#29030)
60+
- Fixed bridge quotes showing a misleading 0% price impact when quote price data was unavailable (#28931)
61+
- Fixed incorrect TRX "locked for" value (#29038)
62+
- Fixed a layout shift that happened when following/unfollowing traders with high PnL (#29021)
63+
- Fixed NFT ownership status not refreshing across all enabled networks when pulling to refresh (#28655)
64+
- Fixed noisy Sentry error reports from expected candle fetch cancellations during navigation (#28953)
65+
- Fixed transient UI flashes on the Buy screen (stale "Powered by" text, brief quote-fetch error banner, and a disabled-looking Continue button) when the selected token isn't supported by the current provider and the app is silently switching to a supporting one (#29178)
66+
- Fixed missing error sheet on auth server and seedless login errors (#29227)
67+
- Fixed missing token icons in the predictions pay-with picker for zero-balance tokens (#27702)
68+
69+
## [7.74.3]
70+
71+
### Fixed
72+
73+
- Fix polymarket adapter contract addresses for Android
74+
75+
## [7.74.2]
76+
77+
### Fixed
78+
79+
- Updated Polymarket adapter contracts so Polymarket prediction transactions continue working after the relayer migration. (#29573)
80+
81+
## [7.74.1]
82+
83+
### Fixed
84+
85+
- Fixed the environmental issue that push notifications not received on Android
86+
1087
## [7.74.0]
1188

1289
### Added
@@ -11288,7 +11365,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1128811365
- [#957](https://github.com/MetaMask/metamask-mobile/pull/957): fix timeouts (#957)
1128911366
- [#954](https://github.com/MetaMask/metamask-mobile/pull/954): Bugfix: onboarding navigation (#954)
1129011367

11291-
[Unreleased]: https://github.com/MetaMask/metamask-mobile/compare/v7.74.0...HEAD
11368+
[Unreleased]: https://github.com/MetaMask/metamask-mobile/compare/v7.75.0...HEAD
11369+
[7.75.0]: https://github.com/MetaMask/metamask-mobile/compare/v7.74.3...v7.75.0
11370+
[7.74.3]: https://github.com/MetaMask/metamask-mobile/compare/v7.74.2...v7.74.3
11371+
[7.74.2]: https://github.com/MetaMask/metamask-mobile/compare/v7.74.1...v7.74.2
11372+
[7.74.1]: https://github.com/MetaMask/metamask-mobile/compare/v7.74.0...v7.74.1
1129211373
[7.74.0]: https://github.com/MetaMask/metamask-mobile/compare/v7.73.2...v7.74.0
1129311374
[7.73.2]: https://github.com/MetaMask/metamask-mobile/compare/v7.73.1...v7.73.2
1129411375
[7.73.1]: https://github.com/MetaMask/metamask-mobile/compare/v7.73.0...v7.73.1

android/app/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ android {
187187
applicationId "io.metamask"
188188
minSdkVersion rootProject.ext.minSdkVersion
189189
targetSdkVersion rootProject.ext.targetSdkVersion
190-
versionName "7.76.0"
190+
versionName "7.77.0"
191191
versionCode 4532
192192
testBuildType System.getProperty('testBuildType', 'debug')
193193
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

app/components/UI/Card/Views/CardHome/CardHome.test.tsx

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,7 @@ jest.mock('../../../../../../locales/i18n', () => ({
517517
'card.card_home.spending_with': 'Spending with',
518518
'card.card_home.add_funds': 'Add funds',
519519
'card.card_home.limited_spending_warning': 'Limited spending allowance',
520+
'card.card_home.spending_limit_available': 'available',
520521
'card.card': 'Card',
521522
'card.card_home.error_title': 'Unable to load card',
522523
'card.card_home.error_description': 'Please try again later',
@@ -2678,8 +2679,7 @@ describe('CardHome Component', () => {
26782679
expect(screen.queryByText('Spending Limit')).not.toBeOnTheScreen();
26792680
});
26802681

2681-
it('does not render for Solana chain assets', () => {
2682-
// Given: authenticated with limited allowance on Solana chain
2682+
it('renders compact "available" indicator for Solana chain assets without originalSpendingCap', () => {
26832683
setupMockSelectors({ isAuthenticated: true });
26842684
const limitedSolanaToken = {
26852685
...mockPriorityToken,
@@ -2698,8 +2698,10 @@ describe('CardHome Component', () => {
26982698
// When: component renders
26992699
render();
27002700

2701-
// Then: should not display spending limit progress bar (Solana not supported)
2702-
expect(screen.queryByText('Spending Limit')).not.toBeOnTheScreen();
2701+
// Then: should display compact spending-limit indicator
2702+
expect(screen.getByText('Spending Limit')).toBeOnTheScreen();
2703+
expect(screen.getByText('500 USDC available')).toBeOnTheScreen();
2704+
expect(screen.queryByText('0/500 USDC')).not.toBeOnTheScreen();
27032705
});
27042706

27052707
it('does not render for unsupported tokens (AUSDC, AMUSD)', () => {
@@ -2782,7 +2784,6 @@ describe('CardHome Component', () => {
27822784
});
27832785

27842786
it('handles undefined allowance values', () => {
2785-
// Given: authenticated with undefined allowance values
27862787
setupMockSelectors({ isAuthenticated: true });
27872788
const limitedAllowanceToken = {
27882789
...mockPriorityToken,
@@ -2801,8 +2802,9 @@ describe('CardHome Component', () => {
28012802
// When: component renders
28022803
render();
28032804

2804-
// Then: should display zero values as fallback
2805-
expect(screen.getByText('0/0 USDC')).toBeOnTheScreen();
2805+
// Then: should display compact zero-fallback indicator
2806+
expect(screen.getByText('0 USDC available')).toBeOnTheScreen();
2807+
expect(screen.queryByText('0/0 USDC')).not.toBeOnTheScreen();
28062808
});
28072809
});
28082810

app/components/UI/Card/Views/CardHome/CardHome.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ import { CardScreenshotDeterrent } from '../../components/CardScreenshotDeterren
5555
import AnimatedSpinner from '../../../AnimatedSpinner';
5656
import Routes from '../../../../../constants/navigation/Routes';
5757
import { TOKEN_RATE_UNDEFINED } from '../../../Tokens/constants';
58-
import { isEvmChain, isSpendingLimitSupportedToken } from '../../constants';
58+
import { isSpendingLimitSupportedToken } from '../../constants';
5959
import { CardHomeSelectors } from './CardHome.testIds';
6060
import CardAlertSection from './components/CardAlertSection';
6161
import CardActionsButtons from './components/CardActionsButtons';
@@ -213,7 +213,6 @@ const CardHome = () => {
213213
const showSpendingLimitProgress =
214214
isAuthenticated &&
215215
data?.primaryFundingAsset?.status === FundingAssetStatus.Limited &&
216-
isEvmChain(data?.primaryFundingAsset?.chainId) &&
217216
isSpendingLimitSupportedToken(data?.primaryFundingAsset?.symbol) &&
218217
!hasSetupActions;
219218

@@ -335,6 +334,9 @@ const CardHome = () => {
335334
remainingAllowance={data.primaryFundingAsset.spendingCap ?? '0'}
336335
symbol={data.primaryFundingAsset.symbol ?? ''}
337336
privacyMode={privacyMode}
337+
hasOriginalAllowance={
338+
!!data.primaryFundingAsset.originalSpendingCap
339+
}
338340
/>
339341
)}
340342

app/components/UI/Card/components/SpendingLimitProgressBar/SpendingLimitProgressBar.test.tsx

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,4 +264,90 @@ describe('SpendingLimitProgressBar', () => {
264264
expect(getByText(`50/200 ${USDC}`)).toBeOnTheScreen();
265265
expect(queryByText('••••••')).not.toBeOnTheScreen();
266266
});
267+
268+
describe('compact mode (hasOriginalAllowance=false)', () => {
269+
it('renders remaining-only label without progress bar', () => {
270+
const { getByText, queryByText } = renderWithProvider(() => (
271+
<SpendingLimitProgressBar
272+
isLoading={false}
273+
decimals={6}
274+
totalAllowance="150"
275+
remainingAllowance="150"
276+
symbol={USDC}
277+
hasOriginalAllowance={false}
278+
/>
279+
));
280+
281+
expect(getByText('Spending Limit')).toBeOnTheScreen();
282+
expect(getByText(`150 ${USDC} available`)).toBeOnTheScreen();
283+
expect(queryByText(`0/150 ${USDC}`)).not.toBeOnTheScreen();
284+
});
285+
286+
it('shows the actual remaining amount, ignoring totalAllowance', () => {
287+
const { getByText, queryByText } = renderWithProvider(() => (
288+
<SpendingLimitProgressBar
289+
isLoading={false}
290+
decimals={6}
291+
totalAllowance="9999"
292+
remainingAllowance="42.5"
293+
symbol="mUSD"
294+
hasOriginalAllowance={false}
295+
/>
296+
));
297+
298+
expect(getByText('Spending Limit')).toBeOnTheScreen();
299+
expect(getByText('42.5 mUSD available')).toBeOnTheScreen();
300+
expect(queryByText('9956.5/9999 mUSD')).not.toBeOnTheScreen();
301+
});
302+
303+
it('hides remaining amount when privacy mode is enabled', () => {
304+
const { getByText, queryByText } = renderWithProvider(() => (
305+
<SpendingLimitProgressBar
306+
isLoading={false}
307+
decimals={6}
308+
totalAllowance="100"
309+
remainingAllowance="100"
310+
symbol={USDC}
311+
hasOriginalAllowance={false}
312+
privacyMode
313+
/>
314+
));
315+
316+
expect(getByText('Spending Limit')).toBeOnTheScreen();
317+
expect(getByText('••••••')).toBeOnTheScreen();
318+
expect(queryByText(`100 ${USDC} available`)).not.toBeOnTheScreen();
319+
});
320+
321+
it('falls back to "0 SYM available" when remainingAllowance is undefined', () => {
322+
const { getByText } = renderWithProvider(() => (
323+
<SpendingLimitProgressBar
324+
isLoading={false}
325+
decimals={6}
326+
totalAllowance={undefined as unknown as string}
327+
remainingAllowance={undefined as unknown as string}
328+
symbol={USDC}
329+
hasOriginalAllowance={false}
330+
/>
331+
));
332+
333+
expect(getByText(`0 ${USDC} available`)).toBeOnTheScreen();
334+
});
335+
336+
it('renders skeleton while loading regardless of hasOriginalAllowance', () => {
337+
const { getByTestId } = renderWithProvider(() => (
338+
<SpendingLimitProgressBar
339+
isLoading
340+
decimals={6}
341+
totalAllowance="100"
342+
remainingAllowance="100"
343+
symbol={USDC}
344+
hasOriginalAllowance={false}
345+
/>
346+
));
347+
348+
expect(
349+
getByTestId('spending-limit-progress-bar-skeleton'),
350+
).toBeOnTheScreen();
351+
});
352+
});
267353
});

0 commit comments

Comments
 (0)