diff --git a/app/scripts/controllers/permissions/specifications.js b/app/scripts/controllers/permissions/specifications.js index f4a1e3320172..5794d03a70eb 100644 --- a/app/scripts/controllers/permissions/specifications.js +++ b/app/scripts/controllers/permissions/specifications.js @@ -50,11 +50,13 @@ export const CaveatFactories = Object.freeze({ export const getCaveatSpecifications = ({ listAccounts, findNetworkClientIdByChainId, + isNonEvmScopeSupported }) => { return { [Caip25CaveatType]: caip25CaveatBuilder({ listAccounts, findNetworkClientIdByChainId, + isNonEvmScopeSupported, }), ...snapsCaveatsSpecifications, ...snapsEndowmentCaveatSpecifications, diff --git a/app/scripts/lib/rpc-method-middleware/handlers/wallet-createSession/handler.ts b/app/scripts/lib/rpc-method-middleware/handlers/wallet-createSession/handler.ts index 8c43d96f3cbb..0307f201e87f 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/wallet-createSession/handler.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/wallet-createSession/handler.ts @@ -24,6 +24,7 @@ import { ValidPermission, } from '@metamask/permission-controller'; import { + CaipChainId, Hex, isPlainObject, Json, @@ -103,6 +104,8 @@ async function walletCreateSessionHandler( grantPermissions: ( ...args: Parameters ) => Record>>; + getNonEvmSupportedMethods: (scope: CaipChainId) => string[]; + isNonEvmScopeSupported: (scope: CaipChainId) => boolean; }, ) { const { origin } = req; @@ -121,9 +124,11 @@ async function walletCreateSessionHandler( const supportedRequiredScopesObjects = getSupportedScopeObjects( normalizedRequiredScopes, + { getNonEvmSupportedMethods: hooks.getNonEvmSupportedMethods }, ); const supportedOptionalScopesObjects = getSupportedScopeObjects( normalizedOptionalScopes, + { getNonEvmSupportedMethods: hooks.getNonEvmSupportedMethods }, ); const existsNetworkClientForChainId = (chainId: Hex) => { @@ -138,16 +143,20 @@ async function walletCreateSessionHandler( const { supportedScopes: supportedRequiredScopes } = bucketScopes( supportedRequiredScopesObjects, { - isChainIdSupported: existsNetworkClientForChainId, - isChainIdSupportable: () => false, // intended for future usage with eip3085 scopedProperties + isEvmChainIdSupported: existsNetworkClientForChainId, + isEvmChainIdSupportable: () => false, // intended for future usage with eip3085 scopedProperties + getNonEvmSupportedMethods: hooks.getNonEvmSupportedMethods, + isNonEvmScopeSupported: hooks.isNonEvmScopeSupported, }, ); const { supportedScopes: supportedOptionalScopes } = bucketScopes( supportedOptionalScopesObjects, { - isChainIdSupported: existsNetworkClientForChainId, - isChainIdSupportable: () => false, // intended for future usage with eip3085 scopedProperties + isEvmChainIdSupported: existsNetworkClientForChainId, + isEvmChainIdSupportable: () => false, // intended for future usage with eip3085 scopedProperties + getNonEvmSupportedMethods: hooks.getNonEvmSupportedMethods, + isNonEvmScopeSupported: hooks.isNonEvmScopeSupported, }, ); @@ -203,7 +212,7 @@ async function walletCreateSessionHandler( legacyApproval.approvedAccounts, ); - const sessionScopes = getSessionScopes(caip25CaveatValue); + const sessionScopes = getSessionScopes(caip25CaveatValue, { getNonEvmSupportedMethods: hooks.getNonEvmSupportedMethods}); hooks.grantPermissions({ subject: { @@ -264,5 +273,7 @@ export const walletCreateSession = { grantPermissions: true, sendMetrics: true, metamaskState: true, + getNonEvmSupportedMethods: true, + isNonEvmScopeSupported: true, }, }; diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index cd439121e3dc..b162f4cef9a1 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -91,6 +91,7 @@ import { SnapInterfaceController, SnapInsightsController, OffscreenExecutionService, + MultichainRouter, } from '@metamask/snaps-controllers'; import { createSnapsMethodMiddleware, @@ -1353,6 +1354,10 @@ export default class MetamaskController extends EventEmitter { this.networkController.findNetworkClientIdByChainId.bind( this.networkController, ), + isNonEvmScopeSupported: this.controllerMessenger.call.bind( + this.controllerMessenger, + 'MultichainRouter:isSupportedScope', + ), }), permissionSpecifications: { ...getPermissionSpecifications(), @@ -1637,6 +1642,25 @@ export default class MetamaskController extends EventEmitter { messenger: snapInsightsControllerMessenger, }); + const multichainRouterMessenger = this.controllerMessenger.getRestricted({ + name: 'MultichainRouter', + allowedActions: [ + `${this.snapController.name}:getAll`, + `${this.snapController.name}:handleRequest`, + `${this.permissionController.name}:getPermissions`, + `AccountsController:listMultichainAccounts`, + ], + allowedEvents: [], + }); + + this.multichainRouter = new MultichainRouter({ + messenger: multichainRouterMessenger, + // Binding the call to provide the selector only giving the controller the option to pass the operation + withSnapKeyring: this.keyringController.withKeyring.bind(this.keyringController, { + type: 'snap', + }), + }); + // Notification Controllers this.authenticationController = new AuthenticationController.Controller({ state: initState.AuthenticationController, @@ -3178,7 +3202,7 @@ export default class MetamaskController extends EventEmitter { // remove any existing notification subscriptions for removed authorizations for (const [origin, authorization] of removedAuthorizations.entries()) { - const sessionScopes = getSessionScopes(authorization); + const sessionScopes = getSessionScopes(authorization, {getNonEvmSupportedMethods: this.getNonEvmSupportedMethods.bind(this)}); // if the eth_subscription notification is in the scope and eth_subscribe is in the methods // then remove middleware and unsubscribe Object.entries(sessionScopes).forEach(([scope, scopeObject]) => { @@ -3200,7 +3224,7 @@ export default class MetamaskController extends EventEmitter { // add new notification subscriptions for changed authorizations for (const [origin, authorization] of changedAuthorizations.entries()) { - const sessionScopes = getSessionScopes(authorization); + const sessionScopes = getSessionScopes(authorization, {getNonEvmSupportedMethods: this.getNonEvmSupportedMethods.bind(this)}); // if the eth_subscription notification is in the scope and eth_subscribe is in the methods // then get the subscriptionManager going for that scope @@ -3241,6 +3265,7 @@ export default class MetamaskController extends EventEmitter { if (previousAuthorization) { const previousSessionScopes = getSessionScopes( previousAuthorization, + {getNonEvmSupportedMethods: this.getNonEvmSupportedMethods.bind(this)} ); Object.entries(previousSessionScopes).forEach( @@ -5773,6 +5798,13 @@ export default class MetamaskController extends EventEmitter { }; } + getNonEvmSupportedMethods(scope) { + return this.controllerMessenger.call( + 'MultichainRouter:getSupportedMethods', + scope + ) + } + // --------------------------------------------------------------------------- // Identity Management (signature operations) @@ -6900,6 +6932,11 @@ export default class MetamaskController extends EventEmitter { this.permissionController, origin, ), + getNonEvmSupportedMethods: this.getNonEvmSupportedMethods.bind(this), + isNonEvmScopeSupported: this.controllerMessenger.call.bind( + this.controllerMessenger, + 'MultichainRouter:isSupportedScope', + ), }), ); @@ -7038,7 +7075,7 @@ export default class MetamaskController extends EventEmitter { ); // add new notification subscriptions for changed authorizations - const sessionScopes = getSessionScopes(caip25Caveat.value); + const sessionScopes = getSessionScopes(caip25Caveat.value, {getNonEvmSupportedMethods: this.getNonEvmSupportedMethods.bind(this)}); // if the eth_subscription notification is in the scope and eth_subscribe is in the methods // then get the subscriptionManager going for that scope @@ -7934,7 +7971,7 @@ export default class MetamaskController extends EventEmitter { { method: NOTIFICATION_NAMES.sessionChanged, params: { - sessionScopes: getSessionScopes(newAuthorization), + sessionScopes: getSessionScopes(newAuthorization, {getNonEvmSupportedMethods: this.getNonEvmSupportedMethods.bind(this)}), }, }, API_TYPE.CAIP_MULTICHAIN, diff --git a/package.json b/package.json index 4fb982f47384..5e24485a6bb6 100644 --- a/package.json +++ b/package.json @@ -322,7 +322,7 @@ "@metamask/message-manager": "^11.0.0", "@metamask/message-signing-snap": "^0.6.0", "@metamask/metamask-eth-abis": "^3.1.1", - "@metamask/multichain": "^2.1.0", + "@metamask/multichain": "npm:@metamask-previews/multichain@2.1.0-preview-abb67026", "@metamask/multichain-transactions-controller": "^0.0.1", "@metamask/name-controller": "^8.0.0", "@metamask/network-controller": "patch:@metamask/network-controller@npm%3A22.1.1#~/.yarn/patches/@metamask-network-controller-npm-22.1.1-09b6510f1e.patch", diff --git a/yarn.lock b/yarn.lock index 494bd5ba3383..7332f07da117 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5865,9 +5865,9 @@ __metadata: languageName: node linkType: hard -"@metamask/multichain@npm:^2.1.0": - version: 2.1.0 - resolution: "@metamask/multichain@npm:2.1.0" +"@metamask/multichain@npm:@metamask-previews/multichain@2.1.0-preview-abb67026": + version: 2.1.0-preview-abb67026 + resolution: "@metamask-previews/multichain@npm:2.1.0-preview-abb67026" dependencies: "@metamask/api-specs": "npm:^0.10.12" "@metamask/controller-utils": "npm:^11.4.5" @@ -5881,7 +5881,7 @@ __metadata: peerDependencies: "@metamask/network-controller": ^22.0.0 "@metamask/permission-controller": ^11.0.0 - checksum: 10/762231b95fa89e25e8a06ff50161301cecc1eaa8095b8eef4b1d938e43307c98549767600b4a37482bb846026f46c66791cd99e385991704b5e9624aa6032332 + checksum: 10/7f138dc6d564300dc1c583d9a57f5ad5aa8c43b310852ac48761a6ef80716588400dd19a5a72eb6ae7f4008d4e14118335f34bfbc9dc6d5bc63a74c6261f1d0d languageName: node linkType: hard @@ -26916,7 +26916,7 @@ __metadata: "@metamask/message-manager": "npm:^11.0.0" "@metamask/message-signing-snap": "npm:^0.6.0" "@metamask/metamask-eth-abis": "npm:^3.1.1" - "@metamask/multichain": "npm:^2.1.0" + "@metamask/multichain": "npm:@metamask-previews/multichain@2.1.0-preview-abb67026" "@metamask/multichain-transactions-controller": "npm:^0.0.1" "@metamask/name-controller": "npm:^8.0.0" "@metamask/network-controller": "patch:@metamask/network-controller@npm%3A22.1.1#~/.yarn/patches/@metamask-network-controller-npm-22.1.1-09b6510f1e.patch"