-
Notifications
You must be signed in to change notification settings - Fork 5.2k
refactor: Add MultichainRouter to new controller instantiation pattern #31951
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
b6f44f4
4c0b3f9
9a02d74
1ad185f
a8fc501
f8b1c3f
05624a6
bed7cd6
3aa4834
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import { Messenger, RestrictedMessenger } from '@metamask/base-controller'; | ||
import { getMultichainRouterMessenger } from './multichain-router-messenger'; | ||
|
||
describe('getMultichainRouterMessenger', () => { | ||
it('returns a restricted messenger', () => { | ||
const messenger = new Messenger<never, never>(); | ||
const multichainRouterMessenger = getMultichainRouterMessenger(messenger); | ||
|
||
expect(multichainRouterMessenger).toBeInstanceOf(RestrictedMessenger); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import { Messenger } from '@metamask/base-controller'; | ||
import { | ||
AccountsControllerListMultichainAccountsAction, | ||
GetAllSnaps, | ||
HandleSnapRequest, | ||
} from '@metamask/snaps-controllers'; | ||
import { GetPermissions } from '@metamask/permission-controller'; | ||
import { KeyringControllerWithKeyringAction } from '@metamask/keyring-controller'; | ||
|
||
type Actions = | ||
| GetAllSnaps | ||
| HandleSnapRequest | ||
| GetPermissions | ||
| AccountsControllerListMultichainAccountsAction; | ||
|
||
type Events = never; | ||
|
||
export type MultichainRouterMessenger = ReturnType< | ||
typeof getMultichainRouterMessenger | ||
>; | ||
|
||
/** | ||
* Get a restricted messenger for the Multichain Router. This is scoped to the | ||
* actions and events that the Multichain Router is allowed to handle. | ||
* | ||
* @param messenger - The controller messenger to restrict. | ||
* @returns The restricted controller messenger. | ||
*/ | ||
export function getMultichainRouterMessenger( | ||
messenger: Messenger<Actions, Events>, | ||
) { | ||
return messenger.getRestricted({ | ||
name: 'MultichainRouter', | ||
allowedActions: [ | ||
`SnapController:getAll`, | ||
`SnapController:handleRequest`, | ||
`PermissionController:getPermissions`, | ||
`AccountsController:listMultichainAccounts`, | ||
], | ||
allowedEvents: [], | ||
}); | ||
} | ||
|
||
type InitActions = KeyringControllerWithKeyringAction; | ||
|
||
export type MultichainRouterInitMessenger = ReturnType< | ||
typeof getMultichainRouterInitMessenger | ||
>; | ||
|
||
/** | ||
* Get a restricted controller messenger for Multichain Router initialization. This is | ||
* scoped to the actions and events that the Multichain Router needs for instantiation. | ||
* | ||
* @param messenger - The messenger to restrict. | ||
* @returns The restricted controller messenger. | ||
*/ | ||
export function getMultichainRouterInitMessenger( | ||
messenger: Messenger<InitActions, never>, | ||
) { | ||
return messenger.getRestricted({ | ||
name: 'MultichainRouter', | ||
allowedActions: ['KeyringController:withKeyring'], | ||
allowedEvents: [], | ||
}); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import { Messenger } from '@metamask/base-controller'; | ||
import { MultichainRouter } from '@metamask/snaps-controllers'; | ||
import { buildControllerInitRequestMock } from '../test/utils'; | ||
import { ControllerInitRequest } from '../types'; | ||
import { | ||
getMultichainRouterMessenger, | ||
MultichainRouterMessenger, | ||
} from '../messengers/multichain'; | ||
import { | ||
getMultichainRouterInitMessenger, | ||
MultichainRouterInitMessenger, | ||
} from '../messengers/multichain/multichain-router-messenger'; | ||
import { MultichainRouterInit } from './multichain-router-init'; | ||
|
||
jest.mock('@metamask/snaps-controllers'); | ||
|
||
function buildInitRequestMock(): jest.Mocked< | ||
ControllerInitRequest< | ||
MultichainRouterMessenger, | ||
MultichainRouterInitMessenger | ||
> | ||
> { | ||
const baseControllerMessenger = new Messenger(); | ||
|
||
return { | ||
...buildControllerInitRequestMock(), | ||
controllerMessenger: getMultichainRouterMessenger(baseControllerMessenger), | ||
initMessenger: getMultichainRouterInitMessenger(baseControllerMessenger), | ||
}; | ||
} | ||
|
||
describe('MultichainRouterInit', () => { | ||
const multichainRouterClassMock = jest.mocked(MultichainRouter); | ||
|
||
beforeEach(() => { | ||
jest.resetAllMocks(); | ||
}); | ||
|
||
it('returns controller instance', () => { | ||
const requestMock = buildInitRequestMock(); | ||
expect(MultichainRouterInit(requestMock).controller).toBeInstanceOf( | ||
MultichainRouter, | ||
); | ||
}); | ||
|
||
it('initializes with correct messenger and state', () => { | ||
const requestMock = buildInitRequestMock(); | ||
MultichainRouterInit(requestMock); | ||
|
||
expect(multichainRouterClassMock).toHaveBeenCalledWith({ | ||
messenger: requestMock.controllerMessenger, | ||
withSnapKeyring: expect.any(Function), | ||
}); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import { MultichainRouter } from '@metamask/snaps-controllers'; | ||
import { ControllerInitFunction } from '../types'; | ||
import { MultichainRouterMessenger } from '../messengers/multichain'; | ||
import { MultichainRouterInitMessenger } from '../messengers/multichain/multichain-router-messenger'; | ||
|
||
/** | ||
* Initialize the Multichain Network controller. | ||
* | ||
* @param request - The request object. | ||
* @param request.controllerMessenger - The messenger to use for the controller. | ||
* @param request.initMessenger - The init messenger. This has access to | ||
* different functions than the controller messenger, and should be used for | ||
* initialization purposes only. | ||
* @returns The initialized controller. | ||
*/ | ||
export const MultichainRouterInit: ControllerInitFunction< | ||
MultichainRouter, | ||
MultichainRouterMessenger, | ||
MultichainRouterInitMessenger | ||
> = ({ controllerMessenger, initMessenger }) => { | ||
const controller = new MultichainRouter({ | ||
messenger: controllerMessenger, | ||
// Binding the call to provide the selector only giving the controller the option to pass the operation | ||
withSnapKeyring: (...args) => | ||
// @ts-expect-error mistmatch with the withSnapKeyring signature and withKeyring. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there an issue tracking this mismatch we can link? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i don't think we need an issue tracking this specifically. Frederik is aware that this hook needs to be called from the messenger inside the MultichainRouter implementation which will allow us to remove it here and resolves the type issue |
||
initMessenger.call( | ||
'KeyringController:withKeyring', | ||
{ | ||
type: 'Snap Keyring', | ||
}, | ||
// @ts-expect-error mistmatch with the withSnapKeyring signature and withKeyring. | ||
...args, | ||
), | ||
}); | ||
|
||
return { | ||
controller, | ||
persistedStateKey: null, | ||
memStateKey: null, | ||
}; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We will need to rethink this as we had to make a change to this function to unblock the release.