diff --git a/.yarn/patches/@metamask-snaps-controllers-npm-8.4.0-574cd5a8a9.patch b/.yarn/patches/@metamask-snaps-controllers-npm-9.2.0-09a31bab4f.patch
similarity index 76%
rename from .yarn/patches/@metamask-snaps-controllers-npm-8.4.0-574cd5a8a9.patch
rename to .yarn/patches/@metamask-snaps-controllers-npm-9.2.0-09a31bab4f.patch
index 4f07bb41d1ff..ab75de4b5022 100644
--- a/.yarn/patches/@metamask-snaps-controllers-npm-8.4.0-574cd5a8a9.patch
+++ b/.yarn/patches/@metamask-snaps-controllers-npm-9.2.0-09a31bab4f.patch
@@ -1,5 +1,5 @@
diff --git a/package.json b/package.json
-index 6dedde043d1bd5fc195e72b3e06ec37cf6532476..3986b5b0c1f3bf7ff49e023c934bed26f44735ae 100644
+index 7e70a2db3606d673d92d38a2755e155bf2af5fd3..67cff443e63bd04e5699279d8101ef4ec547e9cb 100644
--- a/package.json
+++ b/package.json
@@ -9,7 +9,7 @@
diff --git a/app/scripts/controllers/authentication/auth-snap-requests.ts b/app/scripts/controllers/authentication/auth-snap-requests.ts
deleted file mode 100644
index 81c8beaafd40..000000000000
--- a/app/scripts/controllers/authentication/auth-snap-requests.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-import type { SnapId } from '@metamask/snaps-sdk';
-import type { HandleSnapRequest } from '@metamask/snaps-controllers';
-import { HandlerType } from '@metamask/snaps-utils';
-
-type SnapRPCRequest = Parameters[0];
-
-const snapId = 'npm:@metamask/message-signing-snap' as SnapId;
-
-export function createSnapPublicKeyRequest(): SnapRPCRequest {
- return {
- snapId,
- origin: '',
- handler: HandlerType.OnRpcRequest,
- request: {
- method: 'getPublicKey',
- },
- };
-}
-
-export function createSnapSignMessageRequest(
- message: `metamask:${string}`,
-): SnapRPCRequest {
- return {
- snapId,
- origin: '',
- handler: HandlerType.OnRpcRequest,
- request: {
- method: 'signMessage',
- params: { message },
- },
- };
-}
diff --git a/app/scripts/controllers/authentication/authentication-controller.test.ts b/app/scripts/controllers/authentication/authentication-controller.test.ts
deleted file mode 100644
index 1cd01dfb74fc..000000000000
--- a/app/scripts/controllers/authentication/authentication-controller.test.ts
+++ /dev/null
@@ -1,409 +0,0 @@
-import { ControllerMessenger } from '@metamask/base-controller';
-import AuthenticationController, {
- AllowedActions,
- AllowedEvents,
- AuthenticationControllerState,
-} from './authentication-controller';
-import {
- mockEndpointAccessToken,
- mockEndpointGetNonce,
- mockEndpointLogin,
-} from './mocks/mockServices';
-import { MOCK_ACCESS_TOKEN, MOCK_LOGIN_RESPONSE } from './mocks/mockResponses';
-
-const mockSignedInState = (): AuthenticationControllerState => ({
- isSignedIn: true,
- sessionData: {
- accessToken: 'MOCK_ACCESS_TOKEN',
- expiresIn: new Date().toString(),
- profile: {
- identifierId: MOCK_LOGIN_RESPONSE.profile.identifier_id,
- profileId: MOCK_LOGIN_RESPONSE.profile.profile_id,
- },
- },
-});
-
-describe('authentication/authentication-controller - constructor() tests', () => {
- test('should initialize with default state', () => {
- const metametrics = createMockAuthMetaMetrics();
- const controller = new AuthenticationController({
- messenger: createMockAuthenticationMessenger().messenger,
- metametrics,
- });
-
- expect(controller.state.isSignedIn).toBe(false);
- expect(controller.state.sessionData).toBeUndefined();
- });
-
- test('should initialize with override state', () => {
- const metametrics = createMockAuthMetaMetrics();
- const controller = new AuthenticationController({
- messenger: createMockAuthenticationMessenger().messenger,
- state: mockSignedInState(),
- metametrics,
- });
-
- expect(controller.state.isSignedIn).toBe(true);
- expect(controller.state.sessionData).toBeDefined();
- });
-});
-
-describe('authentication/authentication-controller - performSignIn() tests', () => {
- test('Should create access token and update state', async () => {
- const metametrics = createMockAuthMetaMetrics();
- const mockEndpoints = mockAuthenticationFlowEndpoints();
- const { messenger, mockSnapGetPublicKey, mockSnapSignMessage } =
- createMockAuthenticationMessenger();
-
- const controller = new AuthenticationController({ messenger, metametrics });
-
- const result = await controller.performSignIn();
- expect(mockSnapGetPublicKey).toBeCalled();
- expect(mockSnapSignMessage).toBeCalled();
- mockEndpoints.mockGetNonceEndpoint.done();
- mockEndpoints.mockLoginEndpoint.done();
- mockEndpoints.mockAccessTokenEndpoint.done();
- expect(result).toBe(MOCK_ACCESS_TOKEN);
-
- // Assert - state shows user is logged in
- expect(controller.state.isSignedIn).toBe(true);
- expect(controller.state.sessionData).toBeDefined();
- });
-
- test('Should error when nonce endpoint fails', async () => {
- await testAndAssertFailingEndpoints('nonce');
- });
-
- test('Should error when login endpoint fails', async () => {
- await testAndAssertFailingEndpoints('login');
- });
-
- test('Should error when tokens endpoint fails', async () => {
- await testAndAssertFailingEndpoints('token');
- });
-
- // When the wallet is locked, we are unable to call the snap
- test('Should error when wallet is locked', async () => {
- const { messenger, mockKeyringControllerGetState } =
- createMockAuthenticationMessenger();
- const metametrics = createMockAuthMetaMetrics();
-
- // Mock wallet is locked
- mockKeyringControllerGetState.mockReturnValue({ isUnlocked: false });
-
- const controller = new AuthenticationController({ messenger, metametrics });
-
- await expect(controller.performSignIn()).rejects.toThrow();
- });
-
- async function testAndAssertFailingEndpoints(
- endpointFail: 'nonce' | 'login' | 'token',
- ) {
- const mockEndpoints = mockAuthenticationFlowEndpoints({
- endpointFail,
- });
- const { messenger } = createMockAuthenticationMessenger();
- const metametrics = createMockAuthMetaMetrics();
- const controller = new AuthenticationController({ messenger, metametrics });
-
- await expect(controller.performSignIn()).rejects.toThrow();
- expect(controller.state.isSignedIn).toBe(false);
-
- const endpointsCalled = [
- mockEndpoints.mockGetNonceEndpoint.isDone(),
- mockEndpoints.mockLoginEndpoint.isDone(),
- mockEndpoints.mockAccessTokenEndpoint.isDone(),
- ];
- if (endpointFail === 'nonce') {
- expect(endpointsCalled).toEqual([true, false, false]);
- }
-
- if (endpointFail === 'login') {
- expect(endpointsCalled).toEqual([true, true, false]);
- }
-
- if (endpointFail === 'token') {
- expect(endpointsCalled).toEqual([true, true, true]);
- }
- }
-});
-
-describe('authentication/authentication-controller - performSignOut() tests', () => {
- test('Should remove signed in user and any access tokens', () => {
- const metametrics = createMockAuthMetaMetrics();
- const { messenger } = createMockAuthenticationMessenger();
- const controller = new AuthenticationController({
- messenger,
- state: mockSignedInState(),
- metametrics,
- });
-
- controller.performSignOut();
- expect(controller.state.isSignedIn).toBe(false);
- expect(controller.state.sessionData).toBeUndefined();
- });
-
- test('Should throw error if attempting to sign out when user is not logged in', () => {
- const metametrics = createMockAuthMetaMetrics();
- const { messenger } = createMockAuthenticationMessenger();
- const controller = new AuthenticationController({
- messenger,
- state: { isSignedIn: false },
- metametrics,
- });
-
- expect(() => controller.performSignOut()).toThrow();
- });
-});
-
-describe('authentication/authentication-controller - getBearerToken() tests', () => {
- test('Should throw error if not logged in', async () => {
- const metametrics = createMockAuthMetaMetrics();
- const { messenger } = createMockAuthenticationMessenger();
- const controller = new AuthenticationController({
- messenger,
- state: { isSignedIn: false },
- metametrics,
- });
-
- await expect(controller.getBearerToken()).rejects.toThrow();
- });
-
- test('Should return original access token in state', async () => {
- const metametrics = createMockAuthMetaMetrics();
- const { messenger } = createMockAuthenticationMessenger();
- const originalState = mockSignedInState();
- const controller = new AuthenticationController({
- messenger,
- state: originalState,
- metametrics,
- });
-
- const result = await controller.getBearerToken();
- expect(result).toBeDefined();
- expect(result).toBe(originalState.sessionData?.accessToken);
- });
-
- test('Should return new access token if state is invalid', async () => {
- const metametrics = createMockAuthMetaMetrics();
- const { messenger } = createMockAuthenticationMessenger();
- mockAuthenticationFlowEndpoints();
-
- // Invalid/old state
- const originalState = mockSignedInState();
- if (originalState.sessionData) {
- originalState.sessionData.accessToken = 'ACCESS_TOKEN_1';
-
- const d = new Date();
- d.setMinutes(d.getMinutes() - 31); // expires at 30 mins
- originalState.sessionData.expiresIn = d.toString();
- }
-
- const controller = new AuthenticationController({
- messenger,
- state: originalState,
- metametrics,
- });
-
- const result = await controller.getBearerToken();
- expect(result).toBeDefined();
- expect(result).toBe(MOCK_ACCESS_TOKEN);
- });
-
- // If the state is invalid, we need to re-login.
- // But as wallet is locked, we will not be able to call the snap
- test('Should throw error if wallet is locked', async () => {
- const metametrics = createMockAuthMetaMetrics();
- const { messenger, mockKeyringControllerGetState } =
- createMockAuthenticationMessenger();
- mockAuthenticationFlowEndpoints();
-
- // Invalid/old state
- const originalState = mockSignedInState();
- if (originalState.sessionData) {
- originalState.sessionData.accessToken = 'ACCESS_TOKEN_1';
-
- const d = new Date();
- d.setMinutes(d.getMinutes() - 31); // expires at 30 mins
- originalState.sessionData.expiresIn = d.toString();
- }
-
- // Mock wallet is locked
- mockKeyringControllerGetState.mockReturnValue({ isUnlocked: false });
-
- const controller = new AuthenticationController({
- messenger,
- state: originalState,
- metametrics,
- });
-
- await expect(controller.getBearerToken()).rejects.toThrow();
- });
-});
-
-describe('authentication/authentication-controller - getSessionProfile() tests', () => {
- test('Should throw error if not logged in', async () => {
- const metametrics = createMockAuthMetaMetrics();
- const { messenger } = createMockAuthenticationMessenger();
- const controller = new AuthenticationController({
- messenger,
- state: { isSignedIn: false },
- metametrics,
- });
-
- await expect(controller.getSessionProfile()).rejects.toThrow();
- });
-
- test('Should return original access token in state', async () => {
- const metametrics = createMockAuthMetaMetrics();
- const { messenger } = createMockAuthenticationMessenger();
- const originalState = mockSignedInState();
- const controller = new AuthenticationController({
- messenger,
- state: originalState,
- metametrics,
- });
-
- const result = await controller.getSessionProfile();
- expect(result).toBeDefined();
- expect(result).toEqual(originalState.sessionData?.profile);
- });
-
- test('Should return new access token if state is invalid', async () => {
- const metametrics = createMockAuthMetaMetrics();
- const { messenger } = createMockAuthenticationMessenger();
- mockAuthenticationFlowEndpoints();
-
- // Invalid/old state
- const originalState = mockSignedInState();
- if (originalState.sessionData) {
- originalState.sessionData.profile.identifierId = 'ID_1';
-
- const d = new Date();
- d.setMinutes(d.getMinutes() - 31); // expires at 30 mins
- originalState.sessionData.expiresIn = d.toString();
- }
-
- const controller = new AuthenticationController({
- messenger,
- state: originalState,
- metametrics,
- });
-
- const result = await controller.getSessionProfile();
- expect(result).toBeDefined();
- expect(result.identifierId).toBe(MOCK_LOGIN_RESPONSE.profile.identifier_id);
- expect(result.profileId).toBe(MOCK_LOGIN_RESPONSE.profile.profile_id);
- });
-
- // If the state is invalid, we need to re-login.
- // But as wallet is locked, we will not be able to call the snap
- test('Should throw error if wallet is locked', async () => {
- const metametrics = createMockAuthMetaMetrics();
- const { messenger, mockKeyringControllerGetState } =
- createMockAuthenticationMessenger();
- mockAuthenticationFlowEndpoints();
-
- // Invalid/old state
- const originalState = mockSignedInState();
- if (originalState.sessionData) {
- originalState.sessionData.profile.identifierId = 'ID_1';
-
- const d = new Date();
- d.setMinutes(d.getMinutes() - 31); // expires at 30 mins
- originalState.sessionData.expiresIn = d.toString();
- }
-
- // Mock wallet is locked
- mockKeyringControllerGetState.mockReturnValue({ isUnlocked: false });
-
- const controller = new AuthenticationController({
- messenger,
- state: originalState,
- metametrics,
- });
-
- await expect(controller.getSessionProfile()).rejects.toThrow();
- });
-});
-
-function createAuthenticationMessenger() {
- const messenger = new ControllerMessenger();
- return messenger.getRestricted({
- name: 'AuthenticationController',
- allowedActions: [
- `SnapController:handleRequest`,
- 'KeyringController:getState',
- ],
- allowedEvents: ['KeyringController:lock', 'KeyringController:unlock'],
- });
-}
-
-function createMockAuthenticationMessenger() {
- const messenger = createAuthenticationMessenger();
- const mockCall = jest.spyOn(messenger, 'call');
- const mockSnapGetPublicKey = jest.fn().mockResolvedValue('MOCK_PUBLIC_KEY');
- const mockSnapSignMessage = jest
- .fn()
- .mockResolvedValue('MOCK_SIGNED_MESSAGE');
-
- const mockKeyringControllerGetState = jest
- .fn()
- .mockReturnValue({ isUnlocked: true });
-
- mockCall.mockImplementation((...args) => {
- const [actionType, params] = args;
- if (actionType === 'SnapController:handleRequest') {
- if (params?.request.method === 'getPublicKey') {
- return mockSnapGetPublicKey();
- }
-
- if (params?.request.method === 'signMessage') {
- return mockSnapSignMessage();
- }
-
- throw new Error(
- `MOCK_FAIL - unsupported SnapController:handleRequest call: ${params?.request.method}`,
- );
- }
-
- if (actionType === 'KeyringController:getState') {
- return mockKeyringControllerGetState();
- }
-
- throw new Error(`MOCK_FAIL - unsupported messenger call: ${actionType}`);
- });
-
- return {
- messenger,
- mockSnapGetPublicKey,
- mockSnapSignMessage,
- mockKeyringControllerGetState,
- };
-}
-
-function mockAuthenticationFlowEndpoints(params?: {
- endpointFail: 'nonce' | 'login' | 'token';
-}) {
- const mockGetNonceEndpoint = mockEndpointGetNonce(
- params?.endpointFail === 'nonce' ? { status: 500 } : undefined,
- );
- const mockLoginEndpoint = mockEndpointLogin(
- params?.endpointFail === 'login' ? { status: 500 } : undefined,
- );
- const mockAccessTokenEndpoint = mockEndpointAccessToken(
- params?.endpointFail === 'token' ? { status: 500 } : undefined,
- );
-
- return {
- mockGetNonceEndpoint,
- mockLoginEndpoint,
- mockAccessTokenEndpoint,
- };
-}
-
-function createMockAuthMetaMetrics() {
- const getMetaMetricsId = jest.fn().mockReturnValue('MOCK_METAMETRICS_ID');
-
- return { getMetaMetricsId };
-}
diff --git a/app/scripts/controllers/authentication/authentication-controller.ts b/app/scripts/controllers/authentication/authentication-controller.ts
deleted file mode 100644
index 4bcb1528c323..000000000000
--- a/app/scripts/controllers/authentication/authentication-controller.ts
+++ /dev/null
@@ -1,384 +0,0 @@
-import {
- BaseController,
- RestrictedControllerMessenger,
- StateMetadata,
-} from '@metamask/base-controller';
-import type {
- KeyringControllerGetStateAction,
- KeyringControllerLockEvent,
- KeyringControllerUnlockEvent,
-} from '@metamask/keyring-controller';
-import { HandleSnapRequest } from '@metamask/snaps-controllers';
-import {
- createSnapPublicKeyRequest,
- createSnapSignMessageRequest,
-} from './auth-snap-requests';
-import {
- createLoginRawMessage,
- getAccessToken,
- getNonce,
- login,
-} from './services';
-
-const THIRTY_MIN_MS = 1000 * 60 * 30;
-
-const controllerName = 'AuthenticationController';
-
-// State
-type SessionProfile = {
- identifierId: string;
- profileId: string;
-};
-
-type SessionData = {
- /** profile - anonymous profile data for the given logged in user */
- profile: SessionProfile;
- /** accessToken - used to make requests authorized endpoints */
- accessToken: string;
- /** expiresIn - string date to determine if new access token is required */
- expiresIn: string;
-};
-
-type MetaMetricsAuth = {
- getMetaMetricsId: () => string;
-};
-
-export type AuthenticationControllerState = {
- /**
- * Global isSignedIn state.
- * Can be used to determine if "Profile Syncing" is enabled.
- */
- isSignedIn: boolean;
- sessionData?: SessionData;
-};
-const defaultState: AuthenticationControllerState = { isSignedIn: false };
-const metadata: StateMetadata = {
- isSignedIn: {
- persist: true,
- anonymous: true,
- },
- sessionData: {
- persist: true,
- anonymous: false,
- },
-};
-
-// Messenger Actions
-type CreateActionsObj = {
- [K in T]: {
- type: `${typeof controllerName}:${K}`;
- handler: AuthenticationController[K];
- };
-};
-type ActionsObj = CreateActionsObj<
- | 'performSignIn'
- | 'performSignOut'
- | 'getBearerToken'
- | 'getSessionProfile'
- | 'isSignedIn'
->;
-export type Actions = ActionsObj[keyof ActionsObj];
-export type AuthenticationControllerPerformSignIn = ActionsObj['performSignIn'];
-export type AuthenticationControllerPerformSignOut =
- ActionsObj['performSignOut'];
-export type AuthenticationControllerGetBearerToken =
- ActionsObj['getBearerToken'];
-export type AuthenticationControllerGetSessionProfile =
- ActionsObj['getSessionProfile'];
-export type AuthenticationControllerIsSignedIn = ActionsObj['isSignedIn'];
-
-// Allowed Actions
-export type AllowedActions =
- | HandleSnapRequest
- | KeyringControllerGetStateAction;
-
-export type AllowedEvents =
- | KeyringControllerLockEvent
- | KeyringControllerUnlockEvent;
-
-// Messenger
-export type AuthenticationControllerMessenger = RestrictedControllerMessenger<
- typeof controllerName,
- Actions | AllowedActions,
- AllowedEvents,
- AllowedActions['type'],
- AllowedEvents['type']
->;
-
-/**
- * Controller that enables authentication for restricted endpoints.
- * Used for Global Profile Syncing and Notifications
- */
-export default class AuthenticationController extends BaseController<
- typeof controllerName,
- AuthenticationControllerState,
- AuthenticationControllerMessenger
-> {
- #metametrics: MetaMetricsAuth;
-
- #isUnlocked = false;
-
- #keyringController = {
- setupLockedStateSubscriptions: () => {
- const { isUnlocked } = this.messagingSystem.call(
- 'KeyringController:getState',
- );
- this.#isUnlocked = isUnlocked;
-
- this.messagingSystem.subscribe('KeyringController:unlock', () => {
- this.#isUnlocked = true;
- });
-
- this.messagingSystem.subscribe('KeyringController:lock', () => {
- this.#isUnlocked = false;
- });
- },
- };
-
- constructor({
- messenger,
- state,
- metametrics,
- }: {
- messenger: AuthenticationControllerMessenger;
- state?: AuthenticationControllerState;
- /**
- * Not using the Messaging System as we
- * do not want to tie this strictly to extension
- */
- metametrics: MetaMetricsAuth;
- }) {
- super({
- messenger,
- metadata,
- name: controllerName,
- state: { ...defaultState, ...state },
- });
-
- this.#metametrics = metametrics;
-
- this.#keyringController.setupLockedStateSubscriptions();
- this.#registerMessageHandlers();
- }
-
- /**
- * Constructor helper for registering this controller's messaging system
- * actions.
- */
- #registerMessageHandlers(): void {
- this.messagingSystem.registerActionHandler(
- 'AuthenticationController:getBearerToken',
- this.getBearerToken.bind(this),
- );
-
- this.messagingSystem.registerActionHandler(
- 'AuthenticationController:getSessionProfile',
- this.getSessionProfile.bind(this),
- );
-
- this.messagingSystem.registerActionHandler(
- 'AuthenticationController:isSignedIn',
- this.isSignedIn.bind(this),
- );
-
- this.messagingSystem.registerActionHandler(
- 'AuthenticationController:performSignIn',
- this.performSignIn.bind(this),
- );
-
- this.messagingSystem.registerActionHandler(
- 'AuthenticationController:performSignOut',
- this.performSignOut.bind(this),
- );
- }
-
- public async performSignIn(): Promise {
- const { accessToken } = await this.#performAuthenticationFlow();
- return accessToken;
- }
-
- public performSignOut(): void {
- this.#assertLoggedIn();
-
- this.update((state) => {
- state.isSignedIn = false;
- state.sessionData = undefined;
- });
- }
-
- public async getBearerToken(): Promise {
- this.#assertLoggedIn();
-
- if (this.#hasValidSession(this.state.sessionData)) {
- return this.state.sessionData.accessToken;
- }
-
- const { accessToken } = await this.#performAuthenticationFlow();
- return accessToken;
- }
-
- /**
- * Will return a session profile.
- * Throws if a user is not logged in.
- *
- * @returns profile for the session.
- */
- public async getSessionProfile(): Promise {
- this.#assertLoggedIn();
-
- if (this.#hasValidSession(this.state.sessionData)) {
- return this.state.sessionData.profile;
- }
-
- const { profile } = await this.#performAuthenticationFlow();
- return profile;
- }
-
- public isSignedIn(): boolean {
- return this.state.isSignedIn;
- }
-
- #assertLoggedIn(): void {
- if (!this.state.isSignedIn) {
- throw new Error(
- `${controllerName}: Unable to call method, user is not authenticated`,
- );
- }
- }
-
- async #performAuthenticationFlow(): Promise<{
- profile: SessionProfile;
- accessToken: string;
- }> {
- try {
- // 1. Nonce
- const publicKey = await this.#snapGetPublicKey();
- const nonce = await getNonce(publicKey);
- if (!nonce) {
- throw new Error(`Unable to get nonce`);
- }
-
- // 2. Login
- const rawMessage = createLoginRawMessage(nonce, publicKey);
- const signature = await this.#snapSignMessage(rawMessage);
- const loginResponse = await login(
- rawMessage,
- signature,
- this.#metametrics.getMetaMetricsId(),
- );
- if (!loginResponse?.token) {
- throw new Error(`Unable to login`);
- }
-
- const profile: SessionProfile = {
- identifierId: loginResponse.profile.identifier_id,
- profileId: loginResponse.profile.profile_id,
- };
-
- // 3. Trade for Access Token
- const accessToken = await getAccessToken(loginResponse.token);
- if (!accessToken) {
- throw new Error(`Unable to get Access Token`);
- }
-
- // Update Internal State
- this.update((state) => {
- state.isSignedIn = true;
- const expiresIn = new Date();
- expiresIn.setTime(expiresIn.getTime() + THIRTY_MIN_MS);
- state.sessionData = {
- profile,
- accessToken,
- expiresIn: expiresIn.toString(),
- };
- });
-
- return {
- profile,
- accessToken,
- };
- } catch (e) {
- console.error('Failed to authenticate', e);
- const errorMessage =
- e instanceof Error ? e.message : JSON.stringify(e ?? '');
- throw new Error(
- `${controllerName}: Failed to authenticate - ${errorMessage}`,
- );
- }
- }
-
- #hasValidSession(
- sessionData: SessionData | undefined,
- ): sessionData is SessionData {
- if (!sessionData) {
- return false;
- }
-
- const prevDate = Date.parse(sessionData.expiresIn);
- if (isNaN(prevDate)) {
- return false;
- }
-
- const currentDate = new Date();
- const diffMs = Math.abs(currentDate.getTime() - prevDate);
-
- return THIRTY_MIN_MS > diffMs;
- }
-
- #_snapPublicKeyCache: string | undefined;
-
- /**
- * Returns the auth snap public key.
- *
- * @returns The snap public key.
- */
- async #snapGetPublicKey(): Promise {
- if (this.#_snapPublicKeyCache) {
- return this.#_snapPublicKeyCache;
- }
-
- if (!this.#isUnlocked) {
- throw new Error(
- '#snapGetPublicKey - unable to call snap, wallet is locked',
- );
- }
-
- const result = (await this.messagingSystem.call(
- 'SnapController:handleRequest',
- createSnapPublicKeyRequest(),
- )) as string;
-
- this.#_snapPublicKeyCache = result;
-
- return result;
- }
-
- #_snapSignMessageCache: Record<`metamask:${string}`, string> = {};
-
- /**
- * Signs a specific message using an underlying auth snap.
- *
- * @param message - A specific tagged message to sign.
- * @returns A Signature created by the snap.
- */
- async #snapSignMessage(message: `metamask:${string}`): Promise {
- if (this.#_snapSignMessageCache[message]) {
- return this.#_snapSignMessageCache[message];
- }
-
- if (!this.#isUnlocked) {
- throw new Error(
- '#snapSignMessage - unable to call snap, wallet is locked',
- );
- }
-
- const result = (await this.messagingSystem.call(
- 'SnapController:handleRequest',
- createSnapSignMessageRequest(message),
- )) as string;
-
- this.#_snapSignMessageCache[message] = result;
-
- return result;
- }
-}
diff --git a/app/scripts/controllers/authentication/mocks/mockResponses.ts b/app/scripts/controllers/authentication/mocks/mockResponses.ts
deleted file mode 100644
index 4882bd81d812..000000000000
--- a/app/scripts/controllers/authentication/mocks/mockResponses.ts
+++ /dev/null
@@ -1,60 +0,0 @@
-import {
- AUTH_LOGIN_ENDPOINT,
- AUTH_NONCE_ENDPOINT,
- LoginResponse,
- NonceResponse,
- OAuthTokenResponse,
- OIDC_TOKENS_ENDPOINT,
-} from '../services';
-
-type MockResponse = {
- url: string;
- requestMethod: 'GET' | 'POST' | 'PUT';
- response: unknown;
-};
-
-export const MOCK_NONCE = '4cbfqzoQpcNxVImGv';
-export const MOCK_NONCE_RESPONSE: NonceResponse = {
- nonce: MOCK_NONCE,
-};
-
-export function getMockAuthNonceResponse() {
- return {
- url: AUTH_NONCE_ENDPOINT,
- requestMethod: 'GET',
- response: MOCK_NONCE_RESPONSE,
- } satisfies MockResponse;
-}
-
-export const MOCK_JWT =
- 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c';
-export const MOCK_LOGIN_RESPONSE: LoginResponse = {
- token: MOCK_JWT,
- expires_in: new Date().toString(),
- profile: {
- identifier_id: 'MOCK_IDENTIFIER',
- profile_id: 'MOCK_PROFILE_ID',
- },
-};
-
-export function getMockAuthLoginResponse() {
- return {
- url: AUTH_LOGIN_ENDPOINT,
- requestMethod: 'POST',
- response: MOCK_LOGIN_RESPONSE,
- } satisfies MockResponse;
-}
-
-export const MOCK_ACCESS_TOKEN = `MOCK_ACCESS_TOKEN-${MOCK_JWT}`;
-export const MOCK_OATH_TOKEN_RESPONSE: OAuthTokenResponse = {
- access_token: MOCK_ACCESS_TOKEN,
- expires_in: new Date().getTime(),
-};
-
-export function getMockAuthAccessTokenResponse() {
- return {
- url: OIDC_TOKENS_ENDPOINT,
- requestMethod: 'POST',
- response: MOCK_OATH_TOKEN_RESPONSE,
- } satisfies MockResponse;
-}
diff --git a/app/scripts/controllers/authentication/mocks/mockServices.ts b/app/scripts/controllers/authentication/mocks/mockServices.ts
deleted file mode 100644
index 69d3cf56c5d5..000000000000
--- a/app/scripts/controllers/authentication/mocks/mockServices.ts
+++ /dev/null
@@ -1,42 +0,0 @@
-import nock from 'nock';
-import {
- getMockAuthAccessTokenResponse,
- getMockAuthLoginResponse,
- getMockAuthNonceResponse,
-} from './mockResponses';
-
-type MockReply = {
- status: nock.StatusCode;
- body?: nock.Body;
-};
-
-export function mockEndpointGetNonce(mockReply?: MockReply) {
- const mockResponse = getMockAuthNonceResponse();
- const reply = mockReply ?? { status: 200, body: mockResponse.response };
- const mockNonceEndpoint = nock(mockResponse.url)
- .get('')
- .query(true)
- .reply(reply.status, reply.body);
-
- return mockNonceEndpoint;
-}
-
-export function mockEndpointLogin(mockReply?: MockReply) {
- const mockResponse = getMockAuthLoginResponse();
- const reply = mockReply ?? { status: 200, body: mockResponse.response };
- const mockLoginEndpoint = nock(mockResponse.url)
- .post('')
- .reply(reply.status, reply.body);
-
- return mockLoginEndpoint;
-}
-
-export function mockEndpointAccessToken(mockReply?: MockReply) {
- const mockResponse = getMockAuthAccessTokenResponse();
- const reply = mockReply ?? { status: 200, body: mockResponse.response };
- const mockOidcTokensEndpoint = nock(mockResponse.url)
- .post('')
- .reply(reply.status, reply.body);
-
- return mockOidcTokensEndpoint;
-}
diff --git a/app/scripts/controllers/authentication/services.test.ts b/app/scripts/controllers/authentication/services.test.ts
deleted file mode 100644
index 759b3c535936..000000000000
--- a/app/scripts/controllers/authentication/services.test.ts
+++ /dev/null
@@ -1,108 +0,0 @@
-import { MOCK_ACCESS_TOKEN, MOCK_JWT, MOCK_NONCE } from './mocks/mockResponses';
-import {
- mockEndpointAccessToken,
- mockEndpointGetNonce,
- mockEndpointLogin,
-} from './mocks/mockServices';
-import {
- createLoginRawMessage,
- getAccessToken,
- getNonce,
- login,
-} from './services';
-
-const MOCK_METAMETRICS_ID = '0x123';
-
-describe('authentication/services.ts - getNonce() tests', () => {
- test('returns nonce on valid request', async () => {
- const mockNonceEndpoint = mockEndpointGetNonce();
- const response = await getNonce('MOCK_PUBLIC_KEY');
-
- mockNonceEndpoint.done();
- expect(response).toBe(MOCK_NONCE);
- });
-
- test('returns null if request is invalid', async () => {
- async function testInvalidResponse(
- status: number,
- body: Record,
- ) {
- const mockNonceEndpoint = mockEndpointGetNonce({ status, body });
- const response = await getNonce('MOCK_PUBLIC_KEY');
-
- mockNonceEndpoint.done();
- expect(response).toBe(null);
- }
-
- await testInvalidResponse(500, { error: 'mock server error' });
- await testInvalidResponse(400, { error: 'mock bad request' });
- });
-});
-
-describe('authentication/services.ts - login() tests', () => {
- test('returns single-use jwt if successful login', async () => {
- const mockLoginEndpoint = mockEndpointLogin();
- const response = await login(
- 'mock raw message',
- 'mock signature',
- MOCK_METAMETRICS_ID,
- );
-
- mockLoginEndpoint.done();
- expect(response?.token).toBe(MOCK_JWT);
- expect(response?.profile).toBeDefined();
- });
-
- test('returns null if request is invalid', async () => {
- async function testInvalidResponse(
- status: number,
- body: Record,
- ) {
- const mockLoginEndpoint = mockEndpointLogin({ status, body });
- const response = await login(
- 'mock raw message',
- 'mock signature',
- MOCK_METAMETRICS_ID,
- );
-
- mockLoginEndpoint.done();
- expect(response).toBe(null);
- }
-
- await testInvalidResponse(500, { error: 'mock server error' });
- await testInvalidResponse(400, { error: 'mock bad request' });
- });
-});
-
-describe('authentication/services.ts - getAccessToken() tests', () => {
- test('returns access token jwt if successful OIDC token request', async () => {
- const mockLoginEndpoint = mockEndpointAccessToken();
- const response = await getAccessToken('mock single-use jwt');
-
- mockLoginEndpoint.done();
- expect(response).toBe(MOCK_ACCESS_TOKEN);
- });
-
- test('returns null if request is invalid', async () => {
- async function testInvalidResponse(
- status: number,
- body: Record,
- ) {
- const mockLoginEndpoint = mockEndpointAccessToken({ status, body });
- const response = await getAccessToken('mock single-use jwt');
-
- mockLoginEndpoint.done();
- expect(response).toBe(null);
- }
-
- await testInvalidResponse(500, { error: 'mock server error' });
- await testInvalidResponse(400, { error: 'mock bad request' });
- });
-});
-
-describe('authentication/services.ts - createLoginRawMessage() tests', () => {
- test('creates the raw message format for login request', () => {
- const message = createLoginRawMessage('NONCE', 'PUBLIC_KEY');
- expect(message).toBe('metamask:NONCE:PUBLIC_KEY');
- });
-});
diff --git a/app/scripts/controllers/authentication/services.ts b/app/scripts/controllers/authentication/services.ts
deleted file mode 100644
index a4bbbf2f11cb..000000000000
--- a/app/scripts/controllers/authentication/services.ts
+++ /dev/null
@@ -1,120 +0,0 @@
-const AUTH_ENDPOINT = process.env.AUTH_API || '';
-export const AUTH_NONCE_ENDPOINT = `${AUTH_ENDPOINT}/api/v2/nonce`;
-export const AUTH_LOGIN_ENDPOINT = `${AUTH_ENDPOINT}/api/v2/srp/login`;
-
-const OIDC_ENDPOINT = process.env.OIDC_API || '';
-export const OIDC_TOKENS_ENDPOINT = `${OIDC_ENDPOINT}/oauth2/token`;
-const OIDC_CLIENT_ID = process.env.OIDC_CLIENT_ID || '';
-const OIDC_GRANT_TYPE = process.env.OIDC_GRANT_TYPE || '';
-
-export type NonceResponse = {
- nonce: string;
-};
-export async function getNonce(publicKey: string): Promise {
- const nonceUrl = new URL(AUTH_NONCE_ENDPOINT);
- nonceUrl.searchParams.set('identifier', publicKey);
-
- try {
- const nonceResponse = await fetch(nonceUrl.toString());
- if (!nonceResponse.ok) {
- return null;
- }
-
- const nonceJson: NonceResponse = await nonceResponse.json();
- return nonceJson?.nonce ?? null;
- } catch (e) {
- console.error('authentication-controller/services: unable to get nonce', e);
- return null;
- }
-}
-
-export type LoginResponse = {
- token: string;
- expires_in: string;
- /**
- * Contains anonymous information about the logged in profile.
- *
- * @property identifier_id - a deterministic unique identifier on the method used to sign in
- * @property profile_id - a unique id for a given profile
- * @property metametrics_id - an anonymous server id
- */
- profile: {
- identifier_id: string;
- profile_id: string;
- };
-};
-export async function login(
- rawMessage: string,
- signature: string,
- clientMetaMetricsId: string,
-): Promise {
- try {
- const response = await fetch(AUTH_LOGIN_ENDPOINT, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify({
- signature,
- raw_message: rawMessage,
- metametrics: {
- metametrics_id: clientMetaMetricsId,
- agent: 'extension',
- },
- }),
- });
-
- if (!response.ok) {
- return null;
- }
-
- const loginResponse: LoginResponse = await response.json();
- return loginResponse ?? null;
- } catch (e) {
- console.error('authentication-controller/services: unable to login', e);
- return null;
- }
-}
-
-export type OAuthTokenResponse = {
- access_token: string;
- expires_in: number;
-};
-export async function getAccessToken(jwtToken: string): Promise {
- const headers = new Headers({
- 'Content-Type': 'application/x-www-form-urlencoded',
- });
-
- const urlEncodedBody = new URLSearchParams();
- urlEncodedBody.append('grant_type', OIDC_GRANT_TYPE);
- urlEncodedBody.append('client_id', OIDC_CLIENT_ID);
- urlEncodedBody.append('assertion', jwtToken);
-
- try {
- const response = await fetch(OIDC_TOKENS_ENDPOINT, {
- method: 'POST',
- headers,
- body: urlEncodedBody.toString(),
- });
-
- if (!response.ok) {
- return null;
- }
-
- const accessTokenResponse: OAuthTokenResponse = await response.json();
- return accessTokenResponse?.access_token ?? null;
- } catch (e) {
- console.error(
- 'authentication-controller/services: unable to get access token',
- e,
- );
- return null;
- }
-}
-
-export function createLoginRawMessage(
- nonce: string,
- publicKey: string,
-): `metamask:${string}:${string}` {
- return `metamask:${nonce}:${publicKey}` as const;
-}
diff --git a/app/scripts/controllers/metamask-notifications/metamask-notifications.test.ts b/app/scripts/controllers/metamask-notifications/metamask-notifications.test.ts
index c96ccfe2e989..125b7f965bac 100644
--- a/app/scripts/controllers/metamask-notifications/metamask-notifications.test.ts
+++ b/app/scripts/controllers/metamask-notifications/metamask-notifications.test.ts
@@ -7,16 +7,9 @@ import {
} from '@metamask/keyring-controller';
import { waitFor } from '@testing-library/react';
import {
- AuthenticationControllerGetBearerToken,
- AuthenticationControllerIsSignedIn,
-} from '../authentication/authentication-controller';
-import { MOCK_ACCESS_TOKEN } from '../authentication/mocks/mockResponses';
-import {
- UserStorageControllerGetStorageKey,
- UserStorageControllerPerformGetStorage,
- UserStorageControllerPerformSetStorage,
- UserStorageControllerEnableProfileSyncing,
-} from '../user-storage/user-storage-controller';
+ AuthenticationController,
+ UserStorageController,
+} from '@metamask/profile-sync-controller';
import {
PushPlatformNotificationsControllerEnablePushNotifications,
PushPlatformNotificationsControllerDisablePushNotifications,
@@ -50,6 +43,8 @@ import * as OnChainNotifications from './services/onchain-notifications';
import { UserStorage } from './types/user-storage/user-storage';
import * as MetamaskNotificationsUtils from './utils/utils';
+const AuthMocks = AuthenticationController.Mocks;
+
// Mock type used for testing purposes
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type MockVar = any;
@@ -654,12 +649,14 @@ function mockNotificationMessenger() {
typedMockAction().mockResolvedValue([]);
const mockGetBearerToken =
- typedMockAction().mockResolvedValue(
- MOCK_ACCESS_TOKEN,
+ typedMockAction().mockResolvedValue(
+ AuthMocks.MOCK_ACCESS_TOKEN,
);
const mockIsSignedIn =
- typedMockAction().mockReturnValue(true);
+ typedMockAction().mockReturnValue(
+ true,
+ );
const mockDisablePushNotifications =
typedMockAction();
@@ -671,20 +668,20 @@ function mockNotificationMessenger() {
typedMockAction();
const mockGetStorageKey =
- typedMockAction().mockResolvedValue(
+ typedMockAction().mockResolvedValue(
'MOCK_STORAGE_KEY',
);
const mockEnableProfileSyncing =
- typedMockAction();
+ typedMockAction();
const mockPerformGetStorage =
- typedMockAction().mockResolvedValue(
+ typedMockAction().mockResolvedValue(
JSON.stringify(createMockFullUserStorage()),
);
const mockPerformSetStorage =
- typedMockAction();
+ typedMockAction();
jest.spyOn(messenger, 'call').mockImplementation((...args) => {
const [actionType] = args;
@@ -746,10 +743,7 @@ function mockNotificationMessenger() {
return { isUnlocked: true } as MockVar;
}
- function exhaustedMessengerMocks(action: never) {
- return new Error(`MOCK_FAIL - unsupported messenger call: ${action}`);
- }
- throw exhaustedMessengerMocks(actionType);
+ throw new Error(`MOCK_FAIL - unsupported messenger call: ${actionType}`);
});
return {
diff --git a/app/scripts/controllers/metamask-notifications/metamask-notifications.ts b/app/scripts/controllers/metamask-notifications/metamask-notifications.ts
index 6a28b96c0ad3..65b4aed86b37 100644
--- a/app/scripts/controllers/metamask-notifications/metamask-notifications.ts
+++ b/app/scripts/controllers/metamask-notifications/metamask-notifications.ts
@@ -14,21 +14,15 @@ import {
KeyringControllerUnlockEvent,
} from '@metamask/keyring-controller';
import {
- AuthenticationControllerGetBearerToken,
- AuthenticationControllerIsSignedIn,
-} from '../authentication/authentication-controller';
+ AuthenticationController,
+ UserStorageController,
+} from '@metamask/profile-sync-controller';
import {
PushPlatformNotificationsControllerEnablePushNotifications,
PushPlatformNotificationsControllerDisablePushNotifications,
PushPlatformNotificationsControllerUpdateTriggerPushNotifications,
PushPlatformNotificationsControllerOnNewNotificationEvent,
} from '../push-platform-notifications/push-platform-notifications';
-import {
- UserStorageControllerEnableProfileSyncing,
- UserStorageControllerGetStorageKey,
- UserStorageControllerPerformGetStorage,
- UserStorageControllerPerformSetStorage,
-} from '../user-storage/user-storage-controller';
import {
TRIGGER_TYPES,
TRIGGER_TYPES_GROUPS,
@@ -200,13 +194,13 @@ export type AllowedActions =
| KeyringControllerGetAccountsAction
| KeyringControllerGetStateAction
// Auth Controller Requests
- | AuthenticationControllerGetBearerToken
- | AuthenticationControllerIsSignedIn
+ | AuthenticationController.AuthenticationControllerGetBearerToken
+ | AuthenticationController.AuthenticationControllerIsSignedIn
// User Storage Controller Requests
- | UserStorageControllerEnableProfileSyncing
- | UserStorageControllerGetStorageKey
- | UserStorageControllerPerformGetStorage
- | UserStorageControllerPerformSetStorage
+ | UserStorageController.UserStorageControllerEnableProfileSyncing
+ | UserStorageController.UserStorageControllerGetStorageKey
+ | UserStorageController.UserStorageControllerPerformGetStorage
+ | UserStorageController.UserStorageControllerPerformSetStorage
// Push Notifications Controller Requests
| PushPlatformNotificationsControllerEnablePushNotifications
| PushPlatformNotificationsControllerDisablePushNotifications
@@ -295,13 +289,13 @@ export class MetamaskNotificationsController extends BaseController<
getNotificationStorage: async () => {
return await this.messagingSystem.call(
'UserStorageController:performGetStorage',
- 'notification_settings',
+ 'notifications.notificationSettings',
);
},
setNotificationStorage: async (state: string) => {
return await this.messagingSystem.call(
'UserStorageController:performSetStorage',
- 'notification_settings',
+ 'notifications.notificationSettings',
state,
);
},
diff --git a/app/scripts/controllers/metamask-notifications/services/onchain-notifications.ts b/app/scripts/controllers/metamask-notifications/services/onchain-notifications.ts
index a43e678ac798..d4a95bf1aa4e 100644
--- a/app/scripts/controllers/metamask-notifications/services/onchain-notifications.ts
+++ b/app/scripts/controllers/metamask-notifications/services/onchain-notifications.ts
@@ -1,4 +1,5 @@
import log from 'loglevel';
+import { UserStorageController } from '@metamask/profile-sync-controller';
import type { UserStorage } from '../types/user-storage/user-storage';
import type { OnChainRawNotification } from '../types/on-chain-notification/on-chain-notification';
import {
@@ -8,7 +9,8 @@ import {
} from '../utils/utils';
import { TRIGGER_TYPES } from '../constants/notification-schema';
import type { components } from '../types/on-chain-notification/schema';
-import { createSHA256Hash } from '../../user-storage/encryption';
+
+const { createSHA256Hash } = UserStorageController;
export type NotificationTrigger = {
id: string;
diff --git a/app/scripts/controllers/push-platform-notifications/push-platform-notifications.test.ts b/app/scripts/controllers/push-platform-notifications/push-platform-notifications.test.ts
index 2b0c97b5d9c2..2387c4dcfd9c 100644
--- a/app/scripts/controllers/push-platform-notifications/push-platform-notifications.test.ts
+++ b/app/scripts/controllers/push-platform-notifications/push-platform-notifications.test.ts
@@ -1,6 +1,6 @@
import { ControllerMessenger } from '@metamask/base-controller';
+import { AuthenticationController } from '@metamask/profile-sync-controller';
import { isManifestV3 } from '../../../../shared/modules/mv3.utils';
-import type { AuthenticationControllerGetBearerToken } from '../authentication/authentication-controller';
import type {
PushPlatformNotificationsControllerMessenger,
PushPlatformNotificationsControllerState,
@@ -118,7 +118,7 @@ type WithControllerCallback = ({
function buildMessenger() {
return new ControllerMessenger<
- AuthenticationControllerGetBearerToken,
+ AuthenticationController.AuthenticationControllerGetBearerToken,
never
>();
}
@@ -152,7 +152,8 @@ async function withController(
function mockAuthBearerTokenCall(
messenger: PushPlatformNotificationsControllerMessenger,
) {
- type Fn = AuthenticationControllerGetBearerToken['handler'];
+ type Fn =
+ AuthenticationController.AuthenticationControllerGetBearerToken['handler'];
const mockAuthGetBearerToken = jest
.fn, Parameters>()
.mockResolvedValue(MOCK_JWT);
diff --git a/app/scripts/controllers/push-platform-notifications/push-platform-notifications.ts b/app/scripts/controllers/push-platform-notifications/push-platform-notifications.ts
index dab9bce47a30..51cc81c14e7c 100644
--- a/app/scripts/controllers/push-platform-notifications/push-platform-notifications.ts
+++ b/app/scripts/controllers/push-platform-notifications/push-platform-notifications.ts
@@ -3,10 +3,11 @@ import {
RestrictedControllerMessenger,
ControllerGetStateAction,
} from '@metamask/base-controller';
+import { AuthenticationController } from '@metamask/profile-sync-controller';
+
import log from 'loglevel';
import { isManifestV3 } from '../../../../shared/modules/mv3.utils';
-import type { AuthenticationControllerGetBearerToken } from '../authentication/authentication-controller';
import type { Notification } from '../metamask-notifications/types/notification/notification';
import {
activatePushNotifications,
@@ -44,7 +45,8 @@ export type PushPlatformNotificationsControllerMessengerActions =
| PushPlatformNotificationsControllerUpdateTriggerPushNotifications
| ControllerGetStateAction<'state', PushPlatformNotificationsControllerState>;
-type AllowedActions = AuthenticationControllerGetBearerToken;
+type AllowedActions =
+ AuthenticationController.AuthenticationControllerGetBearerToken;
export type PushPlatformNotificationsControllerOnNewNotificationEvent = {
type: `${typeof controllerName}:onNewNotifications`;
diff --git a/app/scripts/controllers/user-storage/encryption/cache.ts b/app/scripts/controllers/user-storage/encryption/cache.ts
deleted file mode 100644
index 7e9d80c78442..000000000000
--- a/app/scripts/controllers/user-storage/encryption/cache.ts
+++ /dev/null
@@ -1,105 +0,0 @@
-import { base64ToByteArray, byteArrayToBase64 } from './utils';
-
-type CachedEntry = {
- salt: Uint8Array;
- base64Salt: string;
- key: Uint8Array;
-};
-
-const MAX_PASSWORD_CACHES = 3;
-const MAX_SALT_CACHES = 10;
-
-/**
- * In-Memory Caching derived keys based from a given salt and password.
- */
-type PasswordMemCachedKDF = {
- [hashedPassword: string]: Map;
-};
-let inMemCachedKDF: PasswordMemCachedKDF = {};
-const getPasswordCache = (hashedPassword: string) => {
- inMemCachedKDF[hashedPassword] ??= new Map();
- return inMemCachedKDF[hashedPassword];
-};
-
-/**
- * Returns a given cached derived key from a hashed password and salt
- *
- * @param hashedPassword - hashed password for cache lookup
- * @param salt - provide salt to receive cached key
- * @returns cached key
- */
-export function getCachedKeyBySalt(
- hashedPassword: string,
- salt: Uint8Array,
-): CachedEntry | undefined {
- const cache = getPasswordCache(hashedPassword);
- const base64Salt = byteArrayToBase64(salt);
- const cachedKey = cache.get(base64Salt);
- if (!cachedKey) {
- return undefined;
- }
-
- return {
- salt,
- base64Salt,
- key: cachedKey,
- };
-}
-
-/**
- * Gets any cached key for a given hashed password
- *
- * @param hashedPassword - hashed password for cache lookup
- * @returns any (the first) cached key
- */
-export function getAnyCachedKey(
- hashedPassword: string,
-): CachedEntry | undefined {
- const cache = getPasswordCache(hashedPassword);
-
- // Takes 1 item from an Iterator via Map.entries()
- const cachedEntry: [string, Uint8Array] | undefined = cache
- .entries()
- .next().value;
-
- if (!cachedEntry) {
- return undefined;
- }
-
- const base64Salt = cachedEntry[0];
- const bytesSalt = base64ToByteArray(base64Salt);
- return {
- salt: bytesSalt,
- base64Salt,
- key: cachedEntry[1],
- };
-}
-
-/**
- * Sets a key to the in memory cache.
- * We have set an arbitrary size of 10 cached keys per hashed password.
- *
- * @param hashedPassword - hashed password for cache lookup
- * @param salt - salt to set new derived key
- * @param key - derived key we are setting
- */
-export function setCachedKey(
- hashedPassword: string,
- salt: Uint8Array,
- key: Uint8Array,
-): void {
- // Max password caches
- if (Object.keys(inMemCachedKDF).length > MAX_PASSWORD_CACHES) {
- inMemCachedKDF = {};
- }
-
- const cache = getPasswordCache(hashedPassword);
- const base64Salt = byteArrayToBase64(salt);
-
- // Max salt caches
- if (cache.size > MAX_SALT_CACHES) {
- cache.clear();
- }
-
- cache.set(base64Salt, key);
-}
diff --git a/app/scripts/controllers/user-storage/encryption/encryption.test.ts b/app/scripts/controllers/user-storage/encryption/encryption.test.ts
deleted file mode 100644
index dbe34a73a5e8..000000000000
--- a/app/scripts/controllers/user-storage/encryption/encryption.test.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-import { createMockFullUserStorage } from '../../metamask-notifications/mocks/mock-notification-user-storage';
-import encryption, { createSHA256Hash } from './encryption';
-
-describe('encryption tests', () => {
- const PASSWORD = '123';
- const DATA1 = 'Hello World';
- const DATA2 = JSON.stringify({ foo: 'bar' });
- const DATA3 = JSON.stringify(createMockFullUserStorage());
-
- it('Should encrypt and decrypt data', () => {
- function actEncryptDecrypt(data: string) {
- const encryptedString = encryption.encryptString(data, PASSWORD);
- const decryptString = encryption.decryptString(encryptedString, PASSWORD);
- return decryptString;
- }
-
- expect(actEncryptDecrypt(DATA1)).toBe(DATA1);
- expect(actEncryptDecrypt(DATA2)).toBe(DATA2);
- expect(actEncryptDecrypt(DATA3)).toBe(DATA3);
- });
-
- it('Should decrypt some existing data', () => {
- const encryptedData = `{"v":"1","t":"scrypt","d":"WNEp1QXUZsxCfW9b27uzZ18CtsMvKP6+cqLq8NLAItXeYcFcUjtKprfvedHxf5JN9Q7pe50qnA==","o":{"N":131072,"r":8,"p":1,"dkLen":16},"saltLen":16}`;
- const result = encryption.decryptString(encryptedData, PASSWORD);
- expect(result).toBe(DATA1);
- });
-
- it('Should sha-256 hash a value and should be deterministic', () => {
- const DATA = 'Hello World';
- const EXPECTED_HASH =
- 'a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e';
-
- const hash1 = createSHA256Hash(DATA);
- expect(hash1).toBe(EXPECTED_HASH);
-
- // Hash should be deterministic (same output with same input)
- const hash2 = createSHA256Hash(DATA);
- expect(hash1).toBe(hash2);
- });
-});
diff --git a/app/scripts/controllers/user-storage/encryption/encryption.ts b/app/scripts/controllers/user-storage/encryption/encryption.ts
deleted file mode 100644
index cdd8feaf77b3..000000000000
--- a/app/scripts/controllers/user-storage/encryption/encryption.ts
+++ /dev/null
@@ -1,196 +0,0 @@
-import { scrypt } from '@noble/hashes/scrypt';
-import { sha256 } from '@noble/hashes/sha256';
-import { utf8ToBytes, concatBytes, bytesToHex } from '@noble/hashes/utils';
-import { gcm } from '@noble/ciphers/aes';
-import { randomBytes } from '@noble/ciphers/webcrypto';
-import { getAnyCachedKey, getCachedKeyBySalt, setCachedKey } from './cache';
-import { base64ToByteArray, byteArrayToBase64, bytesToUtf8 } from './utils';
-
-export type EncryptedPayload = {
- // version
- v: '1';
-
- // key derivation function algorithm - scrypt
- t: 'scrypt';
-
- // data
- d: string;
-
- // encryption options - scrypt
- o: {
- N: number;
- r: number;
- p: number;
- dkLen: number;
- };
-
- // Salt options
- saltLen: number;
-};
-
-class EncryptorDecryptor {
- #ALGORITHM_NONCE_SIZE: number = 12; // 12 bytes
-
- #ALGORITHM_KEY_SIZE: number = 16; // 16 bytes
-
- #SCRYPT_SALT_SIZE: number = 16; // 16 bytes
-
- // see: https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#scrypt
- #SCRYPT_N: number = 2 ** 17; // CPU/memory cost parameter (must be a power of 2, > 1)
-
- #SCRYPT_r: number = 8; // Block size parameter
-
- #SCRYPT_p: number = 1; // Parallelization parameter
-
- encryptString(plaintext: string, password: string): string {
- try {
- return this.#encryptStringV1(plaintext, password);
- } catch (e) {
- const errorMessage = e instanceof Error ? e.message : e;
- throw new Error(`Unable to encrypt string - ${errorMessage}`);
- }
- }
-
- decryptString(encryptedDataStr: string, password: string): string {
- try {
- const encryptedData: EncryptedPayload = JSON.parse(encryptedDataStr);
- if (encryptedData.v === '1') {
- if (encryptedData.t === 'scrypt') {
- return this.#decryptStringV1(encryptedData, password);
- }
- }
- throw new Error(
- `Unsupported encrypted data payload - ${encryptedDataStr}`,
- );
- } catch (e) {
- const errorMessage = e instanceof Error ? e.message : e;
- throw new Error(`Unable to decrypt string - ${errorMessage}`);
- }
- }
-
- #encryptStringV1(plaintext: string, password: string): string {
- const { key, salt } = this.#getOrGenerateScryptKey(password, {
- N: this.#SCRYPT_N,
- r: this.#SCRYPT_r,
- p: this.#SCRYPT_p,
- dkLen: this.#ALGORITHM_KEY_SIZE,
- });
-
- // Encrypt and prepend salt.
- const plaintextRaw = utf8ToBytes(plaintext);
- const ciphertextAndNonceAndSalt = concatBytes(
- salt,
- this.#encrypt(plaintextRaw, key),
- );
-
- // Convert to Base64
- const encryptedData = byteArrayToBase64(ciphertextAndNonceAndSalt);
-
- const encryptedPayload: EncryptedPayload = {
- v: '1',
- t: 'scrypt',
- d: encryptedData,
- o: {
- N: this.#SCRYPT_N,
- r: this.#SCRYPT_r,
- p: this.#SCRYPT_p,
- dkLen: this.#ALGORITHM_KEY_SIZE,
- },
- saltLen: this.#SCRYPT_SALT_SIZE,
- };
-
- return JSON.stringify(encryptedPayload);
- }
-
- #decryptStringV1(data: EncryptedPayload, password: string): string {
- const { o, d: base64CiphertextAndNonceAndSalt, saltLen } = data;
-
- // Decode the base64.
- const ciphertextAndNonceAndSalt = base64ToByteArray(
- base64CiphertextAndNonceAndSalt,
- );
-
- // Create buffers of salt and ciphertextAndNonce.
- const salt = ciphertextAndNonceAndSalt.slice(0, saltLen);
- const ciphertextAndNonce = ciphertextAndNonceAndSalt.slice(
- saltLen,
- ciphertextAndNonceAndSalt.length,
- );
-
- // Derive the key.
- const { key } = this.#getOrGenerateScryptKey(
- password,
- {
- N: o.N,
- r: o.r,
- p: o.p,
- dkLen: o.dkLen,
- },
- salt,
- );
-
- // Decrypt and return result.
- return bytesToUtf8(this.#decrypt(ciphertextAndNonce, key));
- }
-
- #encrypt(plaintext: Uint8Array, key: Uint8Array): Uint8Array {
- const nonce = randomBytes(this.#ALGORITHM_NONCE_SIZE);
-
- // Encrypt and prepend nonce.
- const ciphertext = gcm(key, nonce).encrypt(plaintext);
-
- return concatBytes(nonce, ciphertext);
- }
-
- #decrypt(ciphertextAndNonce: Uint8Array, key: Uint8Array): Uint8Array {
- // Create buffers of nonce and ciphertext.
- const nonce = ciphertextAndNonce.slice(0, this.#ALGORITHM_NONCE_SIZE);
- const ciphertext = ciphertextAndNonce.slice(
- this.#ALGORITHM_NONCE_SIZE,
- ciphertextAndNonce.length,
- );
-
- // Decrypt and return result.
- return gcm(key, nonce).decrypt(ciphertext);
- }
-
- #getOrGenerateScryptKey(
- password: string,
- o: EncryptedPayload['o'],
- salt?: Uint8Array,
- ) {
- const hashedPassword = createSHA256Hash(password);
- const cachedKey = salt
- ? getCachedKeyBySalt(hashedPassword, salt)
- : getAnyCachedKey(hashedPassword);
-
- if (cachedKey) {
- return {
- key: cachedKey.key,
- salt: cachedKey.salt,
- };
- }
-
- const newSalt = salt ?? randomBytes(this.#SCRYPT_SALT_SIZE);
- const newKey = scrypt(password, newSalt, {
- N: o.N,
- r: o.r,
- p: o.p,
- dkLen: o.dkLen,
- });
- setCachedKey(hashedPassword, newSalt, newKey);
-
- return {
- key: newKey,
- salt: newSalt,
- };
- }
-}
-
-const encryption = new EncryptorDecryptor();
-export default encryption;
-
-export function createSHA256Hash(data: string): string {
- const hashedData = sha256(data);
- return bytesToHex(hashedData);
-}
diff --git a/app/scripts/controllers/user-storage/encryption/index.ts b/app/scripts/controllers/user-storage/encryption/index.ts
deleted file mode 100644
index 3582e3b9e2a1..000000000000
--- a/app/scripts/controllers/user-storage/encryption/index.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-import Encryption from './encryption';
-
-export * from './encryption';
-export default Encryption;
diff --git a/app/scripts/controllers/user-storage/encryption/utils.ts b/app/scripts/controllers/user-storage/encryption/utils.ts
deleted file mode 100644
index 76ceda77eb7b..000000000000
--- a/app/scripts/controllers/user-storage/encryption/utils.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-export function byteArrayToBase64(byteArray: Uint8Array) {
- return Buffer.from(byteArray).toString('base64');
-}
-
-export function base64ToByteArray(base64: string) {
- return new Uint8Array(Buffer.from(base64, 'base64'));
-}
-
-export function bytesToUtf8(byteArray: Uint8Array) {
- const decoder = new TextDecoder('utf-8');
- return decoder.decode(byteArray);
-}
diff --git a/app/scripts/controllers/user-storage/mocks/mockResponses.ts b/app/scripts/controllers/user-storage/mocks/mockResponses.ts
deleted file mode 100644
index c1b3896f446f..000000000000
--- a/app/scripts/controllers/user-storage/mocks/mockResponses.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-import { createEntryPath } from '../schema';
-import { GetUserStorageResponse, USER_STORAGE_ENDPOINT } from '../services';
-import { MOCK_ENCRYPTED_STORAGE_DATA, MOCK_STORAGE_KEY } from './mockStorage';
-
-type MockResponse = {
- url: string;
- requestMethod: 'GET' | 'POST' | 'PUT';
- response: unknown;
-};
-
-export const MOCK_USER_STORAGE_NOTIFICATIONS_ENDPOINT = `${USER_STORAGE_ENDPOINT}${createEntryPath(
- 'notification_settings',
- MOCK_STORAGE_KEY,
-)}`;
-
-const MOCK_GET_USER_STORAGE_RESPONSE: GetUserStorageResponse = {
- HashedKey: 'HASHED_KEY',
- Data: MOCK_ENCRYPTED_STORAGE_DATA,
-};
-
-export function getMockUserStorageGetResponse() {
- return {
- url: MOCK_USER_STORAGE_NOTIFICATIONS_ENDPOINT,
- requestMethod: 'GET',
- response: MOCK_GET_USER_STORAGE_RESPONSE,
- } satisfies MockResponse;
-}
-
-export function getMockUserStoragePutResponse() {
- return {
- url: MOCK_USER_STORAGE_NOTIFICATIONS_ENDPOINT,
- requestMethod: 'PUT',
- response: null,
- } satisfies MockResponse;
-}
diff --git a/app/scripts/controllers/user-storage/mocks/mockServices.ts b/app/scripts/controllers/user-storage/mocks/mockServices.ts
deleted file mode 100644
index d612ebe6ed55..000000000000
--- a/app/scripts/controllers/user-storage/mocks/mockServices.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-import nock from 'nock';
-import {
- getMockUserStorageGetResponse,
- getMockUserStoragePutResponse,
-} from './mockResponses';
-
-type MockReply = {
- status: nock.StatusCode;
- body?: nock.Body;
-};
-
-export function mockEndpointGetUserStorage(mockReply?: MockReply) {
- const mockResponse = getMockUserStorageGetResponse();
- const reply = mockReply ?? {
- status: 200,
- body: mockResponse.response,
- };
-
- const mockEndpoint = nock(mockResponse.url)
- .get('')
- .reply(reply.status, reply.body);
-
- return mockEndpoint;
-}
-
-export function mockEndpointUpsertUserStorage(
- mockReply?: Pick,
-) {
- const mockResponse = getMockUserStoragePutResponse();
- const mockEndpoint = nock(mockResponse.url)
- .put('')
- .reply(mockReply?.status ?? 204);
- return mockEndpoint;
-}
diff --git a/app/scripts/controllers/user-storage/mocks/mockStorage.ts b/app/scripts/controllers/user-storage/mocks/mockStorage.ts
deleted file mode 100644
index 4a43a80556e1..000000000000
--- a/app/scripts/controllers/user-storage/mocks/mockStorage.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import encryption, { createSHA256Hash } from '../encryption';
-
-export const MOCK_STORAGE_KEY_SIGNATURE = 'mockStorageKey';
-export const MOCK_STORAGE_KEY = createSHA256Hash(MOCK_STORAGE_KEY_SIGNATURE);
-export const MOCK_STORAGE_DATA = JSON.stringify({ hello: 'world' });
-export const MOCK_ENCRYPTED_STORAGE_DATA = encryption.encryptString(
- MOCK_STORAGE_DATA,
- MOCK_STORAGE_KEY,
-);
diff --git a/app/scripts/controllers/user-storage/schema.test.ts b/app/scripts/controllers/user-storage/schema.test.ts
deleted file mode 100644
index d08b0802bf49..000000000000
--- a/app/scripts/controllers/user-storage/schema.test.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-import { USER_STORAGE_ENTRIES, createEntryPath } from './schema';
-
-describe('schema.ts - createEntryPath()', () => {
- const MOCK_STORAGE_KEY = 'MOCK_STORAGE_KEY';
-
- test('creates a valid entry path', () => {
- const result = createEntryPath('notification_settings', MOCK_STORAGE_KEY);
-
- // Ensures that the path and the entry name are correct.
- // If this differs then indicates a potential change on how this path is computed
- const expected = `/${USER_STORAGE_ENTRIES.notification_settings.path}/50f65447980018849b991e038d7ad87de5cf07fbad9736b0280e93972e17bac8`;
- expect(result).toBe(expected);
- });
-
- test('Should throw if using an entry that does not exist', () => {
- expect(() => {
- // @ts-expect-error mocking a fake entry for testing.
- createEntryPath('fake_entry');
- }).toThrow();
- });
-});
diff --git a/app/scripts/controllers/user-storage/schema.ts b/app/scripts/controllers/user-storage/schema.ts
deleted file mode 100644
index 19bc0ccfae52..000000000000
--- a/app/scripts/controllers/user-storage/schema.ts
+++ /dev/null
@@ -1,38 +0,0 @@
-import { createSHA256Hash } from './encryption';
-
-type UserStorageEntry = { path: string; entryName: string };
-
-/**
- * The User Storage Endpoint requires a path and an entry name.
- * Developers can provide additional paths by extending this variable below
- */
-export const USER_STORAGE_ENTRIES = {
- notification_settings: {
- path: 'notifications',
- entryName: 'notification_settings',
- },
-} satisfies Record;
-
-export type UserStorageEntryKeys = keyof typeof USER_STORAGE_ENTRIES;
-
-/**
- * Constructs a unique entry path for a user.
- * This can be done due to the uniqueness of the storage key (no users will share the same storage key).
- * The users entry is a unique hash that cannot be reversed.
- *
- * @param entryKey
- * @param storageKey
- * @returns
- */
-export function createEntryPath(
- entryKey: UserStorageEntryKeys,
- storageKey: string,
-): string {
- const entry = USER_STORAGE_ENTRIES[entryKey];
- if (!entry) {
- throw new Error(`user-storage - invalid entry provided: ${entryKey}`);
- }
-
- const hashedKey = createSHA256Hash(entry.entryName + storageKey);
- return `/${entry.path}/${hashedKey}`;
-}
diff --git a/app/scripts/controllers/user-storage/services.test.ts b/app/scripts/controllers/user-storage/services.test.ts
deleted file mode 100644
index a746dcee858f..000000000000
--- a/app/scripts/controllers/user-storage/services.test.ts
+++ /dev/null
@@ -1,89 +0,0 @@
-import {
- mockEndpointGetUserStorage,
- mockEndpointUpsertUserStorage,
-} from './mocks/mockServices';
-import {
- MOCK_ENCRYPTED_STORAGE_DATA,
- MOCK_STORAGE_DATA,
- MOCK_STORAGE_KEY,
-} from './mocks/mockStorage';
-import {
- GetUserStorageResponse,
- getUserStorage,
- upsertUserStorage,
-} from './services';
-
-describe('user-storage/services.ts - getUserStorage() tests', () => {
- test('returns user storage data', async () => {
- const mockGetUserStorage = mockEndpointGetUserStorage();
- const result = await actCallGetUserStorage();
-
- mockGetUserStorage.done();
- expect(result).toBe(MOCK_STORAGE_DATA);
- });
-
- test('returns null if endpoint does not have entry', async () => {
- const mockGetUserStorage = mockEndpointGetUserStorage({ status: 404 });
- const result = await actCallGetUserStorage();
-
- mockGetUserStorage.done();
- expect(result).toBe(null);
- });
-
- test('returns null if endpoint fails', async () => {
- const mockGetUserStorage = mockEndpointGetUserStorage({ status: 500 });
- const result = await actCallGetUserStorage();
-
- mockGetUserStorage.done();
- expect(result).toBe(null);
- });
-
- test('returns null if unable to decrypt data', async () => {
- const badResponseData: GetUserStorageResponse = {
- HashedKey: 'MOCK_HASH',
- Data: 'Bad Encrypted Data',
- };
- const mockGetUserStorage = mockEndpointGetUserStorage({
- status: 200,
- body: badResponseData,
- });
- const result = await actCallGetUserStorage();
-
- mockGetUserStorage.done();
- expect(result).toBe(null);
- });
-
- function actCallGetUserStorage() {
- return getUserStorage({
- bearerToken: 'MOCK_BEARER_TOKEN',
- entryKey: 'notification_settings',
- storageKey: MOCK_STORAGE_KEY,
- });
- }
-});
-
-describe('user-storage/services.ts - upsertUserStorage() tests', () => {
- test('invokes upsert endpoint with no errors', async () => {
- const mockUpsertUserStorage = mockEndpointUpsertUserStorage();
- await actCallUpsertUserStorage();
-
- expect(mockUpsertUserStorage.isDone()).toBe(true);
- });
-
- test('throws error if unable to upsert user storage', async () => {
- const mockUpsertUserStorage = mockEndpointUpsertUserStorage({
- status: 500,
- });
-
- await expect(actCallUpsertUserStorage()).rejects.toThrow();
- mockUpsertUserStorage.done();
- });
-
- function actCallUpsertUserStorage() {
- return upsertUserStorage(MOCK_ENCRYPTED_STORAGE_DATA, {
- bearerToken: 'MOCK_BEARER_TOKEN',
- entryKey: 'notification_settings',
- storageKey: MOCK_STORAGE_KEY,
- });
- }
-});
diff --git a/app/scripts/controllers/user-storage/services.ts b/app/scripts/controllers/user-storage/services.ts
deleted file mode 100644
index 269009850079..000000000000
--- a/app/scripts/controllers/user-storage/services.ts
+++ /dev/null
@@ -1,83 +0,0 @@
-import log from 'loglevel';
-
-import encryption from './encryption';
-import { UserStorageEntryKeys, createEntryPath } from './schema';
-
-export const USER_STORAGE_API = process.env.USER_STORAGE_API || '';
-export const USER_STORAGE_ENDPOINT = `${USER_STORAGE_API}/api/v1/userstorage`;
-
-export type GetUserStorageResponse = {
- HashedKey: string;
- Data: string;
-};
-
-export type UserStorageOptions = {
- bearerToken: string;
- entryKey: UserStorageEntryKeys;
- storageKey: string;
-};
-
-export async function getUserStorage(
- opts: UserStorageOptions,
-): Promise {
- try {
- const path = createEntryPath(opts.entryKey, opts.storageKey);
- const url = new URL(`${USER_STORAGE_ENDPOINT}${path}`);
-
- const userStorageResponse = await fetch(url.toString(), {
- headers: {
- 'Content-Type': 'application/json',
- Authorization: `Bearer ${opts.bearerToken}`,
- },
- });
-
- // Acceptable error - since indicates entry does not exist.
- if (userStorageResponse.status === 404) {
- return null;
- }
-
- if (userStorageResponse.status !== 200) {
- throw new Error('Unable to get User Storage');
- }
-
- const userStorage: GetUserStorageResponse | null =
- await userStorageResponse.json();
- const encryptedData = userStorage?.Data ?? null;
-
- if (!encryptedData) {
- return null;
- }
-
- const decryptedData = encryption.decryptString(
- encryptedData,
- opts.storageKey,
- );
-
- return decryptedData;
- } catch (e) {
- log.error('Failed to get user storage', e);
- return null;
- }
-}
-
-export async function upsertUserStorage(
- data: string,
- opts: UserStorageOptions,
-): Promise {
- const encryptedData = encryption.encryptString(data, opts.storageKey);
- const path = createEntryPath(opts.entryKey, opts.storageKey);
- const url = new URL(`${USER_STORAGE_ENDPOINT}${path}`);
-
- const res = await fetch(url.toString(), {
- method: 'PUT',
- headers: {
- 'Content-Type': 'application/json',
- Authorization: `Bearer ${opts.bearerToken}`,
- },
- body: JSON.stringify({ data: encryptedData }),
- });
-
- if (!res.ok) {
- throw new Error('user-storage - unable to upsert data');
- }
-}
diff --git a/app/scripts/controllers/user-storage/user-storage-controller.test.ts b/app/scripts/controllers/user-storage/user-storage-controller.test.ts
deleted file mode 100644
index 8d3f61d6ab21..000000000000
--- a/app/scripts/controllers/user-storage/user-storage-controller.test.ts
+++ /dev/null
@@ -1,465 +0,0 @@
-import nock from 'nock';
-import { ControllerMessenger } from '@metamask/base-controller';
-import {
- AuthenticationControllerGetBearerToken,
- AuthenticationControllerGetSessionProfile,
- AuthenticationControllerIsSignedIn,
- AuthenticationControllerPerformSignIn,
-} from '../authentication/authentication-controller';
-import {
- MetamaskNotificationsControllerDisableMetamaskNotifications,
- MetamaskNotificationsControllerSelectIsMetamaskNotificationsEnabled,
-} from '../metamask-notifications/metamask-notifications';
-import {
- MOCK_STORAGE_DATA,
- MOCK_STORAGE_KEY,
- MOCK_STORAGE_KEY_SIGNATURE,
-} from './mocks/mockStorage';
-import UserStorageController, {
- AllowedActions,
- AllowedEvents,
-} from './user-storage-controller';
-import {
- mockEndpointGetUserStorage,
- mockEndpointUpsertUserStorage,
-} from './mocks/mockServices';
-
-const typedMockFn = unknown>() =>
- jest.fn, Parameters>();
-
-describe('user-storage/user-storage-controller - constructor() tests', () => {
- test('Creates UserStorage with default state', () => {
- const { messengerMocks } = arrangeMocks();
- const controller = new UserStorageController({
- messenger: messengerMocks.messenger,
- getMetaMetricsState: () => true,
- });
-
- expect(controller.state.isProfileSyncingEnabled).toBe(true);
- });
-
- function arrangeMocks() {
- return {
- messengerMocks: mockUserStorageMessenger(),
- };
- }
-});
-
-describe('user-storage/user-storage-controller - performGetStorage() tests', () => {
- test('returns users notification storage', async () => {
- const { messengerMocks, mockAPI } = arrangeMocks();
- const controller = new UserStorageController({
- messenger: messengerMocks.messenger,
- getMetaMetricsState: () => true,
- });
-
- const result = await controller.performGetStorage('notification_settings');
- mockAPI.done();
- expect(result).toBe(MOCK_STORAGE_DATA);
- });
-
- test('rejects if UserStorage is not enabled', async () => {
- const { messengerMocks } = arrangeMocks();
- const controller = new UserStorageController({
- messenger: messengerMocks.messenger,
- getMetaMetricsState: () => true,
- state: {
- isProfileSyncingEnabled: false,
- isProfileSyncingUpdateLoading: false,
- },
- });
-
- await expect(
- controller.performGetStorage('notification_settings'),
- ).rejects.toThrow();
- });
-
- test('rejects if wallet is locked', async () => {
- const { messengerMocks } = arrangeMocks();
-
- // Mock wallet is locked
- messengerMocks.mockKeyringControllerGetState.mockReturnValue({
- isUnlocked: false,
- });
-
- const controller = new UserStorageController({
- messenger: messengerMocks.messenger,
- getMetaMetricsState: () => true,
- });
-
- await expect(
- controller.performGetStorage('notification_settings'),
- ).rejects.toThrow();
- });
-
- // @ts-expect-error This is missing from the Mocha type definitions
- test.each([
- [
- 'fails when no bearer token is found (auth errors)',
- (messengerMocks: ReturnType) =>
- messengerMocks.mockAuthGetBearerToken.mockRejectedValue(
- new Error('MOCK FAILURE'),
- ),
- ],
- [
- 'fails when no session identifier is found (auth errors)',
- (messengerMocks: ReturnType) =>
- messengerMocks.mockAuthGetSessionProfile.mockRejectedValue(
- new Error('MOCK FAILURE'),
- ),
- ],
- ])(
- 'rejects on auth failure - %s',
- async (
- _: string,
- arrangeFailureCase: (
- messengerMocks: ReturnType,
- ) => void,
- ) => {
- const { messengerMocks } = arrangeMocks();
- arrangeFailureCase(messengerMocks);
- const controller = new UserStorageController({
- messenger: messengerMocks.messenger,
- getMetaMetricsState: () => true,
- });
-
- await expect(
- controller.performGetStorage('notification_settings'),
- ).rejects.toThrow();
- },
- );
-
- function arrangeMocks() {
- return {
- messengerMocks: mockUserStorageMessenger(),
- mockAPI: mockEndpointGetUserStorage(),
- };
- }
-});
-
-describe('user-storage/user-storage-controller - performSetStorage() tests', () => {
- test('saves users storage', async () => {
- const { messengerMocks, mockAPI } = arrangeMocks();
- const controller = new UserStorageController({
- messenger: messengerMocks.messenger,
- getMetaMetricsState: () => true,
- });
-
- await controller.performSetStorage('notification_settings', 'new data');
- mockAPI.done();
- });
-
- test('rejects if UserStorage is not enabled', async () => {
- const { messengerMocks } = arrangeMocks();
- const controller = new UserStorageController({
- messenger: messengerMocks.messenger,
- getMetaMetricsState: () => true,
- state: {
- isProfileSyncingEnabled: false,
- isProfileSyncingUpdateLoading: false,
- },
- });
-
- await expect(
- controller.performSetStorage('notification_settings', 'new data'),
- ).rejects.toThrow();
- });
-
- test('rejects if wallet is locked', async () => {
- const { messengerMocks } = arrangeMocks();
-
- // Mock wallet is locked
- messengerMocks.mockKeyringControllerGetState.mockReturnValue({
- isUnlocked: false,
- });
-
- const controller = new UserStorageController({
- messenger: messengerMocks.messenger,
- getMetaMetricsState: () => true,
- });
-
- await expect(
- controller.performSetStorage('notification_settings', 'new data'),
- ).rejects.toThrow();
- });
-
- // @ts-expect-error This is missing from the Mocha type definitions
- test.each([
- [
- 'fails when no bearer token is found (auth errors)',
- (messengerMocks: ReturnType) =>
- messengerMocks.mockAuthGetBearerToken.mockRejectedValue(
- new Error('MOCK FAILURE'),
- ),
- ],
- [
- 'fails when no session identifier is found (auth errors)',
- (messengerMocks: ReturnType) =>
- messengerMocks.mockAuthGetSessionProfile.mockRejectedValue(
- new Error('MOCK FAILURE'),
- ),
- ],
- ])(
- 'rejects on auth failure - %s',
- async (
- _: string,
- arrangeFailureCase: (
- messengerMocks: ReturnType,
- ) => void,
- ) => {
- const { messengerMocks } = arrangeMocks();
- arrangeFailureCase(messengerMocks);
- const controller = new UserStorageController({
- messenger: messengerMocks.messenger,
- getMetaMetricsState: () => true,
- });
-
- await expect(
- controller.performSetStorage('notification_settings', 'new data'),
- ).rejects.toThrow();
- },
- );
-
- test('rejects if api call fails', async () => {
- const { messengerMocks } = arrangeMocks({
- mockAPI: mockEndpointUpsertUserStorage({ status: 500 }),
- });
- const controller = new UserStorageController({
- messenger: messengerMocks.messenger,
- getMetaMetricsState: () => true,
- });
- await expect(
- controller.performSetStorage('notification_settings', 'new data'),
- ).rejects.toThrow();
- });
-
- function arrangeMocks(overrides?: { mockAPI?: nock.Scope }) {
- return {
- messengerMocks: mockUserStorageMessenger(),
- mockAPI: overrides?.mockAPI ?? mockEndpointUpsertUserStorage(),
- };
- }
-});
-
-describe('user-storage/user-storage-controller - performSetStorage() tests', () => {
- test('Should return a storage key', async () => {
- const { messengerMocks } = arrangeMocks();
- const controller = new UserStorageController({
- messenger: messengerMocks.messenger,
- getMetaMetricsState: () => true,
- });
-
- const result = await controller.getStorageKey();
- expect(result).toBe(MOCK_STORAGE_KEY);
- });
-
- test('rejects if UserStorage is not enabled', async () => {
- const { messengerMocks } = arrangeMocks();
- const controller = new UserStorageController({
- messenger: messengerMocks.messenger,
- getMetaMetricsState: () => true,
- state: {
- isProfileSyncingEnabled: false,
- isProfileSyncingUpdateLoading: false,
- },
- });
-
- await expect(controller.getStorageKey()).rejects.toThrow();
- });
-
- function arrangeMocks() {
- return {
- messengerMocks: mockUserStorageMessenger(),
- };
- }
-});
-
-describe('user-storage/user-storage-controller - disableProfileSyncing() tests', () => {
- test('should disable user storage / profile syncing when called', async () => {
- const { messengerMocks } = arrangeMocks();
- const controller = new UserStorageController({
- messenger: messengerMocks.messenger,
- getMetaMetricsState: () => true,
- });
-
- expect(controller.state.isProfileSyncingEnabled).toBe(true);
- await controller.disableProfileSyncing();
- expect(controller.state.isProfileSyncingEnabled).toBe(false);
- });
-
- function arrangeMocks() {
- return {
- messengerMocks: mockUserStorageMessenger(),
- };
- }
-});
-
-describe('user-storage/user-storage-controller - enableProfileSyncing() tests', () => {
- test('should enable user storage / profile syncing', async () => {
- const { messengerMocks } = arrangeMocks();
- messengerMocks.mockAuthIsSignedIn.mockReturnValue(false); // mock that auth is not enabled
-
- const controller = new UserStorageController({
- messenger: messengerMocks.messenger,
- getMetaMetricsState: () => true,
- state: {
- isProfileSyncingEnabled: false,
- isProfileSyncingUpdateLoading: false,
- },
- });
-
- expect(controller.state.isProfileSyncingEnabled).toBe(false);
- await controller.enableProfileSyncing();
- expect(controller.state.isProfileSyncingEnabled).toBe(true);
- expect(messengerMocks.mockAuthIsSignedIn).toBeCalled();
- expect(messengerMocks.mockAuthPerformSignIn).toBeCalled();
- });
-
- function arrangeMocks() {
- return {
- messengerMocks: mockUserStorageMessenger(),
- };
- }
-});
-
-function mockUserStorageMessenger() {
- const messenger = new ControllerMessenger<
- AllowedActions,
- AllowedEvents
- >().getRestricted({
- name: 'UserStorageController',
- allowedActions: [
- 'KeyringController:getState',
- 'SnapController:handleRequest',
- 'AuthenticationController:getBearerToken',
- 'AuthenticationController:getSessionProfile',
- 'AuthenticationController:isSignedIn',
- 'AuthenticationController:performSignIn',
- 'AuthenticationController:performSignOut',
- 'MetamaskNotificationsController:disableMetamaskNotifications',
- 'MetamaskNotificationsController:selectIsMetamaskNotificationsEnabled',
- ],
- allowedEvents: ['KeyringController:lock', 'KeyringController:unlock'],
- });
-
- const mockSnapGetPublicKey = jest.fn().mockResolvedValue('MOCK_PUBLIC_KEY');
- const mockSnapSignMessage = jest
- .fn()
- .mockResolvedValue(MOCK_STORAGE_KEY_SIGNATURE);
-
- const mockAuthGetBearerToken =
- typedMockFn<
- AuthenticationControllerGetBearerToken['handler']
- >().mockResolvedValue('MOCK_BEARER_TOKEN');
-
- const mockAuthGetSessionProfile = typedMockFn<
- AuthenticationControllerGetSessionProfile['handler']
- >().mockResolvedValue({
- identifierId: '',
- profileId: 'MOCK_PROFILE_ID',
- });
-
- const mockAuthPerformSignIn =
- typedMockFn<
- AuthenticationControllerPerformSignIn['handler']
- >().mockResolvedValue('New Access Token');
-
- const mockAuthIsSignedIn =
- typedMockFn<
- AuthenticationControllerIsSignedIn['handler']
- >().mockReturnValue(true);
-
- const mockAuthPerformSignOut =
- typedMockFn<
- AuthenticationControllerIsSignedIn['handler']
- >().mockReturnValue(true);
-
- const mockMetamaskNotificationsIsMetamaskNotificationsEnabled =
- typedMockFn<
- MetamaskNotificationsControllerSelectIsMetamaskNotificationsEnabled['handler']
- >().mockReturnValue(true);
-
- const mockMetamaskNotificationsDisableNotifications =
- typedMockFn<
- MetamaskNotificationsControllerDisableMetamaskNotifications['handler']
- >().mockResolvedValue();
-
- const mockKeyringControllerGetState = typedMockFn<
- () => { isUnlocked: boolean }
- >().mockReturnValue({ isUnlocked: true });
-
- jest.spyOn(messenger, 'call').mockImplementation((...args) => {
- const [actionType, params] = args;
- if (actionType === 'SnapController:handleRequest') {
- if (params?.request.method === 'getPublicKey') {
- return mockSnapGetPublicKey();
- }
-
- if (params?.request.method === 'signMessage') {
- return mockSnapSignMessage();
- }
-
- throw new Error(
- `MOCK_FAIL - unsupported SnapController:handleRequest call: ${params?.request.method}`,
- );
- }
-
- if (actionType === 'AuthenticationController:getBearerToken') {
- return mockAuthGetBearerToken();
- }
-
- if (actionType === 'AuthenticationController:getSessionProfile') {
- return mockAuthGetSessionProfile();
- }
-
- if (actionType === 'AuthenticationController:performSignIn') {
- return mockAuthPerformSignIn();
- }
-
- if (actionType === 'AuthenticationController:isSignedIn') {
- return mockAuthIsSignedIn();
- }
-
- if (
- actionType ===
- 'MetamaskNotificationsController:selectIsMetamaskNotificationsEnabled'
- ) {
- return mockMetamaskNotificationsIsMetamaskNotificationsEnabled();
- }
-
- if (
- actionType ===
- 'MetamaskNotificationsController:disableMetamaskNotifications'
- ) {
- return mockMetamaskNotificationsDisableNotifications();
- }
-
- if (actionType === 'AuthenticationController:performSignOut') {
- return mockAuthPerformSignOut();
- }
-
- if (actionType === 'KeyringController:getState') {
- return mockKeyringControllerGetState();
- }
-
- function exhaustedMessengerMocks(action: never) {
- throw new Error(`MOCK_FAIL - unsupported messenger call: ${action}`);
- }
-
- return exhaustedMessengerMocks(actionType);
- });
-
- return {
- messenger,
- mockSnapGetPublicKey,
- mockSnapSignMessage,
- mockAuthGetBearerToken,
- mockAuthGetSessionProfile,
- mockAuthPerformSignIn,
- mockAuthIsSignedIn,
- mockMetamaskNotificationsIsMetamaskNotificationsEnabled,
- mockMetamaskNotificationsDisableNotifications,
- mockAuthPerformSignOut,
- mockKeyringControllerGetState,
- };
-}
diff --git a/app/scripts/controllers/user-storage/user-storage-controller.ts b/app/scripts/controllers/user-storage/user-storage-controller.ts
deleted file mode 100644
index 9a286f801330..000000000000
--- a/app/scripts/controllers/user-storage/user-storage-controller.ts
+++ /dev/null
@@ -1,434 +0,0 @@
-import {
- BaseController,
- RestrictedControllerMessenger,
- StateMetadata,
-} from '@metamask/base-controller';
-import type {
- KeyringControllerGetStateAction,
- KeyringControllerLockEvent,
- KeyringControllerUnlockEvent,
-} from '@metamask/keyring-controller';
-import { HandleSnapRequest } from '@metamask/snaps-controllers';
-import {
- AuthenticationControllerGetBearerToken,
- AuthenticationControllerGetSessionProfile,
- AuthenticationControllerIsSignedIn,
- AuthenticationControllerPerformSignIn,
- AuthenticationControllerPerformSignOut,
-} from '../authentication/authentication-controller';
-import {
- MetamaskNotificationsControllerDisableMetamaskNotifications,
- MetamaskNotificationsControllerSelectIsMetamaskNotificationsEnabled,
-} from '../metamask-notifications/metamask-notifications';
-import { createSnapSignMessageRequest } from '../authentication/auth-snap-requests';
-import { getUserStorage, upsertUserStorage } from './services';
-import { UserStorageEntryKeys } from './schema';
-import { createSHA256Hash } from './encryption';
-
-const controllerName = 'UserStorageController';
-
-// State
-export type UserStorageControllerState = {
- /**
- * Condition used by UI and to determine if we can use some of the User Storage methods.
- */
- isProfileSyncingEnabled: boolean | null;
- /**
- * Loading state for the profile syncing update
- */
- isProfileSyncingUpdateLoading: boolean;
-};
-
-const defaultState: UserStorageControllerState = {
- isProfileSyncingEnabled: true,
- isProfileSyncingUpdateLoading: false,
-};
-
-const metadata: StateMetadata = {
- isProfileSyncingEnabled: {
- persist: true,
- anonymous: true,
- },
- isProfileSyncingUpdateLoading: {
- persist: false,
- anonymous: false,
- },
-};
-
-// Messenger Actions
-type CreateActionsObj = {
- [K in T]: {
- type: `${typeof controllerName}:${K}`;
- handler: UserStorageController[K];
- };
-};
-type ActionsObj = CreateActionsObj<
- | 'performGetStorage'
- | 'performSetStorage'
- | 'getStorageKey'
- | 'enableProfileSyncing'
- | 'disableProfileSyncing'
->;
-export type Actions = ActionsObj[keyof ActionsObj];
-export type UserStorageControllerPerformGetStorage =
- ActionsObj['performGetStorage'];
-export type UserStorageControllerPerformSetStorage =
- ActionsObj['performSetStorage'];
-export type UserStorageControllerGetStorageKey = ActionsObj['getStorageKey'];
-export type UserStorageControllerEnableProfileSyncing =
- ActionsObj['enableProfileSyncing'];
-export type UserStorageControllerDisableProfileSyncing =
- ActionsObj['disableProfileSyncing'];
-
-export type AllowedActions =
- // Keyring Requests
- | KeyringControllerGetStateAction
- // Snap Requests
- | HandleSnapRequest
- // Auth Requests
- | AuthenticationControllerGetBearerToken
- | AuthenticationControllerGetSessionProfile
- | AuthenticationControllerPerformSignIn
- | AuthenticationControllerIsSignedIn
- | AuthenticationControllerPerformSignOut
- // Metamask Notifications
- | MetamaskNotificationsControllerDisableMetamaskNotifications
- | MetamaskNotificationsControllerSelectIsMetamaskNotificationsEnabled;
-
-export type AllowedEvents =
- | KeyringControllerLockEvent
- | KeyringControllerUnlockEvent;
-
-// Messenger
-export type UserStorageControllerMessenger = RestrictedControllerMessenger<
- typeof controllerName,
- Actions | AllowedActions,
- AllowedEvents,
- AllowedActions['type'],
- AllowedEvents['type']
->;
-
-/**
- * Reusable controller that allows any team to store synchronized data for a given user.
- * These can be settings shared cross MetaMask clients, or data we want to persist when uninstalling/reinstalling.
- *
- * NOTE:
- * - data stored on UserStorage is FULLY encrypted, with the only keys stored/managed on the client.
- * - No one can access this data unless they are have the SRP and are able to run the signing snap.
- */
-export default class UserStorageController extends BaseController<
- typeof controllerName,
- UserStorageControllerState,
- UserStorageControllerMessenger
-> {
- #auth = {
- getBearerToken: async () => {
- return await this.messagingSystem.call(
- 'AuthenticationController:getBearerToken',
- );
- },
- getProfileId: async () => {
- const sessionProfile = await this.messagingSystem.call(
- 'AuthenticationController:getSessionProfile',
- );
- return sessionProfile?.profileId;
- },
- isAuthEnabled: () => {
- return this.messagingSystem.call('AuthenticationController:isSignedIn');
- },
- signIn: async () => {
- return await this.messagingSystem.call(
- 'AuthenticationController:performSignIn',
- );
- },
- signOut: async () => {
- return await this.messagingSystem.call(
- 'AuthenticationController:performSignOut',
- );
- },
- };
-
- #metamaskNotifications = {
- disableMetamaskNotifications: async () => {
- return await this.messagingSystem.call(
- 'MetamaskNotificationsController:disableMetamaskNotifications',
- );
- },
- selectIsMetamaskNotificationsEnabled: async () => {
- return await this.messagingSystem.call(
- 'MetamaskNotificationsController:selectIsMetamaskNotificationsEnabled',
- );
- },
- };
-
- #isUnlocked = false;
-
- #keyringController = {
- setupLockedStateSubscriptions: () => {
- const { isUnlocked } = this.messagingSystem.call(
- 'KeyringController:getState',
- );
- this.#isUnlocked = isUnlocked;
-
- this.messagingSystem.subscribe('KeyringController:unlock', () => {
- this.#isUnlocked = true;
- });
-
- this.messagingSystem.subscribe('KeyringController:lock', () => {
- this.#isUnlocked = false;
- });
- },
- };
-
- getMetaMetricsState: () => boolean;
-
- constructor(params: {
- messenger: UserStorageControllerMessenger;
- state?: UserStorageControllerState;
- getMetaMetricsState: () => boolean;
- }) {
- super({
- messenger: params.messenger,
- metadata,
- name: controllerName,
- state: { ...defaultState, ...params.state },
- });
-
- this.getMetaMetricsState = params.getMetaMetricsState;
- this.#keyringController.setupLockedStateSubscriptions();
- this.#registerMessageHandlers();
- }
-
- /**
- * Constructor helper for registering this controller's messaging system
- * actions.
- */
- #registerMessageHandlers(): void {
- this.messagingSystem.registerActionHandler(
- 'UserStorageController:performGetStorage',
- this.performGetStorage.bind(this),
- );
-
- this.messagingSystem.registerActionHandler(
- 'UserStorageController:performSetStorage',
- this.performSetStorage.bind(this),
- );
-
- this.messagingSystem.registerActionHandler(
- 'UserStorageController:getStorageKey',
- this.getStorageKey.bind(this),
- );
-
- this.messagingSystem.registerActionHandler(
- 'UserStorageController:enableProfileSyncing',
- this.enableProfileSyncing.bind(this),
- );
-
- this.messagingSystem.registerActionHandler(
- 'UserStorageController:disableProfileSyncing',
- this.disableProfileSyncing.bind(this),
- );
- }
-
- public async enableProfileSyncing(): Promise {
- try {
- this.#setIsProfileSyncingUpdateLoading(true);
-
- const authEnabled = this.#auth.isAuthEnabled();
- if (!authEnabled) {
- await this.#auth.signIn();
- }
-
- this.update((state) => {
- state.isProfileSyncingEnabled = true;
- });
-
- this.#setIsProfileSyncingUpdateLoading(false);
- } catch (e) {
- this.#setIsProfileSyncingUpdateLoading(false);
- const errorMessage = e instanceof Error ? e.message : e;
- throw new Error(
- `${controllerName} - failed to enable profile syncing - ${errorMessage}`,
- );
- }
- }
-
- public async setIsProfileSyncingEnabled(
- isProfileSyncingEnabled: boolean,
- ): Promise {
- this.update((state) => {
- state.isProfileSyncingEnabled = isProfileSyncingEnabled;
- });
- }
-
- public async disableProfileSyncing(): Promise {
- const isAlreadyDisabled = !this.state.isProfileSyncingEnabled;
- if (isAlreadyDisabled) {
- return;
- }
-
- try {
- this.#setIsProfileSyncingUpdateLoading(true);
-
- const isMetamaskNotificationsEnabled =
- await this.#metamaskNotifications.selectIsMetamaskNotificationsEnabled();
-
- if (isMetamaskNotificationsEnabled) {
- await this.#metamaskNotifications.disableMetamaskNotifications();
- }
-
- const isMetaMetricsParticipation = this.getMetaMetricsState();
-
- if (!isMetaMetricsParticipation) {
- await this.messagingSystem.call(
- 'AuthenticationController:performSignOut',
- );
- }
-
- this.#setIsProfileSyncingUpdateLoading(false);
-
- this.update((state) => {
- state.isProfileSyncingEnabled = false;
- });
- } catch (e) {
- this.#setIsProfileSyncingUpdateLoading(false);
- const errorMessage = e instanceof Error ? e.message : e;
- throw new Error(
- `${controllerName} - failed to disable profile syncing - ${errorMessage}`,
- );
- }
- }
-
- /**
- * Allows retrieval of stored data. Data stored is string formatted.
- * Developers can extend the entry path and entry name through the `schema.ts` file.
- *
- * @param entryKey
- * @returns the decrypted string contents found from user storage (or null if not found)
- */
- public async performGetStorage(
- entryKey: UserStorageEntryKeys,
- ): Promise {
- this.#assertProfileSyncingEnabled();
- const { bearerToken, storageKey } =
- await this.#getStorageKeyAndBearerToken();
- const result = await getUserStorage({
- entryKey,
- bearerToken,
- storageKey,
- });
-
- return result;
- }
-
- /**
- * Allows storage of user data. Data stored must be string formatted.
- * Developers can extend the entry path and entry name through the `schema.ts` file.
- *
- * @param entryKey
- * @param value - The string data you want to store.
- * @returns nothing. NOTE that an error is thrown if fails to store data.
- */
- public async performSetStorage(
- entryKey: UserStorageEntryKeys,
- value: string,
- ): Promise {
- this.#assertProfileSyncingEnabled();
- const { bearerToken, storageKey } =
- await this.#getStorageKeyAndBearerToken();
-
- await upsertUserStorage(value, {
- entryKey,
- bearerToken,
- storageKey,
- });
- }
-
- /**
- * Retrieves the storage key, for internal use only!
- *
- * @returns the storage key
- */
- public async getStorageKey(): Promise {
- this.#assertProfileSyncingEnabled();
- const storageKey = await this.#createStorageKey();
- return storageKey;
- }
-
- #assertProfileSyncingEnabled(): void {
- if (!this.state.isProfileSyncingEnabled) {
- throw new Error(
- `${controllerName}: Unable to call method, user is not authenticated`,
- );
- }
- }
-
- /**
- * Utility to get the bearer token and storage key
- */
- async #getStorageKeyAndBearerToken(): Promise<{
- bearerToken: string;
- storageKey: string;
- }> {
- const bearerToken = await this.#auth.getBearerToken();
- if (!bearerToken) {
- throw new Error('UserStorageController - unable to get bearer token');
- }
- const storageKey = await this.#createStorageKey();
-
- return { bearerToken, storageKey };
- }
-
- /**
- * Rather than storing the storage key, we can compute the storage key when needed.
- *
- * @returns the storage key
- */
- async #createStorageKey(): Promise {
- const id = await this.#auth.getProfileId();
- if (!id) {
- throw new Error('UserStorageController - unable to create storage key');
- }
-
- const storageKeySignature = await this.#snapSignMessage(`metamask:${id}`);
- const storageKey = createSHA256Hash(storageKeySignature);
- return storageKey;
- }
-
- #_snapSignMessageCache: Record<`metamask:${string}`, string> = {};
-
- /**
- * Signs a specific message using an underlying auth snap.
- *
- * @param message - A specific tagged message to sign.
- * @returns A Signature created by the snap.
- */
- async #snapSignMessage(message: `metamask:${string}`): Promise {
- if (this.#_snapSignMessageCache[message]) {
- return this.#_snapSignMessageCache[message];
- }
-
- if (!this.#isUnlocked) {
- throw new Error(
- '#snapSignMessage - unable to call snap, wallet is locked',
- );
- }
-
- const result = (await this.messagingSystem.call(
- 'SnapController:handleRequest',
- createSnapSignMessageRequest(message),
- )) as string;
-
- this.#_snapSignMessageCache[message] = result;
-
- return result;
- }
-
- async #setIsProfileSyncingUpdateLoading(
- isProfileSyncingUpdateLoading: boolean,
- ): Promise {
- this.update((state) => {
- state.isProfileSyncingUpdateLoading = isProfileSyncingUpdateLoading;
- });
- }
-}
diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js
index f185d227d9ee..3f8993da613d 100644
--- a/app/scripts/metamask-controller.js
+++ b/app/scripts/metamask-controller.js
@@ -142,6 +142,10 @@ import {
import { Interface } from '@ethersproject/abi';
import { abiERC1155, abiERC721 } from '@metamask/metamask-eth-abis';
import { isEvmAccountType } from '@metamask/keyring-api';
+import {
+ AuthenticationController,
+ UserStorageController,
+} from '@metamask/profile-sync-controller';
import {
methodsRequiringNetworkSwitch,
methodsWithConfirmation,
@@ -320,8 +324,6 @@ import PREINSTALLED_SNAPS from './snaps/preinstalled-snaps';
import { WeakRefObjectMap } from './lib/WeakRefObjectMap';
// Notification controllers
-import AuthenticationController from './controllers/authentication/authentication-controller';
-import UserStorageController from './controllers/user-storage/user-storage-controller';
import { PushPlatformNotificationsController } from './controllers/push-platform-notifications/push-platform-notifications';
import { MetamaskNotificationsController } from './controllers/metamask-notifications/metamask-notifications';
import { createTxVerificationMiddleware } from './lib/tx-verification/tx-verification-middleware';
@@ -1414,25 +1416,25 @@ export default class MetamaskController extends EventEmitter {
});
// Notification Controllers
- this.authenticationController = new AuthenticationController({
+ this.authenticationController = new AuthenticationController.Controller({
state: initState.AuthenticationController,
messenger: this.controllerMessenger.getRestricted({
name: 'AuthenticationController',
allowedActions: [
'KeyringController:getState',
'SnapController:handleRequest',
- 'UserStorageController:disableProfileSyncing',
],
allowedEvents: ['KeyringController:lock', 'KeyringController:unlock'],
}),
metametrics: {
getMetaMetricsId: () => this.metaMetricsController.getMetaMetricsId(),
+ agent: 'extension',
},
});
- this.userStorageController = new UserStorageController({
+ this.userStorageController = new UserStorageController.Controller({
getMetaMetricsState: () =>
- this.metaMetricsController.state.participateInMetaMetrics,
+ this.metaMetricsController.state.participateInMetaMetrics ?? false,
state: initState.UserStorageController,
messenger: this.controllerMessenger.getRestricted({
name: 'UserStorageController',
@@ -1444,8 +1446,8 @@ export default class MetamaskController extends EventEmitter {
'AuthenticationController:isSignedIn',
'AuthenticationController:performSignOut',
'AuthenticationController:performSignIn',
- 'MetamaskNotificationsController:disableMetamaskNotifications',
- 'MetamaskNotificationsController:selectIsMetamaskNotificationsEnabled',
+ 'NotificationServicesController:disableNotificationServices',
+ 'NotificationServicesController:selectIsNotificationServicesEnabled',
],
allowedEvents: ['KeyringController:lock', 'KeyringController:unlock'],
}),
@@ -1518,6 +1520,17 @@ export default class MetamaskController extends EventEmitter {
state: initState.MetamaskNotificationsController,
});
+ // Temporary add missing methods (due to notification controller migration)
+ this.controllerMessenger.registerActionHandler(
+ 'NotificationServicesController:disableNotificationServices',
+ () => this.metamaskNotificationsController.disableMetamaskNotifications(),
+ );
+ this.controllerMessenger.registerActionHandler(
+ 'NotificationServicesController:selectIsNotificationServicesEnabled',
+ () =>
+ this.metamaskNotificationsController.selectIsMetamaskNotificationsEnabled(),
+ );
+
// account tracker watches balances, nonces, and any code at their address
this.accountTracker = new AccountTracker({
provider: this.provider,
diff --git a/app/scripts/migrations/088.test.ts b/app/scripts/migrations/088.test.ts
index 51f9ebcd2ed1..45538047d470 100644
--- a/app/scripts/migrations/088.test.ts
+++ b/app/scripts/migrations/088.test.ts
@@ -9,6 +9,8 @@ global.sentry = {
captureException: sentryCaptureExceptionMock,
};
+const invalidKeys = ['null', 'undefined'];
+
describe('migration #88', () => {
afterEach(() => {
jest.resetAllMocks();
@@ -207,28 +209,55 @@ describe('migration #88', () => {
});
});
- it('deletes undefined-keyed properties from state of NftController.allNftContracts', async () => {
- const oldStorage = {
- meta: { version: 87 },
- data: {
+ for (const invalidKey of invalidKeys) {
+ it(`deletes ${invalidKey}-keyed properties from state of NftController.allNftContracts`, async () => {
+ const oldStorage = {
+ meta: { version: 87 },
+ data: {
+ NftController: {
+ allNftContracts: {
+ '0x111': {
+ '16': [
+ {
+ name: 'Contract 1',
+ address: '0xaaa',
+ },
+ ],
+ [invalidKey]: [
+ {
+ name: 'Contract 2',
+ address: '0xbbb',
+ },
+ ],
+ },
+ '0x222': {
+ '64': [
+ {
+ name: 'Contract 3',
+ address: '0xccc',
+ },
+ ],
+ },
+ },
+ },
+ },
+ };
+
+ const newStorage = await migrate(oldStorage);
+
+ expect(newStorage.data).toStrictEqual({
NftController: {
allNftContracts: {
'0x111': {
- '16': [
+ '0x10': [
{
name: 'Contract 1',
address: '0xaaa',
},
],
- undefined: [
- {
- name: 'Contract 2',
- address: '0xbbb',
- },
- ],
},
'0x222': {
- '64': [
+ '0x40': [
{
name: 'Contract 3',
address: '0xccc',
@@ -237,34 +266,9 @@ describe('migration #88', () => {
},
},
},
- },
- };
-
- const newStorage = await migrate(oldStorage);
-
- expect(newStorage.data).toStrictEqual({
- NftController: {
- allNftContracts: {
- '0x111': {
- '0x10': [
- {
- name: 'Contract 1',
- address: '0xaaa',
- },
- ],
- },
- '0x222': {
- '0x40': [
- {
- name: 'Contract 3',
- address: '0xccc',
- },
- ],
- },
- },
- },
+ });
});
- });
+ }
it('does not convert chain IDs in NftController.allNftContracts which are already hex strings', async () => {
const oldStorage = {
@@ -525,14 +529,59 @@ describe('migration #88', () => {
});
});
- it('deletes undefined-keyed properties from state of NftController.allNfts', async () => {
- const oldStorage = {
- meta: { version: 87 },
- data: {
+ for (const invalidKey of invalidKeys) {
+ it(`deletes ${invalidKey}-keyed properties from state of NftController.allNfts`, async () => {
+ const oldStorage = {
+ meta: { version: 87 },
+ data: {
+ NftController: {
+ allNfts: {
+ '0x111': {
+ '16': [
+ {
+ name: 'NFT 1',
+ description: 'Description for NFT 1',
+ image: 'nft1.jpg',
+ standard: 'ERC721',
+ tokenId: '1',
+ address: '0xaaa',
+ },
+ ],
+ [invalidKey]: [
+ {
+ name: 'NFT 2',
+ description: 'Description for NFT 2',
+ image: 'nft2.jpg',
+ standard: 'ERC721',
+ tokenId: '2',
+ address: '0xbbb',
+ },
+ ],
+ },
+ '0x222': {
+ '64': [
+ {
+ name: 'NFT 3',
+ description: 'Description for NFT 3',
+ image: 'nft3.jpg',
+ standard: 'ERC721',
+ tokenId: '3',
+ address: '0xccc',
+ },
+ ],
+ },
+ },
+ },
+ },
+ };
+
+ const newStorage = await migrate(oldStorage);
+
+ expect(newStorage.data).toStrictEqual({
NftController: {
allNfts: {
'0x111': {
- '16': [
+ '0x10': [
{
name: 'NFT 1',
description: 'Description for NFT 1',
@@ -542,19 +591,9 @@ describe('migration #88', () => {
address: '0xaaa',
},
],
- undefined: [
- {
- name: 'NFT 2',
- description: 'Description for NFT 2',
- image: 'nft2.jpg',
- standard: 'ERC721',
- tokenId: '2',
- address: '0xbbb',
- },
- ],
},
'0x222': {
- '64': [
+ '0x40': [
{
name: 'NFT 3',
description: 'Description for NFT 3',
@@ -567,42 +606,9 @@ describe('migration #88', () => {
},
},
},
- },
- };
-
- const newStorage = await migrate(oldStorage);
-
- expect(newStorage.data).toStrictEqual({
- NftController: {
- allNfts: {
- '0x111': {
- '0x10': [
- {
- name: 'NFT 1',
- description: 'Description for NFT 1',
- image: 'nft1.jpg',
- standard: 'ERC721',
- tokenId: '1',
- address: '0xaaa',
- },
- ],
- },
- '0x222': {
- '0x40': [
- {
- name: 'NFT 3',
- description: 'Description for NFT 3',
- image: 'nft3.jpg',
- standard: 'ERC721',
- tokenId: '3',
- address: '0xccc',
- },
- ],
- },
- },
- },
+ });
});
- });
+ }
it('does not convert chain IDs in NftController.allNfts which are already hex strings', async () => {
const oldStorage = {
@@ -946,13 +952,52 @@ describe('migration #88', () => {
});
});
- it('deletes undefined-keyed properties from state of TokenListController.tokensChainsCache', async () => {
- const oldStorage = {
- meta: { version: 87 },
- data: {
+ for (const invalidKey of invalidKeys) {
+ it(`deletes ${invalidKey}-keyed properties from state of TokenListController.tokensChainsCache`, async () => {
+ const oldStorage = {
+ meta: { version: 87 },
+ data: {
+ TokenListController: {
+ tokensChainsCache: {
+ '16': {
+ timestamp: 111111,
+ data: {
+ '0x111': {
+ address: '0x111',
+ symbol: 'TEST1',
+ decimals: 1,
+ occurrences: 1,
+ name: 'Token 1',
+ iconUrl: 'https://url/to/token1.png',
+ aggregators: [],
+ },
+ },
+ },
+ [invalidKey]: {
+ timestamp: 222222,
+ data: {
+ '0x222': {
+ address: '0x222',
+ symbol: 'TEST2',
+ decimals: 1,
+ occurrences: 1,
+ name: 'Token 2',
+ iconUrl: 'https://url/to/token2.png',
+ aggregators: [],
+ },
+ },
+ },
+ },
+ },
+ },
+ };
+
+ const newStorage = await migrate(oldStorage);
+
+ expect(newStorage.data).toStrictEqual({
TokenListController: {
tokensChainsCache: {
- '16': {
+ '0x10': {
timestamp: 111111,
data: {
'0x111': {
@@ -966,48 +1011,11 @@ describe('migration #88', () => {
},
},
},
- undefined: {
- timestamp: 222222,
- data: {
- '0x222': {
- address: '0x222',
- symbol: 'TEST2',
- decimals: 1,
- occurrences: 1,
- name: 'Token 2',
- iconUrl: 'https://url/to/token2.png',
- aggregators: [],
- },
- },
- },
},
},
- },
- };
-
- const newStorage = await migrate(oldStorage);
-
- expect(newStorage.data).toStrictEqual({
- TokenListController: {
- tokensChainsCache: {
- '0x10': {
- timestamp: 111111,
- data: {
- '0x111': {
- address: '0x111',
- symbol: 'TEST1',
- decimals: 1,
- occurrences: 1,
- name: 'Token 1',
- iconUrl: 'https://url/to/token1.png',
- aggregators: [],
- },
- },
- },
- },
- },
+ });
});
- });
+ }
it('does not convert chain IDs in TokenListController.tokensChainsCache which are already hex strings', async () => {
const oldStorage = {
@@ -1241,13 +1249,51 @@ describe('migration #88', () => {
});
});
- it('deletes undefined keyed properties from TokensController.allTokens', async () => {
- const oldStorage = {
- meta: { version: 87 },
- data: {
+ for (const invalidKey of invalidKeys) {
+ it(`deletes ${invalidKey} keyed properties from TokensController.allTokens`, async () => {
+ const oldStorage = {
+ meta: { version: 87 },
+ data: {
+ TokensController: {
+ allTokens: {
+ '16': {
+ '0x111': [
+ {
+ address: '0xaaa',
+ decimals: 1,
+ symbol: 'TEST1',
+ },
+ ],
+ },
+ '32': {
+ '0x222': [
+ {
+ address: '0xbbb',
+ decimals: 1,
+ symbol: 'TEST2',
+ },
+ ],
+ },
+ [invalidKey]: {
+ '0x333': [
+ {
+ address: '0xbbb',
+ decimals: 1,
+ symbol: 'TEST2',
+ },
+ ],
+ },
+ },
+ },
+ },
+ };
+
+ const newStorage = await migrate(oldStorage);
+
+ expect(newStorage.data).toStrictEqual({
TokensController: {
allTokens: {
- '16': {
+ '0x10': {
'0x111': [
{
address: '0xaaa',
@@ -1256,7 +1302,7 @@ describe('migration #88', () => {
},
],
},
- '32': {
+ '0x20': {
'0x222': [
{
address: '0xbbb',
@@ -1265,47 +1311,11 @@ describe('migration #88', () => {
},
],
},
- undefined: {
- '0x333': [
- {
- address: '0xbbb',
- decimals: 1,
- symbol: 'TEST2',
- },
- ],
- },
},
},
- },
- };
-
- const newStorage = await migrate(oldStorage);
-
- expect(newStorage.data).toStrictEqual({
- TokensController: {
- allTokens: {
- '0x10': {
- '0x111': [
- {
- address: '0xaaa',
- decimals: 1,
- symbol: 'TEST1',
- },
- ],
- },
- '0x20': {
- '0x222': [
- {
- address: '0xbbb',
- decimals: 1,
- symbol: 'TEST2',
- },
- ],
- },
- },
- },
+ });
});
- });
+ }
it('does not convert chain IDs in TokensController.allTokens which are already hex strings', async () => {
const oldStorage = {
@@ -1456,51 +1466,53 @@ describe('migration #88', () => {
});
});
- it('deletes undefined-keyed properties from TokensController.allIgnoredTokens', async () => {
- const oldStorage = {
- meta: { version: 87 },
- data: {
- TokensController: {
- allIgnoredTokens: {
- '16': {
- '0x1': {
- '0x111': ['0xaaa'],
+ for (const invalidKey of invalidKeys) {
+ it(`deletes ${invalidKey}-keyed properties from TokensController.allIgnoredTokens`, async () => {
+ const oldStorage = {
+ meta: { version: 87 },
+ data: {
+ TokensController: {
+ allIgnoredTokens: {
+ '16': {
+ '0x1': {
+ '0x111': ['0xaaa'],
+ },
},
- },
- '32': {
- '0x2': {
- '0x222': ['0xbbb'],
+ '32': {
+ '0x2': {
+ '0x222': ['0xbbb'],
+ },
},
- },
- undefined: {
- '0x2': {
- '0x222': ['0xbbb'],
+ [invalidKey]: {
+ '0x2': {
+ '0x222': ['0xbbb'],
+ },
},
},
},
},
- },
- };
+ };
- const newStorage = await migrate(oldStorage);
+ const newStorage = await migrate(oldStorage);
- expect(newStorage.data).toStrictEqual({
- TokensController: {
- allIgnoredTokens: {
- '0x10': {
- '0x1': {
- '0x111': ['0xaaa'],
+ expect(newStorage.data).toStrictEqual({
+ TokensController: {
+ allIgnoredTokens: {
+ '0x10': {
+ '0x1': {
+ '0x111': ['0xaaa'],
+ },
},
- },
- '0x20': {
- '0x2': {
- '0x222': ['0xbbb'],
+ '0x20': {
+ '0x2': {
+ '0x222': ['0xbbb'],
+ },
},
},
},
- },
+ });
});
- });
+ }
it('does not convert chain IDs in TokensController.allIgnoredTokens which are already hex strings', async () => {
const oldStorage = {
@@ -1635,41 +1647,43 @@ describe('migration #88', () => {
});
});
- it('deletes undefined-keyed properties from TokensController.allDetectedTokens', async () => {
- const oldStorage = {
- meta: { version: 87 },
- data: {
- TokensController: {
- allDetectedTokens: {
- '16': {
- '0x1': {
- '0x111': ['0xaaa'],
+ for (const invalidKey of invalidKeys) {
+ it(`deletes ${invalidKey}-keyed properties from TokensController.allDetectedTokens`, async () => {
+ const oldStorage = {
+ meta: { version: 87 },
+ data: {
+ TokensController: {
+ allDetectedTokens: {
+ '16': {
+ '0x1': {
+ '0x111': ['0xaaa'],
+ },
},
- },
- undefined: {
- '0x2': {
- '0x222': ['0xbbb'],
+ [invalidKey]: {
+ '0x2': {
+ '0x222': ['0xbbb'],
+ },
},
},
},
},
- },
- };
+ };
- const newStorage = await migrate(oldStorage);
+ const newStorage = await migrate(oldStorage);
- expect(newStorage.data).toStrictEqual({
- TokensController: {
- allDetectedTokens: {
- '0x10': {
- '0x1': {
- '0x111': ['0xaaa'],
+ expect(newStorage.data).toStrictEqual({
+ TokensController: {
+ allDetectedTokens: {
+ '0x10': {
+ '0x1': {
+ '0x111': ['0xaaa'],
+ },
},
},
},
- },
+ });
});
- });
+ }
it('does not convert chain IDs in TokensController.allDetectedTokens which are already hex strings', async () => {
const oldStorage = {
diff --git a/app/scripts/migrations/088.ts b/app/scripts/migrations/088.ts
index 6fda62183d2e..274b93624a85 100644
--- a/app/scripts/migrations/088.ts
+++ b/app/scripts/migrations/088.ts
@@ -59,7 +59,11 @@ function migrateData(state: Record): void {
if (isObject(nftContractsByChainId)) {
for (const chainId of Object.keys(nftContractsByChainId)) {
- if (chainId === 'undefined' || chainId === undefined) {
+ if (
+ chainId === 'undefined' ||
+ chainId === undefined ||
+ chainId === 'null'
+ ) {
delete nftContractsByChainId[chainId];
}
}
@@ -96,7 +100,11 @@ function migrateData(state: Record): void {
if (isObject(nftsByChainId)) {
for (const chainId of Object.keys(nftsByChainId)) {
- if (chainId === 'undefined' || chainId === undefined) {
+ if (
+ chainId === 'undefined' ||
+ chainId === undefined ||
+ chainId === 'null'
+ ) {
delete nftsByChainId[chainId];
}
}
@@ -142,7 +150,11 @@ function migrateData(state: Record): void {
for (const chainId of Object.keys(
tokenListControllerState.tokensChainsCache,
)) {
- if (chainId === 'undefined' || chainId === undefined) {
+ if (
+ chainId === 'undefined' ||
+ chainId === undefined ||
+ chainId === 'null'
+ ) {
delete tokenListControllerState.tokensChainsCache[chainId];
}
}
@@ -183,7 +195,11 @@ function migrateData(state: Record): void {
const { allTokens } = tokensControllerState;
for (const chainId of Object.keys(allTokens)) {
- if (chainId === 'undefined' || chainId === undefined) {
+ if (
+ chainId === 'undefined' ||
+ chainId === undefined ||
+ chainId === 'null'
+ ) {
delete allTokens[chainId];
}
}
@@ -212,7 +228,11 @@ function migrateData(state: Record): void {
const { allIgnoredTokens } = tokensControllerState;
for (const chainId of Object.keys(allIgnoredTokens)) {
- if (chainId === 'undefined' || chainId === undefined) {
+ if (
+ chainId === 'undefined' ||
+ chainId === undefined ||
+ chainId === 'null'
+ ) {
delete allIgnoredTokens[chainId];
}
}
@@ -241,7 +261,11 @@ function migrateData(state: Record): void {
const { allDetectedTokens } = tokensControllerState;
for (const chainId of Object.keys(allDetectedTokens)) {
- if (chainId === 'undefined' || chainId === undefined) {
+ if (
+ chainId === 'undefined' ||
+ chainId === undefined ||
+ chainId === 'null'
+ ) {
delete allDetectedTokens[chainId];
}
}
diff --git a/app/scripts/migrations/120.4.test.ts b/app/scripts/migrations/120.4.test.ts
new file mode 100644
index 000000000000..d37098a325b7
--- /dev/null
+++ b/app/scripts/migrations/120.4.test.ts
@@ -0,0 +1,231 @@
+import { cloneDeep } from 'lodash';
+import { migrate, version } from './120.4';
+
+const sentryCaptureExceptionMock = jest.fn();
+
+global.sentry = {
+ captureException: sentryCaptureExceptionMock,
+};
+
+const oldVersion = 120.3;
+
+describe('migration #120.4', () => {
+ afterEach(() => {
+ jest.resetAllMocks();
+ });
+
+ it('updates the version metadata', async () => {
+ const oldStorage = {
+ meta: { version: oldVersion },
+ data: {},
+ };
+
+ const newStorage = await migrate(cloneDeep(oldStorage));
+
+ expect(newStorage.meta).toStrictEqual({ version });
+ });
+
+ describe('CurrencyController', () => {
+ it('does nothing if CurrencyController state is not set', async () => {
+ const oldState = {
+ OtherController: {},
+ };
+
+ const transformedState = await migrate({
+ meta: { version: oldVersion },
+ data: cloneDeep(oldState),
+ });
+
+ expect(transformedState.data).toEqual(oldState);
+ });
+
+ it('captures an error and leaves state unchanged if CurrencyController state is corrupted', async () => {
+ const oldState = {
+ CurrencyController: 'invalid',
+ };
+
+ const transformedState = await migrate({
+ meta: { version: oldVersion },
+ data: cloneDeep(oldState),
+ });
+
+ expect(transformedState.data).toEqual(oldState);
+ expect(sentryCaptureExceptionMock).toHaveBeenCalledWith(
+ new Error(
+ `Migration ${version}: Invalid CurrencyController state of type 'string'`,
+ ),
+ );
+ });
+
+ it('deletes obsolete properties from the CurrencyController state', async () => {
+ const oldState = {
+ CurrencyController: {
+ conversionDate: 'test',
+ conversionRate: 'test',
+ nativeCurrency: 'test',
+ pendingCurrentCurrency: 'test',
+ pendingNativeCurrency: 'test',
+ usdConversionRate: 'test',
+ },
+ };
+
+ const transformedState = await migrate({
+ meta: { version: oldVersion },
+ data: cloneDeep(oldState),
+ });
+
+ expect(transformedState.data).toEqual({ CurrencyController: {} });
+ });
+
+ it('does not delete non-obsolete properties from the CurrencyController state', async () => {
+ const oldState = {
+ CurrencyController: {
+ currencyRates: { test: 123 },
+ conversionRate: 'test',
+ },
+ };
+
+ const transformedState = await migrate({
+ meta: { version: oldVersion },
+ data: cloneDeep(oldState),
+ });
+
+ expect(transformedState.data).toEqual({
+ CurrencyController: { currencyRates: { test: 123 } },
+ });
+ });
+ });
+
+ describe('PhishingController', () => {
+ it('does nothing if PhishingController state is not set', async () => {
+ const oldState = {
+ OtherController: {},
+ };
+
+ const transformedState = await migrate({
+ meta: { version: oldVersion },
+ data: cloneDeep(oldState),
+ });
+
+ expect(transformedState.data).toEqual(oldState);
+ });
+
+ it('captures an error and leaves state unchanged if PhishingController state is corrupted', async () => {
+ const oldState = {
+ PhishingController: 'invalid',
+ };
+
+ const transformedState = await migrate({
+ meta: { version: oldVersion },
+ data: cloneDeep(oldState),
+ });
+
+ expect(transformedState.data).toEqual(oldState);
+ expect(sentryCaptureExceptionMock).toHaveBeenCalledWith(
+ new Error(
+ `Migration ${version}: Invalid PhishingController state of type 'string'`,
+ ),
+ );
+ });
+
+ it('deletes obsolete properties from the PhishingController state', async () => {
+ const oldState = {
+ PhishingController: {
+ phishing: 'test',
+ lastFetched: 'test',
+ },
+ };
+
+ const transformedState = await migrate({
+ meta: { version: oldVersion },
+ data: cloneDeep(oldState),
+ });
+
+ expect(transformedState.data).toEqual({ PhishingController: {} });
+ });
+
+ it('does not delete non-obsolete properties from the PhishingController state', async () => {
+ const oldState = {
+ PhishingController: {
+ phishing: 'test',
+ phishingLists: 'test',
+ },
+ };
+
+ const transformedState = await migrate({
+ meta: { version: oldVersion },
+ data: cloneDeep(oldState),
+ });
+
+ expect(transformedState.data).toEqual({
+ PhishingController: { phishingLists: 'test' },
+ });
+ });
+ });
+
+ describe('NetworkController', () => {
+ it('does nothing if NetworkController state is not set', async () => {
+ const oldState = {
+ OtherController: {},
+ };
+
+ const transformedState = await migrate({
+ meta: { version: oldVersion },
+ data: cloneDeep(oldState),
+ });
+
+ expect(transformedState.data).toEqual(oldState);
+ });
+
+ it('captures an error and leaves state unchanged if NetworkController state is corrupted', async () => {
+ const oldState = {
+ NetworkController: 'invalid',
+ };
+
+ const transformedState = await migrate({
+ meta: { version: oldVersion },
+ data: cloneDeep(oldState),
+ });
+
+ expect(transformedState.data).toEqual(oldState);
+ expect(sentryCaptureExceptionMock).toHaveBeenCalledWith(
+ new Error(
+ `Migration ${version}: Invalid NetworkController state of type 'string'`,
+ ),
+ );
+ });
+
+ it('deletes obsolete properties from the NetworkController state', async () => {
+ const oldState = {
+ NetworkController: {
+ network: 'test',
+ },
+ };
+
+ const transformedState = await migrate({
+ meta: { version: oldVersion },
+ data: cloneDeep(oldState),
+ });
+
+ expect(transformedState.data).toEqual({ NetworkController: {} });
+ });
+
+ it('does not delete non-obsolete properties from the NetworkController state', async () => {
+ const oldState = {
+ NetworkController: {
+ network: 'test',
+ selectedNetworkClientId: 'test',
+ },
+ };
+
+ const transformedState = await migrate({
+ meta: { version: oldVersion },
+ data: cloneDeep(oldState),
+ });
+
+ expect(transformedState.data).toEqual({
+ NetworkController: { selectedNetworkClientId: 'test' },
+ });
+ });
+ });
+});
diff --git a/app/scripts/migrations/120.4.ts b/app/scripts/migrations/120.4.ts
new file mode 100644
index 000000000000..fc4162b61d7b
--- /dev/null
+++ b/app/scripts/migrations/120.4.ts
@@ -0,0 +1,119 @@
+import { hasProperty, isObject } from '@metamask/utils';
+import { cloneDeep } from 'lodash';
+
+type VersionedData = {
+ meta: { version: number };
+ data: Record;
+};
+
+export const version = 120.4;
+
+/**
+ * This migration removes properties from the CurrencyController state that
+ * are no longer used. There presence in state causes "No metadata found" errors
+ *
+ * @param originalVersionedData - Versioned MetaMask extension state, exactly
+ * what we persist to dist.
+ * @param originalVersionedData.meta - State metadata.
+ * @param originalVersionedData.meta.version - The current state version.
+ * @param originalVersionedData.data - The persisted MetaMask state, keyed by
+ * controller.
+ * @returns Updated versioned MetaMask extension state.
+ */
+export async function migrate(
+ originalVersionedData: VersionedData,
+): Promise {
+ const versionedData = cloneDeep(originalVersionedData);
+ versionedData.meta.version = version;
+ transformState(versionedData.data);
+ return versionedData;
+}
+
+/**
+ * Remove obsolete CurrencyController state
+ *
+ * The six properties deleted here were no longer used as of
+ * assets-controllers v18.0.0
+ *
+ * See https://github.com/MetaMask/core/pull/1805 for the removal of these
+ * properties from the controller.
+ *
+ * @param state - The persisted MetaMask state, keyed by controller.
+ */
+function removeObsoleteCurrencyControllerState(
+ state: Record,
+): void {
+ if (!hasProperty(state, 'CurrencyController')) {
+ return;
+ } else if (!isObject(state.CurrencyController)) {
+ global.sentry?.captureException?.(
+ new Error(
+ `Migration ${version}: Invalid CurrencyController state of type '${typeof state.CurrencyController}'`,
+ ),
+ );
+ return;
+ }
+
+ delete state.CurrencyController.conversionDate;
+ delete state.CurrencyController.conversionRate;
+ delete state.CurrencyController.nativeCurrency;
+ delete state.CurrencyController.pendingCurrentCurrency;
+ delete state.CurrencyController.pendingNativeCurrency;
+ delete state.CurrencyController.usdConversionRate;
+}
+
+/**
+ * Remove obsolete PhishingController state
+ *
+ * @param state - The persisted MetaMask state, keyed by controller.
+ */
+function removeObsoletePhishingControllerState(
+ state: Record,
+): void {
+ if (!hasProperty(state, 'PhishingController')) {
+ return;
+ } else if (!isObject(state.PhishingController)) {
+ global.sentry?.captureException?.(
+ new Error(
+ `Migration ${version}: Invalid PhishingController state of type '${typeof state.PhishingController}'`,
+ ),
+ );
+ return;
+ }
+
+ delete state.PhishingController.phishing;
+ delete state.PhishingController.lastFetched;
+}
+
+/**
+ * Remove obsolete NetworkController state
+ *
+ * @param state - The persisted MetaMask state, keyed by controller.
+ */
+function removeObsoleteNetworkControllerState(
+ state: Record,
+): void {
+ if (!hasProperty(state, 'NetworkController')) {
+ return;
+ } else if (!isObject(state.NetworkController)) {
+ global.sentry?.captureException?.(
+ new Error(
+ `Migration ${version}: Invalid NetworkController state of type '${typeof state.NetworkController}'`,
+ ),
+ );
+ return;
+ }
+
+ delete state.NetworkController.network;
+}
+
+/**
+ * Remove obsolete controller state.
+ *
+ * @param state - The persisted MetaMask state, keyed by controller.
+ */
+function transformState(state: Record): void {
+ removeObsoleteCurrencyControllerState(state);
+ removeObsoletePhishingControllerState(state);
+ removeObsoleteNetworkControllerState(state);
+}
diff --git a/app/scripts/migrations/index.js b/app/scripts/migrations/index.js
index cb6c2683d9ea..b647ab3d0a44 100644
--- a/app/scripts/migrations/index.js
+++ b/app/scripts/migrations/index.js
@@ -134,6 +134,7 @@ const migrations = [
require('./120.1'),
require('./120.2'),
require('./120.3'),
+ require('./120.4'),
require('./121'),
require('./122'),
require('./123'),
diff --git a/lavamoat/browserify/beta/policy.json b/lavamoat/browserify/beta/policy.json
index ad2d0589b1a9..6bd4b686477a 100644
--- a/lavamoat/browserify/beta/policy.json
+++ b/lavamoat/browserify/beta/policy.json
@@ -1035,12 +1035,8 @@
}
},
"@metamask/controller-utils>@spruceid/siwe-parser>apg-js": {
- "globals": {
- "mode": true
- },
"packages": {
- "browserify>buffer": true,
- "browserify>insert-module-globals>is-buffer": true
+ "browserify>buffer": true
}
},
"@metamask/controllers>web3": {
@@ -1114,8 +1110,8 @@
},
"packages": {
"@metamask/eth-json-rpc-filters>@metamask/eth-query": true,
+ "@metamask/eth-json-rpc-filters>@metamask/json-rpc-engine": true,
"@metamask/eth-json-rpc-filters>async-mutex": true,
- "@metamask/providers>@metamask/json-rpc-engine": true,
"@metamask/safe-event-emitter": true,
"pify": true
}
@@ -1126,6 +1122,13 @@
"watchify>xtend": true
}
},
+ "@metamask/eth-json-rpc-filters>@metamask/json-rpc-engine": {
+ "packages": {
+ "@metamask/rpc-errors": true,
+ "@metamask/safe-event-emitter": true,
+ "@metamask/utils": true
+ }
+ },
"@metamask/eth-json-rpc-filters>async-mutex": {
"globals": {
"setTimeout": true
@@ -1141,10 +1144,10 @@
"setTimeout": true
},
"packages": {
+ "@metamask/eth-json-rpc-middleware>@metamask/json-rpc-engine": true,
"@metamask/eth-json-rpc-middleware>klona": true,
"@metamask/eth-json-rpc-middleware>safe-stable-stringify": true,
"@metamask/eth-sig-util": true,
- "@metamask/providers>@metamask/json-rpc-engine": true,
"@metamask/rpc-errors": true,
"@metamask/utils": true,
"pify": true
@@ -1152,10 +1155,24 @@
},
"@metamask/eth-json-rpc-middleware>@metamask/eth-json-rpc-provider": {
"packages": {
- "@metamask/providers>@metamask/json-rpc-engine": true,
+ "@metamask/eth-json-rpc-middleware>@metamask/eth-json-rpc-provider>@metamask/json-rpc-engine": true,
"@metamask/safe-event-emitter": true
}
},
+ "@metamask/eth-json-rpc-middleware>@metamask/eth-json-rpc-provider>@metamask/json-rpc-engine": {
+ "packages": {
+ "@metamask/rpc-errors": true,
+ "@metamask/safe-event-emitter": true,
+ "@metamask/utils": true
+ }
+ },
+ "@metamask/eth-json-rpc-middleware>@metamask/json-rpc-engine": {
+ "packages": {
+ "@metamask/rpc-errors": true,
+ "@metamask/safe-event-emitter": true,
+ "@metamask/utils": true
+ }
+ },
"@metamask/eth-ledger-bridge-keyring": {
"globals": {
"addEventListener": true,
@@ -1810,6 +1827,13 @@
"browserify>url": true
}
},
+ "@metamask/message-signing-snap>@noble/ciphers": {
+ "globals": {
+ "TextDecoder": true,
+ "TextEncoder": true,
+ "crypto": true
+ }
+ },
"@metamask/message-signing-snap>@noble/curves": {
"globals": {
"TextEncoder": true
@@ -1894,9 +1918,9 @@
"@metamask/network-controller>@metamask/controller-utils": true,
"@metamask/network-controller>@metamask/eth-json-rpc-infura": true,
"@metamask/network-controller>@metamask/eth-json-rpc-provider": true,
- "@metamask/network-controller>@metamask/json-rpc-engine": true,
"@metamask/network-controller>@metamask/swappable-obj-proxy": true,
"@metamask/rpc-errors": true,
+ "@metamask/snaps-controllers>@metamask/json-rpc-engine": true,
"@metamask/utils": true,
"browserify>assert": true,
"uuid": true
@@ -1949,40 +1973,25 @@
},
"packages": {
"@metamask/eth-json-rpc-middleware>@metamask/eth-json-rpc-provider": true,
- "@metamask/providers>@metamask/json-rpc-engine": true,
+ "@metamask/network-controller>@metamask/eth-json-rpc-infura>@metamask/json-rpc-engine": true,
"@metamask/rpc-errors": true,
"@metamask/utils": true,
"node-fetch": true
}
},
- "@metamask/network-controller>@metamask/eth-json-rpc-provider": {
+ "@metamask/network-controller>@metamask/eth-json-rpc-infura>@metamask/json-rpc-engine": {
"packages": {
- "@metamask/network-controller>@metamask/json-rpc-engine": true,
"@metamask/rpc-errors": true,
"@metamask/safe-event-emitter": true,
- "uuid": true
+ "@metamask/utils": true
}
},
- "@metamask/network-controller>@metamask/json-rpc-engine": {
+ "@metamask/network-controller>@metamask/eth-json-rpc-provider": {
"packages": {
- "@metamask/network-controller>@metamask/json-rpc-engine>@metamask/utils": true,
"@metamask/rpc-errors": true,
- "@metamask/safe-event-emitter": true
- }
- },
- "@metamask/network-controller>@metamask/json-rpc-engine>@metamask/utils": {
- "globals": {
- "TextDecoder": true,
- "TextEncoder": true
- },
- "packages": {
- "@metamask/rpc-errors>@metamask/utils>@metamask/superstruct": true,
- "@metamask/utils>@scure/base": true,
- "@metamask/utils>pony-cause": true,
- "@noble/hashes": true,
- "browserify>buffer": true,
- "nock>debug": true,
- "semver": true
+ "@metamask/safe-event-emitter": true,
+ "@metamask/snaps-controllers>@metamask/json-rpc-engine": true,
+ "uuid": true
}
},
"@metamask/notification-controller": {
@@ -2024,9 +2033,9 @@
"packages": {
"@metamask/permission-controller>@metamask/base-controller": true,
"@metamask/permission-controller>@metamask/controller-utils": true,
- "@metamask/permission-controller>@metamask/json-rpc-engine": true,
"@metamask/permission-controller>nanoid": true,
"@metamask/rpc-errors": true,
+ "@metamask/snaps-controllers>@metamask/json-rpc-engine": true,
"@metamask/utils": true,
"deep-freeze-strict": true,
"immer": true
@@ -2073,28 +2082,6 @@
"semver": true
}
},
- "@metamask/permission-controller>@metamask/json-rpc-engine": {
- "packages": {
- "@metamask/permission-controller>@metamask/json-rpc-engine>@metamask/utils": true,
- "@metamask/rpc-errors": true,
- "@metamask/safe-event-emitter": true
- }
- },
- "@metamask/permission-controller>@metamask/json-rpc-engine>@metamask/utils": {
- "globals": {
- "TextDecoder": true,
- "TextEncoder": true
- },
- "packages": {
- "@metamask/rpc-errors>@metamask/utils>@metamask/superstruct": true,
- "@metamask/utils>@scure/base": true,
- "@metamask/utils>pony-cause": true,
- "@noble/hashes": true,
- "browserify>buffer": true,
- "nock>debug": true,
- "semver": true
- }
- },
"@metamask/permission-controller>nanoid": {
"globals": {
"crypto.getRandomValues": true
@@ -2217,50 +2204,90 @@
"ethereumjs-util>ethereum-cryptography>hash.js": true
}
},
- "@metamask/providers>@metamask/json-rpc-engine": {
+ "@metamask/profile-sync-controller": {
+ "globals": {
+ "Event": true,
+ "Headers": true,
+ "TextDecoder": true,
+ "URL": true,
+ "URLSearchParams": true,
+ "addEventListener": true,
+ "console.error": true,
+ "dispatchEvent": true,
+ "fetch": true,
+ "removeEventListener": true,
+ "setTimeout": true
+ },
"packages": {
- "@metamask/rpc-errors": true,
- "@metamask/safe-event-emitter": true,
- "@metamask/utils": true
+ "@metamask/message-signing-snap>@noble/ciphers": true,
+ "@metamask/profile-sync-controller>@metamask/base-controller": true,
+ "@metamask/profile-sync-controller>siwe": true,
+ "@noble/hashes": true,
+ "browserify>buffer": true,
+ "loglevel": true
}
},
- "@metamask/queued-request-controller": {
+ "@metamask/profile-sync-controller>@metamask/base-controller": {
+ "globals": {
+ "setTimeout": true
+ },
"packages": {
- "@metamask/queued-request-controller>@metamask/base-controller": true,
- "@metamask/queued-request-controller>@metamask/json-rpc-engine": true,
- "@metamask/rpc-errors": true,
- "@metamask/selected-network-controller": true,
- "@metamask/utils": true
+ "immer": true
}
},
- "@metamask/queued-request-controller>@metamask/base-controller": {
+ "@metamask/profile-sync-controller>siwe": {
"globals": {
- "setTimeout": true
+ "console.error": true,
+ "console.warn": true
},
"packages": {
- "immer": true
+ "@metamask/controller-utils>@spruceid/siwe-parser>valid-url": true,
+ "@metamask/profile-sync-controller>siwe>@spruceid/siwe-parser": true,
+ "@metamask/profile-sync-controller>siwe>@stablelib/random": true,
+ "@metamask/test-bundler>ethers": true
+ }
+ },
+ "@metamask/profile-sync-controller>siwe>@spruceid/siwe-parser": {
+ "globals": {
+ "console.error": true,
+ "console.log": true
+ },
+ "packages": {
+ "@metamask/controller-utils>@spruceid/siwe-parser>apg-js": true,
+ "@noble/hashes": true
+ }
+ },
+ "@metamask/profile-sync-controller>siwe>@stablelib/random": {
+ "globals": {
+ "crypto": true,
+ "msCrypto": true
+ },
+ "packages": {
+ "@metamask/profile-sync-controller>siwe>@stablelib/random>@stablelib/binary": true,
+ "@metamask/profile-sync-controller>siwe>@stablelib/random>@stablelib/wipe": true,
+ "browserify>browser-resolve": true
+ }
+ },
+ "@metamask/profile-sync-controller>siwe>@stablelib/random>@stablelib/binary": {
+ "packages": {
+ "@metamask/profile-sync-controller>siwe>@stablelib/random>@stablelib/binary>@stablelib/int": true
}
},
- "@metamask/queued-request-controller>@metamask/json-rpc-engine": {
+ "@metamask/queued-request-controller": {
"packages": {
- "@metamask/queued-request-controller>@metamask/json-rpc-engine>@metamask/utils": true,
+ "@metamask/queued-request-controller>@metamask/base-controller": true,
"@metamask/rpc-errors": true,
- "@metamask/safe-event-emitter": true
+ "@metamask/selected-network-controller": true,
+ "@metamask/snaps-controllers>@metamask/json-rpc-engine": true,
+ "@metamask/utils": true
}
},
- "@metamask/queued-request-controller>@metamask/json-rpc-engine>@metamask/utils": {
+ "@metamask/queued-request-controller>@metamask/base-controller": {
"globals": {
- "TextDecoder": true,
- "TextEncoder": true
+ "setTimeout": true
},
"packages": {
- "@metamask/rpc-errors>@metamask/utils>@metamask/superstruct": true,
- "@metamask/utils>@scure/base": true,
- "@metamask/utils>pony-cause": true,
- "@noble/hashes": true,
- "browserify>buffer": true,
- "nock>debug": true,
- "semver": true
+ "immer": true
}
},
"@metamask/rate-limit-controller": {
@@ -2608,21 +2635,19 @@
"globals": {
"DecompressionStream": true,
"URL": true,
- "chrome.offscreen.createDocument": true,
- "chrome.offscreen.hasDocument": true,
"clearTimeout": true,
"document.getElementById": true,
"fetch.bind": true,
"setTimeout": true
},
"packages": {
- "@metamask/base-controller": true,
"@metamask/object-multiplex": true,
+ "@metamask/permission-controller": true,
"@metamask/post-message-stream": true,
"@metamask/rpc-errors": true,
+ "@metamask/snaps-controllers>@metamask/base-controller": true,
"@metamask/snaps-controllers>@metamask/json-rpc-engine": true,
"@metamask/snaps-controllers>@metamask/json-rpc-middleware-stream": true,
- "@metamask/snaps-controllers>@metamask/permission-controller": true,
"@metamask/snaps-controllers>@xstate/fsm": true,
"@metamask/snaps-controllers>concat-stream": true,
"@metamask/snaps-controllers>get-npm-tarball-url": true,
@@ -2644,11 +2669,34 @@
"crypto.getRandomValues": true
}
},
+ "@metamask/snaps-controllers>@metamask/base-controller": {
+ "globals": {
+ "setTimeout": true
+ },
+ "packages": {
+ "immer": true
+ }
+ },
"@metamask/snaps-controllers>@metamask/json-rpc-engine": {
"packages": {
"@metamask/rpc-errors": true,
"@metamask/safe-event-emitter": true,
- "@metamask/utils": true
+ "@metamask/snaps-controllers>@metamask/json-rpc-engine>@metamask/utils": true
+ }
+ },
+ "@metamask/snaps-controllers>@metamask/json-rpc-engine>@metamask/utils": {
+ "globals": {
+ "TextDecoder": true,
+ "TextEncoder": true
+ },
+ "packages": {
+ "@metamask/rpc-errors>@metamask/utils>@metamask/superstruct": true,
+ "@metamask/utils>@scure/base": true,
+ "@metamask/utils>pony-cause": true,
+ "@noble/hashes": true,
+ "browserify>buffer": true,
+ "nock>debug": true,
+ "semver": true
}
},
"@metamask/snaps-controllers>@metamask/json-rpc-middleware-stream": {
@@ -2658,22 +2706,23 @@
},
"packages": {
"@metamask/safe-event-emitter": true,
+ "@metamask/snaps-controllers>@metamask/json-rpc-middleware-stream>@metamask/utils": true,
"readable-stream": true
}
},
- "@metamask/snaps-controllers>@metamask/permission-controller": {
+ "@metamask/snaps-controllers>@metamask/json-rpc-middleware-stream>@metamask/utils": {
"globals": {
- "console.error": true
+ "TextDecoder": true,
+ "TextEncoder": true
},
"packages": {
- "@metamask/base-controller": true,
- "@metamask/controller-utils": true,
- "@metamask/rpc-errors": true,
- "@metamask/snaps-controllers>@metamask/json-rpc-engine": true,
- "@metamask/snaps-controllers>nanoid": true,
- "@metamask/utils": true,
- "deep-freeze-strict": true,
- "immer": true
+ "@metamask/rpc-errors>@metamask/utils>@metamask/superstruct": true,
+ "@metamask/utils>@scure/base": true,
+ "@metamask/utils>pony-cause": true,
+ "@noble/hashes": true,
+ "browserify>buffer": true,
+ "nock>debug": true,
+ "semver": true
}
},
"@metamask/snaps-controllers>concat-stream": {
@@ -2757,11 +2806,26 @@
"packages": {
"@metamask/message-signing-snap>@noble/curves": true,
"@metamask/scure-bip39": true,
- "@metamask/utils": true,
+ "@metamask/snaps-sdk>@metamask/key-tree>@metamask/utils": true,
"@metamask/utils>@scure/base": true,
"@noble/hashes": true
}
},
+ "@metamask/snaps-sdk>@metamask/key-tree>@metamask/utils": {
+ "globals": {
+ "TextDecoder": true,
+ "TextEncoder": true
+ },
+ "packages": {
+ "@metamask/rpc-errors>@metamask/utils>@metamask/superstruct": true,
+ "@metamask/utils>@scure/base": true,
+ "@metamask/utils>pony-cause": true,
+ "@noble/hashes": true,
+ "browserify>buffer": true,
+ "nock>debug": true,
+ "semver": true
+ }
+ },
"@metamask/snaps-utils": {
"globals": {
"File": true,
@@ -2800,9 +2864,24 @@
"@metamask/snaps-utils>@metamask/snaps-registry": {
"packages": {
"@metamask/message-signing-snap>@noble/curves": true,
- "@metamask/utils": true,
+ "@metamask/rpc-errors>@metamask/utils>@metamask/superstruct": true,
+ "@metamask/snaps-utils>@metamask/snaps-registry>@metamask/utils": true,
+ "@noble/hashes": true
+ }
+ },
+ "@metamask/snaps-utils>@metamask/snaps-registry>@metamask/utils": {
+ "globals": {
+ "TextDecoder": true,
+ "TextEncoder": true
+ },
+ "packages": {
+ "@metamask/rpc-errors>@metamask/utils>@metamask/superstruct": true,
+ "@metamask/utils>@scure/base": true,
+ "@metamask/utils>pony-cause": true,
"@noble/hashes": true,
- "superstruct": true
+ "browserify>buffer": true,
+ "nock>debug": true,
+ "semver": true
}
},
"@metamask/snaps-utils>cron-parser": {
@@ -2848,6 +2927,53 @@
"@ethersproject/abi>@ethersproject/logger": true
}
},
+ "@metamask/test-bundler>ethers": {
+ "packages": {
+ "@ethersproject/abi": true,
+ "@ethersproject/abi>@ethersproject/address": true,
+ "@ethersproject/abi>@ethersproject/constants": true,
+ "@ethersproject/abi>@ethersproject/keccak256": true,
+ "@ethersproject/abi>@ethersproject/logger": true,
+ "@ethersproject/abi>@ethersproject/properties": true,
+ "@ethersproject/abi>@ethersproject/strings": true,
+ "@ethersproject/bignumber": true,
+ "@ethersproject/bytes": true,
+ "@ethersproject/contracts": true,
+ "@ethersproject/hash": true,
+ "@ethersproject/hash>@ethersproject/abstract-signer": true,
+ "@ethersproject/hash>@ethersproject/base64": true,
+ "@ethersproject/hdnode": true,
+ "@ethersproject/hdnode>@ethersproject/basex": true,
+ "@ethersproject/hdnode>@ethersproject/sha2": true,
+ "@ethersproject/hdnode>@ethersproject/signing-key": true,
+ "@ethersproject/hdnode>@ethersproject/transactions": true,
+ "@ethersproject/hdnode>@ethersproject/wordlists": true,
+ "@ethersproject/providers": true,
+ "@ethersproject/providers>@ethersproject/rlp": true,
+ "@ethersproject/providers>@ethersproject/web": true,
+ "@ethersproject/wallet": true,
+ "@ethersproject/wallet>@ethersproject/json-wallets": true,
+ "@ethersproject/wallet>@ethersproject/random": true,
+ "@metamask/test-bundler>ethers>@ethersproject/solidity": true,
+ "@metamask/test-bundler>ethers>@ethersproject/units": true
+ }
+ },
+ "@metamask/test-bundler>ethers>@ethersproject/solidity": {
+ "packages": {
+ "@ethersproject/abi>@ethersproject/keccak256": true,
+ "@ethersproject/abi>@ethersproject/logger": true,
+ "@ethersproject/abi>@ethersproject/strings": true,
+ "@ethersproject/bignumber": true,
+ "@ethersproject/bytes": true,
+ "@ethersproject/hdnode>@ethersproject/sha2": true
+ }
+ },
+ "@metamask/test-bundler>ethers>@ethersproject/units": {
+ "packages": {
+ "@ethersproject/abi>@ethersproject/logger": true,
+ "@ethersproject/bignumber": true
+ }
+ },
"@metamask/transaction-controller": {
"globals": {
"clearTimeout": true,
@@ -3062,13 +3188,6 @@
"define": true
}
},
- "@noble/ciphers": {
- "globals": {
- "TextDecoder": true,
- "TextEncoder": true,
- "crypto": true
- }
- },
"@noble/hashes": {
"globals": {
"TextEncoder": true,
diff --git a/lavamoat/browserify/flask/policy.json b/lavamoat/browserify/flask/policy.json
index ad2d0589b1a9..6bd4b686477a 100644
--- a/lavamoat/browserify/flask/policy.json
+++ b/lavamoat/browserify/flask/policy.json
@@ -1035,12 +1035,8 @@
}
},
"@metamask/controller-utils>@spruceid/siwe-parser>apg-js": {
- "globals": {
- "mode": true
- },
"packages": {
- "browserify>buffer": true,
- "browserify>insert-module-globals>is-buffer": true
+ "browserify>buffer": true
}
},
"@metamask/controllers>web3": {
@@ -1114,8 +1110,8 @@
},
"packages": {
"@metamask/eth-json-rpc-filters>@metamask/eth-query": true,
+ "@metamask/eth-json-rpc-filters>@metamask/json-rpc-engine": true,
"@metamask/eth-json-rpc-filters>async-mutex": true,
- "@metamask/providers>@metamask/json-rpc-engine": true,
"@metamask/safe-event-emitter": true,
"pify": true
}
@@ -1126,6 +1122,13 @@
"watchify>xtend": true
}
},
+ "@metamask/eth-json-rpc-filters>@metamask/json-rpc-engine": {
+ "packages": {
+ "@metamask/rpc-errors": true,
+ "@metamask/safe-event-emitter": true,
+ "@metamask/utils": true
+ }
+ },
"@metamask/eth-json-rpc-filters>async-mutex": {
"globals": {
"setTimeout": true
@@ -1141,10 +1144,10 @@
"setTimeout": true
},
"packages": {
+ "@metamask/eth-json-rpc-middleware>@metamask/json-rpc-engine": true,
"@metamask/eth-json-rpc-middleware>klona": true,
"@metamask/eth-json-rpc-middleware>safe-stable-stringify": true,
"@metamask/eth-sig-util": true,
- "@metamask/providers>@metamask/json-rpc-engine": true,
"@metamask/rpc-errors": true,
"@metamask/utils": true,
"pify": true
@@ -1152,10 +1155,24 @@
},
"@metamask/eth-json-rpc-middleware>@metamask/eth-json-rpc-provider": {
"packages": {
- "@metamask/providers>@metamask/json-rpc-engine": true,
+ "@metamask/eth-json-rpc-middleware>@metamask/eth-json-rpc-provider>@metamask/json-rpc-engine": true,
"@metamask/safe-event-emitter": true
}
},
+ "@metamask/eth-json-rpc-middleware>@metamask/eth-json-rpc-provider>@metamask/json-rpc-engine": {
+ "packages": {
+ "@metamask/rpc-errors": true,
+ "@metamask/safe-event-emitter": true,
+ "@metamask/utils": true
+ }
+ },
+ "@metamask/eth-json-rpc-middleware>@metamask/json-rpc-engine": {
+ "packages": {
+ "@metamask/rpc-errors": true,
+ "@metamask/safe-event-emitter": true,
+ "@metamask/utils": true
+ }
+ },
"@metamask/eth-ledger-bridge-keyring": {
"globals": {
"addEventListener": true,
@@ -1810,6 +1827,13 @@
"browserify>url": true
}
},
+ "@metamask/message-signing-snap>@noble/ciphers": {
+ "globals": {
+ "TextDecoder": true,
+ "TextEncoder": true,
+ "crypto": true
+ }
+ },
"@metamask/message-signing-snap>@noble/curves": {
"globals": {
"TextEncoder": true
@@ -1894,9 +1918,9 @@
"@metamask/network-controller>@metamask/controller-utils": true,
"@metamask/network-controller>@metamask/eth-json-rpc-infura": true,
"@metamask/network-controller>@metamask/eth-json-rpc-provider": true,
- "@metamask/network-controller>@metamask/json-rpc-engine": true,
"@metamask/network-controller>@metamask/swappable-obj-proxy": true,
"@metamask/rpc-errors": true,
+ "@metamask/snaps-controllers>@metamask/json-rpc-engine": true,
"@metamask/utils": true,
"browserify>assert": true,
"uuid": true
@@ -1949,40 +1973,25 @@
},
"packages": {
"@metamask/eth-json-rpc-middleware>@metamask/eth-json-rpc-provider": true,
- "@metamask/providers>@metamask/json-rpc-engine": true,
+ "@metamask/network-controller>@metamask/eth-json-rpc-infura>@metamask/json-rpc-engine": true,
"@metamask/rpc-errors": true,
"@metamask/utils": true,
"node-fetch": true
}
},
- "@metamask/network-controller>@metamask/eth-json-rpc-provider": {
+ "@metamask/network-controller>@metamask/eth-json-rpc-infura>@metamask/json-rpc-engine": {
"packages": {
- "@metamask/network-controller>@metamask/json-rpc-engine": true,
"@metamask/rpc-errors": true,
"@metamask/safe-event-emitter": true,
- "uuid": true
+ "@metamask/utils": true
}
},
- "@metamask/network-controller>@metamask/json-rpc-engine": {
+ "@metamask/network-controller>@metamask/eth-json-rpc-provider": {
"packages": {
- "@metamask/network-controller>@metamask/json-rpc-engine>@metamask/utils": true,
"@metamask/rpc-errors": true,
- "@metamask/safe-event-emitter": true
- }
- },
- "@metamask/network-controller>@metamask/json-rpc-engine>@metamask/utils": {
- "globals": {
- "TextDecoder": true,
- "TextEncoder": true
- },
- "packages": {
- "@metamask/rpc-errors>@metamask/utils>@metamask/superstruct": true,
- "@metamask/utils>@scure/base": true,
- "@metamask/utils>pony-cause": true,
- "@noble/hashes": true,
- "browserify>buffer": true,
- "nock>debug": true,
- "semver": true
+ "@metamask/safe-event-emitter": true,
+ "@metamask/snaps-controllers>@metamask/json-rpc-engine": true,
+ "uuid": true
}
},
"@metamask/notification-controller": {
@@ -2024,9 +2033,9 @@
"packages": {
"@metamask/permission-controller>@metamask/base-controller": true,
"@metamask/permission-controller>@metamask/controller-utils": true,
- "@metamask/permission-controller>@metamask/json-rpc-engine": true,
"@metamask/permission-controller>nanoid": true,
"@metamask/rpc-errors": true,
+ "@metamask/snaps-controllers>@metamask/json-rpc-engine": true,
"@metamask/utils": true,
"deep-freeze-strict": true,
"immer": true
@@ -2073,28 +2082,6 @@
"semver": true
}
},
- "@metamask/permission-controller>@metamask/json-rpc-engine": {
- "packages": {
- "@metamask/permission-controller>@metamask/json-rpc-engine>@metamask/utils": true,
- "@metamask/rpc-errors": true,
- "@metamask/safe-event-emitter": true
- }
- },
- "@metamask/permission-controller>@metamask/json-rpc-engine>@metamask/utils": {
- "globals": {
- "TextDecoder": true,
- "TextEncoder": true
- },
- "packages": {
- "@metamask/rpc-errors>@metamask/utils>@metamask/superstruct": true,
- "@metamask/utils>@scure/base": true,
- "@metamask/utils>pony-cause": true,
- "@noble/hashes": true,
- "browserify>buffer": true,
- "nock>debug": true,
- "semver": true
- }
- },
"@metamask/permission-controller>nanoid": {
"globals": {
"crypto.getRandomValues": true
@@ -2217,50 +2204,90 @@
"ethereumjs-util>ethereum-cryptography>hash.js": true
}
},
- "@metamask/providers>@metamask/json-rpc-engine": {
+ "@metamask/profile-sync-controller": {
+ "globals": {
+ "Event": true,
+ "Headers": true,
+ "TextDecoder": true,
+ "URL": true,
+ "URLSearchParams": true,
+ "addEventListener": true,
+ "console.error": true,
+ "dispatchEvent": true,
+ "fetch": true,
+ "removeEventListener": true,
+ "setTimeout": true
+ },
"packages": {
- "@metamask/rpc-errors": true,
- "@metamask/safe-event-emitter": true,
- "@metamask/utils": true
+ "@metamask/message-signing-snap>@noble/ciphers": true,
+ "@metamask/profile-sync-controller>@metamask/base-controller": true,
+ "@metamask/profile-sync-controller>siwe": true,
+ "@noble/hashes": true,
+ "browserify>buffer": true,
+ "loglevel": true
}
},
- "@metamask/queued-request-controller": {
+ "@metamask/profile-sync-controller>@metamask/base-controller": {
+ "globals": {
+ "setTimeout": true
+ },
"packages": {
- "@metamask/queued-request-controller>@metamask/base-controller": true,
- "@metamask/queued-request-controller>@metamask/json-rpc-engine": true,
- "@metamask/rpc-errors": true,
- "@metamask/selected-network-controller": true,
- "@metamask/utils": true
+ "immer": true
}
},
- "@metamask/queued-request-controller>@metamask/base-controller": {
+ "@metamask/profile-sync-controller>siwe": {
"globals": {
- "setTimeout": true
+ "console.error": true,
+ "console.warn": true
},
"packages": {
- "immer": true
+ "@metamask/controller-utils>@spruceid/siwe-parser>valid-url": true,
+ "@metamask/profile-sync-controller>siwe>@spruceid/siwe-parser": true,
+ "@metamask/profile-sync-controller>siwe>@stablelib/random": true,
+ "@metamask/test-bundler>ethers": true
+ }
+ },
+ "@metamask/profile-sync-controller>siwe>@spruceid/siwe-parser": {
+ "globals": {
+ "console.error": true,
+ "console.log": true
+ },
+ "packages": {
+ "@metamask/controller-utils>@spruceid/siwe-parser>apg-js": true,
+ "@noble/hashes": true
+ }
+ },
+ "@metamask/profile-sync-controller>siwe>@stablelib/random": {
+ "globals": {
+ "crypto": true,
+ "msCrypto": true
+ },
+ "packages": {
+ "@metamask/profile-sync-controller>siwe>@stablelib/random>@stablelib/binary": true,
+ "@metamask/profile-sync-controller>siwe>@stablelib/random>@stablelib/wipe": true,
+ "browserify>browser-resolve": true
+ }
+ },
+ "@metamask/profile-sync-controller>siwe>@stablelib/random>@stablelib/binary": {
+ "packages": {
+ "@metamask/profile-sync-controller>siwe>@stablelib/random>@stablelib/binary>@stablelib/int": true
}
},
- "@metamask/queued-request-controller>@metamask/json-rpc-engine": {
+ "@metamask/queued-request-controller": {
"packages": {
- "@metamask/queued-request-controller>@metamask/json-rpc-engine>@metamask/utils": true,
+ "@metamask/queued-request-controller>@metamask/base-controller": true,
"@metamask/rpc-errors": true,
- "@metamask/safe-event-emitter": true
+ "@metamask/selected-network-controller": true,
+ "@metamask/snaps-controllers>@metamask/json-rpc-engine": true,
+ "@metamask/utils": true
}
},
- "@metamask/queued-request-controller>@metamask/json-rpc-engine>@metamask/utils": {
+ "@metamask/queued-request-controller>@metamask/base-controller": {
"globals": {
- "TextDecoder": true,
- "TextEncoder": true
+ "setTimeout": true
},
"packages": {
- "@metamask/rpc-errors>@metamask/utils>@metamask/superstruct": true,
- "@metamask/utils>@scure/base": true,
- "@metamask/utils>pony-cause": true,
- "@noble/hashes": true,
- "browserify>buffer": true,
- "nock>debug": true,
- "semver": true
+ "immer": true
}
},
"@metamask/rate-limit-controller": {
@@ -2608,21 +2635,19 @@
"globals": {
"DecompressionStream": true,
"URL": true,
- "chrome.offscreen.createDocument": true,
- "chrome.offscreen.hasDocument": true,
"clearTimeout": true,
"document.getElementById": true,
"fetch.bind": true,
"setTimeout": true
},
"packages": {
- "@metamask/base-controller": true,
"@metamask/object-multiplex": true,
+ "@metamask/permission-controller": true,
"@metamask/post-message-stream": true,
"@metamask/rpc-errors": true,
+ "@metamask/snaps-controllers>@metamask/base-controller": true,
"@metamask/snaps-controllers>@metamask/json-rpc-engine": true,
"@metamask/snaps-controllers>@metamask/json-rpc-middleware-stream": true,
- "@metamask/snaps-controllers>@metamask/permission-controller": true,
"@metamask/snaps-controllers>@xstate/fsm": true,
"@metamask/snaps-controllers>concat-stream": true,
"@metamask/snaps-controllers>get-npm-tarball-url": true,
@@ -2644,11 +2669,34 @@
"crypto.getRandomValues": true
}
},
+ "@metamask/snaps-controllers>@metamask/base-controller": {
+ "globals": {
+ "setTimeout": true
+ },
+ "packages": {
+ "immer": true
+ }
+ },
"@metamask/snaps-controllers>@metamask/json-rpc-engine": {
"packages": {
"@metamask/rpc-errors": true,
"@metamask/safe-event-emitter": true,
- "@metamask/utils": true
+ "@metamask/snaps-controllers>@metamask/json-rpc-engine>@metamask/utils": true
+ }
+ },
+ "@metamask/snaps-controllers>@metamask/json-rpc-engine>@metamask/utils": {
+ "globals": {
+ "TextDecoder": true,
+ "TextEncoder": true
+ },
+ "packages": {
+ "@metamask/rpc-errors>@metamask/utils>@metamask/superstruct": true,
+ "@metamask/utils>@scure/base": true,
+ "@metamask/utils>pony-cause": true,
+ "@noble/hashes": true,
+ "browserify>buffer": true,
+ "nock>debug": true,
+ "semver": true
}
},
"@metamask/snaps-controllers>@metamask/json-rpc-middleware-stream": {
@@ -2658,22 +2706,23 @@
},
"packages": {
"@metamask/safe-event-emitter": true,
+ "@metamask/snaps-controllers>@metamask/json-rpc-middleware-stream>@metamask/utils": true,
"readable-stream": true
}
},
- "@metamask/snaps-controllers>@metamask/permission-controller": {
+ "@metamask/snaps-controllers>@metamask/json-rpc-middleware-stream>@metamask/utils": {
"globals": {
- "console.error": true
+ "TextDecoder": true,
+ "TextEncoder": true
},
"packages": {
- "@metamask/base-controller": true,
- "@metamask/controller-utils": true,
- "@metamask/rpc-errors": true,
- "@metamask/snaps-controllers>@metamask/json-rpc-engine": true,
- "@metamask/snaps-controllers>nanoid": true,
- "@metamask/utils": true,
- "deep-freeze-strict": true,
- "immer": true
+ "@metamask/rpc-errors>@metamask/utils>@metamask/superstruct": true,
+ "@metamask/utils>@scure/base": true,
+ "@metamask/utils>pony-cause": true,
+ "@noble/hashes": true,
+ "browserify>buffer": true,
+ "nock>debug": true,
+ "semver": true
}
},
"@metamask/snaps-controllers>concat-stream": {
@@ -2757,11 +2806,26 @@
"packages": {
"@metamask/message-signing-snap>@noble/curves": true,
"@metamask/scure-bip39": true,
- "@metamask/utils": true,
+ "@metamask/snaps-sdk>@metamask/key-tree>@metamask/utils": true,
"@metamask/utils>@scure/base": true,
"@noble/hashes": true
}
},
+ "@metamask/snaps-sdk>@metamask/key-tree>@metamask/utils": {
+ "globals": {
+ "TextDecoder": true,
+ "TextEncoder": true
+ },
+ "packages": {
+ "@metamask/rpc-errors>@metamask/utils>@metamask/superstruct": true,
+ "@metamask/utils>@scure/base": true,
+ "@metamask/utils>pony-cause": true,
+ "@noble/hashes": true,
+ "browserify>buffer": true,
+ "nock>debug": true,
+ "semver": true
+ }
+ },
"@metamask/snaps-utils": {
"globals": {
"File": true,
@@ -2800,9 +2864,24 @@
"@metamask/snaps-utils>@metamask/snaps-registry": {
"packages": {
"@metamask/message-signing-snap>@noble/curves": true,
- "@metamask/utils": true,
+ "@metamask/rpc-errors>@metamask/utils>@metamask/superstruct": true,
+ "@metamask/snaps-utils>@metamask/snaps-registry>@metamask/utils": true,
+ "@noble/hashes": true
+ }
+ },
+ "@metamask/snaps-utils>@metamask/snaps-registry>@metamask/utils": {
+ "globals": {
+ "TextDecoder": true,
+ "TextEncoder": true
+ },
+ "packages": {
+ "@metamask/rpc-errors>@metamask/utils>@metamask/superstruct": true,
+ "@metamask/utils>@scure/base": true,
+ "@metamask/utils>pony-cause": true,
"@noble/hashes": true,
- "superstruct": true
+ "browserify>buffer": true,
+ "nock>debug": true,
+ "semver": true
}
},
"@metamask/snaps-utils>cron-parser": {
@@ -2848,6 +2927,53 @@
"@ethersproject/abi>@ethersproject/logger": true
}
},
+ "@metamask/test-bundler>ethers": {
+ "packages": {
+ "@ethersproject/abi": true,
+ "@ethersproject/abi>@ethersproject/address": true,
+ "@ethersproject/abi>@ethersproject/constants": true,
+ "@ethersproject/abi>@ethersproject/keccak256": true,
+ "@ethersproject/abi>@ethersproject/logger": true,
+ "@ethersproject/abi>@ethersproject/properties": true,
+ "@ethersproject/abi>@ethersproject/strings": true,
+ "@ethersproject/bignumber": true,
+ "@ethersproject/bytes": true,
+ "@ethersproject/contracts": true,
+ "@ethersproject/hash": true,
+ "@ethersproject/hash>@ethersproject/abstract-signer": true,
+ "@ethersproject/hash>@ethersproject/base64": true,
+ "@ethersproject/hdnode": true,
+ "@ethersproject/hdnode>@ethersproject/basex": true,
+ "@ethersproject/hdnode>@ethersproject/sha2": true,
+ "@ethersproject/hdnode>@ethersproject/signing-key": true,
+ "@ethersproject/hdnode>@ethersproject/transactions": true,
+ "@ethersproject/hdnode>@ethersproject/wordlists": true,
+ "@ethersproject/providers": true,
+ "@ethersproject/providers>@ethersproject/rlp": true,
+ "@ethersproject/providers>@ethersproject/web": true,
+ "@ethersproject/wallet": true,
+ "@ethersproject/wallet>@ethersproject/json-wallets": true,
+ "@ethersproject/wallet>@ethersproject/random": true,
+ "@metamask/test-bundler>ethers>@ethersproject/solidity": true,
+ "@metamask/test-bundler>ethers>@ethersproject/units": true
+ }
+ },
+ "@metamask/test-bundler>ethers>@ethersproject/solidity": {
+ "packages": {
+ "@ethersproject/abi>@ethersproject/keccak256": true,
+ "@ethersproject/abi>@ethersproject/logger": true,
+ "@ethersproject/abi>@ethersproject/strings": true,
+ "@ethersproject/bignumber": true,
+ "@ethersproject/bytes": true,
+ "@ethersproject/hdnode>@ethersproject/sha2": true
+ }
+ },
+ "@metamask/test-bundler>ethers>@ethersproject/units": {
+ "packages": {
+ "@ethersproject/abi>@ethersproject/logger": true,
+ "@ethersproject/bignumber": true
+ }
+ },
"@metamask/transaction-controller": {
"globals": {
"clearTimeout": true,
@@ -3062,13 +3188,6 @@
"define": true
}
},
- "@noble/ciphers": {
- "globals": {
- "TextDecoder": true,
- "TextEncoder": true,
- "crypto": true
- }
- },
"@noble/hashes": {
"globals": {
"TextEncoder": true,
diff --git a/lavamoat/browserify/main/policy.json b/lavamoat/browserify/main/policy.json
index ad2d0589b1a9..6bd4b686477a 100644
--- a/lavamoat/browserify/main/policy.json
+++ b/lavamoat/browserify/main/policy.json
@@ -1035,12 +1035,8 @@
}
},
"@metamask/controller-utils>@spruceid/siwe-parser>apg-js": {
- "globals": {
- "mode": true
- },
"packages": {
- "browserify>buffer": true,
- "browserify>insert-module-globals>is-buffer": true
+ "browserify>buffer": true
}
},
"@metamask/controllers>web3": {
@@ -1114,8 +1110,8 @@
},
"packages": {
"@metamask/eth-json-rpc-filters>@metamask/eth-query": true,
+ "@metamask/eth-json-rpc-filters>@metamask/json-rpc-engine": true,
"@metamask/eth-json-rpc-filters>async-mutex": true,
- "@metamask/providers>@metamask/json-rpc-engine": true,
"@metamask/safe-event-emitter": true,
"pify": true
}
@@ -1126,6 +1122,13 @@
"watchify>xtend": true
}
},
+ "@metamask/eth-json-rpc-filters>@metamask/json-rpc-engine": {
+ "packages": {
+ "@metamask/rpc-errors": true,
+ "@metamask/safe-event-emitter": true,
+ "@metamask/utils": true
+ }
+ },
"@metamask/eth-json-rpc-filters>async-mutex": {
"globals": {
"setTimeout": true
@@ -1141,10 +1144,10 @@
"setTimeout": true
},
"packages": {
+ "@metamask/eth-json-rpc-middleware>@metamask/json-rpc-engine": true,
"@metamask/eth-json-rpc-middleware>klona": true,
"@metamask/eth-json-rpc-middleware>safe-stable-stringify": true,
"@metamask/eth-sig-util": true,
- "@metamask/providers>@metamask/json-rpc-engine": true,
"@metamask/rpc-errors": true,
"@metamask/utils": true,
"pify": true
@@ -1152,10 +1155,24 @@
},
"@metamask/eth-json-rpc-middleware>@metamask/eth-json-rpc-provider": {
"packages": {
- "@metamask/providers>@metamask/json-rpc-engine": true,
+ "@metamask/eth-json-rpc-middleware>@metamask/eth-json-rpc-provider>@metamask/json-rpc-engine": true,
"@metamask/safe-event-emitter": true
}
},
+ "@metamask/eth-json-rpc-middleware>@metamask/eth-json-rpc-provider>@metamask/json-rpc-engine": {
+ "packages": {
+ "@metamask/rpc-errors": true,
+ "@metamask/safe-event-emitter": true,
+ "@metamask/utils": true
+ }
+ },
+ "@metamask/eth-json-rpc-middleware>@metamask/json-rpc-engine": {
+ "packages": {
+ "@metamask/rpc-errors": true,
+ "@metamask/safe-event-emitter": true,
+ "@metamask/utils": true
+ }
+ },
"@metamask/eth-ledger-bridge-keyring": {
"globals": {
"addEventListener": true,
@@ -1810,6 +1827,13 @@
"browserify>url": true
}
},
+ "@metamask/message-signing-snap>@noble/ciphers": {
+ "globals": {
+ "TextDecoder": true,
+ "TextEncoder": true,
+ "crypto": true
+ }
+ },
"@metamask/message-signing-snap>@noble/curves": {
"globals": {
"TextEncoder": true
@@ -1894,9 +1918,9 @@
"@metamask/network-controller>@metamask/controller-utils": true,
"@metamask/network-controller>@metamask/eth-json-rpc-infura": true,
"@metamask/network-controller>@metamask/eth-json-rpc-provider": true,
- "@metamask/network-controller>@metamask/json-rpc-engine": true,
"@metamask/network-controller>@metamask/swappable-obj-proxy": true,
"@metamask/rpc-errors": true,
+ "@metamask/snaps-controllers>@metamask/json-rpc-engine": true,
"@metamask/utils": true,
"browserify>assert": true,
"uuid": true
@@ -1949,40 +1973,25 @@
},
"packages": {
"@metamask/eth-json-rpc-middleware>@metamask/eth-json-rpc-provider": true,
- "@metamask/providers>@metamask/json-rpc-engine": true,
+ "@metamask/network-controller>@metamask/eth-json-rpc-infura>@metamask/json-rpc-engine": true,
"@metamask/rpc-errors": true,
"@metamask/utils": true,
"node-fetch": true
}
},
- "@metamask/network-controller>@metamask/eth-json-rpc-provider": {
+ "@metamask/network-controller>@metamask/eth-json-rpc-infura>@metamask/json-rpc-engine": {
"packages": {
- "@metamask/network-controller>@metamask/json-rpc-engine": true,
"@metamask/rpc-errors": true,
"@metamask/safe-event-emitter": true,
- "uuid": true
+ "@metamask/utils": true
}
},
- "@metamask/network-controller>@metamask/json-rpc-engine": {
+ "@metamask/network-controller>@metamask/eth-json-rpc-provider": {
"packages": {
- "@metamask/network-controller>@metamask/json-rpc-engine>@metamask/utils": true,
"@metamask/rpc-errors": true,
- "@metamask/safe-event-emitter": true
- }
- },
- "@metamask/network-controller>@metamask/json-rpc-engine>@metamask/utils": {
- "globals": {
- "TextDecoder": true,
- "TextEncoder": true
- },
- "packages": {
- "@metamask/rpc-errors>@metamask/utils>@metamask/superstruct": true,
- "@metamask/utils>@scure/base": true,
- "@metamask/utils>pony-cause": true,
- "@noble/hashes": true,
- "browserify>buffer": true,
- "nock>debug": true,
- "semver": true
+ "@metamask/safe-event-emitter": true,
+ "@metamask/snaps-controllers>@metamask/json-rpc-engine": true,
+ "uuid": true
}
},
"@metamask/notification-controller": {
@@ -2024,9 +2033,9 @@
"packages": {
"@metamask/permission-controller>@metamask/base-controller": true,
"@metamask/permission-controller>@metamask/controller-utils": true,
- "@metamask/permission-controller>@metamask/json-rpc-engine": true,
"@metamask/permission-controller>nanoid": true,
"@metamask/rpc-errors": true,
+ "@metamask/snaps-controllers>@metamask/json-rpc-engine": true,
"@metamask/utils": true,
"deep-freeze-strict": true,
"immer": true
@@ -2073,28 +2082,6 @@
"semver": true
}
},
- "@metamask/permission-controller>@metamask/json-rpc-engine": {
- "packages": {
- "@metamask/permission-controller>@metamask/json-rpc-engine>@metamask/utils": true,
- "@metamask/rpc-errors": true,
- "@metamask/safe-event-emitter": true
- }
- },
- "@metamask/permission-controller>@metamask/json-rpc-engine>@metamask/utils": {
- "globals": {
- "TextDecoder": true,
- "TextEncoder": true
- },
- "packages": {
- "@metamask/rpc-errors>@metamask/utils>@metamask/superstruct": true,
- "@metamask/utils>@scure/base": true,
- "@metamask/utils>pony-cause": true,
- "@noble/hashes": true,
- "browserify>buffer": true,
- "nock>debug": true,
- "semver": true
- }
- },
"@metamask/permission-controller>nanoid": {
"globals": {
"crypto.getRandomValues": true
@@ -2217,50 +2204,90 @@
"ethereumjs-util>ethereum-cryptography>hash.js": true
}
},
- "@metamask/providers>@metamask/json-rpc-engine": {
+ "@metamask/profile-sync-controller": {
+ "globals": {
+ "Event": true,
+ "Headers": true,
+ "TextDecoder": true,
+ "URL": true,
+ "URLSearchParams": true,
+ "addEventListener": true,
+ "console.error": true,
+ "dispatchEvent": true,
+ "fetch": true,
+ "removeEventListener": true,
+ "setTimeout": true
+ },
"packages": {
- "@metamask/rpc-errors": true,
- "@metamask/safe-event-emitter": true,
- "@metamask/utils": true
+ "@metamask/message-signing-snap>@noble/ciphers": true,
+ "@metamask/profile-sync-controller>@metamask/base-controller": true,
+ "@metamask/profile-sync-controller>siwe": true,
+ "@noble/hashes": true,
+ "browserify>buffer": true,
+ "loglevel": true
}
},
- "@metamask/queued-request-controller": {
+ "@metamask/profile-sync-controller>@metamask/base-controller": {
+ "globals": {
+ "setTimeout": true
+ },
"packages": {
- "@metamask/queued-request-controller>@metamask/base-controller": true,
- "@metamask/queued-request-controller>@metamask/json-rpc-engine": true,
- "@metamask/rpc-errors": true,
- "@metamask/selected-network-controller": true,
- "@metamask/utils": true
+ "immer": true
}
},
- "@metamask/queued-request-controller>@metamask/base-controller": {
+ "@metamask/profile-sync-controller>siwe": {
"globals": {
- "setTimeout": true
+ "console.error": true,
+ "console.warn": true
},
"packages": {
- "immer": true
+ "@metamask/controller-utils>@spruceid/siwe-parser>valid-url": true,
+ "@metamask/profile-sync-controller>siwe>@spruceid/siwe-parser": true,
+ "@metamask/profile-sync-controller>siwe>@stablelib/random": true,
+ "@metamask/test-bundler>ethers": true
+ }
+ },
+ "@metamask/profile-sync-controller>siwe>@spruceid/siwe-parser": {
+ "globals": {
+ "console.error": true,
+ "console.log": true
+ },
+ "packages": {
+ "@metamask/controller-utils>@spruceid/siwe-parser>apg-js": true,
+ "@noble/hashes": true
+ }
+ },
+ "@metamask/profile-sync-controller>siwe>@stablelib/random": {
+ "globals": {
+ "crypto": true,
+ "msCrypto": true
+ },
+ "packages": {
+ "@metamask/profile-sync-controller>siwe>@stablelib/random>@stablelib/binary": true,
+ "@metamask/profile-sync-controller>siwe>@stablelib/random>@stablelib/wipe": true,
+ "browserify>browser-resolve": true
+ }
+ },
+ "@metamask/profile-sync-controller>siwe>@stablelib/random>@stablelib/binary": {
+ "packages": {
+ "@metamask/profile-sync-controller>siwe>@stablelib/random>@stablelib/binary>@stablelib/int": true
}
},
- "@metamask/queued-request-controller>@metamask/json-rpc-engine": {
+ "@metamask/queued-request-controller": {
"packages": {
- "@metamask/queued-request-controller>@metamask/json-rpc-engine>@metamask/utils": true,
+ "@metamask/queued-request-controller>@metamask/base-controller": true,
"@metamask/rpc-errors": true,
- "@metamask/safe-event-emitter": true
+ "@metamask/selected-network-controller": true,
+ "@metamask/snaps-controllers>@metamask/json-rpc-engine": true,
+ "@metamask/utils": true
}
},
- "@metamask/queued-request-controller>@metamask/json-rpc-engine>@metamask/utils": {
+ "@metamask/queued-request-controller>@metamask/base-controller": {
"globals": {
- "TextDecoder": true,
- "TextEncoder": true
+ "setTimeout": true
},
"packages": {
- "@metamask/rpc-errors>@metamask/utils>@metamask/superstruct": true,
- "@metamask/utils>@scure/base": true,
- "@metamask/utils>pony-cause": true,
- "@noble/hashes": true,
- "browserify>buffer": true,
- "nock>debug": true,
- "semver": true
+ "immer": true
}
},
"@metamask/rate-limit-controller": {
@@ -2608,21 +2635,19 @@
"globals": {
"DecompressionStream": true,
"URL": true,
- "chrome.offscreen.createDocument": true,
- "chrome.offscreen.hasDocument": true,
"clearTimeout": true,
"document.getElementById": true,
"fetch.bind": true,
"setTimeout": true
},
"packages": {
- "@metamask/base-controller": true,
"@metamask/object-multiplex": true,
+ "@metamask/permission-controller": true,
"@metamask/post-message-stream": true,
"@metamask/rpc-errors": true,
+ "@metamask/snaps-controllers>@metamask/base-controller": true,
"@metamask/snaps-controllers>@metamask/json-rpc-engine": true,
"@metamask/snaps-controllers>@metamask/json-rpc-middleware-stream": true,
- "@metamask/snaps-controllers>@metamask/permission-controller": true,
"@metamask/snaps-controllers>@xstate/fsm": true,
"@metamask/snaps-controllers>concat-stream": true,
"@metamask/snaps-controllers>get-npm-tarball-url": true,
@@ -2644,11 +2669,34 @@
"crypto.getRandomValues": true
}
},
+ "@metamask/snaps-controllers>@metamask/base-controller": {
+ "globals": {
+ "setTimeout": true
+ },
+ "packages": {
+ "immer": true
+ }
+ },
"@metamask/snaps-controllers>@metamask/json-rpc-engine": {
"packages": {
"@metamask/rpc-errors": true,
"@metamask/safe-event-emitter": true,
- "@metamask/utils": true
+ "@metamask/snaps-controllers>@metamask/json-rpc-engine>@metamask/utils": true
+ }
+ },
+ "@metamask/snaps-controllers>@metamask/json-rpc-engine>@metamask/utils": {
+ "globals": {
+ "TextDecoder": true,
+ "TextEncoder": true
+ },
+ "packages": {
+ "@metamask/rpc-errors>@metamask/utils>@metamask/superstruct": true,
+ "@metamask/utils>@scure/base": true,
+ "@metamask/utils>pony-cause": true,
+ "@noble/hashes": true,
+ "browserify>buffer": true,
+ "nock>debug": true,
+ "semver": true
}
},
"@metamask/snaps-controllers>@metamask/json-rpc-middleware-stream": {
@@ -2658,22 +2706,23 @@
},
"packages": {
"@metamask/safe-event-emitter": true,
+ "@metamask/snaps-controllers>@metamask/json-rpc-middleware-stream>@metamask/utils": true,
"readable-stream": true
}
},
- "@metamask/snaps-controllers>@metamask/permission-controller": {
+ "@metamask/snaps-controllers>@metamask/json-rpc-middleware-stream>@metamask/utils": {
"globals": {
- "console.error": true
+ "TextDecoder": true,
+ "TextEncoder": true
},
"packages": {
- "@metamask/base-controller": true,
- "@metamask/controller-utils": true,
- "@metamask/rpc-errors": true,
- "@metamask/snaps-controllers>@metamask/json-rpc-engine": true,
- "@metamask/snaps-controllers>nanoid": true,
- "@metamask/utils": true,
- "deep-freeze-strict": true,
- "immer": true
+ "@metamask/rpc-errors>@metamask/utils>@metamask/superstruct": true,
+ "@metamask/utils>@scure/base": true,
+ "@metamask/utils>pony-cause": true,
+ "@noble/hashes": true,
+ "browserify>buffer": true,
+ "nock>debug": true,
+ "semver": true
}
},
"@metamask/snaps-controllers>concat-stream": {
@@ -2757,11 +2806,26 @@
"packages": {
"@metamask/message-signing-snap>@noble/curves": true,
"@metamask/scure-bip39": true,
- "@metamask/utils": true,
+ "@metamask/snaps-sdk>@metamask/key-tree>@metamask/utils": true,
"@metamask/utils>@scure/base": true,
"@noble/hashes": true
}
},
+ "@metamask/snaps-sdk>@metamask/key-tree>@metamask/utils": {
+ "globals": {
+ "TextDecoder": true,
+ "TextEncoder": true
+ },
+ "packages": {
+ "@metamask/rpc-errors>@metamask/utils>@metamask/superstruct": true,
+ "@metamask/utils>@scure/base": true,
+ "@metamask/utils>pony-cause": true,
+ "@noble/hashes": true,
+ "browserify>buffer": true,
+ "nock>debug": true,
+ "semver": true
+ }
+ },
"@metamask/snaps-utils": {
"globals": {
"File": true,
@@ -2800,9 +2864,24 @@
"@metamask/snaps-utils>@metamask/snaps-registry": {
"packages": {
"@metamask/message-signing-snap>@noble/curves": true,
- "@metamask/utils": true,
+ "@metamask/rpc-errors>@metamask/utils>@metamask/superstruct": true,
+ "@metamask/snaps-utils>@metamask/snaps-registry>@metamask/utils": true,
+ "@noble/hashes": true
+ }
+ },
+ "@metamask/snaps-utils>@metamask/snaps-registry>@metamask/utils": {
+ "globals": {
+ "TextDecoder": true,
+ "TextEncoder": true
+ },
+ "packages": {
+ "@metamask/rpc-errors>@metamask/utils>@metamask/superstruct": true,
+ "@metamask/utils>@scure/base": true,
+ "@metamask/utils>pony-cause": true,
"@noble/hashes": true,
- "superstruct": true
+ "browserify>buffer": true,
+ "nock>debug": true,
+ "semver": true
}
},
"@metamask/snaps-utils>cron-parser": {
@@ -2848,6 +2927,53 @@
"@ethersproject/abi>@ethersproject/logger": true
}
},
+ "@metamask/test-bundler>ethers": {
+ "packages": {
+ "@ethersproject/abi": true,
+ "@ethersproject/abi>@ethersproject/address": true,
+ "@ethersproject/abi>@ethersproject/constants": true,
+ "@ethersproject/abi>@ethersproject/keccak256": true,
+ "@ethersproject/abi>@ethersproject/logger": true,
+ "@ethersproject/abi>@ethersproject/properties": true,
+ "@ethersproject/abi>@ethersproject/strings": true,
+ "@ethersproject/bignumber": true,
+ "@ethersproject/bytes": true,
+ "@ethersproject/contracts": true,
+ "@ethersproject/hash": true,
+ "@ethersproject/hash>@ethersproject/abstract-signer": true,
+ "@ethersproject/hash>@ethersproject/base64": true,
+ "@ethersproject/hdnode": true,
+ "@ethersproject/hdnode>@ethersproject/basex": true,
+ "@ethersproject/hdnode>@ethersproject/sha2": true,
+ "@ethersproject/hdnode>@ethersproject/signing-key": true,
+ "@ethersproject/hdnode>@ethersproject/transactions": true,
+ "@ethersproject/hdnode>@ethersproject/wordlists": true,
+ "@ethersproject/providers": true,
+ "@ethersproject/providers>@ethersproject/rlp": true,
+ "@ethersproject/providers>@ethersproject/web": true,
+ "@ethersproject/wallet": true,
+ "@ethersproject/wallet>@ethersproject/json-wallets": true,
+ "@ethersproject/wallet>@ethersproject/random": true,
+ "@metamask/test-bundler>ethers>@ethersproject/solidity": true,
+ "@metamask/test-bundler>ethers>@ethersproject/units": true
+ }
+ },
+ "@metamask/test-bundler>ethers>@ethersproject/solidity": {
+ "packages": {
+ "@ethersproject/abi>@ethersproject/keccak256": true,
+ "@ethersproject/abi>@ethersproject/logger": true,
+ "@ethersproject/abi>@ethersproject/strings": true,
+ "@ethersproject/bignumber": true,
+ "@ethersproject/bytes": true,
+ "@ethersproject/hdnode>@ethersproject/sha2": true
+ }
+ },
+ "@metamask/test-bundler>ethers>@ethersproject/units": {
+ "packages": {
+ "@ethersproject/abi>@ethersproject/logger": true,
+ "@ethersproject/bignumber": true
+ }
+ },
"@metamask/transaction-controller": {
"globals": {
"clearTimeout": true,
@@ -3062,13 +3188,6 @@
"define": true
}
},
- "@noble/ciphers": {
- "globals": {
- "TextDecoder": true,
- "TextEncoder": true,
- "crypto": true
- }
- },
"@noble/hashes": {
"globals": {
"TextEncoder": true,
diff --git a/lavamoat/browserify/mmi/policy.json b/lavamoat/browserify/mmi/policy.json
index cb10c2e972d3..015a2e6706a0 100644
--- a/lavamoat/browserify/mmi/policy.json
+++ b/lavamoat/browserify/mmi/policy.json
@@ -1127,12 +1127,8 @@
}
},
"@metamask/controller-utils>@spruceid/siwe-parser>apg-js": {
- "globals": {
- "mode": true
- },
"packages": {
- "browserify>buffer": true,
- "browserify>insert-module-globals>is-buffer": true
+ "browserify>buffer": true
}
},
"@metamask/controllers>web3": {
@@ -1206,8 +1202,8 @@
},
"packages": {
"@metamask/eth-json-rpc-filters>@metamask/eth-query": true,
+ "@metamask/eth-json-rpc-filters>@metamask/json-rpc-engine": true,
"@metamask/eth-json-rpc-filters>async-mutex": true,
- "@metamask/providers>@metamask/json-rpc-engine": true,
"@metamask/safe-event-emitter": true,
"pify": true
}
@@ -1218,6 +1214,13 @@
"watchify>xtend": true
}
},
+ "@metamask/eth-json-rpc-filters>@metamask/json-rpc-engine": {
+ "packages": {
+ "@metamask/rpc-errors": true,
+ "@metamask/safe-event-emitter": true,
+ "@metamask/utils": true
+ }
+ },
"@metamask/eth-json-rpc-filters>async-mutex": {
"globals": {
"setTimeout": true
@@ -1233,10 +1236,10 @@
"setTimeout": true
},
"packages": {
+ "@metamask/eth-json-rpc-middleware>@metamask/json-rpc-engine": true,
"@metamask/eth-json-rpc-middleware>klona": true,
"@metamask/eth-json-rpc-middleware>safe-stable-stringify": true,
"@metamask/eth-sig-util": true,
- "@metamask/providers>@metamask/json-rpc-engine": true,
"@metamask/rpc-errors": true,
"@metamask/utils": true,
"pify": true
@@ -1244,10 +1247,24 @@
},
"@metamask/eth-json-rpc-middleware>@metamask/eth-json-rpc-provider": {
"packages": {
- "@metamask/providers>@metamask/json-rpc-engine": true,
+ "@metamask/eth-json-rpc-middleware>@metamask/eth-json-rpc-provider>@metamask/json-rpc-engine": true,
"@metamask/safe-event-emitter": true
}
},
+ "@metamask/eth-json-rpc-middleware>@metamask/eth-json-rpc-provider>@metamask/json-rpc-engine": {
+ "packages": {
+ "@metamask/rpc-errors": true,
+ "@metamask/safe-event-emitter": true,
+ "@metamask/utils": true
+ }
+ },
+ "@metamask/eth-json-rpc-middleware>@metamask/json-rpc-engine": {
+ "packages": {
+ "@metamask/rpc-errors": true,
+ "@metamask/safe-event-emitter": true,
+ "@metamask/utils": true
+ }
+ },
"@metamask/eth-ledger-bridge-keyring": {
"globals": {
"addEventListener": true,
@@ -1902,6 +1919,13 @@
"browserify>url": true
}
},
+ "@metamask/message-signing-snap>@noble/ciphers": {
+ "globals": {
+ "TextDecoder": true,
+ "TextEncoder": true,
+ "crypto": true
+ }
+ },
"@metamask/message-signing-snap>@noble/curves": {
"globals": {
"TextEncoder": true
@@ -1986,9 +2010,9 @@
"@metamask/network-controller>@metamask/controller-utils": true,
"@metamask/network-controller>@metamask/eth-json-rpc-infura": true,
"@metamask/network-controller>@metamask/eth-json-rpc-provider": true,
- "@metamask/network-controller>@metamask/json-rpc-engine": true,
"@metamask/network-controller>@metamask/swappable-obj-proxy": true,
"@metamask/rpc-errors": true,
+ "@metamask/snaps-controllers>@metamask/json-rpc-engine": true,
"@metamask/utils": true,
"browserify>assert": true,
"uuid": true
@@ -2041,40 +2065,25 @@
},
"packages": {
"@metamask/eth-json-rpc-middleware>@metamask/eth-json-rpc-provider": true,
- "@metamask/providers>@metamask/json-rpc-engine": true,
+ "@metamask/network-controller>@metamask/eth-json-rpc-infura>@metamask/json-rpc-engine": true,
"@metamask/rpc-errors": true,
"@metamask/utils": true,
"node-fetch": true
}
},
- "@metamask/network-controller>@metamask/eth-json-rpc-provider": {
+ "@metamask/network-controller>@metamask/eth-json-rpc-infura>@metamask/json-rpc-engine": {
"packages": {
- "@metamask/network-controller>@metamask/json-rpc-engine": true,
"@metamask/rpc-errors": true,
"@metamask/safe-event-emitter": true,
- "uuid": true
+ "@metamask/utils": true
}
},
- "@metamask/network-controller>@metamask/json-rpc-engine": {
+ "@metamask/network-controller>@metamask/eth-json-rpc-provider": {
"packages": {
- "@metamask/network-controller>@metamask/json-rpc-engine>@metamask/utils": true,
"@metamask/rpc-errors": true,
- "@metamask/safe-event-emitter": true
- }
- },
- "@metamask/network-controller>@metamask/json-rpc-engine>@metamask/utils": {
- "globals": {
- "TextDecoder": true,
- "TextEncoder": true
- },
- "packages": {
- "@metamask/rpc-errors>@metamask/utils>@metamask/superstruct": true,
- "@metamask/utils>@scure/base": true,
- "@metamask/utils>pony-cause": true,
- "@noble/hashes": true,
- "browserify>buffer": true,
- "nock>debug": true,
- "semver": true
+ "@metamask/safe-event-emitter": true,
+ "@metamask/snaps-controllers>@metamask/json-rpc-engine": true,
+ "uuid": true
}
},
"@metamask/notification-controller": {
@@ -2116,9 +2125,9 @@
"packages": {
"@metamask/permission-controller>@metamask/base-controller": true,
"@metamask/permission-controller>@metamask/controller-utils": true,
- "@metamask/permission-controller>@metamask/json-rpc-engine": true,
"@metamask/permission-controller>nanoid": true,
"@metamask/rpc-errors": true,
+ "@metamask/snaps-controllers>@metamask/json-rpc-engine": true,
"@metamask/utils": true,
"deep-freeze-strict": true,
"immer": true
@@ -2165,28 +2174,6 @@
"semver": true
}
},
- "@metamask/permission-controller>@metamask/json-rpc-engine": {
- "packages": {
- "@metamask/permission-controller>@metamask/json-rpc-engine>@metamask/utils": true,
- "@metamask/rpc-errors": true,
- "@metamask/safe-event-emitter": true
- }
- },
- "@metamask/permission-controller>@metamask/json-rpc-engine>@metamask/utils": {
- "globals": {
- "TextDecoder": true,
- "TextEncoder": true
- },
- "packages": {
- "@metamask/rpc-errors>@metamask/utils>@metamask/superstruct": true,
- "@metamask/utils>@scure/base": true,
- "@metamask/utils>pony-cause": true,
- "@noble/hashes": true,
- "browserify>buffer": true,
- "nock>debug": true,
- "semver": true
- }
- },
"@metamask/permission-controller>nanoid": {
"globals": {
"crypto.getRandomValues": true
@@ -2309,50 +2296,90 @@
"ethereumjs-util>ethereum-cryptography>hash.js": true
}
},
- "@metamask/providers>@metamask/json-rpc-engine": {
+ "@metamask/profile-sync-controller": {
+ "globals": {
+ "Event": true,
+ "Headers": true,
+ "TextDecoder": true,
+ "URL": true,
+ "URLSearchParams": true,
+ "addEventListener": true,
+ "console.error": true,
+ "dispatchEvent": true,
+ "fetch": true,
+ "removeEventListener": true,
+ "setTimeout": true
+ },
"packages": {
- "@metamask/rpc-errors": true,
- "@metamask/safe-event-emitter": true,
- "@metamask/utils": true
+ "@metamask/message-signing-snap>@noble/ciphers": true,
+ "@metamask/profile-sync-controller>@metamask/base-controller": true,
+ "@metamask/profile-sync-controller>siwe": true,
+ "@noble/hashes": true,
+ "browserify>buffer": true,
+ "loglevel": true
}
},
- "@metamask/queued-request-controller": {
+ "@metamask/profile-sync-controller>@metamask/base-controller": {
+ "globals": {
+ "setTimeout": true
+ },
"packages": {
- "@metamask/queued-request-controller>@metamask/base-controller": true,
- "@metamask/queued-request-controller>@metamask/json-rpc-engine": true,
- "@metamask/rpc-errors": true,
- "@metamask/selected-network-controller": true,
- "@metamask/utils": true
+ "immer": true
}
},
- "@metamask/queued-request-controller>@metamask/base-controller": {
+ "@metamask/profile-sync-controller>siwe": {
"globals": {
- "setTimeout": true
+ "console.error": true,
+ "console.warn": true
},
"packages": {
- "immer": true
+ "@metamask/controller-utils>@spruceid/siwe-parser>valid-url": true,
+ "@metamask/profile-sync-controller>siwe>@spruceid/siwe-parser": true,
+ "@metamask/profile-sync-controller>siwe>@stablelib/random": true,
+ "@metamask/test-bundler>ethers": true
+ }
+ },
+ "@metamask/profile-sync-controller>siwe>@spruceid/siwe-parser": {
+ "globals": {
+ "console.error": true,
+ "console.log": true
+ },
+ "packages": {
+ "@metamask/controller-utils>@spruceid/siwe-parser>apg-js": true,
+ "@noble/hashes": true
+ }
+ },
+ "@metamask/profile-sync-controller>siwe>@stablelib/random": {
+ "globals": {
+ "crypto": true,
+ "msCrypto": true
+ },
+ "packages": {
+ "@metamask/profile-sync-controller>siwe>@stablelib/random>@stablelib/binary": true,
+ "@metamask/profile-sync-controller>siwe>@stablelib/random>@stablelib/wipe": true,
+ "browserify>browser-resolve": true
+ }
+ },
+ "@metamask/profile-sync-controller>siwe>@stablelib/random>@stablelib/binary": {
+ "packages": {
+ "@metamask/profile-sync-controller>siwe>@stablelib/random>@stablelib/binary>@stablelib/int": true
}
},
- "@metamask/queued-request-controller>@metamask/json-rpc-engine": {
+ "@metamask/queued-request-controller": {
"packages": {
- "@metamask/queued-request-controller>@metamask/json-rpc-engine>@metamask/utils": true,
+ "@metamask/queued-request-controller>@metamask/base-controller": true,
"@metamask/rpc-errors": true,
- "@metamask/safe-event-emitter": true
+ "@metamask/selected-network-controller": true,
+ "@metamask/snaps-controllers>@metamask/json-rpc-engine": true,
+ "@metamask/utils": true
}
},
- "@metamask/queued-request-controller>@metamask/json-rpc-engine>@metamask/utils": {
+ "@metamask/queued-request-controller>@metamask/base-controller": {
"globals": {
- "TextDecoder": true,
- "TextEncoder": true
+ "setTimeout": true
},
"packages": {
- "@metamask/rpc-errors>@metamask/utils>@metamask/superstruct": true,
- "@metamask/utils>@scure/base": true,
- "@metamask/utils>pony-cause": true,
- "@noble/hashes": true,
- "browserify>buffer": true,
- "nock>debug": true,
- "semver": true
+ "immer": true
}
},
"@metamask/rate-limit-controller": {
@@ -2700,21 +2727,19 @@
"globals": {
"DecompressionStream": true,
"URL": true,
- "chrome.offscreen.createDocument": true,
- "chrome.offscreen.hasDocument": true,
"clearTimeout": true,
"document.getElementById": true,
"fetch.bind": true,
"setTimeout": true
},
"packages": {
- "@metamask/base-controller": true,
"@metamask/object-multiplex": true,
+ "@metamask/permission-controller": true,
"@metamask/post-message-stream": true,
"@metamask/rpc-errors": true,
+ "@metamask/snaps-controllers>@metamask/base-controller": true,
"@metamask/snaps-controllers>@metamask/json-rpc-engine": true,
"@metamask/snaps-controllers>@metamask/json-rpc-middleware-stream": true,
- "@metamask/snaps-controllers>@metamask/permission-controller": true,
"@metamask/snaps-controllers>@xstate/fsm": true,
"@metamask/snaps-controllers>concat-stream": true,
"@metamask/snaps-controllers>get-npm-tarball-url": true,
@@ -2736,11 +2761,34 @@
"crypto.getRandomValues": true
}
},
+ "@metamask/snaps-controllers>@metamask/base-controller": {
+ "globals": {
+ "setTimeout": true
+ },
+ "packages": {
+ "immer": true
+ }
+ },
"@metamask/snaps-controllers>@metamask/json-rpc-engine": {
"packages": {
"@metamask/rpc-errors": true,
"@metamask/safe-event-emitter": true,
- "@metamask/utils": true
+ "@metamask/snaps-controllers>@metamask/json-rpc-engine>@metamask/utils": true
+ }
+ },
+ "@metamask/snaps-controllers>@metamask/json-rpc-engine>@metamask/utils": {
+ "globals": {
+ "TextDecoder": true,
+ "TextEncoder": true
+ },
+ "packages": {
+ "@metamask/rpc-errors>@metamask/utils>@metamask/superstruct": true,
+ "@metamask/utils>@scure/base": true,
+ "@metamask/utils>pony-cause": true,
+ "@noble/hashes": true,
+ "browserify>buffer": true,
+ "nock>debug": true,
+ "semver": true
}
},
"@metamask/snaps-controllers>@metamask/json-rpc-middleware-stream": {
@@ -2750,22 +2798,23 @@
},
"packages": {
"@metamask/safe-event-emitter": true,
+ "@metamask/snaps-controllers>@metamask/json-rpc-middleware-stream>@metamask/utils": true,
"readable-stream": true
}
},
- "@metamask/snaps-controllers>@metamask/permission-controller": {
+ "@metamask/snaps-controllers>@metamask/json-rpc-middleware-stream>@metamask/utils": {
"globals": {
- "console.error": true
+ "TextDecoder": true,
+ "TextEncoder": true
},
"packages": {
- "@metamask/base-controller": true,
- "@metamask/controller-utils": true,
- "@metamask/rpc-errors": true,
- "@metamask/snaps-controllers>@metamask/json-rpc-engine": true,
- "@metamask/snaps-controllers>nanoid": true,
- "@metamask/utils": true,
- "deep-freeze-strict": true,
- "immer": true
+ "@metamask/rpc-errors>@metamask/utils>@metamask/superstruct": true,
+ "@metamask/utils>@scure/base": true,
+ "@metamask/utils>pony-cause": true,
+ "@noble/hashes": true,
+ "browserify>buffer": true,
+ "nock>debug": true,
+ "semver": true
}
},
"@metamask/snaps-controllers>concat-stream": {
@@ -2849,11 +2898,26 @@
"packages": {
"@metamask/message-signing-snap>@noble/curves": true,
"@metamask/scure-bip39": true,
- "@metamask/utils": true,
+ "@metamask/snaps-sdk>@metamask/key-tree>@metamask/utils": true,
"@metamask/utils>@scure/base": true,
"@noble/hashes": true
}
},
+ "@metamask/snaps-sdk>@metamask/key-tree>@metamask/utils": {
+ "globals": {
+ "TextDecoder": true,
+ "TextEncoder": true
+ },
+ "packages": {
+ "@metamask/rpc-errors>@metamask/utils>@metamask/superstruct": true,
+ "@metamask/utils>@scure/base": true,
+ "@metamask/utils>pony-cause": true,
+ "@noble/hashes": true,
+ "browserify>buffer": true,
+ "nock>debug": true,
+ "semver": true
+ }
+ },
"@metamask/snaps-utils": {
"globals": {
"File": true,
@@ -2892,9 +2956,24 @@
"@metamask/snaps-utils>@metamask/snaps-registry": {
"packages": {
"@metamask/message-signing-snap>@noble/curves": true,
- "@metamask/utils": true,
+ "@metamask/rpc-errors>@metamask/utils>@metamask/superstruct": true,
+ "@metamask/snaps-utils>@metamask/snaps-registry>@metamask/utils": true,
+ "@noble/hashes": true
+ }
+ },
+ "@metamask/snaps-utils>@metamask/snaps-registry>@metamask/utils": {
+ "globals": {
+ "TextDecoder": true,
+ "TextEncoder": true
+ },
+ "packages": {
+ "@metamask/rpc-errors>@metamask/utils>@metamask/superstruct": true,
+ "@metamask/utils>@scure/base": true,
+ "@metamask/utils>pony-cause": true,
"@noble/hashes": true,
- "superstruct": true
+ "browserify>buffer": true,
+ "nock>debug": true,
+ "semver": true
}
},
"@metamask/snaps-utils>cron-parser": {
@@ -2940,6 +3019,53 @@
"@ethersproject/abi>@ethersproject/logger": true
}
},
+ "@metamask/test-bundler>ethers": {
+ "packages": {
+ "@ethersproject/abi": true,
+ "@ethersproject/abi>@ethersproject/address": true,
+ "@ethersproject/abi>@ethersproject/constants": true,
+ "@ethersproject/abi>@ethersproject/keccak256": true,
+ "@ethersproject/abi>@ethersproject/logger": true,
+ "@ethersproject/abi>@ethersproject/properties": true,
+ "@ethersproject/abi>@ethersproject/strings": true,
+ "@ethersproject/bignumber": true,
+ "@ethersproject/bytes": true,
+ "@ethersproject/contracts": true,
+ "@ethersproject/hash": true,
+ "@ethersproject/hash>@ethersproject/abstract-signer": true,
+ "@ethersproject/hash>@ethersproject/base64": true,
+ "@ethersproject/hdnode": true,
+ "@ethersproject/hdnode>@ethersproject/basex": true,
+ "@ethersproject/hdnode>@ethersproject/sha2": true,
+ "@ethersproject/hdnode>@ethersproject/signing-key": true,
+ "@ethersproject/hdnode>@ethersproject/transactions": true,
+ "@ethersproject/hdnode>@ethersproject/wordlists": true,
+ "@ethersproject/providers": true,
+ "@ethersproject/providers>@ethersproject/rlp": true,
+ "@ethersproject/providers>@ethersproject/web": true,
+ "@ethersproject/wallet": true,
+ "@ethersproject/wallet>@ethersproject/json-wallets": true,
+ "@ethersproject/wallet>@ethersproject/random": true,
+ "@metamask/test-bundler>ethers>@ethersproject/solidity": true,
+ "@metamask/test-bundler>ethers>@ethersproject/units": true
+ }
+ },
+ "@metamask/test-bundler>ethers>@ethersproject/solidity": {
+ "packages": {
+ "@ethersproject/abi>@ethersproject/keccak256": true,
+ "@ethersproject/abi>@ethersproject/logger": true,
+ "@ethersproject/abi>@ethersproject/strings": true,
+ "@ethersproject/bignumber": true,
+ "@ethersproject/bytes": true,
+ "@ethersproject/hdnode>@ethersproject/sha2": true
+ }
+ },
+ "@metamask/test-bundler>ethers>@ethersproject/units": {
+ "packages": {
+ "@ethersproject/abi>@ethersproject/logger": true,
+ "@ethersproject/bignumber": true
+ }
+ },
"@metamask/transaction-controller": {
"globals": {
"clearTimeout": true,
@@ -3154,13 +3280,6 @@
"define": true
}
},
- "@noble/ciphers": {
- "globals": {
- "TextDecoder": true,
- "TextEncoder": true,
- "crypto": true
- }
- },
"@noble/hashes": {
"globals": {
"TextEncoder": true,
diff --git a/package.json b/package.json
index d8b69ce36eba..af20530a7bda 100644
--- a/package.json
+++ b/package.json
@@ -261,12 +261,8 @@
"@solana/web3.js/rpc-websockets": "^8.0.1",
"@metamask/network-controller@npm:^19.0.0": "patch:@metamask/network-controller@npm%3A19.0.0#~/.yarn/patches/@metamask-network-controller-npm-19.0.0-a5e0d1fe14.patch",
"@metamask/gas-fee-controller@npm:^15.1.1": "patch:@metamask/gas-fee-controller@npm%3A15.1.2#~/.yarn/patches/@metamask-gas-fee-controller-npm-15.1.2-db4d2976aa.patch",
- "@metamask/snaps-controllers@npm:^4.1.0": "patch:@metamask/snaps-controllers@npm%3A8.4.0#~/.yarn/patches/@metamask-snaps-controllers-npm-8.4.0-574cd5a8a9.patch",
- "@metamask/snaps-controllers@npm:^3.4.1": "patch:@metamask/snaps-controllers@npm%3A8.4.0#~/.yarn/patches/@metamask-snaps-controllers-npm-8.4.0-574cd5a8a9.patch",
- "@metamask/snaps-controllers@npm:^7.0.1": "patch:@metamask/snaps-controllers@npm%3A8.4.0#~/.yarn/patches/@metamask-snaps-controllers-npm-8.4.0-574cd5a8a9.patch",
- "@metamask/snaps-controllers@npm:^8.1.1": "patch:@metamask/snaps-controllers@npm%3A8.4.0#~/.yarn/patches/@metamask-snaps-controllers-npm-8.4.0-574cd5a8a9.patch",
- "@metamask/snaps-controllers@npm:^9.0.0": "patch:@metamask/snaps-controllers@npm%3A8.4.0#~/.yarn/patches/@metamask-snaps-controllers-npm-8.4.0-574cd5a8a9.patch",
- "@metamask/snaps-controllers@npm:^9.2.0": "patch:@metamask/snaps-controllers@npm%3A8.4.0#~/.yarn/patches/@metamask-snaps-controllers-npm-8.4.0-574cd5a8a9.patch",
+ "@metamask/snaps-controllers@npm:^8.1.1": "patch:@metamask/snaps-controllers@npm%3A9.2.0#~/.yarn/patches/@metamask-snaps-controllers-npm-9.2.0-09a31bab4f.patch",
+ "@metamask/snaps-controllers@npm:^9.2.0": "patch:@metamask/snaps-controllers@npm%3A9.2.0#~/.yarn/patches/@metamask-snaps-controllers-npm-9.2.0-09a31bab4f.patch",
"@metamask/nonce-tracker@npm:^5.0.0": "patch:@metamask/nonce-tracker@npm%3A5.0.0#~/.yarn/patches/@metamask-nonce-tracker-npm-5.0.0-d81478218e.patch",
"@metamask/snaps-utils@npm:^7.7.0": "patch:@metamask/snaps-utils@npm%3A7.7.0#~/.yarn/patches/@metamask-snaps-utils-npm-7.7.0-2cc1f044af.patch",
"@metamask/snaps-utils@npm:^7.4.0": "patch:@metamask/snaps-utils@npm%3A7.7.0#~/.yarn/patches/@metamask-snaps-utils-npm-7.7.0-2cc1f044af.patch",
@@ -347,6 +343,7 @@
"@metamask/phishing-controller": "^9.0.3",
"@metamask/post-message-stream": "^8.0.0",
"@metamask/ppom-validator": "^0.32.0",
+ "@metamask/profile-sync-controller": "^0.2.0",
"@metamask/providers": "^14.0.2",
"@metamask/queued-request-controller": "^2.0.0",
"@metamask/rate-limit-controller": "^5.0.1",
@@ -365,7 +362,6 @@
"@metamask/user-operation-controller": "^13.0.0",
"@metamask/utils": "^8.2.1",
"@ngraveio/bc-ur": "^1.1.12",
- "@noble/ciphers": "^0.5.2",
"@noble/hashes": "^1.3.3",
"@popperjs/core": "^2.4.0",
"@reduxjs/toolkit": "patch:@reduxjs/toolkit@npm%3A1.9.7#~/.yarn/patches/@reduxjs-toolkit-npm-1.9.7-b14925495c.patch",
diff --git a/shared/constants/metametrics.ts b/shared/constants/metametrics.ts
index 69571f2725ac..02cdde12d159 100644
--- a/shared/constants/metametrics.ts
+++ b/shared/constants/metametrics.ts
@@ -708,7 +708,7 @@ export enum MetaMetricsEventName {
NotificationReceived = 'Notification Received',
NotificationsSettingsUpdated = 'Notifications Settings Updated',
NotificationClicked = 'Notification Clicked',
- NotificationsEnablingFlowHandled = 'Notifications Enabling Flow Handled',
+ NotificationMenuOpened = 'Notification Menu Opened',
NftAutoDetectionEnableModal = 'Nft Autodetection Enabled from modal',
NftAutoDetectionDisableModal = 'Nft Autodetection Disabled from modal',
@@ -756,7 +756,6 @@ export enum MetaMetricsEventCategory {
Messages = 'Messages',
Navigation = 'Navigation',
Network = 'Network',
- EnableNotifications = 'Enable Notifications',
Onboarding = 'Onboarding',
NotificationInteraction = 'Notification Interaction',
NotificationSettings = 'Notification Settings',
diff --git a/test/e2e/accounts/common.ts b/test/e2e/accounts/common.ts
index 95f33a9bdfb0..d25983d0a816 100644
--- a/test/e2e/accounts/common.ts
+++ b/test/e2e/accounts/common.ts
@@ -183,7 +183,7 @@ async function switchToAccount2(driver: Driver) {
await driver.clickElement({
tag: 'Button',
- text: 'Snap Account 1',
+ text: 'SSK Account',
});
await driver.assertElementNotPresent({
diff --git a/test/e2e/accounts/create-snap-account.spec.ts b/test/e2e/accounts/create-snap-account.spec.ts
index 28bc4d44bb19..2a35b4b4c805 100644
--- a/test/e2e/accounts/create-snap-account.spec.ts
+++ b/test/e2e/accounts/create-snap-account.spec.ts
@@ -1,14 +1,9 @@
import { Suite } from 'mocha';
import FixtureBuilder from '../fixture-builder';
-import {
- defaultGanacheOptions,
- unlockWallet,
- WINDOW_TITLES,
- withFixtures,
-} from '../helpers';
+import { defaultGanacheOptions, WINDOW_TITLES, withFixtures } from '../helpers';
import { Driver } from '../webdriver/driver';
-import { TEST_SNAPS_SIMPLE_KEYRING_WEBSITE_URL } from '../constants';
+import { installSnapSimpleKeyring } from './common';
/**
* Starts the flow to create a Snap account, including unlocking the wallet,
@@ -20,34 +15,7 @@ import { TEST_SNAPS_SIMPLE_KEYRING_WEBSITE_URL } from '../constants';
* @returns A promise that resolves when the setup steps are complete.
*/
async function startCreateSnapAccountFlow(driver: Driver): Promise {
- await unlockWallet(driver);
-
- // navigate to test Snaps page and connect
- await driver.openNewPage(TEST_SNAPS_SIMPLE_KEYRING_WEBSITE_URL);
- await driver.clickElement('#connectButton');
-
- // switch to metamask extension and click connect to start installing the snap
- await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog);
- await driver.clickElement({
- text: 'Connect',
- tag: 'button',
- });
-
- // scroll to the bottom of the page
- await driver.waitForSelector({ text: 'Confirm' });
- await driver.clickElementSafe('[data-testid="snap-install-scroll"]');
-
- // click the install button to install the snap
- await driver.waitForSelector({ text: 'Confirm' });
- await driver.clickElement({
- text: 'Confirm',
- tag: 'button',
- });
- await driver.waitForSelector({ text: 'OK' });
- await driver.clickElement({
- text: 'OK',
- tag: 'button',
- });
+ await installSnapSimpleKeyring(driver, false);
// move back to the Snap window to test the create account flow
await driver.waitAndSwitchToWindowWithTitle(
@@ -125,14 +93,14 @@ describe('Create Snap Account', function (this: Suite) {
'[data-testid="submit-add-account-with-name"]',
);
- // success screen should show account created with the default name
+ // success screen should show account created with the snap suggested name
await driver.findElement({
tag: 'h3',
text: 'Account created',
});
await driver.findElement({
css: '.multichain-account-list-item__account-name__button',
- text: 'Snap Account 1',
+ text: 'SSK Account',
});
// click the okay button
@@ -155,15 +123,80 @@ describe('Create Snap Account', function (this: Suite) {
WINDOW_TITLES.ExtensionInFullScreenView,
);
- // account should be created with the default name
+ // account should be created with the snap suggested name
await driver.findElement({
css: '[data-testid="account-menu-icon"]',
- text: 'Snap Account 1',
+ text: 'SSK Account',
});
},
);
});
+ it('creates multiple Snap accounts with increasing numeric suffixes', async function () {
+ await withFixtures(
+ {
+ fixtures: new FixtureBuilder().build(),
+ ganacheOptions: defaultGanacheOptions,
+ title: this.test?.fullTitle(),
+ },
+ async ({ driver }: { driver: Driver }) => {
+ await installSnapSimpleKeyring(driver, false);
+
+ const expectedNames = ['SSK Account', 'SSK Account 2', 'SSK Account 3'];
+
+ for (const [index, expectedName] of expectedNames.entries()) {
+ // move to the dapp window
+ await driver.waitAndSwitchToWindowWithTitle(
+ 2,
+ WINDOW_TITLES.SnapSimpleKeyringDapp,
+ );
+
+ // create new account on dapp
+ if (index === 0) {
+ // Only click the div for the first snap account creation
+ await driver.clickElement({
+ text: 'Create account',
+ tag: 'div',
+ });
+ }
+ await driver.clickElement({
+ text: 'Create Account',
+ tag: 'button',
+ });
+
+ // wait until dialog is opened before proceeding
+ await driver.waitAndSwitchToWindowWithTitle(3, WINDOW_TITLES.Dialog);
+
+ // click the create button on the confirmation modal
+ await driver.clickElement(
+ '[data-testid="confirmation-submit-button"]',
+ );
+
+ // click the add account button on the naming modal
+ await driver.clickElement(
+ '[data-testid="submit-add-account-with-name"]',
+ );
+
+ // click the okay button on the success screen
+ await driver.clickElement(
+ '[data-testid="confirmation-submit-button"]',
+ );
+
+ // switch to extension full screen view
+ await driver.switchToWindowWithTitle(
+ WINDOW_TITLES.ExtensionInFullScreenView,
+ );
+
+ // verify the account is created with the expected name
+ await driver.findElement({
+ css: '[data-testid="account-menu-icon"]',
+ text: expectedName,
+ });
+ }
+ },
+ );
+ });
+
it('create Snap account confirmation flow ends in approval success with custom name input', async function () {
await withFixtures(
{
@@ -180,7 +213,7 @@ describe('Create Snap Account', function (this: Suite) {
// Add a custom name to the account
const newAccountLabel = 'Custom name';
- await driver.fill('[placeholder="Snap Account 1"]', newAccountLabel);
+ await driver.fill('[placeholder="SSK Account"]', newAccountLabel);
// click the add account button on the naming modal
await driver.clickElement(
'[data-testid="submit-add-account-with-name"]',
@@ -259,7 +292,7 @@ describe('Create Snap Account', function (this: Suite) {
// account should not be created
await driver.assertElementNotPresent({
css: '[data-testid="account-menu-icon"]',
- text: 'Snap Account 1',
+ text: 'SSK Account',
});
},
);
@@ -304,7 +337,7 @@ describe('Create Snap Account', function (this: Suite) {
// account should not be created
await driver.assertElementNotPresent({
css: '[data-testid="account-menu-icon"]',
- text: 'Snap Account 1',
+ text: 'SSK Account',
});
},
);
diff --git a/test/e2e/accounts/snap-account-contract-interaction.spec.ts b/test/e2e/accounts/snap-account-contract-interaction.spec.ts
new file mode 100644
index 000000000000..4588d014802c
--- /dev/null
+++ b/test/e2e/accounts/snap-account-contract-interaction.spec.ts
@@ -0,0 +1,87 @@
+import GanacheContractAddressRegistry from '../seeder/ganache-contract-address-registry';
+import { scrollAndConfirmAndAssertConfirm } from '../tests/confirmations/helpers';
+import {
+ createDepositTransaction,
+ TestSuiteArguments,
+} from '../tests/confirmations/transactions/shared';
+import {
+ multipleGanacheOptionsForType2Transactions,
+ withFixtures,
+ openDapp,
+ WINDOW_TITLES,
+ locateAccountBalanceDOM,
+ clickNestedButton,
+ ACCOUNT_2,
+} from '../helpers';
+import FixtureBuilder from '../fixture-builder';
+import { SMART_CONTRACTS } from '../seeder/smart-contracts';
+import { installSnapSimpleKeyring, importKeyAndSwitch } from './common';
+
+describe('Snap Account Contract interaction', function () {
+ const smartContract = SMART_CONTRACTS.PIGGYBANK;
+
+ it('deposits to piggybank contract', async function () {
+ await withFixtures(
+ {
+ dapp: true,
+ fixtures: new FixtureBuilder()
+ .withPermissionControllerSnapAccountConnectedToTestDapp()
+ .withPreferencesController({
+ preferences: {
+ redesignedConfirmationsEnabled: true,
+ isRedesignedConfirmationsDeveloperEnabled: true,
+ },
+ })
+ .build(),
+ ganacheOptions: multipleGanacheOptionsForType2Transactions,
+ smartContract,
+ title: this.test?.fullTitle(),
+ },
+ async ({
+ driver,
+ contractRegistry,
+ ganacheServer,
+ }: TestSuiteArguments) => {
+ // Install Snap Simple Keyring and import key
+ await installSnapSimpleKeyring(driver, false);
+ await importKeyAndSwitch(driver);
+
+ // Open DApp with contract
+ const contractAddress = await (
+ contractRegistry as GanacheContractAddressRegistry
+ ).getContractAddress(smartContract);
+ await openDapp(driver, contractAddress);
+
+ // Create and confirm deposit transaction
+ await driver.switchToWindowWithTitle(
+ WINDOW_TITLES.ExtensionInFullScreenView,
+ );
+ await createDepositTransaction(driver);
+ await driver.waitUntilXWindowHandles(4);
+ await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog);
+ await driver.waitForSelector({
+ css: 'h2',
+ text: 'Transaction request',
+ });
+ await scrollAndConfirmAndAssertConfirm(driver);
+
+ // Confirm the transaction activity
+ await driver.waitUntilXWindowHandles(3);
+ await driver.switchToWindowWithTitle(
+ WINDOW_TITLES.ExtensionInFullScreenView,
+ );
+ await clickNestedButton(driver, 'Activity');
+ await driver.waitForSelector(
+ '.transaction-list__completed-transactions .activity-list-item:nth-of-type(1)',
+ );
+ await driver.waitForSelector({
+ css: '[data-testid="transaction-list-item-primary-currency"]',
+ text: '-4 ETH',
+ });
+
+ // renders the correct ETH balance
+ await locateAccountBalanceDOM(driver, ganacheServer, ACCOUNT_2);
+ },
+ );
+ });
+});
diff --git a/test/e2e/constants.ts b/test/e2e/constants.ts
index ca5d5cd88543..4dee1053aaee 100644
--- a/test/e2e/constants.ts
+++ b/test/e2e/constants.ts
@@ -31,7 +31,7 @@ export const SIMPLE_ACCOUNT_FACTORY =
/* URL of the Snap Simple Keyring site. */
export const TEST_SNAPS_SIMPLE_KEYRING_WEBSITE_URL =
- 'https://metamask.github.io/snap-simple-keyring/1.1.1/';
+ 'https://metamask.github.io/snap-simple-keyring/1.1.2/';
/* Address of the VerifyingPaymaster smart contract deployed to Ganache. */
export const VERIFYING_PAYMASTER = '0xbdbDEc38ed168331b1F7004cc9e5392A2272C1D7';
diff --git a/test/e2e/fixture-builder.js b/test/e2e/fixture-builder.js
index 018a75b8206b..798b570c5a2d 100644
--- a/test/e2e/fixture-builder.js
+++ b/test/e2e/fixture-builder.js
@@ -395,6 +395,32 @@ class FixtureBuilder {
});
}
+ withPermissionControllerSnapAccountConnectedToTestDapp(
+ restrictReturnedAccounts = true,
+ ) {
+ return this.withPermissionController({
+ subjects: {
+ [DAPP_URL]: {
+ origin: DAPP_URL,
+ permissions: {
+ eth_accounts: {
+ id: 'ZaqPEWxyhNCJYACFw93jE',
+ parentCapability: 'eth_accounts',
+ invoker: DAPP_URL,
+ caveats: restrictReturnedAccounts && [
+ {
+ type: 'restrictReturnedAccounts',
+ value: ['0x09781764c08de8ca82e156bbf156a3ca217c7950'],
+ },
+ ],
+ date: 1664388714636,
+ },
+ },
+ },
+ },
+ });
+ }
+
withPermissionControllerConnectedToTwoTestDapps(
restrictReturnedAccounts = true,
) {
diff --git a/test/e2e/helpers.js b/test/e2e/helpers.js
index d8ffc4f8cd87..15ff5dd64606 100644
--- a/test/e2e/helpers.js
+++ b/test/e2e/helpers.js
@@ -745,7 +745,7 @@ const PRIVATE_KEY =
'0x7C9529A67102755B7E6102D6D950AC5D5863C98713805CEC576B945B15B71EAC';
const PRIVATE_KEY_TWO =
- '0xa444f52ea41e3a39586d7069cb8e8233e9f6b9dea9cbb700cce69ae860661cc8';
+ '0xf444f52ea41e3a39586d7069cb8e8233e9f6b9dea9cbb700cce69ae860661cc8';
const ACCOUNT_1 = '0x5cfe73b6021e818b776b421b1c4db2474086a7e1';
const ACCOUNT_2 = '0x09781764c08de8ca82e156bbf156a3ca217c7950';
@@ -778,6 +778,12 @@ const multipleGanacheOptions = {
],
};
+const multipleGanacheOptionsForType2Transactions = {
+ ...multipleGanacheOptions,
+ // EVM version that supports type 2 transactions (EIP1559)
+ hardfork: 'london',
+};
+
const generateGanacheOptions = ({
secretKey = PRIVATE_KEY,
balance = convertETHToHexGwei(DEFAULT_GANACHE_ETH_BALANCE_DEC),
@@ -887,7 +893,15 @@ const TEST_SEED_PHRASE =
const TEST_SEED_PHRASE_TWO =
'phrase upgrade clock rough situate wedding elder clever doctor stamp excess tent';
-// Usually happens when onboarded to make sure the state is retrieved from metamaskState properly, or after txn is made
+/**
+ * Checks the balance for a specific address. If no address is provided, it defaults to the first address.
+ * This function is typically used during onboarding to ensure the state is retrieved correctly from metamaskState,
+ * or after a transaction is made.
+ *
+ * @param {WebDriver} driver - The WebDriver instance.
+ * @param {Ganache} [ganacheServer] - The Ganache server instance (optional).
+ * @param {string} [address] - The address to check the balance for (optional).
+ */
const locateAccountBalanceDOM = async (
driver,
ganacheServer,
@@ -1230,6 +1244,8 @@ module.exports = {
connectToDapp,
multipleGanacheOptions,
defaultGanacheOptions,
+ defaultGanacheOptionsForType2Transactions,
+ multipleGanacheOptionsForType2Transactions,
sendTransaction,
sendScreenToConfirmScreen,
findAnotherAccountFromAccountList,
@@ -1258,7 +1274,6 @@ module.exports = {
getCleanAppState,
editGasFeeForm,
clickNestedButton,
- defaultGanacheOptionsForType2Transactions,
removeSelectedAccount,
getSelectedAccountAddress,
tempToggleSettingRedesignedConfirmations,
diff --git a/test/e2e/tests/confirmations/helpers.ts b/test/e2e/tests/confirmations/helpers.ts
index ba27cbb44da2..882990e4bf66 100644
--- a/test/e2e/tests/confirmations/helpers.ts
+++ b/test/e2e/tests/confirmations/helpers.ts
@@ -4,7 +4,7 @@ import { Mockttp } from '../../mock-e2e';
import { Driver } from '../../webdriver/driver';
export async function scrollAndConfirmAndAssertConfirm(driver: Driver) {
- await driver.clickElement('.confirm-scroll-to-bottom__button');
+ await driver.clickElementSafe('.confirm-scroll-to-bottom__button');
await driver.clickElement('[data-testid="confirm-footer-button"]');
}
diff --git a/test/e2e/tests/confirmations/transactions/shared.ts b/test/e2e/tests/confirmations/transactions/shared.ts
index b699e9c0d61a..bf3063e3809a 100644
--- a/test/e2e/tests/confirmations/transactions/shared.ts
+++ b/test/e2e/tests/confirmations/transactions/shared.ts
@@ -1,8 +1,9 @@
/* eslint-disable @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires */
import { MockedEndpoint } from 'mockttp';
+import { veryLargeDelayMs } from '../../../helpers';
+import { Ganache } from '../../../seeder/ganache';
import GanacheContractAddressRegistry from '../../../seeder/ganache-contract-address-registry';
import { Driver } from '../../../webdriver/driver';
-import { Ganache } from '../../../seeder/ganache';
const {
logInWithBalanceValidation,
@@ -109,6 +110,7 @@ export async function confirmDepositTransactionWithCustomNonce(
text: 'Save',
tag: 'button',
});
+ await driver.delay(veryLargeDelayMs);
await scrollAndConfirmAndAssertConfirm(driver);
// Confirm tx was submitted with the higher nonce
diff --git a/test/e2e/tests/notifications/mocks.ts b/test/e2e/tests/notifications/mocks.ts
index cb14382f8763..16fd122532f3 100644
--- a/test/e2e/tests/notifications/mocks.ts
+++ b/test/e2e/tests/notifications/mocks.ts
@@ -1,13 +1,8 @@
import { Mockttp, RequestRuleBuilder } from 'mockttp';
import {
- getMockAuthNonceResponse,
- getMockAuthLoginResponse,
- getMockAuthAccessTokenResponse,
-} from '../../../../app/scripts/controllers/authentication/mocks/mockResponses';
-import {
- getMockUserStorageGetResponse,
- getMockUserStoragePutResponse,
-} from '../../../../app/scripts/controllers/user-storage/mocks/mockResponses';
+ AuthenticationController,
+ UserStorageController,
+} from '@metamask/profile-sync-controller';
import {
getMockFeatureAnnouncementResponse,
getMockBatchCreateTriggersResponse,
@@ -22,6 +17,9 @@ import {
getMockDeleteFCMRegistrationTokenResponse,
} from '../../../../app/scripts/controllers/push-platform-notifications/mocks/mockResponse';
+const AuthMocks = AuthenticationController.Mocks;
+const StorageMocks = UserStorageController.Mocks;
+
type MockResponse = {
url: string | RegExp;
requestMethod: 'GET' | 'POST' | 'PUT' | 'DELETE';
@@ -35,13 +33,13 @@ type MockResponse = {
*/
export function mockNotificationServices(server: Mockttp) {
// Auth
- mockAPICall(server, getMockAuthNonceResponse());
- mockAPICall(server, getMockAuthLoginResponse());
- mockAPICall(server, getMockAuthAccessTokenResponse());
+ mockAPICall(server, AuthMocks.getMockAuthNonceResponse());
+ mockAPICall(server, AuthMocks.getMockAuthLoginResponse());
+ mockAPICall(server, AuthMocks.getMockAuthAccessTokenResponse());
// Storage
- mockAPICall(server, getMockUserStorageGetResponse());
- mockAPICall(server, getMockUserStoragePutResponse());
+ mockAPICall(server, StorageMocks.getMockUserStorageGetResponse());
+ mockAPICall(server, StorageMocks.getMockUserStoragePutResponse());
// Notifications
mockAPICall(server, getMockFeatureAnnouncementResponse());
diff --git a/ui/components/app/modals/turn-on-metamask-notifications/turn-on-metamask-notifications.tsx b/ui/components/app/modals/turn-on-metamask-notifications/turn-on-metamask-notifications.tsx
index 46510d0584fb..39d554759b29 100644
--- a/ui/components/app/modals/turn-on-metamask-notifications/turn-on-metamask-notifications.tsx
+++ b/ui/components/app/modals/turn-on-metamask-notifications/turn-on-metamask-notifications.tsx
@@ -61,8 +61,8 @@ export default function TurnOnMetamaskNotifications() {
setButtonState(true);
await createNotifications();
trackEvent({
- category: MetaMetricsEventCategory.EnableNotifications,
- event: MetaMetricsEventName.NotificationsEnablingFlowHandled,
+ category: MetaMetricsEventCategory.NotificationInteraction,
+ event: MetaMetricsEventName.NotificationMenuOpened,
properties: {
is_profile_syncing_enabled: isProfileSyncingEnabled,
is_notifications_enabled: isNotificationEnabled,
@@ -74,8 +74,8 @@ export default function TurnOnMetamaskNotifications() {
const handleHideModal = () => {
hideModal();
trackEvent({
- category: MetaMetricsEventCategory.EnableNotifications,
- event: MetaMetricsEventName.NotificationsEnablingFlowHandled,
+ category: MetaMetricsEventCategory.NotificationInteraction,
+ event: MetaMetricsEventName.NotificationMenuOpened,
properties: {
is_profile_syncing_enabled: isProfileSyncingEnabled,
is_notifications_enabled: isNotificationEnabled,
diff --git a/ui/components/institutional/custody-confirm-link-modal/custody-confirm-link-modal.tsx b/ui/components/institutional/custody-confirm-link-modal/custody-confirm-link-modal.tsx
index df333dff42e4..ec29138a5696 100644
--- a/ui/components/institutional/custody-confirm-link-modal/custody-confirm-link-modal.tsx
+++ b/ui/components/institutional/custody-confirm-link-modal/custody-confirm-link-modal.tsx
@@ -1,6 +1,5 @@
import React, { useContext } from 'react';
import { useSelector, useDispatch } from 'react-redux';
-import { ICustodianType } from '@metamask-institutional/types';
import { MetaMetricsContext } from '../../../contexts/metametrics';
import {
MetaMetricsEventName,
@@ -71,16 +70,15 @@ const CustodyConfirmLink: React.FC = ({
const trackEvent = useContext(MetaMetricsContext);
const mmiAccounts = useSelector(getInternalAccounts);
const address = useSelector(getMMIAddressFromModalOrAddress);
- const custodyAccountDetails = useSelector(getCustodyAccountDetails);
- const { custodians } = useSelector(getMMIConfiguration);
- const { custodianName } =
- custodyAccountDetails[toChecksumHexAddress(address)] || {};
- const { displayName, iconUrl } =
- custodians.find((item: ICustodianType) => item.envName === custodianName) ||
- {};
+ const custodyAccountDetails = useSelector(getCustodyAccountDetails) || {};
const { url, ethereum, text, action } = useSelector(
(state: State) => state.appState.modal.modalState.props.link || {},
);
+ const { custodians } = useSelector(getMMIConfiguration) || {};
+ const { custodianName } =
+ custodyAccountDetails[toChecksumHexAddress(address)] || {};
+ const { displayName, iconUrl } =
+ custodians?.find((item) => item.envName === custodianName) || {};
const onClick = () => {
if (url) {
@@ -127,7 +125,7 @@ const CustodyConfirmLink: React.FC = ({
) : (
diff --git a/ui/components/institutional/custody-labels/custody-labels.tsx b/ui/components/institutional/custody-labels/custody-labels.tsx
index b753e7939652..002b54c8f0a5 100644
--- a/ui/components/institutional/custody-labels/custody-labels.tsx
+++ b/ui/components/institutional/custody-labels/custody-labels.tsx
@@ -1,5 +1,4 @@
import React from 'react';
-import PropTypes from 'prop-types';
import { Label, Text } from '../../component-library';
import {
@@ -54,16 +53,4 @@ const CustodyLabels: React.FC = (props) => {
);
};
-CustodyLabels.propTypes = {
- labels: PropTypes.arrayOf(
- PropTypes.shape({
- key: PropTypes.string.isRequired,
- value: PropTypes.string.isRequired,
- }).isRequired,
- ).isRequired,
- index: PropTypes.string,
- background: PropTypes.string,
- hideNetwork: PropTypes.bool,
-};
-
export default CustodyLabels;
diff --git a/ui/components/institutional/interactive-replacement-token-notification/interactive-replacement-token-notification.tsx b/ui/components/institutional/interactive-replacement-token-notification/interactive-replacement-token-notification.tsx
index 30b72fc2b882..10dc049b8678 100644
--- a/ui/components/institutional/interactive-replacement-token-notification/interactive-replacement-token-notification.tsx
+++ b/ui/components/institutional/interactive-replacement-token-notification/interactive-replacement-token-notification.tsx
@@ -102,7 +102,7 @@ const InteractiveReplacementTokenNotification: React.FC<
handleShowNotification();
}, [
address,
- interactiveReplacementToken.oldRefreshToken,
+ interactiveReplacementToken?.oldRefreshToken,
isUnlocked,
dispatch,
keyring.type,
diff --git a/ui/components/institutional/wrong-network-notification/wrong-network-notification.tsx b/ui/components/institutional/wrong-network-notification/wrong-network-notification.tsx
index bf16a5598c9b..faf74c61d4e4 100644
--- a/ui/components/institutional/wrong-network-notification/wrong-network-notification.tsx
+++ b/ui/components/institutional/wrong-network-notification/wrong-network-notification.tsx
@@ -20,9 +20,7 @@ const WrongNetworkNotification: React.FC = () => {
const providerConfig = useSelector
+