@metamask/snaps-controllers
Advanced tools
@@ -77,6 +77,3 @@ "use strict"; | ||
| async #getSnapAccountId(connectedAddresses, scope, request) { | ||
| const accounts = this.#messenger | ||
| .call('AccountsController:listMultichainAccounts', scope) | ||
| .filter((account) => Boolean(account.metadata.snap?.enabled) && | ||
| account.methods.includes(request.method)); | ||
| const accounts = this.#getSupportedAccountsMetadata(scope, request.method); | ||
| // If no accounts can service the request, return null. | ||
@@ -199,8 +196,14 @@ if (accounts.length === 0) { | ||
| * @param scope - The CAIP-2 scope. | ||
| * @param method - An optional method that the account should support. | ||
| * @returns A list of metadata for the supported accounts. | ||
| */ | ||
| #getSupportedAccountsMetadata(scope) { | ||
| #getSupportedAccountsMetadata(scope, method) { | ||
| const runnableSnaps = this.#messenger | ||
| .call('SnapController:getRunnableSnaps') | ||
| .map((snap) => snap.id); | ||
| return this.#messenger | ||
| .call('AccountsController:listMultichainAccounts', scope) | ||
| .filter((account) => account.metadata.snap?.enabled); | ||
| .filter((account) => account.metadata.snap?.id !== undefined && | ||
| runnableSnaps.includes(account.metadata.snap?.id) && | ||
| (method === undefined || account.methods.includes(method))); | ||
| } | ||
@@ -235,7 +238,5 @@ /** | ||
| isSupportedScope(scope) { | ||
| const hasAccountSnap = this.#messenger | ||
| .call('AccountsController:listMultichainAccounts', scope) | ||
| .some((account) => account.metadata.snap?.enabled); | ||
| // We currently assume here that if one Snap exists that service the scope, we can service the scope generally. | ||
| return hasAccountSnap || this.#getProtocolSnaps(scope).length > 0; | ||
| return (this.#getSupportedAccountsMetadata(scope).length > 0 || | ||
| this.#getProtocolSnaps(scope).length > 0); | ||
| } | ||
@@ -242,0 +243,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"MultichainRoutingService.cjs","sourceRoot":"","sources":["../../src/multichain/MultichainRoutingService.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Messenger } from '@metamask/messenger';\nimport type { PermissionControllerGetPermissionsAction } from '@metamask/permission-controller';\nimport { rpcErrors } from '@metamask/rpc-errors';\nimport {\n getProtocolCaveatScopes,\n SnapEndowments,\n} from '@metamask/snaps-rpc-methods';\nimport type { Json, JsonRpcRequest, SnapId } from '@metamask/snaps-sdk';\nimport type { InternalAccount } from '@metamask/snaps-utils';\nimport { HandlerType } from '@metamask/snaps-utils';\nimport type {\n CaipAccountId,\n CaipChainId,\n JsonRpcParams,\n} from '@metamask/utils';\nimport {\n assert,\n hasProperty,\n isObject,\n KnownCaipNamespace,\n parseCaipAccountId,\n} from '@metamask/utils';\nimport { nanoid } from 'nanoid';\n\nimport type { MultichainRoutingServiceMethodActions } from './MultichainRoutingService-method-action-types';\nimport type {\n SnapControllerGetRunnableSnapsAction,\n SnapControllerHandleRequestAction,\n} from '../snaps';\n\ntype SnapKeyring = {\n submitRequest: (request: {\n origin: string;\n account: string;\n method: string;\n params?: Json[] | Record<string, Json>;\n scope: CaipChainId;\n }) => Promise<Json>;\n};\n\n// Expecting a bound function that calls KeyringController.withKeyring selecting the Snap keyring\nexport type WithSnapKeyringFunction = <ReturnType>(\n operation: ({ keyring }: { keyring: SnapKeyring }) => Promise<ReturnType>,\n) => Promise<ReturnType>;\n\nexport type AccountsControllerListMultichainAccountsAction = {\n type: `AccountsController:listMultichainAccounts`;\n handler: (chainId?: CaipChainId) => InternalAccount[];\n};\n\nexport type MultichainRoutingServiceActions =\n MultichainRoutingServiceMethodActions;\n\ntype AllowedActions =\n | SnapControllerGetRunnableSnapsAction\n | SnapControllerHandleRequestAction\n | PermissionControllerGetPermissionsAction\n | AccountsControllerListMultichainAccountsAction;\n\nexport type MultichainRoutingServiceEvents = never;\n\nexport type MultichainRoutingServiceMessenger = Messenger<\n typeof name,\n MultichainRoutingServiceActions | AllowedActions\n>;\n\nexport type MultichainRoutingServiceArgs = {\n messenger: MultichainRoutingServiceMessenger;\n withSnapKeyring: WithSnapKeyringFunction;\n};\n\ntype ProtocolSnap = {\n snapId: SnapId;\n methods: string[];\n};\n\nconst name = 'MultichainRoutingService';\n\nconst MESSENGER_EXPOSED_METHODS = [\n 'handleRequest',\n 'getSupportedMethods',\n 'getSupportedAccounts',\n 'isSupportedScope',\n] as const;\n\nexport class MultichainRoutingService {\n name: typeof name = name;\n\n state = null;\n\n readonly #messenger: MultichainRoutingServiceMessenger;\n\n readonly #withSnapKeyring: WithSnapKeyringFunction;\n\n constructor({ messenger, withSnapKeyring }: MultichainRoutingServiceArgs) {\n this.#messenger = messenger;\n this.#withSnapKeyring = withSnapKeyring;\n\n this.#messenger.registerMethodActionHandlers(\n this,\n MESSENGER_EXPOSED_METHODS,\n );\n }\n\n /**\n * Attempts to resolve the account address to use for a given request by inspecting the request itself.\n *\n * The request is sent to an account Snap that will attempt this resolution.\n *\n * We manually construct the request instead of using the SnapKeyring, as the keyring may not be available.\n *\n * @param snapId - The ID of the Snap to send the request to.\n * @param scope - The CAIP-2 scope for the request.\n * @param request - The JSON-RPC request.\n * @returns The resolved address if found, otherwise null.\n * @throws If the invocation of the SnapKeyring fails.\n */\n async #resolveRequestAddress(\n snapId: SnapId,\n scope: CaipChainId,\n request: JsonRpcRequest,\n ) {\n try {\n const result = await this.#messenger.call(\n 'SnapController:handleRequest',\n {\n snapId,\n origin: 'metamask',\n request: {\n method: 'keyring_resolveAccountAddress',\n params: {\n request,\n scope,\n },\n },\n handler: HandlerType.OnKeyringRequest,\n },\n );\n\n assert(result === null || isObject(result));\n\n const address = result?.address as CaipAccountId;\n return address ? parseCaipAccountId(address).address : null;\n } catch {\n throw rpcErrors.internal();\n }\n }\n\n /**\n * Get the account ID of the account that should service the RPC request via an account Snap.\n *\n * This function checks whether any accounts exist that can service a given request by\n * using a combination of the resolveAccountAddress functionality and the connected accounts.\n *\n * If an account is expected to service this request but none is found, the function will throw.\n *\n * @param connectedAddresses - The CAIP-10 addresses connected to the\n * requesting origin for the requested scope.\n * @param scope - The CAIP-2 scope for the request.\n * @param request - The JSON-RPC request.\n * @returns An account ID if found, otherwise null.\n * @throws If no account is found, but the accounts exist that could service the request.\n */\n async #getSnapAccountId(\n connectedAddresses: CaipAccountId[],\n scope: CaipChainId,\n request: JsonRpcRequest,\n ) {\n const accounts = this.#messenger\n .call('AccountsController:listMultichainAccounts', scope)\n .filter(\n (\n account: InternalAccount,\n ): account is InternalAccount & {\n metadata: Required<InternalAccount['metadata']>;\n } =>\n Boolean(account.metadata.snap?.enabled) &&\n account.methods.includes(request.method),\n );\n\n // If no accounts can service the request, return null.\n if (accounts.length === 0) {\n return null;\n }\n\n const parsedConnectedAddresses = connectedAddresses.map(\n (connectedAddress) => parseCaipAccountId(connectedAddress).address,\n );\n\n const connectedAccounts = accounts.filter((account) =>\n parsedConnectedAddresses.includes(account.address),\n );\n\n if (connectedAccounts.length === 0) {\n throw rpcErrors.invalidParams({\n message: 'No available account found for request.',\n });\n }\n\n const resolutionSnapId = connectedAccounts[0].metadata.snap.id;\n\n // Attempt to resolve the address that should be used for signing.\n const address = await this.#resolveRequestAddress(\n resolutionSnapId,\n scope,\n request,\n );\n\n // If we have a resolved address, try to find the selected account based on that\n // otherwise, default to one of the connected accounts.\n // TODO: Eventually let the user choose if we have more than one option for the account.\n const selectedAccount = address\n ? connectedAccounts.find((account) => account.address === address)\n : connectedAccounts[0];\n\n if (!selectedAccount) {\n throw rpcErrors.invalidParams({\n message: 'No available account found for request.',\n });\n }\n\n return selectedAccount.id;\n }\n\n /**\n * Get all protocol Snaps that can service a given CAIP-2 scope.\n *\n * Protocol Snaps are deemed fit to service a scope if they are runnable\n * and have the proper permissions set for the scope.\n *\n * @param scope - A CAIP-2 scope.\n * @returns A list of all the protocol Snaps available and their RPC methods.\n */\n #getProtocolSnaps(scope: CaipChainId) {\n const filteredSnaps = this.#messenger.call(\n 'SnapController:getRunnableSnaps',\n );\n\n return filteredSnaps.reduce<ProtocolSnap[]>((accumulator, snap) => {\n const permissions = this.#messenger.call(\n 'PermissionController:getPermissions',\n snap.id,\n );\n\n if (permissions && hasProperty(permissions, SnapEndowments.Protocol)) {\n const permission = permissions[SnapEndowments.Protocol];\n const scopes = getProtocolCaveatScopes(permission);\n if (scopes && hasProperty(scopes, scope)) {\n accumulator.push({\n snapId: snap.id,\n methods: scopes[scope].methods,\n });\n }\n }\n\n return accumulator;\n }, []);\n }\n\n /**\n * Handle an incoming JSON-RPC request tied to a specific scope by routing\n * to either a protocol Snap or an account Snap.\n *\n * Note: Addresses are considered case-sensitive by the MultichainRoutingService as\n * not all non-EVM chains are case-insensitive.\n *\n * @param options - An options bag.\n * @param options.connectedAddresses - Addresses currently connected to the\n * origin for the requested scope.\n * @param options.origin - The origin of the RPC request.\n * @param options.request - The JSON-RPC request.\n * @param options.scope - The CAIP-2 scope for the request.\n * @returns The response from the chosen Snap.\n * @throws If no handler was found.\n */\n async handleRequest({\n connectedAddresses,\n origin,\n scope,\n request: rawRequest,\n }: {\n connectedAddresses: CaipAccountId[];\n origin: string;\n scope: CaipChainId;\n request: JsonRpcRequest;\n }): Promise<Json> {\n // Explicitly block EVM scopes, just in case.\n assert(\n !scope.startsWith(KnownCaipNamespace.Eip155) &&\n !scope.startsWith('wallet:eip155'),\n );\n\n // Re-create the request to simplify and remove additional properties that may be present in MM middleware.\n const request = {\n jsonrpc: '2.0' as const,\n id: rawRequest.id ?? nanoid(),\n method: rawRequest.method,\n ...(rawRequest.params ? { params: rawRequest.params } : {}),\n };\n\n const { method, params } = request;\n\n // If the RPC request can be serviced by an account Snap, route it there.\n const accountId = await this.#getSnapAccountId(\n connectedAddresses,\n scope,\n request,\n );\n\n if (accountId) {\n return this.#withSnapKeyring(async ({ keyring }) =>\n keyring.submitRequest({\n origin,\n account: accountId,\n scope,\n method,\n params: params as JsonRpcParams,\n }),\n );\n }\n\n // If the RPC request cannot be serviced by an account Snap,\n // but has a protocol Snap available, route it there.\n const protocolSnaps = this.#getProtocolSnaps(scope);\n const protocolSnap = protocolSnaps.find((snap) =>\n snap.methods.includes(method),\n );\n\n if (protocolSnap) {\n return this.#messenger.call('SnapController:handleRequest', {\n snapId: protocolSnap.snapId,\n origin,\n request: {\n method: '',\n params: {\n request,\n scope,\n },\n },\n handler: HandlerType.OnProtocolRequest,\n }) as Promise<Json>;\n }\n\n // If no compatible account or protocol Snaps were found, throw.\n throw rpcErrors.methodNotFound();\n }\n\n /**\n * Get a list of metadata for supported accounts for a given scope from the client.\n *\n * @param scope - The CAIP-2 scope.\n * @returns A list of metadata for the supported accounts.\n */\n #getSupportedAccountsMetadata(scope: CaipChainId): InternalAccount[] {\n return this.#messenger\n .call('AccountsController:listMultichainAccounts', scope)\n .filter((account: InternalAccount) => account.metadata.snap?.enabled);\n }\n\n /**\n * Get a list of supported methods for a given scope.\n * This combines both protocol and account Snaps supported methods.\n *\n * @param scope - The CAIP-2 scope.\n * @returns A list of supported methods.\n */\n getSupportedMethods(scope: CaipChainId): string[] {\n const accountMethods = this.#getSupportedAccountsMetadata(scope).flatMap(\n (account) => account.methods,\n );\n\n const protocolMethods = this.#getProtocolSnaps(scope).flatMap(\n (snap) => snap.methods,\n );\n\n return Array.from(new Set([...accountMethods, ...protocolMethods]));\n }\n\n /**\n * Get a list of supported accounts for a given scope.\n *\n * @param scope - The CAIP-2 scope.\n * @returns A list of CAIP-10 addresses.\n */\n getSupportedAccounts(scope: CaipChainId): string[] {\n return this.#getSupportedAccountsMetadata(scope).map(\n (account) => `${scope}:${account.address}`,\n );\n }\n\n /**\n * Determine whether a given CAIP-2 scope is supported by the router.\n *\n * @param scope - The CAIP-2 scope.\n * @returns True if the router can service the scope, otherwise false.\n */\n isSupportedScope(scope: CaipChainId): boolean {\n const hasAccountSnap = this.#messenger\n .call('AccountsController:listMultichainAccounts', scope)\n .some((account: InternalAccount) => account.metadata.snap?.enabled);\n // We currently assume here that if one Snap exists that service the scope, we can service the scope generally.\n return hasAccountSnap || this.#getProtocolSnaps(scope).length > 0;\n }\n}\n"]} | ||
| {"version":3,"file":"MultichainRoutingService.cjs","sourceRoot":"","sources":["../../src/multichain/MultichainRoutingService.ts"],"names":[],"mappings":";;;AAEA,qDAAiD;AACjD,mEAGqC;AAGrC,uDAAoD;AAMpD,2CAMyB;AACzB,mCAAgC;AAsDhC,MAAM,IAAI,GAAG,0BAA0B,CAAC;AAExC,MAAM,yBAAyB,GAAG;IAChC,eAAe;IACf,qBAAqB;IACrB,sBAAsB;IACtB,kBAAkB;CACV,CAAC;AAEX,MAAa,wBAAwB;IACnC,IAAI,GAAgB,IAAI,CAAC;IAEzB,KAAK,GAAG,IAAI,CAAC;IAEJ,UAAU,CAAoC;IAE9C,gBAAgB,CAA0B;IAEnD,YAAY,EAAE,SAAS,EAAE,eAAe,EAAgC;QACtE,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;QAC5B,IAAI,CAAC,gBAAgB,GAAG,eAAe,CAAC;QAExC,IAAI,CAAC,UAAU,CAAC,4BAA4B,CAC1C,IAAI,EACJ,yBAAyB,CAC1B,CAAC;IACJ,CAAC;IAED;;;;;;;;;;;;OAYG;IACH,KAAK,CAAC,sBAAsB,CAC1B,MAAc,EACd,KAAkB,EAClB,OAAuB;QAEvB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,IAAI,CACvC,8BAA8B,EAC9B;gBACE,MAAM;gBACN,MAAM,EAAE,UAAU;gBAClB,OAAO,EAAE;oBACP,MAAM,EAAE,+BAA+B;oBACvC,MAAM,EAAE;wBACN,OAAO;wBACP,KAAK;qBACN;iBACF;gBACD,OAAO,EAAE,yBAAW,CAAC,gBAAgB;aACtC,CACF,CAAC;YAEF,IAAA,cAAM,EAAC,MAAM,KAAK,IAAI,IAAI,IAAA,gBAAQ,EAAC,MAAM,CAAC,CAAC,CAAC;YAE5C,MAAM,OAAO,GAAG,MAAM,EAAE,OAAwB,CAAC;YACjD,OAAO,OAAO,CAAC,CAAC,CAAC,IAAA,0BAAkB,EAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;QAC9D,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,sBAAS,CAAC,QAAQ,EAAE,CAAC;QAC7B,CAAC;IACH,CAAC;IAED;;;;;;;;;;;;;;OAcG;IACH,KAAK,CAAC,iBAAiB,CACrB,kBAAmC,EACnC,KAAkB,EAClB,OAAuB;QAEvB,MAAM,QAAQ,GAAG,IAAI,CAAC,6BAA6B,CAAC,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;QAE3E,uDAAuD;QACvD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC1B,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,wBAAwB,GAAG,kBAAkB,CAAC,GAAG,CACrD,CAAC,gBAAgB,EAAE,EAAE,CAAC,IAAA,0BAAkB,EAAC,gBAAgB,CAAC,CAAC,OAAO,CACnE,CAAC;QAEF,MAAM,iBAAiB,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CACpD,wBAAwB,CAAC,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CACnD,CAAC;QAEF,IAAI,iBAAiB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACnC,MAAM,sBAAS,CAAC,aAAa,CAAC;gBAC5B,OAAO,EAAE,yCAAyC;aACnD,CAAC,CAAC;QACL,CAAC;QAED,MAAM,gBAAgB,GAAG,iBAAiB,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QAE/D,kEAAkE;QAClE,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,sBAAsB,CAC/C,gBAAgB,EAChB,KAAK,EACL,OAAO,CACR,CAAC;QAEF,gFAAgF;QAChF,uDAAuD;QACvD,wFAAwF;QACxF,MAAM,eAAe,GAAG,OAAO;YAC7B,CAAC,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,KAAK,OAAO,CAAC;YAClE,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC;QAEzB,IAAI,CAAC,eAAe,EAAE,CAAC;YACrB,MAAM,sBAAS,CAAC,aAAa,CAAC;gBAC5B,OAAO,EAAE,yCAAyC;aACnD,CAAC,CAAC;QACL,CAAC;QAED,OAAO,eAAe,CAAC,EAAE,CAAC;IAC5B,CAAC;IAED;;;;;;;;OAQG;IACH,iBAAiB,CAAC,KAAkB;QAClC,MAAM,aAAa,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CACxC,iCAAiC,CAClC,CAAC;QAEF,OAAO,aAAa,CAAC,MAAM,CAAiB,CAAC,WAAW,EAAE,IAAI,EAAE,EAAE;YAChE,MAAM,WAAW,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CACtC,qCAAqC,EACrC,IAAI,CAAC,EAAE,CACR,CAAC;YAEF,IAAI,WAAW,IAAI,IAAA,mBAAW,EAAC,WAAW,EAAE,kCAAc,CAAC,QAAQ,CAAC,EAAE,CAAC;gBACrE,MAAM,UAAU,GAAG,WAAW,CAAC,kCAAc,CAAC,QAAQ,CAAC,CAAC;gBACxD,MAAM,MAAM,GAAG,IAAA,2CAAuB,EAAC,UAAU,CAAC,CAAC;gBACnD,IAAI,MAAM,IAAI,IAAA,mBAAW,EAAC,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC;oBACzC,WAAW,CAAC,IAAI,CAAC;wBACf,MAAM,EAAE,IAAI,CAAC,EAAE;wBACf,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO;qBAC/B,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;YAED,OAAO,WAAW,CAAC;QACrB,CAAC,EAAE,EAAE,CAAC,CAAC;IACT,CAAC;IAED;;;;;;;;;;;;;;;OAeG;IACH,KAAK,CAAC,aAAa,CAAC,EAClB,kBAAkB,EAClB,MAAM,EACN,KAAK,EACL,OAAO,EAAE,UAAU,GAMpB;QACC,6CAA6C;QAC7C,IAAA,cAAM,EACJ,CAAC,KAAK,CAAC,UAAU,CAAC,0BAAkB,CAAC,MAAM,CAAC;YAC1C,CAAC,KAAK,CAAC,UAAU,CAAC,eAAe,CAAC,CACrC,CAAC;QAEF,2GAA2G;QAC3G,MAAM,OAAO,GAAG;YACd,OAAO,EAAE,KAAc;YACvB,EAAE,EAAE,UAAU,CAAC,EAAE,IAAI,IAAA,eAAM,GAAE;YAC7B,MAAM,EAAE,UAAU,CAAC,MAAM;YACzB,GAAG,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC5D,CAAC;QAEF,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;QAEnC,yEAAyE;QACzE,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,iBAAiB,CAC5C,kBAAkB,EAClB,KAAK,EACL,OAAO,CACR,CAAC;QAEF,IAAI,SAAS,EAAE,CAAC;YACd,OAAO,IAAI,CAAC,gBAAgB,CAAC,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,CACjD,OAAO,CAAC,aAAa,CAAC;gBACpB,MAAM;gBACN,OAAO,EAAE,SAAS;gBAClB,KAAK;gBACL,MAAM;gBACN,MAAM,EAAE,MAAuB;aAChC,CAAC,CACH,CAAC;QACJ,CAAC;QAED,4DAA4D;QAC5D,qDAAqD;QACrD,MAAM,aAAa,GAAG,IAAI,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC;QACpD,MAAM,YAAY,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAC/C,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAC9B,CAAC;QAEF,IAAI,YAAY,EAAE,CAAC;YACjB,OAAO,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,8BAA8B,EAAE;gBAC1D,MAAM,EAAE,YAAY,CAAC,MAAM;gBAC3B,MAAM;gBACN,OAAO,EAAE;oBACP,MAAM,EAAE,EAAE;oBACV,MAAM,EAAE;wBACN,OAAO;wBACP,KAAK;qBACN;iBACF;gBACD,OAAO,EAAE,yBAAW,CAAC,iBAAiB;aACvC,CAAkB,CAAC;QACtB,CAAC;QAED,gEAAgE;QAChE,MAAM,sBAAS,CAAC,cAAc,EAAE,CAAC;IACnC,CAAC;IAED;;;;;;OAMG;IACH,6BAA6B,CAC3B,KAAkB,EAClB,MAAe;QAIf,MAAM,aAAa,GAAG,IAAI,CAAC,UAAU;aAClC,IAAI,CAAC,iCAAiC,CAAC;aACvC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAE1B,OAAO,IAAI,CAAC,UAAU;aACnB,IAAI,CAAC,2CAA2C,EAAE,KAAK,CAAC;aACxD,MAAM,CACL,CACE,OAAwB,EAGxB,EAAE,CACF,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,KAAK,SAAS;YACvC,aAAa,CAAC,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC;YACjD,CAAC,MAAM,KAAK,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAC7D,CAAC;IACN,CAAC;IAED;;;;;;OAMG;IACH,mBAAmB,CAAC,KAAkB;QACpC,MAAM,cAAc,GAAG,IAAI,CAAC,6BAA6B,CAAC,KAAK,CAAC,CAAC,OAAO,CACtE,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,CAC7B,CAAC;QAEF,MAAM,eAAe,GAAG,IAAI,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC,OAAO,CAC3D,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CACvB,CAAC;QAEF,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,cAAc,EAAE,GAAG,eAAe,CAAC,CAAC,CAAC,CAAC;IACtE,CAAC;IAED;;;;;OAKG;IACH,oBAAoB,CAAC,KAAkB;QACrC,OAAO,IAAI,CAAC,6BAA6B,CAAC,KAAK,CAAC,CAAC,GAAG,CAClD,CAAC,OAAO,EAAE,EAAE,CAAC,GAAG,KAAK,IAAI,OAAO,CAAC,OAAO,EAAE,CAC3C,CAAC;IACJ,CAAC;IAED;;;;;OAKG;IACH,gBAAgB,CAAC,KAAkB;QACjC,+GAA+G;QAC/G,OAAO,CACL,IAAI,CAAC,6BAA6B,CAAC,KAAK,CAAC,CAAC,MAAM,GAAG,CAAC;YACpD,IAAI,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC,MAAM,GAAG,CAAC,CACzC,CAAC;IACJ,CAAC;CACF;AAvUD,4DAuUC","sourcesContent":["import type { Messenger } from '@metamask/messenger';\nimport type { PermissionControllerGetPermissionsAction } from '@metamask/permission-controller';\nimport { rpcErrors } from '@metamask/rpc-errors';\nimport {\n getProtocolCaveatScopes,\n SnapEndowments,\n} from '@metamask/snaps-rpc-methods';\nimport type { Json, JsonRpcRequest, SnapId } from '@metamask/snaps-sdk';\nimport type { InternalAccount } from '@metamask/snaps-utils';\nimport { HandlerType } from '@metamask/snaps-utils';\nimport type {\n CaipAccountId,\n CaipChainId,\n JsonRpcParams,\n} from '@metamask/utils';\nimport {\n assert,\n hasProperty,\n isObject,\n KnownCaipNamespace,\n parseCaipAccountId,\n} from '@metamask/utils';\nimport { nanoid } from 'nanoid';\n\nimport type { MultichainRoutingServiceMethodActions } from './MultichainRoutingService-method-action-types';\nimport type {\n SnapControllerGetRunnableSnapsAction,\n SnapControllerHandleRequestAction,\n} from '../snaps';\n\ntype SnapKeyring = {\n submitRequest: (request: {\n origin: string;\n account: string;\n method: string;\n params?: Json[] | Record<string, Json>;\n scope: CaipChainId;\n }) => Promise<Json>;\n};\n\n// Expecting a bound function that calls KeyringController.withKeyring selecting the Snap keyring\nexport type WithSnapKeyringFunction = <ReturnType>(\n operation: ({ keyring }: { keyring: SnapKeyring }) => Promise<ReturnType>,\n) => Promise<ReturnType>;\n\nexport type AccountsControllerListMultichainAccountsAction = {\n type: `AccountsController:listMultichainAccounts`;\n handler: (chainId?: CaipChainId) => InternalAccount[];\n};\n\nexport type MultichainRoutingServiceActions =\n MultichainRoutingServiceMethodActions;\n\ntype AllowedActions =\n | SnapControllerGetRunnableSnapsAction\n | SnapControllerHandleRequestAction\n | PermissionControllerGetPermissionsAction\n | AccountsControllerListMultichainAccountsAction;\n\nexport type MultichainRoutingServiceEvents = never;\n\nexport type MultichainRoutingServiceMessenger = Messenger<\n typeof name,\n MultichainRoutingServiceActions | AllowedActions\n>;\n\nexport type MultichainRoutingServiceArgs = {\n messenger: MultichainRoutingServiceMessenger;\n withSnapKeyring: WithSnapKeyringFunction;\n};\n\ntype ProtocolSnap = {\n snapId: SnapId;\n methods: string[];\n};\n\nconst name = 'MultichainRoutingService';\n\nconst MESSENGER_EXPOSED_METHODS = [\n 'handleRequest',\n 'getSupportedMethods',\n 'getSupportedAccounts',\n 'isSupportedScope',\n] as const;\n\nexport class MultichainRoutingService {\n name: typeof name = name;\n\n state = null;\n\n readonly #messenger: MultichainRoutingServiceMessenger;\n\n readonly #withSnapKeyring: WithSnapKeyringFunction;\n\n constructor({ messenger, withSnapKeyring }: MultichainRoutingServiceArgs) {\n this.#messenger = messenger;\n this.#withSnapKeyring = withSnapKeyring;\n\n this.#messenger.registerMethodActionHandlers(\n this,\n MESSENGER_EXPOSED_METHODS,\n );\n }\n\n /**\n * Attempts to resolve the account address to use for a given request by inspecting the request itself.\n *\n * The request is sent to an account Snap that will attempt this resolution.\n *\n * We manually construct the request instead of using the SnapKeyring, as the keyring may not be available.\n *\n * @param snapId - The ID of the Snap to send the request to.\n * @param scope - The CAIP-2 scope for the request.\n * @param request - The JSON-RPC request.\n * @returns The resolved address if found, otherwise null.\n * @throws If the invocation of the SnapKeyring fails.\n */\n async #resolveRequestAddress(\n snapId: SnapId,\n scope: CaipChainId,\n request: JsonRpcRequest,\n ) {\n try {\n const result = await this.#messenger.call(\n 'SnapController:handleRequest',\n {\n snapId,\n origin: 'metamask',\n request: {\n method: 'keyring_resolveAccountAddress',\n params: {\n request,\n scope,\n },\n },\n handler: HandlerType.OnKeyringRequest,\n },\n );\n\n assert(result === null || isObject(result));\n\n const address = result?.address as CaipAccountId;\n return address ? parseCaipAccountId(address).address : null;\n } catch {\n throw rpcErrors.internal();\n }\n }\n\n /**\n * Get the account ID of the account that should service the RPC request via an account Snap.\n *\n * This function checks whether any accounts exist that can service a given request by\n * using a combination of the resolveAccountAddress functionality and the connected accounts.\n *\n * If an account is expected to service this request but none is found, the function will throw.\n *\n * @param connectedAddresses - The CAIP-10 addresses connected to the\n * requesting origin for the requested scope.\n * @param scope - The CAIP-2 scope for the request.\n * @param request - The JSON-RPC request.\n * @returns An account ID if found, otherwise null.\n * @throws If no account is found, but the accounts exist that could service the request.\n */\n async #getSnapAccountId(\n connectedAddresses: CaipAccountId[],\n scope: CaipChainId,\n request: JsonRpcRequest,\n ) {\n const accounts = this.#getSupportedAccountsMetadata(scope, request.method);\n\n // If no accounts can service the request, return null.\n if (accounts.length === 0) {\n return null;\n }\n\n const parsedConnectedAddresses = connectedAddresses.map(\n (connectedAddress) => parseCaipAccountId(connectedAddress).address,\n );\n\n const connectedAccounts = accounts.filter((account) =>\n parsedConnectedAddresses.includes(account.address),\n );\n\n if (connectedAccounts.length === 0) {\n throw rpcErrors.invalidParams({\n message: 'No available account found for request.',\n });\n }\n\n const resolutionSnapId = connectedAccounts[0].metadata.snap.id;\n\n // Attempt to resolve the address that should be used for signing.\n const address = await this.#resolveRequestAddress(\n resolutionSnapId,\n scope,\n request,\n );\n\n // If we have a resolved address, try to find the selected account based on that\n // otherwise, default to one of the connected accounts.\n // TODO: Eventually let the user choose if we have more than one option for the account.\n const selectedAccount = address\n ? connectedAccounts.find((account) => account.address === address)\n : connectedAccounts[0];\n\n if (!selectedAccount) {\n throw rpcErrors.invalidParams({\n message: 'No available account found for request.',\n });\n }\n\n return selectedAccount.id;\n }\n\n /**\n * Get all protocol Snaps that can service a given CAIP-2 scope.\n *\n * Protocol Snaps are deemed fit to service a scope if they are runnable\n * and have the proper permissions set for the scope.\n *\n * @param scope - A CAIP-2 scope.\n * @returns A list of all the protocol Snaps available and their RPC methods.\n */\n #getProtocolSnaps(scope: CaipChainId) {\n const filteredSnaps = this.#messenger.call(\n 'SnapController:getRunnableSnaps',\n );\n\n return filteredSnaps.reduce<ProtocolSnap[]>((accumulator, snap) => {\n const permissions = this.#messenger.call(\n 'PermissionController:getPermissions',\n snap.id,\n );\n\n if (permissions && hasProperty(permissions, SnapEndowments.Protocol)) {\n const permission = permissions[SnapEndowments.Protocol];\n const scopes = getProtocolCaveatScopes(permission);\n if (scopes && hasProperty(scopes, scope)) {\n accumulator.push({\n snapId: snap.id,\n methods: scopes[scope].methods,\n });\n }\n }\n\n return accumulator;\n }, []);\n }\n\n /**\n * Handle an incoming JSON-RPC request tied to a specific scope by routing\n * to either a protocol Snap or an account Snap.\n *\n * Note: Addresses are considered case-sensitive by the MultichainRoutingService as\n * not all non-EVM chains are case-insensitive.\n *\n * @param options - An options bag.\n * @param options.connectedAddresses - Addresses currently connected to the\n * origin for the requested scope.\n * @param options.origin - The origin of the RPC request.\n * @param options.request - The JSON-RPC request.\n * @param options.scope - The CAIP-2 scope for the request.\n * @returns The response from the chosen Snap.\n * @throws If no handler was found.\n */\n async handleRequest({\n connectedAddresses,\n origin,\n scope,\n request: rawRequest,\n }: {\n connectedAddresses: CaipAccountId[];\n origin: string;\n scope: CaipChainId;\n request: JsonRpcRequest;\n }): Promise<Json> {\n // Explicitly block EVM scopes, just in case.\n assert(\n !scope.startsWith(KnownCaipNamespace.Eip155) &&\n !scope.startsWith('wallet:eip155'),\n );\n\n // Re-create the request to simplify and remove additional properties that may be present in MM middleware.\n const request = {\n jsonrpc: '2.0' as const,\n id: rawRequest.id ?? nanoid(),\n method: rawRequest.method,\n ...(rawRequest.params ? { params: rawRequest.params } : {}),\n };\n\n const { method, params } = request;\n\n // If the RPC request can be serviced by an account Snap, route it there.\n const accountId = await this.#getSnapAccountId(\n connectedAddresses,\n scope,\n request,\n );\n\n if (accountId) {\n return this.#withSnapKeyring(async ({ keyring }) =>\n keyring.submitRequest({\n origin,\n account: accountId,\n scope,\n method,\n params: params as JsonRpcParams,\n }),\n );\n }\n\n // If the RPC request cannot be serviced by an account Snap,\n // but has a protocol Snap available, route it there.\n const protocolSnaps = this.#getProtocolSnaps(scope);\n const protocolSnap = protocolSnaps.find((snap) =>\n snap.methods.includes(method),\n );\n\n if (protocolSnap) {\n return this.#messenger.call('SnapController:handleRequest', {\n snapId: protocolSnap.snapId,\n origin,\n request: {\n method: '',\n params: {\n request,\n scope,\n },\n },\n handler: HandlerType.OnProtocolRequest,\n }) as Promise<Json>;\n }\n\n // If no compatible account or protocol Snaps were found, throw.\n throw rpcErrors.methodNotFound();\n }\n\n /**\n * Get a list of metadata for supported accounts for a given scope from the client.\n *\n * @param scope - The CAIP-2 scope.\n * @param method - An optional method that the account should support.\n * @returns A list of metadata for the supported accounts.\n */\n #getSupportedAccountsMetadata(\n scope: CaipChainId,\n method?: string,\n ): (InternalAccount & {\n metadata: Required<InternalAccount['metadata']>;\n })[] {\n const runnableSnaps = this.#messenger\n .call('SnapController:getRunnableSnaps')\n .map((snap) => snap.id);\n\n return this.#messenger\n .call('AccountsController:listMultichainAccounts', scope)\n .filter(\n (\n account: InternalAccount,\n ): account is InternalAccount & {\n metadata: Required<InternalAccount['metadata']>;\n } =>\n account.metadata.snap?.id !== undefined &&\n runnableSnaps.includes(account.metadata.snap?.id) &&\n (method === undefined || account.methods.includes(method)),\n );\n }\n\n /**\n * Get a list of supported methods for a given scope.\n * This combines both protocol and account Snaps supported methods.\n *\n * @param scope - The CAIP-2 scope.\n * @returns A list of supported methods.\n */\n getSupportedMethods(scope: CaipChainId): string[] {\n const accountMethods = this.#getSupportedAccountsMetadata(scope).flatMap(\n (account) => account.methods,\n );\n\n const protocolMethods = this.#getProtocolSnaps(scope).flatMap(\n (snap) => snap.methods,\n );\n\n return Array.from(new Set([...accountMethods, ...protocolMethods]));\n }\n\n /**\n * Get a list of supported accounts for a given scope.\n *\n * @param scope - The CAIP-2 scope.\n * @returns A list of CAIP-10 addresses.\n */\n getSupportedAccounts(scope: CaipChainId): string[] {\n return this.#getSupportedAccountsMetadata(scope).map(\n (account) => `${scope}:${account.address}`,\n );\n }\n\n /**\n * Determine whether a given CAIP-2 scope is supported by the router.\n *\n * @param scope - The CAIP-2 scope.\n * @returns True if the router can service the scope, otherwise false.\n */\n isSupportedScope(scope: CaipChainId): boolean {\n // We currently assume here that if one Snap exists that service the scope, we can service the scope generally.\n return (\n this.#getSupportedAccountsMetadata(scope).length > 0 ||\n this.#getProtocolSnaps(scope).length > 0\n );\n }\n}\n"]} |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"MultichainRoutingService.d.cts","sourceRoot":"","sources":["../../src/multichain/MultichainRoutingService.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,4BAA4B;AACrD,OAAO,KAAK,EAAE,wCAAwC,EAAE,wCAAwC;AAMhG,OAAO,KAAK,EAAE,IAAI,EAAE,cAAc,EAAU,4BAA4B;AACxE,OAAO,KAAK,EAAE,eAAe,EAAE,8BAA8B;AAE7D,OAAO,KAAK,EACV,aAAa,EACb,WAAW,EAEZ,wBAAwB;AAUzB,OAAO,KAAK,EAAE,qCAAqC,EAAE,2DAAuD;AAC5G,OAAO,KAAK,EACV,oCAAoC,EACpC,iCAAiC,EAClC,2BAAiB;AAElB,KAAK,WAAW,GAAG;IACjB,aAAa,EAAE,CAAC,OAAO,EAAE;QACvB,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,IAAI,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QACvC,KAAK,EAAE,WAAW,CAAC;KACpB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACrB,CAAC;AAGF,MAAM,MAAM,uBAAuB,GAAG,CAAC,UAAU,EAC/C,SAAS,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE;IAAE,OAAO,EAAE,WAAW,CAAA;CAAE,KAAK,OAAO,CAAC,UAAU,CAAC,KACtE,OAAO,CAAC,UAAU,CAAC,CAAC;AAEzB,MAAM,MAAM,8CAA8C,GAAG;IAC3D,IAAI,EAAE,2CAA2C,CAAC;IAClD,OAAO,EAAE,CAAC,OAAO,CAAC,EAAE,WAAW,KAAK,eAAe,EAAE,CAAC;CACvD,CAAC;AAEF,MAAM,MAAM,+BAA+B,GACzC,qCAAqC,CAAC;AAExC,KAAK,cAAc,GACf,oCAAoC,GACpC,iCAAiC,GACjC,wCAAwC,GACxC,8CAA8C,CAAC;AAEnD,MAAM,MAAM,8BAA8B,GAAG,KAAK,CAAC;AAEnD,MAAM,MAAM,iCAAiC,GAAG,SAAS,CACvD,OAAO,IAAI,EACX,+BAA+B,GAAG,cAAc,CACjD,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG;IACzC,SAAS,EAAE,iCAAiC,CAAC;IAC7C,eAAe,EAAE,uBAAuB,CAAC;CAC1C,CAAC;AAOF,QAAA,MAAM,IAAI,6BAA6B,CAAC;AASxC,qBAAa,wBAAwB;;IACnC,IAAI,EAAE,OAAO,IAAI,CAAQ;IAEzB,KAAK,OAAQ;gBAMD,EAAE,SAAS,EAAE,eAAe,EAAE,EAAE,4BAA4B;IAqKxE;;;;;;;;;;;;;;;OAeG;IACG,aAAa,CAAC,EAClB,kBAAkB,EAClB,MAAM,EACN,KAAK,EACL,OAAO,EAAE,UAAU,GACpB,EAAE;QACD,kBAAkB,EAAE,aAAa,EAAE,CAAC;QACpC,MAAM,EAAE,MAAM,CAAC;QACf,KAAK,EAAE,WAAW,CAAC;QACnB,OAAO,EAAE,cAAc,CAAC;KACzB,GAAG,OAAO,CAAC,IAAI,CAAC;IA0EjB;;;;;;OAMG;IACH,mBAAmB,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,EAAE;IAYjD;;;;;OAKG;IACH,oBAAoB,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,EAAE;IAMlD;;;;;OAKG;IACH,gBAAgB,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO;CAO9C"} | ||
| {"version":3,"file":"MultichainRoutingService.d.cts","sourceRoot":"","sources":["../../src/multichain/MultichainRoutingService.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,4BAA4B;AACrD,OAAO,KAAK,EAAE,wCAAwC,EAAE,wCAAwC;AAMhG,OAAO,KAAK,EAAE,IAAI,EAAE,cAAc,EAAU,4BAA4B;AACxE,OAAO,KAAK,EAAE,eAAe,EAAE,8BAA8B;AAE7D,OAAO,KAAK,EACV,aAAa,EACb,WAAW,EAEZ,wBAAwB;AAUzB,OAAO,KAAK,EAAE,qCAAqC,EAAE,2DAAuD;AAC5G,OAAO,KAAK,EACV,oCAAoC,EACpC,iCAAiC,EAClC,2BAAiB;AAElB,KAAK,WAAW,GAAG;IACjB,aAAa,EAAE,CAAC,OAAO,EAAE;QACvB,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,IAAI,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QACvC,KAAK,EAAE,WAAW,CAAC;KACpB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACrB,CAAC;AAGF,MAAM,MAAM,uBAAuB,GAAG,CAAC,UAAU,EAC/C,SAAS,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE;IAAE,OAAO,EAAE,WAAW,CAAA;CAAE,KAAK,OAAO,CAAC,UAAU,CAAC,KACtE,OAAO,CAAC,UAAU,CAAC,CAAC;AAEzB,MAAM,MAAM,8CAA8C,GAAG;IAC3D,IAAI,EAAE,2CAA2C,CAAC;IAClD,OAAO,EAAE,CAAC,OAAO,CAAC,EAAE,WAAW,KAAK,eAAe,EAAE,CAAC;CACvD,CAAC;AAEF,MAAM,MAAM,+BAA+B,GACzC,qCAAqC,CAAC;AAExC,KAAK,cAAc,GACf,oCAAoC,GACpC,iCAAiC,GACjC,wCAAwC,GACxC,8CAA8C,CAAC;AAEnD,MAAM,MAAM,8BAA8B,GAAG,KAAK,CAAC;AAEnD,MAAM,MAAM,iCAAiC,GAAG,SAAS,CACvD,OAAO,IAAI,EACX,+BAA+B,GAAG,cAAc,CACjD,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG;IACzC,SAAS,EAAE,iCAAiC,CAAC;IAC7C,eAAe,EAAE,uBAAuB,CAAC;CAC1C,CAAC;AAOF,QAAA,MAAM,IAAI,6BAA6B,CAAC;AASxC,qBAAa,wBAAwB;;IACnC,IAAI,EAAE,OAAO,IAAI,CAAQ;IAEzB,KAAK,OAAQ;gBAMD,EAAE,SAAS,EAAE,eAAe,EAAE,EAAE,4BAA4B;IA2JxE;;;;;;;;;;;;;;;OAeG;IACG,aAAa,CAAC,EAClB,kBAAkB,EAClB,MAAM,EACN,KAAK,EACL,OAAO,EAAE,UAAU,GACpB,EAAE;QACD,kBAAkB,EAAE,aAAa,EAAE,CAAC;QACpC,MAAM,EAAE,MAAM,CAAC;QACf,KAAK,EAAE,WAAW,CAAC;QACnB,OAAO,EAAE,cAAc,CAAC;KACzB,GAAG,OAAO,CAAC,IAAI,CAAC;IA6FjB;;;;;;OAMG;IACH,mBAAmB,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,EAAE;IAYjD;;;;;OAKG;IACH,oBAAoB,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,EAAE;IAMlD;;;;;OAKG;IACH,gBAAgB,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO;CAO9C"} |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"MultichainRoutingService.d.mts","sourceRoot":"","sources":["../../src/multichain/MultichainRoutingService.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,4BAA4B;AACrD,OAAO,KAAK,EAAE,wCAAwC,EAAE,wCAAwC;AAMhG,OAAO,KAAK,EAAE,IAAI,EAAE,cAAc,EAAU,4BAA4B;AACxE,OAAO,KAAK,EAAE,eAAe,EAAE,8BAA8B;AAE7D,OAAO,KAAK,EACV,aAAa,EACb,WAAW,EAEZ,wBAAwB;AAUzB,OAAO,KAAK,EAAE,qCAAqC,EAAE,2DAAuD;AAC5G,OAAO,KAAK,EACV,oCAAoC,EACpC,iCAAiC,EAClC,2BAAiB;AAElB,KAAK,WAAW,GAAG;IACjB,aAAa,EAAE,CAAC,OAAO,EAAE;QACvB,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,IAAI,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QACvC,KAAK,EAAE,WAAW,CAAC;KACpB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACrB,CAAC;AAGF,MAAM,MAAM,uBAAuB,GAAG,CAAC,UAAU,EAC/C,SAAS,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE;IAAE,OAAO,EAAE,WAAW,CAAA;CAAE,KAAK,OAAO,CAAC,UAAU,CAAC,KACtE,OAAO,CAAC,UAAU,CAAC,CAAC;AAEzB,MAAM,MAAM,8CAA8C,GAAG;IAC3D,IAAI,EAAE,2CAA2C,CAAC;IAClD,OAAO,EAAE,CAAC,OAAO,CAAC,EAAE,WAAW,KAAK,eAAe,EAAE,CAAC;CACvD,CAAC;AAEF,MAAM,MAAM,+BAA+B,GACzC,qCAAqC,CAAC;AAExC,KAAK,cAAc,GACf,oCAAoC,GACpC,iCAAiC,GACjC,wCAAwC,GACxC,8CAA8C,CAAC;AAEnD,MAAM,MAAM,8BAA8B,GAAG,KAAK,CAAC;AAEnD,MAAM,MAAM,iCAAiC,GAAG,SAAS,CACvD,OAAO,IAAI,EACX,+BAA+B,GAAG,cAAc,CACjD,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG;IACzC,SAAS,EAAE,iCAAiC,CAAC;IAC7C,eAAe,EAAE,uBAAuB,CAAC;CAC1C,CAAC;AAOF,QAAA,MAAM,IAAI,6BAA6B,CAAC;AASxC,qBAAa,wBAAwB;;IACnC,IAAI,EAAE,OAAO,IAAI,CAAQ;IAEzB,KAAK,OAAQ;gBAMD,EAAE,SAAS,EAAE,eAAe,EAAE,EAAE,4BAA4B;IAqKxE;;;;;;;;;;;;;;;OAeG;IACG,aAAa,CAAC,EAClB,kBAAkB,EAClB,MAAM,EACN,KAAK,EACL,OAAO,EAAE,UAAU,GACpB,EAAE;QACD,kBAAkB,EAAE,aAAa,EAAE,CAAC;QACpC,MAAM,EAAE,MAAM,CAAC;QACf,KAAK,EAAE,WAAW,CAAC;QACnB,OAAO,EAAE,cAAc,CAAC;KACzB,GAAG,OAAO,CAAC,IAAI,CAAC;IA0EjB;;;;;;OAMG;IACH,mBAAmB,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,EAAE;IAYjD;;;;;OAKG;IACH,oBAAoB,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,EAAE;IAMlD;;;;;OAKG;IACH,gBAAgB,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO;CAO9C"} | ||
| {"version":3,"file":"MultichainRoutingService.d.mts","sourceRoot":"","sources":["../../src/multichain/MultichainRoutingService.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,4BAA4B;AACrD,OAAO,KAAK,EAAE,wCAAwC,EAAE,wCAAwC;AAMhG,OAAO,KAAK,EAAE,IAAI,EAAE,cAAc,EAAU,4BAA4B;AACxE,OAAO,KAAK,EAAE,eAAe,EAAE,8BAA8B;AAE7D,OAAO,KAAK,EACV,aAAa,EACb,WAAW,EAEZ,wBAAwB;AAUzB,OAAO,KAAK,EAAE,qCAAqC,EAAE,2DAAuD;AAC5G,OAAO,KAAK,EACV,oCAAoC,EACpC,iCAAiC,EAClC,2BAAiB;AAElB,KAAK,WAAW,GAAG;IACjB,aAAa,EAAE,CAAC,OAAO,EAAE;QACvB,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,IAAI,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QACvC,KAAK,EAAE,WAAW,CAAC;KACpB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACrB,CAAC;AAGF,MAAM,MAAM,uBAAuB,GAAG,CAAC,UAAU,EAC/C,SAAS,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE;IAAE,OAAO,EAAE,WAAW,CAAA;CAAE,KAAK,OAAO,CAAC,UAAU,CAAC,KACtE,OAAO,CAAC,UAAU,CAAC,CAAC;AAEzB,MAAM,MAAM,8CAA8C,GAAG;IAC3D,IAAI,EAAE,2CAA2C,CAAC;IAClD,OAAO,EAAE,CAAC,OAAO,CAAC,EAAE,WAAW,KAAK,eAAe,EAAE,CAAC;CACvD,CAAC;AAEF,MAAM,MAAM,+BAA+B,GACzC,qCAAqC,CAAC;AAExC,KAAK,cAAc,GACf,oCAAoC,GACpC,iCAAiC,GACjC,wCAAwC,GACxC,8CAA8C,CAAC;AAEnD,MAAM,MAAM,8BAA8B,GAAG,KAAK,CAAC;AAEnD,MAAM,MAAM,iCAAiC,GAAG,SAAS,CACvD,OAAO,IAAI,EACX,+BAA+B,GAAG,cAAc,CACjD,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG;IACzC,SAAS,EAAE,iCAAiC,CAAC;IAC7C,eAAe,EAAE,uBAAuB,CAAC;CAC1C,CAAC;AAOF,QAAA,MAAM,IAAI,6BAA6B,CAAC;AASxC,qBAAa,wBAAwB;;IACnC,IAAI,EAAE,OAAO,IAAI,CAAQ;IAEzB,KAAK,OAAQ;gBAMD,EAAE,SAAS,EAAE,eAAe,EAAE,EAAE,4BAA4B;IA2JxE;;;;;;;;;;;;;;;OAeG;IACG,aAAa,CAAC,EAClB,kBAAkB,EAClB,MAAM,EACN,KAAK,EACL,OAAO,EAAE,UAAU,GACpB,EAAE;QACD,kBAAkB,EAAE,aAAa,EAAE,CAAC;QACpC,MAAM,EAAE,MAAM,CAAC;QACf,KAAK,EAAE,WAAW,CAAC;QACnB,OAAO,EAAE,cAAc,CAAC;KACzB,GAAG,OAAO,CAAC,IAAI,CAAC;IA6FjB;;;;;;OAMG;IACH,mBAAmB,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,EAAE;IAYjD;;;;;OAKG;IACH,oBAAoB,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,EAAE;IAMlD;;;;;OAKG;IACH,gBAAgB,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO;CAO9C"} |
@@ -74,6 +74,3 @@ import { rpcErrors } from "@metamask/rpc-errors"; | ||
| async #getSnapAccountId(connectedAddresses, scope, request) { | ||
| const accounts = this.#messenger | ||
| .call('AccountsController:listMultichainAccounts', scope) | ||
| .filter((account) => Boolean(account.metadata.snap?.enabled) && | ||
| account.methods.includes(request.method)); | ||
| const accounts = this.#getSupportedAccountsMetadata(scope, request.method); | ||
| // If no accounts can service the request, return null. | ||
@@ -196,8 +193,14 @@ if (accounts.length === 0) { | ||
| * @param scope - The CAIP-2 scope. | ||
| * @param method - An optional method that the account should support. | ||
| * @returns A list of metadata for the supported accounts. | ||
| */ | ||
| #getSupportedAccountsMetadata(scope) { | ||
| #getSupportedAccountsMetadata(scope, method) { | ||
| const runnableSnaps = this.#messenger | ||
| .call('SnapController:getRunnableSnaps') | ||
| .map((snap) => snap.id); | ||
| return this.#messenger | ||
| .call('AccountsController:listMultichainAccounts', scope) | ||
| .filter((account) => account.metadata.snap?.enabled); | ||
| .filter((account) => account.metadata.snap?.id !== undefined && | ||
| runnableSnaps.includes(account.metadata.snap?.id) && | ||
| (method === undefined || account.methods.includes(method))); | ||
| } | ||
@@ -232,9 +235,7 @@ /** | ||
| isSupportedScope(scope) { | ||
| const hasAccountSnap = this.#messenger | ||
| .call('AccountsController:listMultichainAccounts', scope) | ||
| .some((account) => account.metadata.snap?.enabled); | ||
| // We currently assume here that if one Snap exists that service the scope, we can service the scope generally. | ||
| return hasAccountSnap || this.#getProtocolSnaps(scope).length > 0; | ||
| return (this.#getSupportedAccountsMetadata(scope).length > 0 || | ||
| this.#getProtocolSnaps(scope).length > 0); | ||
| } | ||
| } | ||
| //# sourceMappingURL=MultichainRoutingService.mjs.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"MultichainRoutingService.mjs","sourceRoot":"","sources":["../../src/multichain/MultichainRoutingService.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,6BAA6B;AACjD,OAAO,EACL,uBAAuB,EACvB,cAAc,EACf,oCAAoC;AAGrC,OAAO,EAAE,WAAW,EAAE,8BAA8B;AAMpD,OAAO,EACL,MAAM,EACN,WAAW,EACX,QAAQ,EACR,kBAAkB,EAClB,kBAAkB,EACnB,wBAAwB;AACzB,OAAO,EAAE,MAAM,EAAE,eAAe;AAsDhC,MAAM,IAAI,GAAG,0BAA0B,CAAC;AAExC,MAAM,yBAAyB,GAAG;IAChC,eAAe;IACf,qBAAqB;IACrB,sBAAsB;IACtB,kBAAkB;CACV,CAAC;AAEX,MAAM,OAAO,wBAAwB;IACnC,IAAI,GAAgB,IAAI,CAAC;IAEzB,KAAK,GAAG,IAAI,CAAC;IAEJ,UAAU,CAAoC;IAE9C,gBAAgB,CAA0B;IAEnD,YAAY,EAAE,SAAS,EAAE,eAAe,EAAgC;QACtE,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;QAC5B,IAAI,CAAC,gBAAgB,GAAG,eAAe,CAAC;QAExC,IAAI,CAAC,UAAU,CAAC,4BAA4B,CAC1C,IAAI,EACJ,yBAAyB,CAC1B,CAAC;IACJ,CAAC;IAED;;;;;;;;;;;;OAYG;IACH,KAAK,CAAC,sBAAsB,CAC1B,MAAc,EACd,KAAkB,EAClB,OAAuB;QAEvB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,IAAI,CACvC,8BAA8B,EAC9B;gBACE,MAAM;gBACN,MAAM,EAAE,UAAU;gBAClB,OAAO,EAAE;oBACP,MAAM,EAAE,+BAA+B;oBACvC,MAAM,EAAE;wBACN,OAAO;wBACP,KAAK;qBACN;iBACF;gBACD,OAAO,EAAE,WAAW,CAAC,gBAAgB;aACtC,CACF,CAAC;YAEF,MAAM,CAAC,MAAM,KAAK,IAAI,IAAI,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;YAE5C,MAAM,OAAO,GAAG,MAAM,EAAE,OAAwB,CAAC;YACjD,OAAO,OAAO,CAAC,CAAC,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;QAC9D,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,SAAS,CAAC,QAAQ,EAAE,CAAC;QAC7B,CAAC;IACH,CAAC;IAED;;;;;;;;;;;;;;OAcG;IACH,KAAK,CAAC,iBAAiB,CACrB,kBAAmC,EACnC,KAAkB,EAClB,OAAuB;QAEvB,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU;aAC7B,IAAI,CAAC,2CAA2C,EAAE,KAAK,CAAC;aACxD,MAAM,CACL,CACE,OAAwB,EAGxB,EAAE,CACF,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;YACvC,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,CAC3C,CAAC;QAEJ,uDAAuD;QACvD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC1B,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,wBAAwB,GAAG,kBAAkB,CAAC,GAAG,CACrD,CAAC,gBAAgB,EAAE,EAAE,CAAC,kBAAkB,CAAC,gBAAgB,CAAC,CAAC,OAAO,CACnE,CAAC;QAEF,MAAM,iBAAiB,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CACpD,wBAAwB,CAAC,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CACnD,CAAC;QAEF,IAAI,iBAAiB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACnC,MAAM,SAAS,CAAC,aAAa,CAAC;gBAC5B,OAAO,EAAE,yCAAyC;aACnD,CAAC,CAAC;QACL,CAAC;QAED,MAAM,gBAAgB,GAAG,iBAAiB,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QAE/D,kEAAkE;QAClE,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,sBAAsB,CAC/C,gBAAgB,EAChB,KAAK,EACL,OAAO,CACR,CAAC;QAEF,gFAAgF;QAChF,uDAAuD;QACvD,wFAAwF;QACxF,MAAM,eAAe,GAAG,OAAO;YAC7B,CAAC,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,KAAK,OAAO,CAAC;YAClE,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC;QAEzB,IAAI,CAAC,eAAe,EAAE,CAAC;YACrB,MAAM,SAAS,CAAC,aAAa,CAAC;gBAC5B,OAAO,EAAE,yCAAyC;aACnD,CAAC,CAAC;QACL,CAAC;QAED,OAAO,eAAe,CAAC,EAAE,CAAC;IAC5B,CAAC;IAED;;;;;;;;OAQG;IACH,iBAAiB,CAAC,KAAkB;QAClC,MAAM,aAAa,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CACxC,iCAAiC,CAClC,CAAC;QAEF,OAAO,aAAa,CAAC,MAAM,CAAiB,CAAC,WAAW,EAAE,IAAI,EAAE,EAAE;YAChE,MAAM,WAAW,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CACtC,qCAAqC,EACrC,IAAI,CAAC,EAAE,CACR,CAAC;YAEF,IAAI,WAAW,IAAI,WAAW,CAAC,WAAW,EAAE,cAAc,CAAC,QAAQ,CAAC,EAAE,CAAC;gBACrE,MAAM,UAAU,GAAG,WAAW,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;gBACxD,MAAM,MAAM,GAAG,uBAAuB,CAAC,UAAU,CAAC,CAAC;gBACnD,IAAI,MAAM,IAAI,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC;oBACzC,WAAW,CAAC,IAAI,CAAC;wBACf,MAAM,EAAE,IAAI,CAAC,EAAE;wBACf,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO;qBAC/B,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;YAED,OAAO,WAAW,CAAC;QACrB,CAAC,EAAE,EAAE,CAAC,CAAC;IACT,CAAC;IAED;;;;;;;;;;;;;;;OAeG;IACH,KAAK,CAAC,aAAa,CAAC,EAClB,kBAAkB,EAClB,MAAM,EACN,KAAK,EACL,OAAO,EAAE,UAAU,GAMpB;QACC,6CAA6C;QAC7C,MAAM,CACJ,CAAC,KAAK,CAAC,UAAU,CAAC,kBAAkB,CAAC,MAAM,CAAC;YAC1C,CAAC,KAAK,CAAC,UAAU,CAAC,eAAe,CAAC,CACrC,CAAC;QAEF,2GAA2G;QAC3G,MAAM,OAAO,GAAG;YACd,OAAO,EAAE,KAAc;YACvB,EAAE,EAAE,UAAU,CAAC,EAAE,IAAI,MAAM,EAAE;YAC7B,MAAM,EAAE,UAAU,CAAC,MAAM;YACzB,GAAG,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC5D,CAAC;QAEF,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;QAEnC,yEAAyE;QACzE,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,iBAAiB,CAC5C,kBAAkB,EAClB,KAAK,EACL,OAAO,CACR,CAAC;QAEF,IAAI,SAAS,EAAE,CAAC;YACd,OAAO,IAAI,CAAC,gBAAgB,CAAC,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,CACjD,OAAO,CAAC,aAAa,CAAC;gBACpB,MAAM;gBACN,OAAO,EAAE,SAAS;gBAClB,KAAK;gBACL,MAAM;gBACN,MAAM,EAAE,MAAuB;aAChC,CAAC,CACH,CAAC;QACJ,CAAC;QAED,4DAA4D;QAC5D,qDAAqD;QACrD,MAAM,aAAa,GAAG,IAAI,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC;QACpD,MAAM,YAAY,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAC/C,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAC9B,CAAC;QAEF,IAAI,YAAY,EAAE,CAAC;YACjB,OAAO,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,8BAA8B,EAAE;gBAC1D,MAAM,EAAE,YAAY,CAAC,MAAM;gBAC3B,MAAM;gBACN,OAAO,EAAE;oBACP,MAAM,EAAE,EAAE;oBACV,MAAM,EAAE;wBACN,OAAO;wBACP,KAAK;qBACN;iBACF;gBACD,OAAO,EAAE,WAAW,CAAC,iBAAiB;aACvC,CAAkB,CAAC;QACtB,CAAC;QAED,gEAAgE;QAChE,MAAM,SAAS,CAAC,cAAc,EAAE,CAAC;IACnC,CAAC;IAED;;;;;OAKG;IACH,6BAA6B,CAAC,KAAkB;QAC9C,OAAO,IAAI,CAAC,UAAU;aACnB,IAAI,CAAC,2CAA2C,EAAE,KAAK,CAAC;aACxD,MAAM,CAAC,CAAC,OAAwB,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAC1E,CAAC;IAED;;;;;;OAMG;IACH,mBAAmB,CAAC,KAAkB;QACpC,MAAM,cAAc,GAAG,IAAI,CAAC,6BAA6B,CAAC,KAAK,CAAC,CAAC,OAAO,CACtE,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,CAC7B,CAAC;QAEF,MAAM,eAAe,GAAG,IAAI,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC,OAAO,CAC3D,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CACvB,CAAC;QAEF,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,cAAc,EAAE,GAAG,eAAe,CAAC,CAAC,CAAC,CAAC;IACtE,CAAC;IAED;;;;;OAKG;IACH,oBAAoB,CAAC,KAAkB;QACrC,OAAO,IAAI,CAAC,6BAA6B,CAAC,KAAK,CAAC,CAAC,GAAG,CAClD,CAAC,OAAO,EAAE,EAAE,CAAC,GAAG,KAAK,IAAI,OAAO,CAAC,OAAO,EAAE,CAC3C,CAAC;IACJ,CAAC;IAED;;;;;OAKG;IACH,gBAAgB,CAAC,KAAkB;QACjC,MAAM,cAAc,GAAG,IAAI,CAAC,UAAU;aACnC,IAAI,CAAC,2CAA2C,EAAE,KAAK,CAAC;aACxD,IAAI,CAAC,CAAC,OAAwB,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QACtE,+GAA+G;QAC/G,OAAO,cAAc,IAAI,IAAI,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;IACpE,CAAC;CACF","sourcesContent":["import type { Messenger } from '@metamask/messenger';\nimport type { PermissionControllerGetPermissionsAction } from '@metamask/permission-controller';\nimport { rpcErrors } from '@metamask/rpc-errors';\nimport {\n getProtocolCaveatScopes,\n SnapEndowments,\n} from '@metamask/snaps-rpc-methods';\nimport type { Json, JsonRpcRequest, SnapId } from '@metamask/snaps-sdk';\nimport type { InternalAccount } from '@metamask/snaps-utils';\nimport { HandlerType } from '@metamask/snaps-utils';\nimport type {\n CaipAccountId,\n CaipChainId,\n JsonRpcParams,\n} from '@metamask/utils';\nimport {\n assert,\n hasProperty,\n isObject,\n KnownCaipNamespace,\n parseCaipAccountId,\n} from '@metamask/utils';\nimport { nanoid } from 'nanoid';\n\nimport type { MultichainRoutingServiceMethodActions } from './MultichainRoutingService-method-action-types';\nimport type {\n SnapControllerGetRunnableSnapsAction,\n SnapControllerHandleRequestAction,\n} from '../snaps';\n\ntype SnapKeyring = {\n submitRequest: (request: {\n origin: string;\n account: string;\n method: string;\n params?: Json[] | Record<string, Json>;\n scope: CaipChainId;\n }) => Promise<Json>;\n};\n\n// Expecting a bound function that calls KeyringController.withKeyring selecting the Snap keyring\nexport type WithSnapKeyringFunction = <ReturnType>(\n operation: ({ keyring }: { keyring: SnapKeyring }) => Promise<ReturnType>,\n) => Promise<ReturnType>;\n\nexport type AccountsControllerListMultichainAccountsAction = {\n type: `AccountsController:listMultichainAccounts`;\n handler: (chainId?: CaipChainId) => InternalAccount[];\n};\n\nexport type MultichainRoutingServiceActions =\n MultichainRoutingServiceMethodActions;\n\ntype AllowedActions =\n | SnapControllerGetRunnableSnapsAction\n | SnapControllerHandleRequestAction\n | PermissionControllerGetPermissionsAction\n | AccountsControllerListMultichainAccountsAction;\n\nexport type MultichainRoutingServiceEvents = never;\n\nexport type MultichainRoutingServiceMessenger = Messenger<\n typeof name,\n MultichainRoutingServiceActions | AllowedActions\n>;\n\nexport type MultichainRoutingServiceArgs = {\n messenger: MultichainRoutingServiceMessenger;\n withSnapKeyring: WithSnapKeyringFunction;\n};\n\ntype ProtocolSnap = {\n snapId: SnapId;\n methods: string[];\n};\n\nconst name = 'MultichainRoutingService';\n\nconst MESSENGER_EXPOSED_METHODS = [\n 'handleRequest',\n 'getSupportedMethods',\n 'getSupportedAccounts',\n 'isSupportedScope',\n] as const;\n\nexport class MultichainRoutingService {\n name: typeof name = name;\n\n state = null;\n\n readonly #messenger: MultichainRoutingServiceMessenger;\n\n readonly #withSnapKeyring: WithSnapKeyringFunction;\n\n constructor({ messenger, withSnapKeyring }: MultichainRoutingServiceArgs) {\n this.#messenger = messenger;\n this.#withSnapKeyring = withSnapKeyring;\n\n this.#messenger.registerMethodActionHandlers(\n this,\n MESSENGER_EXPOSED_METHODS,\n );\n }\n\n /**\n * Attempts to resolve the account address to use for a given request by inspecting the request itself.\n *\n * The request is sent to an account Snap that will attempt this resolution.\n *\n * We manually construct the request instead of using the SnapKeyring, as the keyring may not be available.\n *\n * @param snapId - The ID of the Snap to send the request to.\n * @param scope - The CAIP-2 scope for the request.\n * @param request - The JSON-RPC request.\n * @returns The resolved address if found, otherwise null.\n * @throws If the invocation of the SnapKeyring fails.\n */\n async #resolveRequestAddress(\n snapId: SnapId,\n scope: CaipChainId,\n request: JsonRpcRequest,\n ) {\n try {\n const result = await this.#messenger.call(\n 'SnapController:handleRequest',\n {\n snapId,\n origin: 'metamask',\n request: {\n method: 'keyring_resolveAccountAddress',\n params: {\n request,\n scope,\n },\n },\n handler: HandlerType.OnKeyringRequest,\n },\n );\n\n assert(result === null || isObject(result));\n\n const address = result?.address as CaipAccountId;\n return address ? parseCaipAccountId(address).address : null;\n } catch {\n throw rpcErrors.internal();\n }\n }\n\n /**\n * Get the account ID of the account that should service the RPC request via an account Snap.\n *\n * This function checks whether any accounts exist that can service a given request by\n * using a combination of the resolveAccountAddress functionality and the connected accounts.\n *\n * If an account is expected to service this request but none is found, the function will throw.\n *\n * @param connectedAddresses - The CAIP-10 addresses connected to the\n * requesting origin for the requested scope.\n * @param scope - The CAIP-2 scope for the request.\n * @param request - The JSON-RPC request.\n * @returns An account ID if found, otherwise null.\n * @throws If no account is found, but the accounts exist that could service the request.\n */\n async #getSnapAccountId(\n connectedAddresses: CaipAccountId[],\n scope: CaipChainId,\n request: JsonRpcRequest,\n ) {\n const accounts = this.#messenger\n .call('AccountsController:listMultichainAccounts', scope)\n .filter(\n (\n account: InternalAccount,\n ): account is InternalAccount & {\n metadata: Required<InternalAccount['metadata']>;\n } =>\n Boolean(account.metadata.snap?.enabled) &&\n account.methods.includes(request.method),\n );\n\n // If no accounts can service the request, return null.\n if (accounts.length === 0) {\n return null;\n }\n\n const parsedConnectedAddresses = connectedAddresses.map(\n (connectedAddress) => parseCaipAccountId(connectedAddress).address,\n );\n\n const connectedAccounts = accounts.filter((account) =>\n parsedConnectedAddresses.includes(account.address),\n );\n\n if (connectedAccounts.length === 0) {\n throw rpcErrors.invalidParams({\n message: 'No available account found for request.',\n });\n }\n\n const resolutionSnapId = connectedAccounts[0].metadata.snap.id;\n\n // Attempt to resolve the address that should be used for signing.\n const address = await this.#resolveRequestAddress(\n resolutionSnapId,\n scope,\n request,\n );\n\n // If we have a resolved address, try to find the selected account based on that\n // otherwise, default to one of the connected accounts.\n // TODO: Eventually let the user choose if we have more than one option for the account.\n const selectedAccount = address\n ? connectedAccounts.find((account) => account.address === address)\n : connectedAccounts[0];\n\n if (!selectedAccount) {\n throw rpcErrors.invalidParams({\n message: 'No available account found for request.',\n });\n }\n\n return selectedAccount.id;\n }\n\n /**\n * Get all protocol Snaps that can service a given CAIP-2 scope.\n *\n * Protocol Snaps are deemed fit to service a scope if they are runnable\n * and have the proper permissions set for the scope.\n *\n * @param scope - A CAIP-2 scope.\n * @returns A list of all the protocol Snaps available and their RPC methods.\n */\n #getProtocolSnaps(scope: CaipChainId) {\n const filteredSnaps = this.#messenger.call(\n 'SnapController:getRunnableSnaps',\n );\n\n return filteredSnaps.reduce<ProtocolSnap[]>((accumulator, snap) => {\n const permissions = this.#messenger.call(\n 'PermissionController:getPermissions',\n snap.id,\n );\n\n if (permissions && hasProperty(permissions, SnapEndowments.Protocol)) {\n const permission = permissions[SnapEndowments.Protocol];\n const scopes = getProtocolCaveatScopes(permission);\n if (scopes && hasProperty(scopes, scope)) {\n accumulator.push({\n snapId: snap.id,\n methods: scopes[scope].methods,\n });\n }\n }\n\n return accumulator;\n }, []);\n }\n\n /**\n * Handle an incoming JSON-RPC request tied to a specific scope by routing\n * to either a protocol Snap or an account Snap.\n *\n * Note: Addresses are considered case-sensitive by the MultichainRoutingService as\n * not all non-EVM chains are case-insensitive.\n *\n * @param options - An options bag.\n * @param options.connectedAddresses - Addresses currently connected to the\n * origin for the requested scope.\n * @param options.origin - The origin of the RPC request.\n * @param options.request - The JSON-RPC request.\n * @param options.scope - The CAIP-2 scope for the request.\n * @returns The response from the chosen Snap.\n * @throws If no handler was found.\n */\n async handleRequest({\n connectedAddresses,\n origin,\n scope,\n request: rawRequest,\n }: {\n connectedAddresses: CaipAccountId[];\n origin: string;\n scope: CaipChainId;\n request: JsonRpcRequest;\n }): Promise<Json> {\n // Explicitly block EVM scopes, just in case.\n assert(\n !scope.startsWith(KnownCaipNamespace.Eip155) &&\n !scope.startsWith('wallet:eip155'),\n );\n\n // Re-create the request to simplify and remove additional properties that may be present in MM middleware.\n const request = {\n jsonrpc: '2.0' as const,\n id: rawRequest.id ?? nanoid(),\n method: rawRequest.method,\n ...(rawRequest.params ? { params: rawRequest.params } : {}),\n };\n\n const { method, params } = request;\n\n // If the RPC request can be serviced by an account Snap, route it there.\n const accountId = await this.#getSnapAccountId(\n connectedAddresses,\n scope,\n request,\n );\n\n if (accountId) {\n return this.#withSnapKeyring(async ({ keyring }) =>\n keyring.submitRequest({\n origin,\n account: accountId,\n scope,\n method,\n params: params as JsonRpcParams,\n }),\n );\n }\n\n // If the RPC request cannot be serviced by an account Snap,\n // but has a protocol Snap available, route it there.\n const protocolSnaps = this.#getProtocolSnaps(scope);\n const protocolSnap = protocolSnaps.find((snap) =>\n snap.methods.includes(method),\n );\n\n if (protocolSnap) {\n return this.#messenger.call('SnapController:handleRequest', {\n snapId: protocolSnap.snapId,\n origin,\n request: {\n method: '',\n params: {\n request,\n scope,\n },\n },\n handler: HandlerType.OnProtocolRequest,\n }) as Promise<Json>;\n }\n\n // If no compatible account or protocol Snaps were found, throw.\n throw rpcErrors.methodNotFound();\n }\n\n /**\n * Get a list of metadata for supported accounts for a given scope from the client.\n *\n * @param scope - The CAIP-2 scope.\n * @returns A list of metadata for the supported accounts.\n */\n #getSupportedAccountsMetadata(scope: CaipChainId): InternalAccount[] {\n return this.#messenger\n .call('AccountsController:listMultichainAccounts', scope)\n .filter((account: InternalAccount) => account.metadata.snap?.enabled);\n }\n\n /**\n * Get a list of supported methods for a given scope.\n * This combines both protocol and account Snaps supported methods.\n *\n * @param scope - The CAIP-2 scope.\n * @returns A list of supported methods.\n */\n getSupportedMethods(scope: CaipChainId): string[] {\n const accountMethods = this.#getSupportedAccountsMetadata(scope).flatMap(\n (account) => account.methods,\n );\n\n const protocolMethods = this.#getProtocolSnaps(scope).flatMap(\n (snap) => snap.methods,\n );\n\n return Array.from(new Set([...accountMethods, ...protocolMethods]));\n }\n\n /**\n * Get a list of supported accounts for a given scope.\n *\n * @param scope - The CAIP-2 scope.\n * @returns A list of CAIP-10 addresses.\n */\n getSupportedAccounts(scope: CaipChainId): string[] {\n return this.#getSupportedAccountsMetadata(scope).map(\n (account) => `${scope}:${account.address}`,\n );\n }\n\n /**\n * Determine whether a given CAIP-2 scope is supported by the router.\n *\n * @param scope - The CAIP-2 scope.\n * @returns True if the router can service the scope, otherwise false.\n */\n isSupportedScope(scope: CaipChainId): boolean {\n const hasAccountSnap = this.#messenger\n .call('AccountsController:listMultichainAccounts', scope)\n .some((account: InternalAccount) => account.metadata.snap?.enabled);\n // We currently assume here that if one Snap exists that service the scope, we can service the scope generally.\n return hasAccountSnap || this.#getProtocolSnaps(scope).length > 0;\n }\n}\n"]} | ||
| {"version":3,"file":"MultichainRoutingService.mjs","sourceRoot":"","sources":["../../src/multichain/MultichainRoutingService.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Messenger } from '@metamask/messenger';\nimport type { PermissionControllerGetPermissionsAction } from '@metamask/permission-controller';\nimport { rpcErrors } from '@metamask/rpc-errors';\nimport {\n getProtocolCaveatScopes,\n SnapEndowments,\n} from '@metamask/snaps-rpc-methods';\nimport type { Json, JsonRpcRequest, SnapId } from '@metamask/snaps-sdk';\nimport type { InternalAccount } from '@metamask/snaps-utils';\nimport { HandlerType } from '@metamask/snaps-utils';\nimport type {\n CaipAccountId,\n CaipChainId,\n JsonRpcParams,\n} from '@metamask/utils';\nimport {\n assert,\n hasProperty,\n isObject,\n KnownCaipNamespace,\n parseCaipAccountId,\n} from '@metamask/utils';\nimport { nanoid } from 'nanoid';\n\nimport type { MultichainRoutingServiceMethodActions } from './MultichainRoutingService-method-action-types';\nimport type {\n SnapControllerGetRunnableSnapsAction,\n SnapControllerHandleRequestAction,\n} from '../snaps';\n\ntype SnapKeyring = {\n submitRequest: (request: {\n origin: string;\n account: string;\n method: string;\n params?: Json[] | Record<string, Json>;\n scope: CaipChainId;\n }) => Promise<Json>;\n};\n\n// Expecting a bound function that calls KeyringController.withKeyring selecting the Snap keyring\nexport type WithSnapKeyringFunction = <ReturnType>(\n operation: ({ keyring }: { keyring: SnapKeyring }) => Promise<ReturnType>,\n) => Promise<ReturnType>;\n\nexport type AccountsControllerListMultichainAccountsAction = {\n type: `AccountsController:listMultichainAccounts`;\n handler: (chainId?: CaipChainId) => InternalAccount[];\n};\n\nexport type MultichainRoutingServiceActions =\n MultichainRoutingServiceMethodActions;\n\ntype AllowedActions =\n | SnapControllerGetRunnableSnapsAction\n | SnapControllerHandleRequestAction\n | PermissionControllerGetPermissionsAction\n | AccountsControllerListMultichainAccountsAction;\n\nexport type MultichainRoutingServiceEvents = never;\n\nexport type MultichainRoutingServiceMessenger = Messenger<\n typeof name,\n MultichainRoutingServiceActions | AllowedActions\n>;\n\nexport type MultichainRoutingServiceArgs = {\n messenger: MultichainRoutingServiceMessenger;\n withSnapKeyring: WithSnapKeyringFunction;\n};\n\ntype ProtocolSnap = {\n snapId: SnapId;\n methods: string[];\n};\n\nconst name = 'MultichainRoutingService';\n\nconst MESSENGER_EXPOSED_METHODS = [\n 'handleRequest',\n 'getSupportedMethods',\n 'getSupportedAccounts',\n 'isSupportedScope',\n] as const;\n\nexport class MultichainRoutingService {\n name: typeof name = name;\n\n state = null;\n\n readonly #messenger: MultichainRoutingServiceMessenger;\n\n readonly #withSnapKeyring: WithSnapKeyringFunction;\n\n constructor({ messenger, withSnapKeyring }: MultichainRoutingServiceArgs) {\n this.#messenger = messenger;\n this.#withSnapKeyring = withSnapKeyring;\n\n this.#messenger.registerMethodActionHandlers(\n this,\n MESSENGER_EXPOSED_METHODS,\n );\n }\n\n /**\n * Attempts to resolve the account address to use for a given request by inspecting the request itself.\n *\n * The request is sent to an account Snap that will attempt this resolution.\n *\n * We manually construct the request instead of using the SnapKeyring, as the keyring may not be available.\n *\n * @param snapId - The ID of the Snap to send the request to.\n * @param scope - The CAIP-2 scope for the request.\n * @param request - The JSON-RPC request.\n * @returns The resolved address if found, otherwise null.\n * @throws If the invocation of the SnapKeyring fails.\n */\n async #resolveRequestAddress(\n snapId: SnapId,\n scope: CaipChainId,\n request: JsonRpcRequest,\n ) {\n try {\n const result = await this.#messenger.call(\n 'SnapController:handleRequest',\n {\n snapId,\n origin: 'metamask',\n request: {\n method: 'keyring_resolveAccountAddress',\n params: {\n request,\n scope,\n },\n },\n handler: HandlerType.OnKeyringRequest,\n },\n );\n\n assert(result === null || isObject(result));\n\n const address = result?.address as CaipAccountId;\n return address ? parseCaipAccountId(address).address : null;\n } catch {\n throw rpcErrors.internal();\n }\n }\n\n /**\n * Get the account ID of the account that should service the RPC request via an account Snap.\n *\n * This function checks whether any accounts exist that can service a given request by\n * using a combination of the resolveAccountAddress functionality and the connected accounts.\n *\n * If an account is expected to service this request but none is found, the function will throw.\n *\n * @param connectedAddresses - The CAIP-10 addresses connected to the\n * requesting origin for the requested scope.\n * @param scope - The CAIP-2 scope for the request.\n * @param request - The JSON-RPC request.\n * @returns An account ID if found, otherwise null.\n * @throws If no account is found, but the accounts exist that could service the request.\n */\n async #getSnapAccountId(\n connectedAddresses: CaipAccountId[],\n scope: CaipChainId,\n request: JsonRpcRequest,\n ) {\n const accounts = this.#getSupportedAccountsMetadata(scope, request.method);\n\n // If no accounts can service the request, return null.\n if (accounts.length === 0) {\n return null;\n }\n\n const parsedConnectedAddresses = connectedAddresses.map(\n (connectedAddress) => parseCaipAccountId(connectedAddress).address,\n );\n\n const connectedAccounts = accounts.filter((account) =>\n parsedConnectedAddresses.includes(account.address),\n );\n\n if (connectedAccounts.length === 0) {\n throw rpcErrors.invalidParams({\n message: 'No available account found for request.',\n });\n }\n\n const resolutionSnapId = connectedAccounts[0].metadata.snap.id;\n\n // Attempt to resolve the address that should be used for signing.\n const address = await this.#resolveRequestAddress(\n resolutionSnapId,\n scope,\n request,\n );\n\n // If we have a resolved address, try to find the selected account based on that\n // otherwise, default to one of the connected accounts.\n // TODO: Eventually let the user choose if we have more than one option for the account.\n const selectedAccount = address\n ? connectedAccounts.find((account) => account.address === address)\n : connectedAccounts[0];\n\n if (!selectedAccount) {\n throw rpcErrors.invalidParams({\n message: 'No available account found for request.',\n });\n }\n\n return selectedAccount.id;\n }\n\n /**\n * Get all protocol Snaps that can service a given CAIP-2 scope.\n *\n * Protocol Snaps are deemed fit to service a scope if they are runnable\n * and have the proper permissions set for the scope.\n *\n * @param scope - A CAIP-2 scope.\n * @returns A list of all the protocol Snaps available and their RPC methods.\n */\n #getProtocolSnaps(scope: CaipChainId) {\n const filteredSnaps = this.#messenger.call(\n 'SnapController:getRunnableSnaps',\n );\n\n return filteredSnaps.reduce<ProtocolSnap[]>((accumulator, snap) => {\n const permissions = this.#messenger.call(\n 'PermissionController:getPermissions',\n snap.id,\n );\n\n if (permissions && hasProperty(permissions, SnapEndowments.Protocol)) {\n const permission = permissions[SnapEndowments.Protocol];\n const scopes = getProtocolCaveatScopes(permission);\n if (scopes && hasProperty(scopes, scope)) {\n accumulator.push({\n snapId: snap.id,\n methods: scopes[scope].methods,\n });\n }\n }\n\n return accumulator;\n }, []);\n }\n\n /**\n * Handle an incoming JSON-RPC request tied to a specific scope by routing\n * to either a protocol Snap or an account Snap.\n *\n * Note: Addresses are considered case-sensitive by the MultichainRoutingService as\n * not all non-EVM chains are case-insensitive.\n *\n * @param options - An options bag.\n * @param options.connectedAddresses - Addresses currently connected to the\n * origin for the requested scope.\n * @param options.origin - The origin of the RPC request.\n * @param options.request - The JSON-RPC request.\n * @param options.scope - The CAIP-2 scope for the request.\n * @returns The response from the chosen Snap.\n * @throws If no handler was found.\n */\n async handleRequest({\n connectedAddresses,\n origin,\n scope,\n request: rawRequest,\n }: {\n connectedAddresses: CaipAccountId[];\n origin: string;\n scope: CaipChainId;\n request: JsonRpcRequest;\n }): Promise<Json> {\n // Explicitly block EVM scopes, just in case.\n assert(\n !scope.startsWith(KnownCaipNamespace.Eip155) &&\n !scope.startsWith('wallet:eip155'),\n );\n\n // Re-create the request to simplify and remove additional properties that may be present in MM middleware.\n const request = {\n jsonrpc: '2.0' as const,\n id: rawRequest.id ?? nanoid(),\n method: rawRequest.method,\n ...(rawRequest.params ? { params: rawRequest.params } : {}),\n };\n\n const { method, params } = request;\n\n // If the RPC request can be serviced by an account Snap, route it there.\n const accountId = await this.#getSnapAccountId(\n connectedAddresses,\n scope,\n request,\n );\n\n if (accountId) {\n return this.#withSnapKeyring(async ({ keyring }) =>\n keyring.submitRequest({\n origin,\n account: accountId,\n scope,\n method,\n params: params as JsonRpcParams,\n }),\n );\n }\n\n // If the RPC request cannot be serviced by an account Snap,\n // but has a protocol Snap available, route it there.\n const protocolSnaps = this.#getProtocolSnaps(scope);\n const protocolSnap = protocolSnaps.find((snap) =>\n snap.methods.includes(method),\n );\n\n if (protocolSnap) {\n return this.#messenger.call('SnapController:handleRequest', {\n snapId: protocolSnap.snapId,\n origin,\n request: {\n method: '',\n params: {\n request,\n scope,\n },\n },\n handler: HandlerType.OnProtocolRequest,\n }) as Promise<Json>;\n }\n\n // If no compatible account or protocol Snaps were found, throw.\n throw rpcErrors.methodNotFound();\n }\n\n /**\n * Get a list of metadata for supported accounts for a given scope from the client.\n *\n * @param scope - The CAIP-2 scope.\n * @param method - An optional method that the account should support.\n * @returns A list of metadata for the supported accounts.\n */\n #getSupportedAccountsMetadata(\n scope: CaipChainId,\n method?: string,\n ): (InternalAccount & {\n metadata: Required<InternalAccount['metadata']>;\n })[] {\n const runnableSnaps = this.#messenger\n .call('SnapController:getRunnableSnaps')\n .map((snap) => snap.id);\n\n return this.#messenger\n .call('AccountsController:listMultichainAccounts', scope)\n .filter(\n (\n account: InternalAccount,\n ): account is InternalAccount & {\n metadata: Required<InternalAccount['metadata']>;\n } =>\n account.metadata.snap?.id !== undefined &&\n runnableSnaps.includes(account.metadata.snap?.id) &&\n (method === undefined || account.methods.includes(method)),\n );\n }\n\n /**\n * Get a list of supported methods for a given scope.\n * This combines both protocol and account Snaps supported methods.\n *\n * @param scope - The CAIP-2 scope.\n * @returns A list of supported methods.\n */\n getSupportedMethods(scope: CaipChainId): string[] {\n const accountMethods = this.#getSupportedAccountsMetadata(scope).flatMap(\n (account) => account.methods,\n );\n\n const protocolMethods = this.#getProtocolSnaps(scope).flatMap(\n (snap) => snap.methods,\n );\n\n return Array.from(new Set([...accountMethods, ...protocolMethods]));\n }\n\n /**\n * Get a list of supported accounts for a given scope.\n *\n * @param scope - The CAIP-2 scope.\n * @returns A list of CAIP-10 addresses.\n */\n getSupportedAccounts(scope: CaipChainId): string[] {\n return this.#getSupportedAccountsMetadata(scope).map(\n (account) => `${scope}:${account.address}`,\n );\n }\n\n /**\n * Determine whether a given CAIP-2 scope is supported by the router.\n *\n * @param scope - The CAIP-2 scope.\n * @returns True if the router can service the scope, otherwise false.\n */\n isSupportedScope(scope: CaipChainId): boolean {\n // We currently assume here that if one Snap exists that service the scope, we can service the scope generally.\n return (\n this.#getSupportedAccountsMetadata(scope).length > 0 ||\n this.#getProtocolSnaps(scope).length > 0\n );\n }\n}\n"]} |
+1
-1
| { | ||
| "name": "@metamask/snaps-controllers", | ||
| "version": "20.0.2", | ||
| "version": "20.0.3", | ||
| "description": "Controllers for MetaMask Snaps", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
Sorry, the diff of this file is too big to display
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
2138836
0.04%13294
0.02%