From d680d4736d9defb84eab9bc9e40c9fa6a1a2fec4 Mon Sep 17 00:00:00 2001 From: Samir Mehta <12882259+samir-acle@users.noreply.github.com> Date: Tue, 12 May 2026 13:20:44 -0400 Subject: [PATCH 1/6] feat: add notification push registration option --- .../NotificationServicesController.test.ts | 61 +++++++++++++++++++ .../NotificationServicesController.ts | 33 +++++++--- 2 files changed, 86 insertions(+), 8 deletions(-) diff --git a/packages/notification-services-controller/src/NotificationServicesController/NotificationServicesController.test.ts b/packages/notification-services-controller/src/NotificationServicesController/NotificationServicesController.test.ts index 1ec067c7c1..f65a607fbd 100644 --- a/packages/notification-services-controller/src/NotificationServicesController/NotificationServicesController.test.ts +++ b/packages/notification-services-controller/src/NotificationServicesController/NotificationServicesController.test.ts @@ -579,6 +579,45 @@ describe('NotificationServicesController', () => { ]); }); + it('skips push registration when registerPushNotifications is false', async () => { + const { + messenger, + mockEnablePushNotifications, + mockGetConfig, + mockUpdateNotifications, + mockKeyringControllerGetState, + } = arrangeMocks({ + configurePrefs: (mock) => mock.mockResolvedValueOnce(null), + }); + + mockKeyringControllerGetState.mockReturnValue({ + isUnlocked: true, + keyrings: [ + { + accounts: [ADDRESS_1], + type: KeyringTypes.hd, + metadata: { id: 'srp-1', name: 'SRP 1' }, + }, + ], + }); + const mockTriggerQuery = mockGetOnChainNotificationsConfig(); + + const controller = new NotificationServicesController({ + messenger, + env: { featureAnnouncements: featureAnnouncementsEnv }, + }); + + await controller.createOnChainTriggers({ + registerPushNotifications: false, + }); + + expect(mockGetConfig).toHaveBeenCalled(); + expect(mockTriggerQuery.isDone()).toBe(true); + expect(mockUpdateNotifications).toHaveBeenCalled(); + expect(controller.state.isNotificationServicesEnabled).toBe(true); + expect(mockEnablePushNotifications).not.toHaveBeenCalled(); + }); + it('enables all wallet-activity accounts when Trigger API has no enabled accounts for first-time setup', async () => { const { messenger, @@ -1447,6 +1486,28 @@ describe('NotificationServicesController', () => { expect(mocks.mockUpdateNotifications).toHaveBeenCalled(); }); + it('forwards registerPushNotifications false when enabling MetaMask notifications', async () => { + const mocks = arrangeMocks({ + configurePrefs: (mock) => mock.mockResolvedValueOnce(null), + }); + const mockTriggerQuery = mockGetOnChainNotificationsConfig(); + + const controller = new NotificationServicesController({ + messenger: mocks.messenger, + env: { featureAnnouncements: featureAnnouncementsEnv }, + }); + + await controller.enableMetamaskNotifications({ + registerPushNotifications: false, + }); + + expect(mocks.mockGetConfig).toHaveBeenCalled(); + expect(mockTriggerQuery.isDone()).toBe(true); + expect(mocks.mockUpdateNotifications).toHaveBeenCalled(); + expect(controller.state.isNotificationServicesEnabled).toBe(true); + expect(mocks.mockEnablePushNotifications).not.toHaveBeenCalled(); + }); + it('should not create new notification subscriptions when enabling an account that already has notifications', async () => { const mocks = arrangeMocks({ // Mock fully-initialized existing notifications diff --git a/packages/notification-services-controller/src/NotificationServicesController/NotificationServicesController.ts b/packages/notification-services-controller/src/NotificationServicesController/NotificationServicesController.ts index 5ddc45c43e..75bb351c99 100644 --- a/packages/notification-services-controller/src/NotificationServicesController/NotificationServicesController.ts +++ b/packages/notification-services-controller/src/NotificationServicesController/NotificationServicesController.ts @@ -204,8 +204,22 @@ export type NotificationServicesControllerEnableNotificationsOptions = { * in-app notifications. */ productAnnouncementEnabled?: boolean; + /** + * Whether to attempt FCM/device push registration after notification + * preferences are initialized or refreshed. This does not request OS push + * permission. + * + * @default true + */ + registerPushNotifications?: boolean; }; +export type NotificationServicesControllerCreateOnChainTriggersOptions = + NotificationServicesControllerEnableNotificationsOptions; + +export type NotificationServicesControllerEnableMetamaskNotificationsOptions = + NotificationServicesControllerEnableNotificationsOptions; + const locallyPersistedNotificationTypes = new Set([ TRIGGER_TYPES.SNAP, ]); @@ -1016,11 +1030,12 @@ export class NotificationServicesController extends BaseController< * Used only during initialization to seed marketing push notifications. * @param opts.productAnnouncementEnabled - The user's product-announcement flag. * Used only during initialization to seed marketing in-app notifications. + * @param opts.registerPushNotifications - Whether to attempt FCM/device push registration. * @returns The updated or newly created user storage. * @throws {Error} Throws an error if unauthenticated or from other operations. */ public async createOnChainTriggers( - opts?: NotificationServicesControllerEnableNotificationsOptions, + opts: NotificationServicesControllerCreateOnChainTriggersOptions = {}, ): Promise { try { this.#setIsUpdatingMetamaskNotifications(true); @@ -1070,12 +1085,14 @@ export class NotificationServicesController extends BaseController< .filter((account) => account.enabled) .map((account) => account.address); - // 2. Lazily enable push notifications (FCM may take some time, so keeps UI unblocked) - this.#pushNotifications - .enablePushNotifications(accountsWithNotifications) - .catch(() => { - // Do Nothing - }); + if (opts.registerPushNotifications ?? true) { + // Attempt FCM/device registration only; clients must request OS permission separately. + this.#pushNotifications + .enablePushNotifications(accountsWithNotifications) + .catch(() => { + // Do Nothing + }); + } // Update the state of the controller this.update((state) => { @@ -1106,7 +1123,7 @@ export class NotificationServicesController extends BaseController< * @throws {Error} If there is an error during the process of enabling notifications. */ public async enableMetamaskNotifications( - opts?: NotificationServicesControllerEnableNotificationsOptions, + opts: NotificationServicesControllerEnableMetamaskNotificationsOptions = {}, ): Promise { try { this.#setIsUpdatingMetamaskNotifications(true); From 94df003edbf8347241d484d67696e80940e36f51 Mon Sep 17 00:00:00 2001 From: Samir Mehta <12882259+samir-acle@users.noreply.github.com> Date: Tue, 12 May 2026 14:30:27 -0400 Subject: [PATCH 2/6] feat: add push token metadata --- .../CHANGELOG.md | 1 + ...NotificationServicesPushController.test.ts | 58 ++++++++++ .../NotificationServicesPushController.ts | 42 ++++++-- .../__fixtures__/mockServices.ts | 9 +- .../services/services.test.ts | 100 ++++++++++++++++-- .../services/services.ts | 11 +- 6 files changed, 206 insertions(+), 15 deletions(-) diff --git a/packages/notification-services-controller/CHANGELOG.md b/packages/notification-services-controller/CHANGELOG.md index 147522beea..8c2bca5692 100644 --- a/packages/notification-services-controller/CHANGELOG.md +++ b/packages/notification-services-controller/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Add `productAnnouncementEnabled` to `NotificationServicesControllerEnableNotificationsOptions`. ([#8784](https://github.com/MetaMask/core/pull/8784)) +- Add optional mobile OS and app version metadata to push token registrations so clients can provide Firebase error attribution data. ### Changed diff --git a/packages/notification-services-controller/src/NotificationServicesPushController/NotificationServicesPushController.test.ts b/packages/notification-services-controller/src/NotificationServicesPushController/NotificationServicesPushController.test.ts index 082c3d69d3..bbe4a74827 100644 --- a/packages/notification-services-controller/src/NotificationServicesPushController/NotificationServicesPushController.test.ts +++ b/packages/notification-services-controller/src/NotificationServicesPushController/NotificationServicesPushController.test.ts @@ -124,6 +124,33 @@ describe('NotificationServicesPushController', () => { }); }); + it('should call activatePushNotifications with mobile OS and app version metadata', async () => { + const mocks = arrangeServicesMocks(); + const { controller, messenger } = arrangeMockMessenger({ + platform: 'mobile', + os: 'android', + appVersion: '7.42.0', + }); + mockAuthBearerTokenCall(messenger); + + await controller.enablePushNotifications(MOCK_ADDRESSES); + + expect(mocks.activatePushNotificationsMock).toHaveBeenCalledWith({ + bearerToken: MOCK_JWT, + addresses: MOCK_ADDRESSES, + env: expect.any(Object), + createRegToken: expect.any(Function), + regToken: { + platform: 'mobile', + locale: 'en', + oldToken: '', + os: 'android', + appVersion: '7.42.0', + }, + controllerEnv: 'prd', + }); + }); + it('should not activate push notifications triggers if there is no auth bearer token', async () => { const mocks = arrangeServicesMocks(); const { controller, messenger } = arrangeMockMessenger(); @@ -384,6 +411,37 @@ describe('NotificationServicesPushController', () => { expect(result).toBe(true); }); + it('should call updateLinksAPI with mobile OS and app version metadata', async () => { + const mocks = arrangeServicesMocks(); + const { controller, messenger } = arrangeMockMessenger({ + platform: 'mobile', + os: 'ios', + appVersion: '7.42.0', + state: { + fcmToken: MOCK_FCM_TOKEN, + isPushEnabled: true, + isUpdatingFCMToken: false, + }, + }); + mockAuthBearerTokenCall(messenger); + + const result = await controller.addPushNotificationLinks(MOCK_ADDRESSES); + + expect(mocks.updateLinksAPIMock).toHaveBeenCalledWith({ + bearerToken: MOCK_JWT, + addresses: MOCK_ADDRESSES, + regToken: { + token: MOCK_FCM_TOKEN, + platform: 'mobile', + locale: 'en', + os: 'ios', + appVersion: '7.42.0', + }, + env: 'prd', + }); + expect(result).toBe(true); + }); + it('should return false when push feature is disabled', async () => { const mocks = arrangeServicesMocks(); const { controller } = arrangeMockMessenger({ diff --git a/packages/notification-services-controller/src/NotificationServicesPushController/NotificationServicesPushController.ts b/packages/notification-services-controller/src/NotificationServicesPushController/NotificationServicesPushController.ts index 22abb736a9..44842609fa 100644 --- a/packages/notification-services-controller/src/NotificationServicesPushController/NotificationServicesPushController.ts +++ b/packages/notification-services-controller/src/NotificationServicesPushController/NotificationServicesPushController.ts @@ -11,6 +11,7 @@ import log from 'loglevel'; import type { Types } from '../NotificationServicesController'; import type { NotificationServicesPushControllerMethodActions } from './NotificationServicesPushController-method-action-types'; import type { ENV } from './services/endpoints'; +import type { RegToken } from './services/services'; import { activatePushNotifications, deleteLinksAPI, @@ -120,6 +121,11 @@ export type ControllerConfig = { */ getLocale?: () => string; + /** + * App or extension version to include when registering push tokens. + */ + appVersion?: string; + /** * Global switch to determine to use push notifications * Allows us to control Builds on extension (MV2 vs MV3) @@ -131,6 +137,11 @@ export type ControllerConfig = { */ platform: 'extension' | 'mobile'; + /** + * Mobile operating system to include when registering push tokens. + */ + os?: 'android' | 'ios'; + /** * Push Service Interface * - create reg token @@ -147,6 +158,11 @@ type StateCommand = | { type: 'disable' } | { type: 'update'; fcmToken: string }; +type RegistrationTokenMetadata = Pick< + RegToken, + 'appVersion' | 'locale' | 'os' | 'platform' +>; + /** * Manages push notifications for the application, including enabling, disabling, and updating triggers for push notifications. * This controller integrates with Firebase Cloud Messaging (FCM) to handle the registration and management of push notifications. @@ -239,6 +255,23 @@ export class NotificationServicesPushController extends BaseController< } } + #getRegistrationTokenMetadata(): RegistrationTokenMetadata { + const tokenMetadata: RegistrationTokenMetadata = { + platform: this.#config.platform, + locale: this.#config.getLocale?.() ?? 'en', + }; + + if (this.#config.os) { + tokenMetadata.os = this.#config.os; + } + + if (this.#config.appVersion) { + tokenMetadata.appVersion = this.#config.appVersion; + } + + return tokenMetadata; + } + public async subscribeToPushNotifications(): Promise { if (!this.#config.isPushFeatureEnabled) { return; @@ -293,8 +326,7 @@ export class NotificationServicesPushController extends BaseController< env: this.#env, createRegToken: this.#config.pushService.createRegToken, regToken: { - platform: this.#config.platform, - locale: this.#config.getLocale?.() ?? 'en', + ...this.#getRegistrationTokenMetadata(), oldToken: this.state.fcmToken, }, controllerEnv: this.#config.env ?? 'prd', @@ -383,8 +415,7 @@ export class NotificationServicesPushController extends BaseController< addresses, regToken: { token: this.state.fcmToken, - platform: this.#config.platform, - locale: this.#config.getLocale?.() ?? 'en', + ...this.#getRegistrationTokenMetadata(), }, env: this.#config.env ?? 'prd', }); @@ -453,8 +484,7 @@ export class NotificationServicesPushController extends BaseController< env: this.#env, createRegToken: this.#config.pushService.createRegToken, regToken: { - platform: this.#config.platform, - locale: this.#config.getLocale?.() ?? 'en', + ...this.#getRegistrationTokenMetadata(), oldToken: this.state.fcmToken, }, controllerEnv: this.#config.env ?? 'prd', diff --git a/packages/notification-services-controller/src/NotificationServicesPushController/__fixtures__/mockServices.ts b/packages/notification-services-controller/src/NotificationServicesPushController/__fixtures__/mockServices.ts index d4610a919e..3603734027 100644 --- a/packages/notification-services-controller/src/NotificationServicesPushController/__fixtures__/mockServices.ts +++ b/packages/notification-services-controller/src/NotificationServicesPushController/__fixtures__/mockServices.ts @@ -12,6 +12,7 @@ type MockReply = { export const mockEndpointUpdatePushNotificationLinks = ( mockReply?: MockReply, + requestBody?: nock.RequestBodyMatcher, ): nock.Scope => { const mockResponse = getMockUpdatePushNotificationLinksResponse(); const reply = mockReply ?? { @@ -19,9 +20,13 @@ export const mockEndpointUpdatePushNotificationLinks = ( body: mockResponse.response, }; - const mockEndpoint = nock(mockResponse.url).post('').reply(reply.status); + const endpoint = nock(mockResponse.url); + const mockEndpoint = + requestBody === undefined + ? endpoint.post('') + : endpoint.post('', requestBody); - return mockEndpoint; + return mockEndpoint.reply(reply.status); }; export const mockEndpointDeletePushNotificationLinks = ( diff --git a/packages/notification-services-controller/src/NotificationServicesPushController/services/services.test.ts b/packages/notification-services-controller/src/NotificationServicesPushController/services/services.test.ts index 0c302b25ab..f45494d312 100644 --- a/packages/notification-services-controller/src/NotificationServicesPushController/services/services.test.ts +++ b/packages/notification-services-controller/src/NotificationServicesPushController/services/services.test.ts @@ -11,6 +11,7 @@ import { deleteLinksAPI, updateLinksAPI, } from './services'; +import type { RegToken } from './services'; // Testing util to clean up verbose logs when testing errors const mockErrorLog = (): jest.SpyInstance => @@ -21,9 +22,35 @@ const MOCK_NEW_REG_TOKEN = 'NEW_REG_TOKEN'; const MOCK_ADDRESSES = ['0x123', '0x456', '0x789']; const MOCK_JWT = 'MOCK_JWT'; +type CreateRegTokenMock = jest.Mock< + Promise, + [PushNotificationEnv] +>; + +type ArrangeMocksParams = { + bearerToken: string; + addresses: string[]; + createRegToken: CreateRegTokenMock; + regToken: { + platform: Platform; + locale: string; + }; + env: PushNotificationEnv; +}; + +type ArrangeMocksResult = { + params: ArrangeMocksParams<'extension'>; + mobileParams: ArrangeMocksParams<'mobile'>; + apis: { + mockPut: ReturnType; + }; +}; + describe('NotificationServicesPushController Services', () => { describe('updateLinksAPI', () => { - const act = async (): Promise => + const act = async ( + regTokenOverrides?: Partial, + ): Promise => await updateLinksAPI({ bearerToken: MOCK_JWT, addresses: MOCK_ADDRESSES, @@ -31,6 +58,7 @@ describe('NotificationServicesPushController Services', () => { token: MOCK_NEW_REG_TOKEN, platform: 'extension', locale: 'en', + ...regTokenOverrides, }, }); @@ -55,16 +83,44 @@ describe('NotificationServicesPushController Services', () => { const result = await act(); expect(result).toBe(false); }); + + it('should include mobile metadata when provided', async () => { + const mockAPI = mockEndpointUpdatePushNotificationLinks(undefined, { + addresses: MOCK_ADDRESSES, + registration_token: { + token: MOCK_NEW_REG_TOKEN, + platform: 'mobile', + locale: 'en', + os: 'ios', + appVersion: '7.42.0', + }, + }); + + const result = await act({ + platform: 'mobile', + os: 'ios', + appVersion: '7.42.0', + }); + + expect(mockAPI.isDone()).toBe(true); + expect(result).toBe(true); + }); }); describe('activatePushNotifications', () => { - // Internal testing utility - return type is inferred - // eslint-disable-next-line @typescript-eslint/explicit-function-return-type - const arrangeMocks = (override?: { mockPut?: { status: number } }) => { + const arrangeMocks = (override?: { + mockPut?: { status: number }; + requestBody?: Parameters< + typeof mockEndpointUpdatePushNotificationLinks + >[1]; + }): ArrangeMocksResult => { + const createRegToken: CreateRegTokenMock = jest + .fn, [PushNotificationEnv]>() + .mockResolvedValue(MOCK_NEW_REG_TOKEN); const params = { bearerToken: MOCK_JWT, addresses: MOCK_ADDRESSES, - createRegToken: jest.fn().mockResolvedValue(MOCK_NEW_REG_TOKEN), + createRegToken, regToken: { platform: 'extension' as const, locale: 'en', @@ -84,7 +140,10 @@ describe('NotificationServicesPushController Services', () => { params, mobileParams, apis: { - mockPut: mockEndpointUpdatePushNotificationLinks(override?.mockPut), + mockPut: mockEndpointUpdatePushNotificationLinks( + override?.mockPut, + override?.requestBody, + ), }, }; }; @@ -127,6 +186,35 @@ describe('NotificationServicesPushController Services', () => { expect(apis.mockPut.isDone()).toBe(true); expect(result).toBe(MOCK_NEW_REG_TOKEN); }); + + it('should pass mobile metadata when provided', async () => { + const { mobileParams, apis } = arrangeMocks({ + requestBody: { + addresses: MOCK_ADDRESSES, + registration_token: { + token: MOCK_NEW_REG_TOKEN, + platform: 'mobile', + locale: 'en', + os: 'android', + appVersion: '7.42.0', + }, + }, + }); + const paramsWithMetadata = { + ...mobileParams, + regToken: { + ...mobileParams.regToken, + os: 'android' as const, + appVersion: '7.42.0', + }, + }; + + const result = await activatePushNotifications(paramsWithMetadata); + + expect(mobileParams.createRegToken).toHaveBeenCalled(); + expect(apis.mockPut.isDone()).toBe(true); + expect(result).toBe(MOCK_NEW_REG_TOKEN); + }); }); describe('deleteLinksAPI', () => { diff --git a/packages/notification-services-controller/src/NotificationServicesPushController/services/services.ts b/packages/notification-services-controller/src/NotificationServicesPushController/services/services.ts index 26895745a1..95067488ea 100644 --- a/packages/notification-services-controller/src/NotificationServicesPushController/services/services.ts +++ b/packages/notification-services-controller/src/NotificationServicesPushController/services/services.ts @@ -10,6 +10,8 @@ export type RegToken = { token: string; platform: 'extension' | 'mobile' | 'portfolio'; locale: string; + os?: 'android' | 'ios'; + appVersion?: string; oldToken?: string; }; @@ -26,6 +28,8 @@ export type PushTokenRequest = { token: string; platform: 'extension' | 'mobile' | 'portfolio'; locale: string; + os?: 'android' | 'ios'; + appVersion?: string; oldToken?: string; }; }; @@ -129,7 +133,10 @@ type ActivatePushNotificationsParams = { // Other Request Parameters bearerToken: string; addresses: string[]; - regToken: Pick; + regToken: Pick< + RegToken, + 'appVersion' | 'locale' | 'oldToken' | 'os' | 'platform' + >; }; /** @@ -155,6 +162,8 @@ export async function activatePushNotifications( token: regToken, platform: params.regToken.platform, locale: params.regToken.locale, + os: params.regToken.os, + appVersion: params.regToken.appVersion, oldToken: params.regToken.oldToken, }, env: params.controllerEnv, From ed4c364c5321784eefb3bcbaec477531ff8000c4 Mon Sep 17 00:00:00 2001 From: Samir Mehta <12882259+samir-acle@users.noreply.github.com> Date: Tue, 12 May 2026 14:44:42 -0400 Subject: [PATCH 3/6] fix: notification controller JSDoc lint --- .../NotificationServicesController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/notification-services-controller/src/NotificationServicesController/NotificationServicesController.ts b/packages/notification-services-controller/src/NotificationServicesController/NotificationServicesController.ts index 75bb351c99..1d479ef99d 100644 --- a/packages/notification-services-controller/src/NotificationServicesController/NotificationServicesController.ts +++ b/packages/notification-services-controller/src/NotificationServicesController/NotificationServicesController.ts @@ -1119,7 +1119,7 @@ export class NotificationServicesController extends BaseController< * Enables all MetaMask notifications for the user. * This is identical flow when initializing notifications for the first time. * - * @param opts - Optional settings for first-time AUS notification preferences initialization. + * @param opts - Optional options to mutate this functionality. * @throws {Error} If there is an error during the process of enabling notifications. */ public async enableMetamaskNotifications( From d59561d6f09f8456b5cca81a4a471b28fb43cbde Mon Sep 17 00:00:00 2001 From: Samir Mehta <12882259+samir-acle@users.noreply.github.com> Date: Wed, 13 May 2026 14:51:19 -0400 Subject: [PATCH 4/6] docs(notification-services-controller): add changelog PR link --- packages/notification-services-controller/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/notification-services-controller/CHANGELOG.md b/packages/notification-services-controller/CHANGELOG.md index 8c2bca5692..c4f5816303 100644 --- a/packages/notification-services-controller/CHANGELOG.md +++ b/packages/notification-services-controller/CHANGELOG.md @@ -12,7 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Add `productAnnouncementEnabled` to `NotificationServicesControllerEnableNotificationsOptions`. ([#8784](https://github.com/MetaMask/core/pull/8784)) -- Add optional mobile OS and app version metadata to push token registrations so clients can provide Firebase error attribution data. +- Add optional mobile OS and app version metadata to push token registrations so clients can provide Firebase error attribution data. ([#8782](https://github.com/MetaMask/core/pull/8782)) ### Changed From a6b39e3557f045f030550d8fa656000f7856d9cc Mon Sep 17 00:00:00 2001 From: Samir Mehta <12882259+samir-acle@users.noreply.github.com> Date: Wed, 13 May 2026 15:16:16 -0400 Subject: [PATCH 5/6] chore(notification-services-controller): update messenger action types --- .../NotificationServicesController-method-action-types.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/notification-services-controller/src/NotificationServicesController/NotificationServicesController-method-action-types.ts b/packages/notification-services-controller/src/NotificationServicesController/NotificationServicesController-method-action-types.ts index df3a4ff636..92eeee08c5 100644 --- a/packages/notification-services-controller/src/NotificationServicesController/NotificationServicesController-method-action-types.ts +++ b/packages/notification-services-controller/src/NotificationServicesController/NotificationServicesController-method-action-types.ts @@ -60,6 +60,7 @@ export type NotificationServicesControllerSetFeatureAnnouncementsEnabledAction = * Used only during initialization to seed marketing push notifications. * @param opts.productAnnouncementEnabled - The user's product-announcement flag. * Used only during initialization to seed marketing in-app notifications. + * @param opts.registerPushNotifications - Whether to attempt FCM/device push registration. * @returns The updated or newly created user storage. * @throws {Error} Throws an error if unauthenticated or from other operations. */ @@ -72,7 +73,7 @@ export type NotificationServicesControllerCreateOnChainTriggersAction = { * Enables all MetaMask notifications for the user. * This is identical flow when initializing notifications for the first time. * - * @param opts - Optional settings for first-time AUS notification preferences initialization. + * @param opts - Optional options to mutate this functionality. * @throws {Error} If there is an error during the process of enabling notifications. */ export type NotificationServicesControllerEnableMetamaskNotificationsAction = { From 258c7e90a89c00713b6b4c91c38e1285e75c0d1f Mon Sep 17 00:00:00 2001 From: Samir Mehta <12882259+samir-acle@users.noreply.github.com> Date: Wed, 13 May 2026 15:53:40 -0400 Subject: [PATCH 6/6] docs(notification-services-controller): update unreleased changelog --- packages/notification-services-controller/CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/notification-services-controller/CHANGELOG.md b/packages/notification-services-controller/CHANGELOG.md index c4f5816303..88ae266c9c 100644 --- a/packages/notification-services-controller/CHANGELOG.md +++ b/packages/notification-services-controller/CHANGELOG.md @@ -7,12 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Add `registerPushNotifications` to `NotificationServicesControllerEnableNotificationsOptions` so clients can enable MetaMask notifications without registering push notifications. ([#8782](https://github.com/MetaMask/core/pull/8782)) +- Add optional mobile OS and app version metadata to push token registrations so clients can provide Firebase error attribution data. ([#8782](https://github.com/MetaMask/core/pull/8782)) + ## [24.0.0] ### Added - Add `productAnnouncementEnabled` to `NotificationServicesControllerEnableNotificationsOptions`. ([#8784](https://github.com/MetaMask/core/pull/8784)) -- Add optional mobile OS and app version metadata to push token registrations so clients can provide Firebase error attribution data. ([#8782](https://github.com/MetaMask/core/pull/8782)) ### Changed