From f62d8fce618e8473b6759270ef9c78827063e37a Mon Sep 17 00:00:00 2001 From: Matthew Walsh Date: Fri, 5 May 2023 13:05:52 +0100 Subject: [PATCH] Use core signature controller (#18654) --- app/scripts/background.js | 6 +- app/scripts/controllers/sign.test.ts | 588 --------------------- app/scripts/controllers/sign.ts | 658 ------------------------ app/scripts/metamask-controller.js | 88 ++-- lavamoat/browserify/beta/policy.json | 14 + lavamoat/browserify/desktop/policy.json | 14 + lavamoat/browserify/flask/policy.json | 14 + lavamoat/browserify/main/policy.json | 14 + package.json | 3 +- yarn.lock | 38 +- 10 files changed, 141 insertions(+), 1296 deletions(-) delete mode 100644 app/scripts/controllers/sign.test.ts delete mode 100644 app/scripts/controllers/sign.ts diff --git a/app/scripts/background.js b/app/scripts/background.js index 808de08ec..082a13b67 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -726,7 +726,7 @@ export function setupController( METAMASK_CONTROLLER_EVENTS.UPDATE_BADGE, updateBadge, ); - controller.signController.hub.on( + controller.signatureController.hub.on( METAMASK_CONTROLLER_EVENTS.UPDATE_BADGE, updateBadge, ); @@ -785,7 +785,9 @@ export function setupController( ).forEach((txId) => controller.txController.txStateManager.setTxStatusRejected(txId), ); - controller.signController.rejectUnapproved(REJECT_NOTIFICATION_CLOSE_SIG); + controller.signatureController.rejectUnapproved( + REJECT_NOTIFICATION_CLOSE_SIG, + ); controller.decryptMessageController.rejectUnapproved( REJECT_NOTIFICATION_CLOSE, ); diff --git a/app/scripts/controllers/sign.test.ts b/app/scripts/controllers/sign.test.ts deleted file mode 100644 index 078d99291..000000000 --- a/app/scripts/controllers/sign.test.ts +++ /dev/null @@ -1,588 +0,0 @@ -import { - MessageManager, - PersonalMessageManager, - TypedMessageManager, -} from '@metamask/message-manager'; -import { - AbstractMessage, - OriginalRequest, -} from '@metamask/message-manager/dist/AbstractMessageManager'; -import { MetaMetricsEventCategory } from '../../../shared/constants/metametrics'; -import SignController, { - SignControllerMessenger, - SignControllerOptions, -} from './sign'; - -jest.mock('@metamask/message-manager', () => ({ - MessageManager: jest.fn(), - PersonalMessageManager: jest.fn(), - TypedMessageManager: jest.fn(), -})); - -const messageIdMock = '123'; -const messageIdMock2 = '456'; -const versionMock = '1'; -const signatureMock = '0xAABBCC'; -const stateMock = { test: 123 }; -const securityProviderResponseMock = { test2: 345 }; - -const messageParamsMock = { - from: '0x123', - origin: 'http://test.com', - data: '0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', - metamaskId: messageIdMock, - version: 'V1', -}; - -const messageParamsMock2 = { - from: '0x124', - origin: 'http://test4.com', - data: '0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA', - metamaskId: messageIdMock, -}; - -const messageMock = { - id: messageIdMock, - time: 123, - status: 'unapproved', - type: 'testType', - rawSig: undefined, -} as any as AbstractMessage; - -const coreMessageMock = { - ...messageMock, - messageParams: messageParamsMock, - securityProviderResponse: securityProviderResponseMock, -}; - -const stateMessageMock = { - ...messageMock, - msgParams: messageParamsMock, - securityProviderResponse: securityProviderResponseMock, -}; - -const requestMock = { - origin: 'http://test2.com', -} as OriginalRequest; - -const createMessengerMock = () => - ({ - registerActionHandler: jest.fn(), - publish: jest.fn(), - call: jest.fn(), - } as any as jest.Mocked); - -const createMessageManagerMock = () => - ({ - getUnapprovedMessages: jest.fn(), - getUnapprovedMessagesCount: jest.fn(), - addUnapprovedMessageAsync: jest.fn(), - approveMessage: jest.fn(), - setMessageStatusSigned: jest.fn(), - rejectMessage: jest.fn(), - subscribe: jest.fn(), - update: jest.fn(), - hub: { - on: jest.fn(), - }, - } as any as jest.Mocked); - -const createPreferencesControllerMock = () => ({ - store: { - getState: jest.fn(), - }, -}); - -const createKeyringControllerMock = () => ({ - signMessage: jest.fn(), - signPersonalMessage: jest.fn(), - signTypedMessage: jest.fn(), -}); - -describe('SignController', () => { - let signController: SignController; - - const messageManagerConstructorMock = MessageManager as jest.MockedClass< - typeof MessageManager - >; - const personalMessageManagerConstructorMock = - PersonalMessageManager as jest.MockedClass; - const typedMessageManagerConstructorMock = - TypedMessageManager as jest.MockedClass; - const messageManagerMock = createMessageManagerMock(); - const personalMessageManagerMock = - createMessageManagerMock(); - const typedMessageManagerMock = - createMessageManagerMock(); - const messengerMock = createMessengerMock(); - const preferencesControllerMock = createPreferencesControllerMock(); - const keyringControllerMock = createKeyringControllerMock(); - const getStateMock = jest.fn(); - const securityProviderRequestMock = jest.fn(); - const metricsEventMock = jest.fn(); - - beforeEach(() => { - jest.resetAllMocks(); - - messageManagerConstructorMock.mockReturnValue(messageManagerMock); - personalMessageManagerConstructorMock.mockReturnValue( - personalMessageManagerMock, - ); - - typedMessageManagerConstructorMock.mockReturnValue(typedMessageManagerMock); - - preferencesControllerMock.store.getState.mockReturnValue({ - disabledRpcMethodPreferences: { eth_sign: true }, - }); - - signController = new SignController({ - messenger: messengerMock as any, - preferencesController: preferencesControllerMock as any, - keyringController: keyringControllerMock as any, - getState: getStateMock as any, - securityProviderRequest: securityProviderRequestMock as any, - metricsEvent: metricsEventMock as any, - } as SignControllerOptions); - }); - - describe('unapprovedMsgCount', () => { - it('returns value from message manager getter', () => { - messageManagerMock.getUnapprovedMessagesCount.mockReturnValueOnce(10); - expect(signController.unapprovedMsgCount).toBe(10); - }); - }); - - describe('unapprovedPersonalMessagesCount', () => { - it('returns value from personal message manager getter', () => { - personalMessageManagerMock.getUnapprovedMessagesCount.mockReturnValueOnce( - 11, - ); - expect(signController.unapprovedPersonalMessagesCount).toBe(11); - }); - }); - - describe('unapprovedTypedMessagesCount', () => { - it('returns value from typed message manager getter', () => { - typedMessageManagerMock.getUnapprovedMessagesCount.mockReturnValueOnce( - 12, - ); - expect(signController.unapprovedTypedMessagesCount).toBe(12); - }); - }); - - describe('resetState', () => { - it('sets state to initial state', () => { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - signController.update(() => ({ - unapprovedMsgs: { [messageIdMock]: messageMock } as any, - unapprovedPersonalMsgs: { [messageIdMock]: messageMock } as any, - unapprovedTypedMessages: { [messageIdMock]: messageMock } as any, - unapprovedMsgCount: 1, - unapprovedPersonalMsgCount: 2, - unapprovedTypedMessagesCount: 3, - })); - - signController.resetState(); - - expect(signController.state).toEqual({ - unapprovedMsgs: {}, - unapprovedPersonalMsgs: {}, - unapprovedTypedMessages: {}, - unapprovedMsgCount: 0, - unapprovedPersonalMsgCount: 0, - unapprovedTypedMessagesCount: 0, - }); - }); - }); - - describe('rejectUnapproved', () => { - beforeEach(() => { - const messages = { - [messageIdMock]: messageMock, - [messageIdMock2]: messageMock, - }; - - messageManagerMock.getUnapprovedMessages.mockReturnValueOnce( - messages as any, - ); - personalMessageManagerMock.getUnapprovedMessages.mockReturnValueOnce( - messages as any, - ); - typedMessageManagerMock.getUnapprovedMessages.mockReturnValueOnce( - messages as any, - ); - - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - signController.update(() => ({ - unapprovedMsgs: messages as any, - unapprovedPersonalMsgs: messages as any, - unapprovedTypedMessages: messages as any, - })); - }); - - it('rejects all messages in all message managers', () => { - signController.rejectUnapproved('Test Reason'); - - expect(messageManagerMock.rejectMessage).toHaveBeenCalledTimes(2); - expect(messageManagerMock.rejectMessage).toHaveBeenCalledWith( - messageIdMock, - ); - expect(messageManagerMock.rejectMessage).toHaveBeenCalledWith( - messageIdMock2, - ); - - expect(personalMessageManagerMock.rejectMessage).toHaveBeenCalledTimes(2); - expect(personalMessageManagerMock.rejectMessage).toHaveBeenCalledWith( - messageIdMock, - ); - expect(personalMessageManagerMock.rejectMessage).toHaveBeenCalledWith( - messageIdMock2, - ); - - expect(typedMessageManagerMock.rejectMessage).toHaveBeenCalledTimes(2); - expect(typedMessageManagerMock.rejectMessage).toHaveBeenCalledWith( - messageIdMock, - ); - expect(typedMessageManagerMock.rejectMessage).toHaveBeenCalledWith( - messageIdMock2, - ); - }); - - it('fires metrics event with reject reason', () => { - signController.rejectUnapproved('Test Reason'); - - expect(metricsEventMock).toHaveBeenCalledTimes(6); - expect(metricsEventMock).toHaveBeenLastCalledWith({ - event: 'Test Reason', - category: MetaMetricsEventCategory.Transactions, - properties: { - action: 'Sign Request', - type: messageMock.type, - }, - }); - }); - }); - - describe('clearUnapproved', () => { - it('resets state in all message managers', () => { - signController.clearUnapproved(); - - const defaultState = { - unapprovedMessages: {}, - unapprovedMessagesCount: 0, - }; - - expect(messageManagerMock.update).toHaveBeenCalledTimes(1); - expect(messageManagerMock.update).toHaveBeenCalledWith(defaultState); - - expect(personalMessageManagerMock.update).toHaveBeenCalledTimes(1); - expect(personalMessageManagerMock.update).toHaveBeenCalledWith( - defaultState, - ); - - expect(typedMessageManagerMock.update).toHaveBeenCalledTimes(1); - expect(typedMessageManagerMock.update).toHaveBeenCalledWith(defaultState); - }); - }); - - describe('newUnsignedMessage', () => { - it('throws if eth_sign disabled in preferences', async () => { - preferencesControllerMock.store.getState.mockReturnValueOnce({ - disabledRpcMethodPreferences: { eth_sign: false }, - }); - - await expect( - signController.newUnsignedMessage(messageParamsMock, requestMock), - ).rejects.toThrowError( - 'eth_sign has been disabled. You must enable it in the advanced settings', - ); - }); - - it('throws if data has wrong length', async () => { - await expect( - signController.newUnsignedMessage( - { ...messageParamsMock, data: '0xFF' }, - requestMock, - ), - ).rejects.toThrowError('eth_sign requires 32 byte message hash'); - }); - - it('adds message to message manager', async () => { - await signController.newUnsignedMessage(messageParamsMock, requestMock); - - expect( - messageManagerMock.addUnapprovedMessageAsync, - ).toHaveBeenCalledTimes(1); - expect(messageManagerMock.addUnapprovedMessageAsync).toHaveBeenCalledWith( - messageParamsMock, - requestMock, - ); - }); - }); - - describe('newUnsignedPersonalMessage', () => { - it('adds message to personal message manager', async () => { - await signController.newUnsignedPersonalMessage( - messageParamsMock, - requestMock, - ); - - expect( - personalMessageManagerMock.addUnapprovedMessageAsync, - ).toHaveBeenCalledTimes(1); - - expect( - personalMessageManagerMock.addUnapprovedMessageAsync, - ).toHaveBeenCalledWith( - expect.objectContaining(messageParamsMock), - requestMock, - ); - }); - }); - - describe('newUnsignedTypedMessage', () => { - it('adds message to typed message manager', async () => { - signController.newUnsignedTypedMessage( - messageParamsMock, - requestMock, - versionMock, - ); - - expect( - typedMessageManagerMock.addUnapprovedMessageAsync, - ).toHaveBeenCalledTimes(1); - expect( - typedMessageManagerMock.addUnapprovedMessageAsync, - ).toHaveBeenCalledWith(messageParamsMock, versionMock, requestMock); - }); - }); - - describe.each([ - ['signMessage', messageManagerMock], - ['signPersonalMessage', personalMessageManagerMock], - ['signTypedMessage', typedMessageManagerMock], - ])('%s', (signMethodName, messageManager) => { - beforeEach(() => { - messageManager.approveMessage.mockResolvedValueOnce(messageParamsMock2); - - keyringControllerMock[signMethodName].mockResolvedValueOnce( - signatureMock, - ); - }); - - it('approves message and signs', async () => { - await signController[signMethodName](messageParamsMock); - - const keyringControllerExtraArgs = - signMethodName === 'signTypedMessage' - ? [{ version: messageParamsMock.version }] - : []; - - expect(keyringControllerMock[signMethodName]).toHaveBeenCalledTimes(1); - expect(keyringControllerMock[signMethodName]).toHaveBeenCalledWith( - messageParamsMock2, - ...keyringControllerExtraArgs, - ); - - expect(messageManager.setMessageStatusSigned).toHaveBeenCalledTimes(1); - expect(messageManager.setMessageStatusSigned).toHaveBeenCalledWith( - messageParamsMock2.metamaskId, - signatureMock, - ); - }); - - it('returns current state', async () => { - getStateMock.mockReturnValueOnce(stateMock); - expect(await signController[signMethodName](messageParamsMock)).toEqual( - stateMock, - ); - }); - - it('accepts approval', async () => { - await signController[signMethodName](messageParamsMock); - - expect(messengerMock.call).toHaveBeenCalledTimes(1); - expect(messengerMock.call).toHaveBeenCalledWith( - 'ApprovalController:acceptRequest', - messageParamsMock.metamaskId, - ); - }); - - it('does not throw if accepting approval throws', async () => { - messengerMock.call.mockImplementation(() => { - throw new Error('Test Error'); - }); - - await signController[signMethodName](messageParamsMock); - }); - - it('rejects message on error', async () => { - keyringControllerMock[signMethodName].mockReset(); - keyringControllerMock[signMethodName].mockRejectedValue( - new Error('Test Error'), - ); - - await expect( - signController[signMethodName](messageParamsMock), - ).rejects.toThrow('Test Error'); - - expect(messageManager.rejectMessage).toHaveBeenCalledTimes(1); - expect(messageManager.rejectMessage).toHaveBeenCalledWith( - messageParamsMock.metamaskId, - ); - }); - - it('rejects approval on error', async () => { - keyringControllerMock[signMethodName].mockReset(); - keyringControllerMock[signMethodName].mockRejectedValue( - new Error('Test Error'), - ); - - await expect( - signController[signMethodName](messageParamsMock), - ).rejects.toThrow('Test Error'); - - expect(messengerMock.call).toHaveBeenCalledTimes(1); - expect(messengerMock.call).toHaveBeenCalledWith( - 'ApprovalController:rejectRequest', - messageParamsMock.metamaskId, - 'Cancel', - ); - }); - }); - - describe.each([ - ['cancelMessage', messageManagerMock], - ['cancelPersonalMessage', personalMessageManagerMock], - ['cancelTypedMessage', typedMessageManagerMock], - ])('%s', (cancelMethodName, messageManager) => { - it('rejects message using message manager', async () => { - signController[cancelMethodName](messageIdMock); - - expect(messageManager.rejectMessage).toHaveBeenCalledTimes(1); - expect(messageManager.rejectMessage).toHaveBeenCalledWith( - messageParamsMock.metamaskId, - ); - }); - - it('rejects approval using approval controller', async () => { - signController[cancelMethodName](messageIdMock); - - expect(messengerMock.call).toHaveBeenCalledTimes(1); - expect(messengerMock.call).toHaveBeenCalledWith( - 'ApprovalController:rejectRequest', - messageParamsMock.metamaskId, - 'Cancel', - ); - }); - - it('does not throw if rejecting approval throws', async () => { - messengerMock.call.mockImplementation(() => { - throw new Error('Test Error'); - }); - - await signController[cancelMethodName](messageParamsMock); - }); - }); - - describe('message manager events', () => { - it.each([ - ['message manager', messageManagerMock], - ['personal message manager', personalMessageManagerMock], - ['typed message manager', typedMessageManagerMock], - ])('bubbles update badge event from %s', (_, messageManager) => { - const mockListener = jest.fn(); - - signController.hub.on('updateBadge', mockListener); - messageManager.hub.on.mock.calls[0][1](); - - expect(mockListener).toHaveBeenCalledTimes(1); - }); - - it.each([ - ['message manager', messageManagerMock, 'eth_sign'], - ['personal message manager', personalMessageManagerMock, 'personal_sign'], - ['typed message manager', typedMessageManagerMock, 'eth_signTypedData'], - ])( - 'requires approval on unapproved message event from %s', - (_, messageManager, methodName) => { - messengerMock.call.mockResolvedValueOnce({}); - - messageManager.hub.on.mock.calls[1][1](messageParamsMock); - - expect(messengerMock.call).toHaveBeenCalledTimes(1); - expect(messengerMock.call).toHaveBeenCalledWith( - 'ApprovalController:addRequest', - { - id: messageIdMock, - origin: messageParamsMock.origin, - type: methodName, - }, - true, - ); - }, - ); - - it('updates state on message manager state change', async () => { - securityProviderRequestMock.mockResolvedValue( - securityProviderResponseMock, - ); - - await messageManagerMock.subscribe.mock.calls[0][0]({ - unapprovedMessages: { [messageIdMock]: coreMessageMock as any }, - unapprovedMessagesCount: 3, - }); - - expect(await signController.state).toEqual({ - unapprovedMsgs: { [messageIdMock]: stateMessageMock as any }, - unapprovedPersonalMsgs: {}, - unapprovedTypedMessages: {}, - unapprovedMsgCount: 3, - unapprovedPersonalMsgCount: 0, - unapprovedTypedMessagesCount: 0, - }); - }); - - it('updates state on personal message manager state change', async () => { - securityProviderRequestMock.mockResolvedValue( - securityProviderResponseMock, - ); - - await personalMessageManagerMock.subscribe.mock.calls[0][0]({ - unapprovedMessages: { [messageIdMock]: coreMessageMock as any }, - unapprovedMessagesCount: 4, - }); - - expect(await signController.state).toEqual({ - unapprovedMsgs: {}, - unapprovedPersonalMsgs: { [messageIdMock]: stateMessageMock as any }, - unapprovedTypedMessages: {}, - unapprovedMsgCount: 0, - unapprovedPersonalMsgCount: 4, - unapprovedTypedMessagesCount: 0, - }); - }); - - it('updates state on typed message manager state change', async () => { - securityProviderRequestMock.mockResolvedValue( - securityProviderResponseMock, - ); - - await typedMessageManagerMock.subscribe.mock.calls[0][0]({ - unapprovedMessages: { [messageIdMock]: coreMessageMock as any }, - unapprovedMessagesCount: 5, - }); - - expect(await signController.state).toEqual({ - unapprovedMsgs: {}, - unapprovedPersonalMsgs: {}, - unapprovedTypedMessages: { [messageIdMock]: stateMessageMock as any }, - unapprovedMsgCount: 0, - unapprovedPersonalMsgCount: 0, - unapprovedTypedMessagesCount: 5, - }); - }); - }); -}); diff --git a/app/scripts/controllers/sign.ts b/app/scripts/controllers/sign.ts deleted file mode 100644 index 70c65596d..000000000 --- a/app/scripts/controllers/sign.ts +++ /dev/null @@ -1,658 +0,0 @@ -import EventEmitter from 'events'; -import log from 'loglevel'; -import { - MessageManager, - MessageParams, - MessageParamsMetamask, - PersonalMessageManager, - PersonalMessageParams, - PersonalMessageParamsMetamask, - TypedMessageManager, - TypedMessageParams, - TypedMessageParamsMetamask, -} from '@metamask/message-manager'; -import { ethErrors } from 'eth-rpc-errors'; -import { bufferToHex } from 'ethereumjs-util'; -import { KeyringController } from '@metamask/eth-keyring-controller'; -import { - AbstractMessageManager, - AbstractMessage, - MessageManagerState, - AbstractMessageParams, - AbstractMessageParamsMetamask, - OriginalRequest, - SecurityProviderRequest, -} from '@metamask/message-manager/dist/AbstractMessageManager'; -import { - BaseControllerV2, - RestrictedControllerMessenger, -} from '@metamask/base-controller'; -import { Patch } from 'immer'; -import { - AcceptRequest, - AddApprovalRequest, - RejectRequest, -} from '@metamask/approval-controller'; -import { MetaMetricsEventCategory } from '../../../shared/constants/metametrics'; -import { MESSAGE_TYPE } from '../../../shared/constants/app'; -import PreferencesController from './preferences'; - -const controllerName = 'SignController'; -const methodNameSign = MESSAGE_TYPE.ETH_SIGN; -const methodNamePersonalSign = MESSAGE_TYPE.PERSONAL_SIGN; -const methodNameTypedSign = MESSAGE_TYPE.ETH_SIGN_TYPED_DATA; - -const stateMetadata = { - unapprovedMsgs: { persist: false, anonymous: false }, - unapprovedPersonalMsgs: { persist: false, anonymous: false }, - unapprovedTypedMessages: { persist: false, anonymous: false }, - unapprovedMsgCount: { persist: false, anonymous: false }, - unapprovedPersonalMsgCount: { persist: false, anonymous: false }, - unapprovedTypedMessagesCount: { persist: false, anonymous: false }, -}; - -const getDefaultState = () => ({ - unapprovedMsgs: {}, - unapprovedPersonalMsgs: {}, - unapprovedTypedMessages: {}, - unapprovedMsgCount: 0, - unapprovedPersonalMsgCount: 0, - unapprovedTypedMessagesCount: 0, -}); - -export type CoreMessage = AbstractMessage & { - messageParams: AbstractMessageParams; -}; - -export type StateMessage = Required< - Omit -> & { - msgParams: Required; -}; - -export type SignControllerState = { - unapprovedMsgs: Record; - unapprovedPersonalMsgs: Record; - unapprovedTypedMessages: Record; - unapprovedMsgCount: number; - unapprovedPersonalMsgCount: number; - unapprovedTypedMessagesCount: number; -}; - -export type GetSignState = { - type: `${typeof controllerName}:getState`; - handler: () => SignControllerState; -}; - -export type SignStateChange = { - type: `${typeof controllerName}:stateChange`; - payload: [SignControllerState, Patch[]]; -}; - -export type SignControllerActions = GetSignState; - -export type SignControllerEvents = SignStateChange; - -type AllowedActions = AddApprovalRequest | AcceptRequest | RejectRequest; - -export type SignControllerMessenger = RestrictedControllerMessenger< - typeof controllerName, - SignControllerActions | AllowedActions, - SignControllerEvents, - AllowedActions['type'], - never ->; - -export type SignControllerOptions = { - messenger: SignControllerMessenger; - keyringController: KeyringController; - preferencesController: PreferencesController; - getState: () => any; - metricsEvent: (payload: any, options?: any) => void; - securityProviderRequest: SecurityProviderRequest; -}; - -/** - * Controller for creating signing requests requiring user approval. - */ -export default class SignController extends BaseControllerV2< - typeof controllerName, - SignControllerState, - SignControllerMessenger -> { - hub: EventEmitter; - - private _keyringController: KeyringController; - - private _preferencesController: PreferencesController; - - private _getState: () => any; - - private _messageManager: MessageManager; - - private _personalMessageManager: PersonalMessageManager; - - private _typedMessageManager: TypedMessageManager; - - private _messageManagers: AbstractMessageManager< - AbstractMessage, - AbstractMessageParams, - AbstractMessageParamsMetamask - >[]; - - private _metricsEvent: (payload: any, options?: any) => void; - - /** - * Construct a Sign controller. - * - * @param options - The controller options. - * @param options.messenger - The restricted controller messenger for the sign controller. - * @param options.keyringController - An instance of a keyring controller used to perform the signing operations. - * @param options.preferencesController - An instance of a preferences controller to limit operations based on user configuration. - * @param options.getState - Callback to retrieve all user state. - * @param options.metricsEvent - A function for emitting a metric event. - * @param options.securityProviderRequest - A function for verifying a message, whether it is malicious or not. - */ - constructor({ - messenger, - keyringController, - preferencesController, - getState, - metricsEvent, - securityProviderRequest, - }: SignControllerOptions) { - super({ - name: controllerName, - metadata: stateMetadata, - messenger, - state: getDefaultState(), - }); - - this._keyringController = keyringController; - this._preferencesController = preferencesController; - this._getState = getState; - this._metricsEvent = metricsEvent; - - this.hub = new EventEmitter(); - this._messageManager = new MessageManager( - undefined, - undefined, - securityProviderRequest, - ); - this._personalMessageManager = new PersonalMessageManager( - undefined, - undefined, - securityProviderRequest, - ); - this._typedMessageManager = new TypedMessageManager( - undefined, - undefined, - securityProviderRequest, - ); - - this._messageManagers = [ - this._messageManager, - this._personalMessageManager, - this._typedMessageManager, - ]; - - const methodNames = [ - methodNameSign, - methodNamePersonalSign, - methodNameTypedSign, - ]; - - this._messageManagers.forEach((messageManager, index) => { - this._bubbleEvents(messageManager); - - messageManager.hub.on( - 'unapprovedMessage', - (msgParams: AbstractMessageParamsMetamask) => { - this._requestApproval(msgParams, methodNames[index]); - }, - ); - }); - - this._subscribeToMessageState( - this._messageManager, - (state, newMessages, messageCount) => { - state.unapprovedMsgs = newMessages; - state.unapprovedMsgCount = messageCount; - }, - ); - - this._subscribeToMessageState( - this._personalMessageManager, - (state, newMessages, messageCount) => { - state.unapprovedPersonalMsgs = newMessages; - state.unapprovedPersonalMsgCount = messageCount; - }, - ); - - this._subscribeToMessageState( - this._typedMessageManager, - (state, newMessages, messageCount) => { - state.unapprovedTypedMessages = newMessages; - state.unapprovedTypedMessagesCount = messageCount; - }, - ); - } - - /** - * A getter for the number of 'unapproved' Messages in this.messages - * - * @returns The number of 'unapproved' Messages in this.messages - */ - get unapprovedMsgCount(): number { - return this._messageManager.getUnapprovedMessagesCount(); - } - - /** - * A getter for the number of 'unapproved' PersonalMessages in this.messages - * - * @returns The number of 'unapproved' PersonalMessages in this.messages - */ - get unapprovedPersonalMessagesCount(): number { - return this._personalMessageManager.getUnapprovedMessagesCount(); - } - - /** - * A getter for the number of 'unapproved' TypedMessages in this.messages - * - * @returns The number of 'unapproved' TypedMessages in this.messages - */ - get unapprovedTypedMessagesCount(): number { - return this._typedMessageManager.getUnapprovedMessagesCount(); - } - - /** - * Reset the controller state to the initial state. - */ - resetState() { - this.update(() => getDefaultState()); - } - - /** - * Called when a Dapp uses the eth_sign method, to request user approval. - * eth_sign is a pure signature of arbitrary data. It is on a deprecation - * path, since this data can be a transaction, or can leak private key - * information. - * - * @param msgParams - The params passed to eth_sign. - * @param [req] - The original request, containing the origin. - */ - async newUnsignedMessage( - msgParams: MessageParams, - req: OriginalRequest, - ): Promise { - const { - // eslint-disable-next-line camelcase - disabledRpcMethodPreferences: { eth_sign }, - } = this._preferencesController.store.getState() as any; - - // eslint-disable-next-line camelcase - if (!eth_sign) { - throw ethErrors.rpc.methodNotFound( - 'eth_sign has been disabled. You must enable it in the advanced settings', - ); - } - - const data = this._normalizeMsgData(msgParams.data); - - // 64 hex + "0x" at the beginning - // This is needed because Ethereum's EcSign works only on 32 byte numbers - // For 67 length see: https://github.com/MetaMask/metamask-extension/pull/12679/files#r749479607 - if (data.length !== 66 && data.length !== 67) { - throw ethErrors.rpc.invalidParams( - 'eth_sign requires 32 byte message hash', - ); - } - - return this._messageManager.addUnapprovedMessageAsync(msgParams, req); - } - - /** - * Called when a dapp uses the personal_sign method. - * This is identical to the Geth eth_sign method, and may eventually replace - * eth_sign. - * - * We currently define our eth_sign and personal_sign mostly for legacy Dapps. - * - * @param msgParams - The params of the message to sign & return to the Dapp. - * @param req - The original request, containing the origin. - */ - async newUnsignedPersonalMessage( - msgParams: PersonalMessageParams, - req: OriginalRequest, - ): Promise { - return this._personalMessageManager.addUnapprovedMessageAsync( - msgParams, - req, - ); - } - - /** - * Called when a dapp uses the eth_signTypedData method, per EIP 712. - * - * @param msgParams - The params passed to eth_signTypedData. - * @param req - The original request, containing the origin. - * @param version - */ - async newUnsignedTypedMessage( - msgParams: TypedMessageParams, - req: OriginalRequest, - version: string, - ): Promise { - return this._typedMessageManager.addUnapprovedMessageAsync( - msgParams, - version, - req, - ); - } - - /** - * Signifies user intent to complete an eth_sign method. - * - * @param msgParams - The params passed to eth_call. - * @returns Full state update. - */ - async signMessage(msgParams: MessageParamsMetamask) { - return await this._signAbstractMessage( - this._messageManager, - methodNameSign, - msgParams, - async (cleanMsgParams) => - await this._keyringController.signMessage(cleanMsgParams), - ); - } - - /** - * Signifies a user's approval to sign a personal_sign message in queue. - * Triggers signing, and the callback function from newUnsignedPersonalMessage. - * - * @param msgParams - The params of the message to sign & return to the Dapp. - * @returns A full state update. - */ - async signPersonalMessage(msgParams: PersonalMessageParamsMetamask) { - return await this._signAbstractMessage( - this._personalMessageManager, - methodNamePersonalSign, - msgParams, - async (cleanMsgParams) => - await this._keyringController.signPersonalMessage(cleanMsgParams), - ); - } - - /** - * The method for a user approving a call to eth_signTypedData, per EIP 712. - * Triggers the callback in newUnsignedTypedMessage. - * - * @param msgParams - The params passed to eth_signTypedData. - * @returns Full state update. - */ - async signTypedMessage(msgParams: TypedMessageParamsMetamask) { - const { version } = msgParams; - - return await this._signAbstractMessage( - this._typedMessageManager, - methodNameTypedSign, - msgParams, - async (cleanMsgParams) => { - // For some reason every version after V1 used stringified params. - if (version !== 'V1') { - // But we don't have to require that. We can stop suggesting it now: - if (typeof cleanMsgParams.data === 'string') { - cleanMsgParams.data = JSON.parse(cleanMsgParams.data); - } - } - - return await this._keyringController.signTypedMessage(cleanMsgParams, { - version, - }); - }, - ); - } - - /** - * Used to cancel a message submitted via eth_sign. - * - * @param msgId - The id of the message to cancel. - * @returns A full state update. - */ - cancelMessage(msgId: string) { - return this._cancelAbstractMessage(this._messageManager, msgId); - } - - /** - * Used to cancel a personal_sign type message. - * - * @param msgId - The ID of the message to cancel. - * @returns A full state update. - */ - cancelPersonalMessage(msgId: string) { - return this._cancelAbstractMessage(this._personalMessageManager, msgId); - } - - /** - * Used to cancel a eth_signTypedData type message. - * - * @param msgId - The ID of the message to cancel. - * @returns A full state update. - */ - cancelTypedMessage(msgId: string) { - return this._cancelAbstractMessage(this._typedMessageManager, msgId); - } - - /** - * Reject all unapproved messages of any type. - * - * @param reason - A message to indicate why. - */ - rejectUnapproved(reason?: string) { - this._messageManagers.forEach((messageManager) => { - Object.keys(messageManager.getUnapprovedMessages()).forEach( - (messageId) => { - this._cancelAbstractMessage(messageManager, messageId, reason); - }, - ); - }); - } - - /** - * Clears all unapproved messages from memory. - */ - clearUnapproved() { - this._messageManagers.forEach((messageManager) => { - messageManager.update({ - unapprovedMessages: {}, - unapprovedMessagesCount: 0, - }); - }); - } - - private async _signAbstractMessage

( - messageManager: AbstractMessageManager< - AbstractMessage, - P, - AbstractMessageParamsMetamask - >, - methodName: string, - msgParams: AbstractMessageParamsMetamask, - getSignature: (cleanMessageParams: P) => Promise, - ) { - log.info(`MetaMaskController - ${methodName}`); - - const messageId = msgParams.metamaskId as string; - - try { - const cleanMessageParams = await messageManager.approveMessage(msgParams); - const signature = await getSignature(cleanMessageParams); - - messageManager.setMessageStatusSigned(messageId, signature); - - this._acceptApproval(messageId); - - return this._getState(); - } catch (error) { - log.info(`MetaMaskController - ${methodName} failed.`, error); - this._cancelAbstractMessage(messageManager, messageId); - throw error; - } - } - - private _cancelAbstractMessage( - messageManager: AbstractMessageManager< - AbstractMessage, - AbstractMessageParams, - AbstractMessageParamsMetamask - >, - messageId: string, - reason?: string, - ) { - if (reason) { - const message = this._getMessage(messageId); - - this._metricsEvent({ - event: reason, - category: MetaMetricsEventCategory.Transactions, - properties: { - action: 'Sign Request', - type: message.type, - }, - }); - } - - messageManager.rejectMessage(messageId); - this._rejectApproval(messageId); - - return this._getState(); - } - - private _bubbleEvents( - messageManager: AbstractMessageManager< - AbstractMessage, - AbstractMessageParams, - AbstractMessageParamsMetamask - >, - ) { - messageManager.hub.on('updateBadge', () => { - this.hub.emit('updateBadge'); - }); - } - - private _subscribeToMessageState( - messageManager: AbstractMessageManager< - AbstractMessage, - AbstractMessageParams, - AbstractMessageParamsMetamask - >, - updateState: ( - state: SignControllerState, - newMessages: Record, - messageCount: number, - ) => void, - ) { - messageManager.subscribe( - async (state: MessageManagerState) => { - const newMessages = await this._migrateMessages( - state.unapprovedMessages as any, - ); - - this.update((draftState) => { - updateState(draftState, newMessages, state.unapprovedMessagesCount); - }); - }, - ); - } - - private async _migrateMessages( - coreMessages: Record, - ): Promise> { - const stateMessages: Record = {}; - - for (const messageId of Object.keys(coreMessages)) { - const coreMessage = coreMessages[messageId]; - const stateMessage = await this._migrateMessage(coreMessage); - - stateMessages[messageId] = stateMessage; - } - - return stateMessages; - } - - private async _migrateMessage( - coreMessage: CoreMessage, - ): Promise { - const { messageParams, ...coreMessageData } = coreMessage; - - // Core message managers use messageParams but frontend uses msgParams with lots of references - const stateMessage = { - ...coreMessageData, - rawSig: coreMessage.rawSig as string, - msgParams: { - ...messageParams, - origin: messageParams.origin as string, - }, - }; - return stateMessage; - } - - private _normalizeMsgData(data: string) { - if (data.slice(0, 2) === '0x') { - // data is already hex - return data; - } - // data is unicode, convert to hex - return bufferToHex(Buffer.from(data, 'utf8')); - } - - private _getMessage(messageId: string): StateMessage { - return { - ...this.state.unapprovedMsgs, - ...this.state.unapprovedPersonalMsgs, - ...this.state.unapprovedTypedMessages, - }[messageId]; - } - - private _requestApproval( - msgParams: AbstractMessageParamsMetamask, - type: string, - ) { - const id = msgParams.metamaskId as string; - const origin = msgParams.origin || controllerName; - - this.messagingSystem - .call( - 'ApprovalController:addRequest', - { - id, - origin, - type, - }, - true, - ) - .catch(() => { - // Intentionally ignored as promise not currently used - }); - } - - private _acceptApproval(messageId: string) { - try { - this.messagingSystem.call('ApprovalController:acceptRequest', messageId); - } catch (error) { - log.info('Failed to accept signature approval request', error); - } - } - - private _rejectApproval(messageId: string) { - try { - this.messagingSystem.call( - 'ApprovalController:rejectRequest', - messageId, - 'Cancel', - ); - } catch (error) { - log.info('Failed to reject signature approval request', error); - } - } -} diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 601091454..0e239bb73 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -62,6 +62,7 @@ import { } from '@metamask/snaps-controllers'; ///: END:ONLY_INCLUDE_IN +import { SignatureController } from '@metamask/signature-controller'; import { AssetType, TransactionStatus, @@ -160,7 +161,6 @@ import { segment } from './lib/segment'; import createMetaRPCHandler from './lib/createMetaRPCHandler'; import { previousValueComparator } from './lib/util'; import createMetamaskMiddleware from './lib/createMetamaskMiddleware'; -import SignController from './controllers/sign'; import EncryptionPublicKeyController from './controllers/encryption-public-key'; import { @@ -1191,9 +1191,9 @@ export default class MetamaskController extends EventEmitter { ), }); - this.signController = new SignController({ + this.signatureController = new SignatureController({ messenger: this.controllerMessenger.getRestricted({ - name: 'SignController', + name: 'SignatureController', allowedActions: [ `${this.approvalController.name}:addRequest`, `${this.approvalController.name}:acceptRequest`, @@ -1201,12 +1201,22 @@ export default class MetamaskController extends EventEmitter { ], }), keyringController: this.keyringController, - preferencesController: this.preferencesController, - getState: this.getState.bind(this), + isEthSignEnabled: () => + this.preferencesController.store.getState() + ?.disabledRpcMethodPreferences?.eth_sign, + getAllState: this.getState.bind(this), securityProviderRequest: this.securityProviderRequest.bind(this), - metricsEvent: this.metaMetricsController.trackEvent.bind( - this.metaMetricsController, - ), + }); + + this.signatureController.hub.on('cancelWithReason', (message, reason) => { + this.metaMetricsController.trackEvent({ + event: reason, + category: MetaMetricsEventCategory.Transactions, + properties: { + action: 'Sign Request', + type: message.type, + }, + }); }); this.swapsController = new SwapsController({ @@ -1271,7 +1281,7 @@ export default class MetamaskController extends EventEmitter { this.txController.txStateManager.clearUnapprovedTxs(); this.encryptionPublicKeyController.clearUnapproved(); this.decryptMessageController.clearUnapproved(); - this.signController.clearUnapproved(); + this.signatureController.clearUnapproved(); }, ); @@ -1319,21 +1329,24 @@ export default class MetamaskController extends EventEmitter { // tx signing processTransaction: this.newUnapprovedTransaction.bind(this), // msg signing - processEthSignMessage: this.signController.newUnsignedMessage.bind( - this.signController, - ), - processTypedMessage: this.signController.newUnsignedTypedMessage.bind( - this.signController, - ), - processTypedMessageV3: this.signController.newUnsignedTypedMessage.bind( - this.signController, - ), - processTypedMessageV4: this.signController.newUnsignedTypedMessage.bind( - this.signController, + processEthSignMessage: this.signatureController.newUnsignedMessage.bind( + this.signatureController, ), + processTypedMessage: + this.signatureController.newUnsignedTypedMessage.bind( + this.signatureController, + ), + processTypedMessageV3: + this.signatureController.newUnsignedTypedMessage.bind( + this.signatureController, + ), + processTypedMessageV4: + this.signatureController.newUnsignedTypedMessage.bind( + this.signatureController, + ), processPersonalMessage: - this.signController.newUnsignedPersonalMessage.bind( - this.signController, + this.signatureController.newUnsignedPersonalMessage.bind( + this.signatureController, ), processEncryptionPublicKey: this.encryptionPublicKeyController.newRequestEncryptionPublicKey.bind( @@ -1366,7 +1379,7 @@ export default class MetamaskController extends EventEmitter { TokenRatesController: this.tokenRatesController, DecryptMessageController: this.decryptMessageController, EncryptionPublicKeyController: this.encryptionPublicKeyController, - SignController: this.signController, + SignatureController: this.signatureController, SwapsController: this.swapsController.store, EnsController: this.ensController.store, ApprovalController: this.approvalController, @@ -1456,7 +1469,7 @@ export default class MetamaskController extends EventEmitter { this.encryptionPublicKeyController.resetState.bind( this.encryptionPublicKeyController, ), - this.signController.resetState.bind(this.signController), + this.signatureController.resetState.bind(this.signatureController), this.swapsController.resetState, this.ensController.resetState, this.approvalController.clear.bind(this.approvalController), @@ -2142,22 +2155,25 @@ export default class MetamaskController extends EventEmitter { updatePreviousGasParams: txController.updatePreviousGasParams.bind(txController), - // signController - signMessage: this.signController.signMessage.bind(this.signController), - cancelMessage: this.signController.cancelMessage.bind( - this.signController, + // signatureController + signMessage: this.signatureController.signMessage.bind( + this.signatureController, ), - signPersonalMessage: this.signController.signPersonalMessage.bind( - this.signController, + cancelMessage: this.signatureController.cancelMessage.bind( + this.signatureController, ), - cancelPersonalMessage: this.signController.cancelPersonalMessage.bind( - this.signController, + signPersonalMessage: this.signatureController.signPersonalMessage.bind( + this.signatureController, ), - signTypedMessage: this.signController.signTypedMessage.bind( - this.signController, + cancelPersonalMessage: + this.signatureController.cancelPersonalMessage.bind( + this.signatureController, + ), + signTypedMessage: this.signatureController.signTypedMessage.bind( + this.signatureController, ), - cancelTypedMessage: this.signController.cancelTypedMessage.bind( - this.signController, + cancelTypedMessage: this.signatureController.cancelTypedMessage.bind( + this.signatureController, ), // decryptMessageController diff --git a/lavamoat/browserify/beta/policy.json b/lavamoat/browserify/beta/policy.json index 6d049fb83..5f7721bcf 100644 --- a/lavamoat/browserify/beta/policy.json +++ b/lavamoat/browserify/beta/policy.json @@ -1356,6 +1356,20 @@ "@metamask/key-tree>@scure/base": true } }, + "@metamask/signature-controller": { + "globals": { + "console.info": true + }, + "packages": { + "@metamask/base-controller": true, + "@metamask/controller-utils": true, + "@metamask/message-manager": true, + "browserify>buffer": true, + "browserify>events": true, + "eth-rpc-errors": true, + "ethereumjs-util": true + } + }, "@metamask/smart-transactions-controller": { "globals": { "URLSearchParams": true, diff --git a/lavamoat/browserify/desktop/policy.json b/lavamoat/browserify/desktop/policy.json index 6386fa0b8..1fcdff317 100644 --- a/lavamoat/browserify/desktop/policy.json +++ b/lavamoat/browserify/desktop/policy.json @@ -1532,6 +1532,20 @@ "@metamask/key-tree>@scure/base": true } }, + "@metamask/signature-controller": { + "globals": { + "console.info": true + }, + "packages": { + "@metamask/base-controller": true, + "@metamask/controller-utils": true, + "@metamask/message-manager": true, + "browserify>buffer": true, + "browserify>events": true, + "eth-rpc-errors": true, + "ethereumjs-util": true + } + }, "@metamask/smart-transactions-controller": { "globals": { "URLSearchParams": true, diff --git a/lavamoat/browserify/flask/policy.json b/lavamoat/browserify/flask/policy.json index 6386fa0b8..1fcdff317 100644 --- a/lavamoat/browserify/flask/policy.json +++ b/lavamoat/browserify/flask/policy.json @@ -1532,6 +1532,20 @@ "@metamask/key-tree>@scure/base": true } }, + "@metamask/signature-controller": { + "globals": { + "console.info": true + }, + "packages": { + "@metamask/base-controller": true, + "@metamask/controller-utils": true, + "@metamask/message-manager": true, + "browserify>buffer": true, + "browserify>events": true, + "eth-rpc-errors": true, + "ethereumjs-util": true + } + }, "@metamask/smart-transactions-controller": { "globals": { "URLSearchParams": true, diff --git a/lavamoat/browserify/main/policy.json b/lavamoat/browserify/main/policy.json index 6d049fb83..5f7721bcf 100644 --- a/lavamoat/browserify/main/policy.json +++ b/lavamoat/browserify/main/policy.json @@ -1356,6 +1356,20 @@ "@metamask/key-tree>@scure/base": true } }, + "@metamask/signature-controller": { + "globals": { + "console.info": true + }, + "packages": { + "@metamask/base-controller": true, + "@metamask/controller-utils": true, + "@metamask/message-manager": true, + "browserify>buffer": true, + "browserify>events": true, + "eth-rpc-errors": true, + "ethereumjs-util": true + } + }, "@metamask/smart-transactions-controller": { "globals": { "URLSearchParams": true, diff --git a/package.json b/package.json index 0e9356694..56d68993f 100644 --- a/package.json +++ b/package.json @@ -247,7 +247,7 @@ "@metamask/jazzicon": "^2.0.0", "@metamask/key-tree": "^7.0.0", "@metamask/logo": "^3.1.1", - "@metamask/message-manager": "^3.1.1", + "@metamask/message-manager": "^4.0.0", "@metamask/metamask-eth-abis": "^3.0.0", "@metamask/notification-controller": "^2.0.0", "@metamask/obs-store": "^8.1.0", @@ -260,6 +260,7 @@ "@metamask/rpc-methods-flask": "npm:@metamask/rpc-methods@0.32.2", "@metamask/safe-event-emitter": "^2.0.0", "@metamask/scure-bip39": "^2.0.3", + "@metamask/signature-controller": "^1.0.0", "@metamask/slip44": "^3.0.0", "@metamask/smart-transactions-controller": "^3.1.0", "@metamask/snaps-controllers": "^0.32.2", diff --git a/yarn.lock b/yarn.lock index d64e7a547..3d700eced 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3619,16 +3619,16 @@ __metadata: languageName: node linkType: hard -"@metamask/approval-controller@npm:^2.0.0, @metamask/approval-controller@npm:^2.1.0": - version: 2.1.0 - resolution: "@metamask/approval-controller@npm:2.1.0" +"@metamask/approval-controller@npm:^2.0.0, @metamask/approval-controller@npm:^2.1.0, @metamask/approval-controller@npm:^2.1.1": + version: 2.1.1 + resolution: "@metamask/approval-controller@npm:2.1.1" dependencies: "@metamask/base-controller": ^2.0.0 - "@metamask/controller-utils": ^3.1.0 - eth-rpc-errors: ^4.0.0 + "@metamask/controller-utils": ^3.4.0 + eth-rpc-errors: ^4.0.2 immer: ^9.0.6 nanoid: ^3.1.31 - checksum: 207380e3ed0007aec3b9efcde62ac3ece9fa46cc7b9e6157c0d54271e0936b5a9a05e59adcfb1e47e3f3df397d2d2dc757f3b97745528695182e7c66a5207aca + checksum: 2e3798e67821660be7c7a35cbe5d001085fd2814fb58bc21e1525383fe2fba66ddb4074896c530de29407d929790ee3a036e180c5576962e3b50a85afe3fd9df languageName: node linkType: hard @@ -4041,9 +4041,9 @@ __metadata: languageName: node linkType: hard -"@metamask/message-manager@npm:^3.1.1": - version: 3.1.1 - resolution: "@metamask/message-manager@npm:3.1.1" +"@metamask/message-manager@npm:^4.0.0": + version: 4.0.0 + resolution: "@metamask/message-manager@npm:4.0.0" dependencies: "@metamask/base-controller": ^2.0.0 "@metamask/controller-utils": ^3.4.0 @@ -4052,7 +4052,7 @@ __metadata: ethereumjs-util: ^7.0.10 jsonschema: ^1.2.4 uuid: ^8.3.2 - checksum: 5f2341a67826b04a2b9dafff1bd53f1a7d3748e9617da5360248549eb5c048aebe542d3c364e77a5d4263358f8dfac8109f0f5faaabf0630c3be7d5857ab881d + checksum: 9c3295a726eb59c2cc3f764b96010833edf2463e2dd3ee0a4d23ea6bf662615b3bd71110a5c6b60f253eac1de6c8b38af1edc0188ec766770a9d02c0f7401caa languageName: node linkType: hard @@ -4308,6 +4308,21 @@ __metadata: languageName: node linkType: hard +"@metamask/signature-controller@npm:^1.0.0": + version: 1.0.0 + resolution: "@metamask/signature-controller@npm:1.0.0" + dependencies: + "@metamask/approval-controller": ^2.1.1 + "@metamask/base-controller": ^2.0.0 + "@metamask/controller-utils": ^3.4.0 + "@metamask/message-manager": ^4.0.0 + eth-rpc-errors: ^4.0.2 + ethereumjs-util: ^7.0.10 + immer: ^9.0.6 + checksum: 7efb738646a179db15c45d6e1654e17ce6c7a3e42b03fd27d365e50bb2e4287a1757f26efc0b3e99b3e5d320816f3f9e0c7038491289cf6071b7f886eaa0f27b + languageName: node + linkType: hard + "@metamask/slip44@npm:^3.0.0": version: 3.0.0 resolution: "@metamask/slip44@npm:3.0.0" @@ -23950,7 +23965,7 @@ __metadata: "@metamask/jazzicon": ^2.0.0 "@metamask/key-tree": ^7.0.0 "@metamask/logo": ^3.1.1 - "@metamask/message-manager": ^3.1.1 + "@metamask/message-manager": ^4.0.0 "@metamask/metamask-eth-abis": ^3.0.0 "@metamask/notification-controller": ^2.0.0 "@metamask/obs-store": ^8.1.0 @@ -23964,6 +23979,7 @@ __metadata: "@metamask/rpc-methods-flask": "npm:@metamask/rpc-methods@0.32.2" "@metamask/safe-event-emitter": ^2.0.0 "@metamask/scure-bip39": ^2.0.3 + "@metamask/signature-controller": ^1.0.0 "@metamask/slip44": ^3.0.0 "@metamask/smart-transactions-controller": ^3.1.0 "@metamask/snaps-controllers": ^0.32.2