From d920440ed68a5c02ccb9e2a79df049527d9d548b Mon Sep 17 00:00:00 2001 From: Frederik Bolding Date: Mon, 20 Jan 2025 12:28:35 +0100 Subject: [PATCH] Add isSupportedScope --- packages/snaps-controllers/coverage.json | 4 +- .../src/multichain/MultichainRouter.test.ts | 67 +++++++++++++------ .../src/multichain/MultichainRouter.ts | 36 ++++++++-- 3 files changed, 78 insertions(+), 29 deletions(-) diff --git a/packages/snaps-controllers/coverage.json b/packages/snaps-controllers/coverage.json index 6b306ebe66..119f05ade9 100644 --- a/packages/snaps-controllers/coverage.json +++ b/packages/snaps-controllers/coverage.json @@ -1,6 +1,6 @@ { "branches": 93.16, - "functions": 96.73, - "lines": 98.11, + "functions": 96.76, + "lines": 98.12, "statements": 97.84 } diff --git a/packages/snaps-controllers/src/multichain/MultichainRouter.test.ts b/packages/snaps-controllers/src/multichain/MultichainRouter.test.ts index 49b1117336..6c7d247ced 100644 --- a/packages/snaps-controllers/src/multichain/MultichainRouter.test.ts +++ b/packages/snaps-controllers/src/multichain/MultichainRouter.test.ts @@ -297,9 +297,7 @@ describe('MultichainRouter', () => { ); expect( - messenger.call('MultichainRouter:getSupportedMethods', { - scope: SOLANA_CAIP2, - }), + messenger.call('MultichainRouter:getSupportedMethods', SOLANA_CAIP2), ).toStrictEqual(['signAndSendTransaction', 'getVersion']); }); @@ -329,9 +327,7 @@ describe('MultichainRouter', () => { ); expect( - messenger.call('MultichainRouter:getSupportedMethods', { - scope: SOLANA_CAIP2, - }), + messenger.call('MultichainRouter:getSupportedMethods', SOLANA_CAIP2), ).toStrictEqual(['signAndSendTransaction']); }); @@ -361,15 +357,13 @@ describe('MultichainRouter', () => { ); expect( - messenger.call('MultichainRouter:getSupportedMethods', { - scope: SOLANA_CAIP2, - }), + messenger.call('MultichainRouter:getSupportedMethods', SOLANA_CAIP2), ).toStrictEqual(['getVersion']); }); }); describe('getSupportedAccounts', () => { - it('returns a set of both protocol and account Snap methods', async () => { + it('returns a set of accounts for the requested scope', async () => { const rootMessenger = getRootMultichainRouterMessenger(); const messenger = getRestrictedMultichainRouterMessenger(rootMessenger); const withSnapKeyring = getMockWithSnapKeyring(); @@ -380,8 +374,29 @@ describe('MultichainRouter', () => { withSnapKeyring, }); - rootMessenger.registerActionHandler('SnapController:getAll', () => { - return [getTruncatedSnap()]; + rootMessenger.registerActionHandler( + 'AccountsController:listMultichainAccounts', + () => MOCK_SOLANA_ACCOUNTS, + ); + + expect( + messenger.call('MultichainRouter:getSupportedAccounts', SOLANA_CAIP2), + ).toStrictEqual([ + 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:7S3P4HxJpyyigGzodYwHtCxZyUQe9JiBMHyRWXArAaKv', + ]); + }); + }); + + describe('isSupportedScope', () => { + it('returns true if an account Snap exists', async () => { + const rootMessenger = getRootMultichainRouterMessenger(); + const messenger = getRestrictedMultichainRouterMessenger(rootMessenger); + const withSnapKeyring = getMockWithSnapKeyring(); + + /* eslint-disable-next-line no-new */ + new MultichainRouter({ + messenger, + withSnapKeyring, }); rootMessenger.registerActionHandler( @@ -389,18 +404,30 @@ describe('MultichainRouter', () => { () => MOCK_SOLANA_ACCOUNTS, ); + expect( + messenger.call('MultichainRouter:isSupportedScope', SOLANA_CAIP2), + ).toBe(true); + }); + + it('returns false if no account Snap is found', async () => { + const rootMessenger = getRootMultichainRouterMessenger(); + const messenger = getRestrictedMultichainRouterMessenger(rootMessenger); + const withSnapKeyring = getMockWithSnapKeyring(); + + /* eslint-disable-next-line no-new */ + new MultichainRouter({ + messenger, + withSnapKeyring, + }); + rootMessenger.registerActionHandler( - 'PermissionController:getPermissions', - () => MOCK_SOLANA_SNAP_PERMISSIONS, + 'AccountsController:listMultichainAccounts', + () => [], ); expect( - messenger.call('MultichainRouter:getSupportedAccounts', { - scope: SOLANA_CAIP2, - }), - ).toStrictEqual([ - 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:7S3P4HxJpyyigGzodYwHtCxZyUQe9JiBMHyRWXArAaKv', - ]); + messenger.call('MultichainRouter:isSupportedScope', SOLANA_CAIP2), + ).toBe(false); }); }); }); diff --git a/packages/snaps-controllers/src/multichain/MultichainRouter.ts b/packages/snaps-controllers/src/multichain/MultichainRouter.ts index bc1b66ca44..aa675b6536 100644 --- a/packages/snaps-controllers/src/multichain/MultichainRouter.ts +++ b/packages/snaps-controllers/src/multichain/MultichainRouter.ts @@ -33,6 +33,11 @@ export type MultichainRouterGetSupportedAccountsAction = { handler: MultichainRouter['getSupportedAccounts']; }; +export type MultichainRouterIsSupportedScopeAction = { + type: `${typeof name}:isSupportedScope`; + handler: MultichainRouter['isSupportedScope']; +}; + // Since the AccountsController depends on snaps-controllers we manually type this type InternalAccount = { id: string; @@ -68,7 +73,8 @@ export type AccountsControllerListMultichainAccountsAction = { export type MultichainRouterActions = | MultichainRouterHandleRequestAction | MultichainRouterGetSupportedMethodsAction - | MultichainRouterGetSupportedAccountsAction; + | MultichainRouterGetSupportedAccountsAction + | MultichainRouterIsSupportedScopeAction; export type MultichainRouterAllowedActions = | GetAllSnaps @@ -121,6 +127,11 @@ export class MultichainRouter { `${name}:getSupportedAccounts`, (...args) => this.getSupportedAccounts(...args), ); + + this.#messenger.registerActionHandler( + `${name}:isSupportedScope`, + (...args) => this.isSupportedScope(...args), + ); } async #resolveRequestAddress( @@ -294,11 +305,10 @@ export class MultichainRouter { * Get a list of supported methods for a given scope. * This combines both protocol and account Snaps supported methods. * - * @param options - An options bag. - * @param options.scope - The CAIP-2 scope. + * @param scope - The CAIP-2 scope. * @returns A list of supported methods. */ - getSupportedMethods({ scope }: { scope: CaipChainId }): string[] { + getSupportedMethods(scope: CaipChainId): string[] { const accountMethods = this.#messenger .call('AccountsController:listMultichainAccounts', scope) .filter((account: InternalAccount) => account.metadata.snap?.enabled) @@ -314,14 +324,26 @@ export class MultichainRouter { /** * Get a list of supported accounts for a given scope. * - * @param options - An options bag. - * @param options.scope - The CAIP-2 scope. + * @param scope - The CAIP-2 scope. * @returns A list of CAIP-10 addresses. */ - getSupportedAccounts({ scope }: { scope: CaipChainId }): string[] { + getSupportedAccounts(scope: CaipChainId): string[] { return this.#messenger .call('AccountsController:listMultichainAccounts', scope) .filter((account: InternalAccount) => account.metadata.snap?.enabled) .map((account) => `${scope}:${account.address}`); } + + /** + * Determine whether a given CAIP-2 scope is supported by the router. + * + * @param scope - The CAIP-2 scope. + * @returns True if the router can service the scope, otherwise false. + */ + isSupportedScope(scope: CaipChainId): boolean { + // We currently assume here that if one Snap exists that service the scope, we can service the scope generally. + return this.#messenger + .call('AccountsController:listMultichainAccounts', scope) + .some((account: InternalAccount) => account.metadata.snap?.enabled); + } }