@metamask/snaps-controllers 18.0.2 → 18.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/CHANGELOG.md +15 -1
  2. package/dist/interface/SnapInterfaceController.cjs +33 -0
  3. package/dist/interface/SnapInterfaceController.cjs.map +1 -1
  4. package/dist/interface/SnapInterfaceController.d.cts +53 -1
  5. package/dist/interface/SnapInterfaceController.d.cts.map +1 -1
  6. package/dist/interface/SnapInterfaceController.d.mts +53 -1
  7. package/dist/interface/SnapInterfaceController.d.mts.map +1 -1
  8. package/dist/interface/SnapInterfaceController.mjs +33 -0
  9. package/dist/interface/SnapInterfaceController.mjs.map +1 -1
  10. package/dist/services/AbstractExecutionService.cjs +7 -0
  11. package/dist/services/AbstractExecutionService.cjs.map +1 -1
  12. package/dist/services/AbstractExecutionService.d.cts.map +1 -1
  13. package/dist/services/AbstractExecutionService.d.mts.map +1 -1
  14. package/dist/services/AbstractExecutionService.mjs +7 -0
  15. package/dist/services/AbstractExecutionService.mjs.map +1 -1
  16. package/dist/snaps/SnapController.cjs +7 -0
  17. package/dist/snaps/SnapController.cjs.map +1 -1
  18. package/dist/snaps/SnapController.d.cts +2 -2
  19. package/dist/snaps/SnapController.d.cts.map +1 -1
  20. package/dist/snaps/SnapController.d.mts +2 -2
  21. package/dist/snaps/SnapController.d.mts.map +1 -1
  22. package/dist/snaps/SnapController.mjs +9 -2
  23. package/dist/snaps/SnapController.mjs.map +1 -1
  24. package/dist/snaps/constants.cjs +10 -1
  25. package/dist/snaps/constants.cjs.map +1 -1
  26. package/dist/snaps/constants.d.cts +4 -0
  27. package/dist/snaps/constants.d.cts.map +1 -1
  28. package/dist/snaps/constants.d.mts +4 -0
  29. package/dist/snaps/constants.d.mts.map +1 -1
  30. package/dist/snaps/constants.mjs +9 -0
  31. package/dist/snaps/constants.mjs.map +1 -1
  32. package/package.json +5 -5
@@ -1 +1 @@
1
- {"version":3,"file":"SnapInterfaceController.mjs","sourceRoot":"","sources":["../../src/interface/SnapInterfaceController.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n AcceptRequest,\n HasApprovalRequest,\n} from '@metamask/approval-controller';\nimport type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n} from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport type { Messenger } from '@metamask/messenger';\nimport type { HasPermission } from '@metamask/permission-controller';\nimport type {\n InterfaceState,\n SnapId,\n ComponentOrElement,\n InterfaceContext,\n FungibleAssetMetadata,\n} from '@metamask/snaps-sdk';\nimport { ContentType } from '@metamask/snaps-sdk';\nimport type { JSXElement } from '@metamask/snaps-sdk/jsx';\nimport type { InternalAccount } from '@metamask/snaps-utils';\nimport {\n getJsonSizeUnsafe,\n snapOwnsAccount,\n validateJsxElements,\n} from '@metamask/snaps-utils';\nimport type {\n CaipAccountId,\n CaipAssetType,\n CaipChainId,\n Json,\n} from '@metamask/utils';\nimport { assert, hasProperty, parseCaipAccountId } from '@metamask/utils';\nimport { castDraft } from 'immer';\nimport { nanoid } from 'nanoid';\n\nimport {\n constructState,\n getJsxInterface,\n isMatchingChainId,\n validateInterfaceContext,\n} from './utils';\nimport type { GetSnap } from '../snaps';\n\nconst MAX_UI_CONTENT_SIZE = 10_000_000; // 10 mb\n\nconst controllerName = 'SnapInterfaceController';\n\nexport type CreateInterface = {\n type: `${typeof controllerName}:createInterface`;\n handler: SnapInterfaceController['createInterface'];\n};\n\nexport type GetInterface = {\n type: `${typeof controllerName}:getInterface`;\n handler: SnapInterfaceController['getInterface'];\n};\n\nexport type UpdateInterface = {\n type: `${typeof controllerName}:updateInterface`;\n handler: SnapInterfaceController['updateInterface'];\n};\n\nexport type DeleteInterface = {\n type: `${typeof controllerName}:deleteInterface`;\n handler: SnapInterfaceController['deleteInterface'];\n};\n\nexport type UpdateInterfaceState = {\n type: `${typeof controllerName}:updateInterfaceState`;\n handler: SnapInterfaceController['updateInterfaceState'];\n};\n\nexport type ResolveInterface = {\n type: `${typeof controllerName}:resolveInterface`;\n handler: SnapInterfaceController['resolveInterface'];\n};\n\ntype AccountsControllerGetAccountByAddressAction = {\n type: `AccountsController:getAccountByAddress`;\n handler: (address: string) => InternalAccount | undefined;\n};\n\ntype AccountsControllerGetSelectedMultichainAccountAction = {\n type: `AccountsController:getSelectedMultichainAccount`;\n handler: () => InternalAccount;\n};\n\ntype AccountsControllerListMultichainAccountsAction = {\n type: `AccountsController:listMultichainAccounts`;\n handler: (chainId?: CaipChainId) => InternalAccount[];\n};\n\nexport type SnapInterfaceControllerGetStateAction = ControllerGetStateAction<\n typeof controllerName,\n SnapInterfaceControllerState\n>;\n\ntype MultichainAssetsControllerGetStateAction = ControllerGetStateAction<\n 'MultichainAssetsController',\n {\n assetsMetadata: {\n [asset: CaipAssetType]: FungibleAssetMetadata;\n };\n accountsAssets: { [account: string]: CaipAssetType[] };\n }\n>;\n\ntype PhishingControllerTestOrigin = {\n type: 'PhishingController:testOrigin';\n handler: (origin: string) => { result: boolean; type: string };\n};\n\nexport type SnapInterfaceControllerAllowedActions =\n | PhishingControllerTestOrigin\n | HasApprovalRequest\n | AcceptRequest\n | GetSnap\n | MultichainAssetsControllerGetStateAction\n | AccountsControllerGetSelectedMultichainAccountAction\n | AccountsControllerGetAccountByAddressAction\n | AccountsControllerListMultichainAccountsAction\n | HasPermission;\n\nexport type SnapInterfaceControllerActions =\n | CreateInterface\n | GetInterface\n | UpdateInterface\n | DeleteInterface\n | UpdateInterfaceState\n | ResolveInterface\n | SnapInterfaceControllerGetStateAction;\n\nexport type SnapInterfaceControllerStateChangeEvent =\n ControllerStateChangeEvent<\n typeof controllerName,\n SnapInterfaceControllerState\n >;\n\ntype OtherNotification = { type: string; [key: string]: unknown };\n\nexport type ExpandedView = {\n title: string;\n interfaceId: string;\n footerLink?: { href: string; text: string };\n};\n\ntype NormalSnapNotificationData = { message: string; origin: string };\n\ntype ExpandedSnapNotificationData = {\n message: string;\n origin: string;\n detailedView: ExpandedView;\n};\n\ntype SnapNotification = {\n type: 'snap';\n data: NormalSnapNotificationData | ExpandedSnapNotificationData;\n readDate: string | null;\n};\n\ntype Notification = OtherNotification | SnapNotification;\n\ntype NotificationListUpdatedEvent = {\n type: 'NotificationServicesController:notificationsListUpdated';\n payload: [Notification[]];\n};\n\nexport type SnapInterfaceControllerEvents =\n | SnapInterfaceControllerStateChangeEvent\n | NotificationListUpdatedEvent;\n\nexport type SnapInterfaceControllerMessenger = Messenger<\n typeof controllerName,\n SnapInterfaceControllerActions | SnapInterfaceControllerAllowedActions,\n SnapInterfaceControllerEvents\n>;\n\nexport type StoredInterface = {\n snapId: SnapId;\n content: JSXElement;\n state: InterfaceState;\n context: InterfaceContext | null;\n contentType: ContentType | null;\n};\n\nexport type SnapInterfaceControllerState = {\n interfaces: Record<string, StoredInterface>;\n};\n\nexport type SnapInterfaceControllerArgs = {\n messenger: SnapInterfaceControllerMessenger;\n state?: SnapInterfaceControllerState;\n};\n\n/**\n * Use this controller to manage snaps UI interfaces using RPC method hooks.\n */\nexport class SnapInterfaceController extends BaseController<\n typeof controllerName,\n SnapInterfaceControllerState,\n SnapInterfaceControllerMessenger\n> {\n constructor({ messenger, state }: SnapInterfaceControllerArgs) {\n super({\n messenger,\n metadata: {\n interfaces: {\n includeInStateLogs: true,\n persist: (interfaces: Record<string, StoredInterface>) => {\n return Object.entries(interfaces).reduce<\n Record<string, StoredInterface>\n >((persistedInterfaces, [id, snapInterface]) => {\n switch (snapInterface.contentType) {\n case ContentType.Notification:\n persistedInterfaces[id] = snapInterface;\n return persistedInterfaces;\n default:\n return persistedInterfaces;\n }\n }, {});\n },\n includeInDebugSnapshot: false,\n usedInUi: true,\n },\n },\n name: controllerName,\n state: { interfaces: {}, ...state },\n });\n\n this.messenger.subscribe(\n 'NotificationServicesController:notificationsListUpdated',\n this.#onNotificationsListUpdated.bind(this),\n );\n\n this.#registerMessageHandlers();\n }\n\n /**\n * Constructor helper for registering this controller's messaging system\n * actions.\n */\n #registerMessageHandlers() {\n this.messenger.registerActionHandler(\n `${controllerName}:createInterface`,\n this.createInterface.bind(this),\n );\n\n this.messenger.registerActionHandler(\n `${controllerName}:getInterface`,\n this.getInterface.bind(this),\n );\n\n this.messenger.registerActionHandler(\n `${controllerName}:updateInterface`,\n this.updateInterface.bind(this),\n );\n\n this.messenger.registerActionHandler(\n `${controllerName}:deleteInterface`,\n this.deleteInterface.bind(this),\n );\n\n this.messenger.registerActionHandler(\n `${controllerName}:updateInterfaceState`,\n this.updateInterfaceState.bind(this),\n );\n\n this.messenger.registerActionHandler(\n `${controllerName}:resolveInterface`,\n this.resolveInterface.bind(this),\n );\n }\n\n /**\n * Create an interface in the controller state with the associated data.\n *\n * @param snapId - The snap id that created the interface.\n * @param content - The interface content.\n * @param context - An optional interface context object.\n * @param contentType - The type of content.\n * @returns The newly interface id.\n */\n createInterface(\n snapId: SnapId,\n content: ComponentOrElement,\n context?: InterfaceContext,\n contentType?: ContentType,\n ) {\n const element = getJsxInterface(content);\n this.#validateContent(snapId, element);\n validateInterfaceContext(context);\n\n const id = nanoid();\n const componentState = constructState({}, element, {\n getAssetsState: this.#getAssetsState.bind(this),\n getAccountByAddress: this.#getAccountByAddress.bind(this),\n getSelectedAccount: this.#getSelectedAccount.bind(this),\n listAccounts: this.#listAccounts.bind(this),\n snapOwnsAccount: (account: InternalAccount) =>\n snapOwnsAccount(snapId, account),\n });\n\n this.update((draftState) => {\n // @ts-expect-error - TS2589: Type instantiation is excessively deep and\n // possibly infinite.\n draftState.interfaces[id] = {\n snapId,\n content: castDraft(element),\n state: componentState,\n context: context ?? null,\n contentType: contentType ?? null,\n };\n });\n\n return id;\n }\n\n /**\n * Get the data of a given interface id.\n *\n * @param snapId - The snap id requesting the interface data.\n * @param id - The interface id.\n * @returns The interface state.\n */\n getInterface(snapId: SnapId, id: string) {\n this.#validateArgs(snapId, id);\n\n return this.state.interfaces[id];\n }\n\n /**\n * Update the interface with the given content.\n *\n * @param snapId - The snap id requesting the update.\n * @param id - The interface id.\n * @param content - The new content.\n * @param context - An optional interface context object.\n */\n updateInterface(\n snapId: SnapId,\n id: string,\n content: ComponentOrElement,\n context?: InterfaceContext,\n ) {\n this.#validateArgs(snapId, id);\n const element = getJsxInterface(content);\n this.#validateContent(snapId, element);\n validateInterfaceContext(context);\n\n const oldState = this.state.interfaces[id].state;\n const newState = constructState(oldState, element, {\n getAssetsState: this.#getAssetsState.bind(this),\n getAccountByAddress: this.#getAccountByAddress.bind(this),\n getSelectedAccount: this.#getSelectedAccount.bind(this),\n listAccounts: this.#listAccounts.bind(this),\n snapOwnsAccount: (account: InternalAccount) =>\n snapOwnsAccount(snapId, account),\n });\n\n this.update((draftState) => {\n draftState.interfaces[id].state = newState;\n draftState.interfaces[id].content = castDraft(element);\n if (context) {\n draftState.interfaces[id].context = context;\n }\n });\n }\n\n /**\n * Delete an interface from state.\n *\n * @param id - The interface id.\n */\n deleteInterface(id: string) {\n this.update((draftState) => {\n delete draftState.interfaces[id];\n });\n }\n\n /**\n * Update the interface state.\n *\n * @param id - The interface id.\n * @param state - The new state.\n */\n updateInterfaceState(id: string, state: InterfaceState) {\n this.update((draftState) => {\n draftState.interfaces[id].state = state;\n });\n }\n\n /**\n * Resolve the promise of a given interface approval request.\n * The approval needs to have the same ID as the interface.\n *\n * @param snapId - The snap id.\n * @param id - The interface id.\n * @param value - The value to resolve the promise with.\n */\n async resolveInterface(snapId: SnapId, id: string, value: Json) {\n this.#validateArgs(snapId, id);\n this.#validateApproval(id);\n\n await this.#acceptApprovalRequest(id, value);\n\n this.deleteInterface(id);\n }\n\n /**\n * Utility function to validate the args passed to the other methods.\n *\n * @param snapId - The snap id.\n * @param id - The interface id.\n */\n #validateArgs(snapId: SnapId, id: string) {\n const existingInterface = this.state.interfaces[id];\n\n assert(\n existingInterface !== undefined,\n `Interface with id '${id}' not found.`,\n );\n assert(\n existingInterface.snapId === snapId,\n `Interface not created by ${snapId}.`,\n );\n }\n\n /**\n * Utility function to validate that the approval request exists.\n *\n * @param id - The interface id.\n */\n #validateApproval(id: string) {\n assert(\n this.#hasApprovalRequest(id),\n `Approval request with id '${id}' not found.`,\n );\n }\n\n /**\n * Check an origin against the phishing list.\n *\n * @param origin - The origin to check.\n * @returns True if the origin is on the phishing list, otherwise false.\n */\n #checkPhishingList(origin: string) {\n return this.messenger.call('PhishingController:testOrigin', origin).result;\n }\n\n /**\n * Check if an approval request exists for a given interface by looking up\n * if the ApprovalController has a request with the given interface ID.\n *\n * @param id - The interface id.\n * @returns True if an approval request exists, otherwise false.\n */\n #hasApprovalRequest(id: string) {\n return this.messenger.call('ApprovalController:hasRequest', {\n id,\n });\n }\n\n /**\n * Accept an approval request for a given interface.\n *\n * @param id - The interface id.\n * @param value - The value to resolve the promise with.\n */\n async #acceptApprovalRequest(id: string, value: Json) {\n await this.messenger.call('ApprovalController:acceptRequest', id, value);\n }\n\n /**\n * Get the selected account in the client.\n *\n * @returns The selected account.\n */\n #getSelectedAccount() {\n return this.messenger.call(\n 'AccountsController:getSelectedMultichainAccount',\n );\n }\n\n /**\n * Get a list of accounts for the given chain IDs.\n *\n * @param chainIds - The chain IDs to get the accounts for.\n * @returns The list of accounts.\n */\n #listAccounts(chainIds?: CaipChainId[]) {\n const accounts = this.messenger.call(\n 'AccountsController:listMultichainAccounts',\n );\n\n if (!chainIds || chainIds.length === 0) {\n return accounts;\n }\n\n return accounts.filter((account) =>\n account.scopes.some((scope) => isMatchingChainId(scope, chainIds)),\n );\n }\n\n /**\n * Get an account by its address.\n *\n * @param address - The account address.\n * @returns The account or undefined if not found.\n */\n #getAccountByAddress(address: CaipAccountId) {\n const { address: parsedAddress } = parseCaipAccountId(address);\n\n return this.messenger.call(\n 'AccountsController:getAccountByAddress',\n parsedAddress,\n );\n }\n\n /**\n * Get the MultichainAssetsController state.\n *\n * @returns The MultichainAssetsController state.\n */\n #getAssetsState() {\n return this.messenger.call('MultichainAssetsController:getState');\n }\n\n /**\n * Get a snap by its id.\n *\n * @param id - The snap id.\n * @returns The snap.\n */\n #getSnap(id: string) {\n return this.messenger.call('SnapController:get', id);\n }\n\n #hasPermission(snapId: SnapId, permission: string) {\n return this.messenger.call(\n 'PermissionController:hasPermission',\n snapId,\n permission,\n );\n }\n\n /**\n * Utility function to validate the components of an interface.\n * Throws if something is invalid.\n *\n * @param snapId - The Snap ID.\n * @param element - The JSX element to verify.\n */\n #validateContent(snapId: SnapId, element: JSXElement) {\n // We assume the validity of this JSON to be validated by the caller.\n // E.g., in the RPC method implementation.\n const size = getJsonSizeUnsafe(element);\n assert(\n size <= MAX_UI_CONTENT_SIZE,\n `A Snap UI may not be larger than ${MAX_UI_CONTENT_SIZE / 1000000} MB.`,\n );\n\n validateJsxElements(element, {\n isOnPhishingList: this.#checkPhishingList.bind(this),\n getSnap: this.#getSnap.bind(this),\n getAccountByAddress: this.#getAccountByAddress.bind(this),\n hasPermission: this.#hasPermission.bind(this, snapId),\n });\n }\n\n #onNotificationsListUpdated(notificationsList: Notification[]) {\n const snapNotificationsWithInterface = notificationsList.filter(\n (notification) => {\n return (\n notification.type === 'snap' &&\n hasProperty((notification as SnapNotification).data, 'detailedView')\n );\n },\n );\n\n const interfaceIdSet = new Set(\n snapNotificationsWithInterface.map(\n (notification) =>\n (\n (notification as SnapNotification)\n .data as ExpandedSnapNotificationData\n ).detailedView.interfaceId,\n ),\n );\n\n this.update((state) => {\n Object.entries(state.interfaces).forEach(([id, snapInterface]) => {\n if (\n snapInterface.contentType === ContentType.Notification &&\n !interfaceIdSet.has(id)\n ) {\n delete state.interfaces[id];\n }\n });\n });\n }\n}\n"]}
1
+ {"version":3,"file":"SnapInterfaceController.mjs","sourceRoot":"","sources":["../../src/interface/SnapInterfaceController.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,cAAc,EAAE,kCAAkC;AAU3D,OAAO,EAAE,WAAW,EAAE,4BAA4B;AAGlD,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,mBAAmB,EACpB,8BAA8B;AAO/B,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,kBAAkB,EAAE,wBAAwB;AAC1E,OAAO,EAAE,SAAS,EAAE,cAAc;AAClC,OAAO,EAAE,MAAM,EAAE,eAAe;AAEhC,OAAO,EACL,cAAc,EACd,eAAe,EACf,iBAAiB,EACjB,wBAAwB,EACzB,oBAAgB;AAGjB,MAAM,mBAAmB,GAAG,QAAU,CAAC,CAAC,QAAQ;AAEhD,MAAM,cAAc,GAAG,yBAAyB,CAAC;AAsKjD;;GAEG;AACH,MAAM,OAAO,uBAAwB,SAAQ,cAI5C;IACC,YAAY,EAAE,SAAS,EAAE,KAAK,EAA+B;QAC3D,KAAK,CAAC;YACJ,SAAS;YACT,QAAQ,EAAE;gBACR,UAAU,EAAE;oBACV,kBAAkB,EAAE,IAAI;oBACxB,OAAO,EAAE,CAAC,UAA2C,EAAE,EAAE;wBACvD,OAAO,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,MAAM,CAEtC,CAAC,mBAAmB,EAAE,CAAC,EAAE,EAAE,aAAa,CAAC,EAAE,EAAE;4BAC7C,QAAQ,aAAa,CAAC,WAAW,EAAE,CAAC;gCAClC,KAAK,WAAW,CAAC,YAAY;oCAC3B,mBAAmB,CAAC,EAAE,CAAC,GAAG,aAAa,CAAC;oCACxC,OAAO,mBAAmB,CAAC;gCAC7B;oCACE,OAAO,mBAAmB,CAAC;4BAC/B,CAAC;wBACH,CAAC,EAAE,EAAE,CAAC,CAAC;oBACT,CAAC;oBACD,sBAAsB,EAAE,KAAK;oBAC7B,QAAQ,EAAE,IAAI;iBACf;aACF;YACD,IAAI,EAAE,cAAc;YACpB,KAAK,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,GAAG,KAAK,EAAE;SACpC,CAAC,CAAC;QAEH,IAAI,CAAC,SAAS,CAAC,SAAS,CACtB,yDAAyD,EACzD,IAAI,CAAC,2BAA2B,CAAC,IAAI,CAAC,IAAI,CAAC,CAC5C,CAAC;QAEF,IAAI,CAAC,wBAAwB,EAAE,CAAC;IAClC,CAAC;IAED;;;OAGG;IACH,wBAAwB;QACtB,IAAI,CAAC,SAAS,CAAC,qBAAqB,CAClC,GAAG,cAAc,kBAAkB,EACnC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAChC,CAAC;QAEF,IAAI,CAAC,SAAS,CAAC,qBAAqB,CAClC,GAAG,cAAc,eAAe,EAChC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAC7B,CAAC;QAEF,IAAI,CAAC,SAAS,CAAC,qBAAqB,CAClC,GAAG,cAAc,oBAAoB,EACrC,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAClC,CAAC;QAEF,IAAI,CAAC,SAAS,CAAC,qBAAqB,CAClC,GAAG,cAAc,kBAAkB,EACnC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAChC,CAAC;QAEF,IAAI,CAAC,SAAS,CAAC,qBAAqB,CAClC,GAAG,cAAc,kBAAkB,EACnC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAChC,CAAC;QAEF,IAAI,CAAC,SAAS,CAAC,qBAAqB,CAClC,GAAG,cAAc,uBAAuB,EACxC,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,CACrC,CAAC;QAEF,IAAI,CAAC,SAAS,CAAC,qBAAqB,CAClC,GAAG,cAAc,mBAAmB,EACpC,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,CACjC,CAAC;QAEF,IAAI,CAAC,SAAS,CAAC,qBAAqB,CAClC,GAAG,cAAc,wBAAwB,EACzC,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC,CACtC,CAAC;IACJ,CAAC;IAED;;;;;;;;OAQG;IACH,eAAe,CACb,MAAc,EACd,OAA2B,EAC3B,OAA0B,EAC1B,WAAyB;QAEzB,MAAM,OAAO,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;QACzC,IAAI,CAAC,gBAAgB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACvC,wBAAwB,CAAC,OAAO,CAAC,CAAC;QAElC,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC;QACpB,MAAM,cAAc,GAAG,cAAc,CAAC,EAAE,EAAE,OAAO,EAAE;YACjD,cAAc,EAAE,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC;YAC/C,mBAAmB,EAAE,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC;YACzD,kBAAkB,EAAE,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC;YACvD,YAAY,EAAE,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC;YAC3C,eAAe,EAAE,CAAC,OAAwB,EAAE,EAAE,CAC5C,eAAe,CAAC,MAAM,EAAE,OAAO,CAAC;SACnC,CAAC,CAAC;QAEH,IAAI,CAAC,MAAM,CAAC,CAAC,UAAU,EAAE,EAAE;YACzB,wEAAwE;YACxE,qBAAqB;YACrB,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC,GAAG;gBAC1B,MAAM;gBACN,OAAO,EAAE,SAAS,CAAC,OAAO,CAAC;gBAC3B,KAAK,EAAE,cAAc;gBACrB,OAAO,EAAE,OAAO,IAAI,IAAI;gBACxB,WAAW,EAAE,WAAW,IAAI,IAAI;gBAChC,SAAS,EAAE,KAAK;aACjB,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,OAAO,EAAE,CAAC;IACZ,CAAC;IAED;;;;;;OAMG;IACH,YAAY,CAAC,MAAc,EAAE,EAAU;QACrC,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QAE/B,OAAO,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;IACnC,CAAC;IAED;;;;;;;OAOG;IACH,iBAAiB,CAAC,MAAc,EAAE,EAAU;QAC1C,MAAM,eAAe,GAAG,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QACtD,IAAI,CAAC,eAAe,CAAC,SAAS,EAAE,CAAC;YAC/B,MAAM,IAAI,KAAK,CACb,sBAAsB,EAAE,sHAAsH,CAC/I,CAAC;QACJ,CAAC;QAED,OAAO,eAAe,CAAC,KAAK,CAAC;IAC/B,CAAC;IAED;;;;;;;OAOG;IACH,eAAe,CACb,MAAc,EACd,EAAU,EACV,OAA2B,EAC3B,OAA0B;QAE1B,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QAC/B,MAAM,OAAO,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;QACzC,IAAI,CAAC,gBAAgB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACvC,wBAAwB,CAAC,OAAO,CAAC,CAAC;QAElC,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC;QACjD,MAAM,QAAQ,GAAG,cAAc,CAAC,QAAQ,EAAE,OAAO,EAAE;YACjD,cAAc,EAAE,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC;YAC/C,mBAAmB,EAAE,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC;YACzD,kBAAkB,EAAE,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC;YACvD,YAAY,EAAE,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC;YAC3C,eAAe,EAAE,CAAC,OAAwB,EAAE,EAAE,CAC5C,eAAe,CAAC,MAAM,EAAE,OAAO,CAAC;SACnC,CAAC,CAAC;QAEH,IAAI,CAAC,MAAM,CAAC,CAAC,UAAU,EAAE,EAAE;YACzB,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,KAAK,GAAG,QAAQ,CAAC;YAC3C,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,OAAO,GAAG,SAAS,CAAC,OAAO,CAAC,CAAC;YACvD,IAAI,OAAO,EAAE,CAAC;gBACZ,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,OAAO,GAAG,OAAO,CAAC;YAC9C,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;;OAIG;IACH,eAAe,CAAC,EAAU;QACxB,IAAI,CAAC,MAAM,CAAC,CAAC,UAAU,EAAE,EAAE;YACzB,OAAO,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;QACnC,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;;;OAKG;IACH,oBAAoB,CAAC,EAAU,EAAE,KAAqB;QACpD,IAAI,CAAC,MAAM,CAAC,CAAC,UAAU,EAAE,EAAE;YACzB,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,KAAK,GAAG,KAAK,CAAC;QAC1C,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,gBAAgB,CAAC,MAAc,EAAE,EAAU,EAAE,KAAW;QAC5D,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QAC/B,IAAI,CAAC,iBAAiB,CAAC,EAAE,CAAC,CAAC;QAE3B,MAAM,IAAI,CAAC,sBAAsB,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;QAE7C,IAAI,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC;IAC3B,CAAC;IAED;;;;;OAKG;IACH,qBAAqB,CAAC,MAAc,EAAE,EAAU;QAC9C,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QAE/B,IAAI,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,SAAS,EAAE,CAAC;YACxC,OAAO;QACT,CAAC;QAED,IAAI,CAAC,MAAM,CAAC,CAAC,UAAU,EAAE,EAAE;YACzB,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,SAAS,GAAG,IAAI,CAAC;QAC7C,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;;;OAKG;IACH,aAAa,CAAC,MAAc,EAAE,EAAU;QACtC,MAAM,iBAAiB,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;QAEpD,MAAM,CACJ,iBAAiB,KAAK,SAAS,EAC/B,sBAAsB,EAAE,cAAc,CACvC,CAAC;QACF,MAAM,CACJ,iBAAiB,CAAC,MAAM,KAAK,MAAM,EACnC,4BAA4B,MAAM,GAAG,CACtC,CAAC;IACJ,CAAC;IAED;;;;OAIG;IACH,iBAAiB,CAAC,EAAU;QAC1B,MAAM,CACJ,IAAI,CAAC,mBAAmB,CAAC,EAAE,CAAC,EAC5B,6BAA6B,EAAE,cAAc,CAC9C,CAAC;IACJ,CAAC;IAED;;;;;OAKG;IACH,kBAAkB,CAAC,MAAc;QAC/B,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,+BAA+B,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC;IAC7E,CAAC;IAED;;;;;;OAMG;IACH,mBAAmB,CAAC,EAAU;QAC5B,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,+BAA+B,EAAE;YAC1D,EAAE;SACH,CAAC,CAAC;IACL,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,sBAAsB,CAAC,EAAU,EAAE,KAAW;QAClD,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,kCAAkC,EAAE,EAAE,EAAE,KAAK,CAAC,CAAC;IAC3E,CAAC;IAED;;;;OAIG;IACH,mBAAmB;QACjB,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CACxB,iDAAiD,CAClD,CAAC;IACJ,CAAC;IAED;;;;;OAKG;IACH,aAAa,CAAC,QAAwB;QACpC,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAClC,2CAA2C,CAC5C,CAAC;QAEF,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvC,OAAO,QAAQ,CAAC;QAClB,CAAC;QAED,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CACjC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,iBAAiB,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,CACnE,CAAC;IACJ,CAAC;IAED;;;;;OAKG;IACH,oBAAoB,CAAC,OAAsB;QACzC,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC;QAE/D,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CACxB,wCAAwC,EACxC,aAAa,CACd,CAAC;IACJ,CAAC;IAED;;;;OAIG;IACH,eAAe;QACb,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,qCAAqC,CAAC,CAAC;IACpE,CAAC;IAED;;;;;OAKG;IACH,QAAQ,CAAC,EAAU;QACjB,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,oBAAoB,EAAE,EAAE,CAAC,CAAC;IACvD,CAAC;IAED,cAAc,CAAC,MAAc,EAAE,UAAkB;QAC/C,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CACxB,oCAAoC,EACpC,MAAM,EACN,UAAU,CACX,CAAC;IACJ,CAAC;IAED;;;;;;OAMG;IACH,gBAAgB,CAAC,MAAc,EAAE,OAAmB;QAClD,qEAAqE;QACrE,0CAA0C;QAC1C,MAAM,IAAI,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC;QACxC,MAAM,CACJ,IAAI,IAAI,mBAAmB,EAC3B,oCAAoC,mBAAmB,GAAG,OAAO,MAAM,CACxE,CAAC;QAEF,mBAAmB,CAAC,OAAO,EAAE;YAC3B,gBAAgB,EAAE,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC;YACpD,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC;YACjC,mBAAmB,EAAE,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC;YACzD,aAAa,EAAE,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC;SACtD,CAAC,CAAC;IACL,CAAC;IAED,2BAA2B,CAAC,iBAAiC;QAC3D,MAAM,8BAA8B,GAAG,iBAAiB,CAAC,MAAM,CAC7D,CAAC,YAAY,EAAE,EAAE;YACf,OAAO,CACL,YAAY,CAAC,IAAI,KAAK,MAAM;gBAC5B,WAAW,CAAE,YAAiC,CAAC,IAAI,EAAE,cAAc,CAAC,CACrE,CAAC;QACJ,CAAC,CACF,CAAC;QAEF,MAAM,cAAc,GAAG,IAAI,GAAG,CAC5B,8BAA8B,CAAC,GAAG,CAChC,CAAC,YAAY,EAAE,EAAE,CAEZ,YAAiC;aAC/B,IACJ,CAAC,YAAY,CAAC,WAAW,CAC7B,CACF,CAAC;QAEF,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;YACpB,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,aAAa,CAAC,EAAE,EAAE;gBAC/D,IACE,aAAa,CAAC,WAAW,KAAK,WAAW,CAAC,YAAY;oBACtD,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE,CAAC,EACvB,CAAC;oBACD,OAAO,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;gBAC9B,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;CACF","sourcesContent":["import type {\n AcceptRequest,\n HasApprovalRequest,\n} from '@metamask/approval-controller';\nimport type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n} from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport type { Messenger } from '@metamask/messenger';\nimport type { HasPermission } from '@metamask/permission-controller';\nimport type {\n InterfaceState,\n SnapId,\n ComponentOrElement,\n InterfaceContext,\n FungibleAssetMetadata,\n} from '@metamask/snaps-sdk';\nimport { ContentType } from '@metamask/snaps-sdk';\nimport type { JSXElement } from '@metamask/snaps-sdk/jsx';\nimport type { InternalAccount } from '@metamask/snaps-utils';\nimport {\n getJsonSizeUnsafe,\n snapOwnsAccount,\n validateJsxElements,\n} from '@metamask/snaps-utils';\nimport type {\n CaipAccountId,\n CaipAssetType,\n CaipChainId,\n Json,\n} from '@metamask/utils';\nimport { assert, hasProperty, parseCaipAccountId } from '@metamask/utils';\nimport { castDraft } from 'immer';\nimport { nanoid } from 'nanoid';\n\nimport {\n constructState,\n getJsxInterface,\n isMatchingChainId,\n validateInterfaceContext,\n} from './utils';\nimport type { GetSnap } from '../snaps';\n\nconst MAX_UI_CONTENT_SIZE = 10_000_000; // 10 mb\n\nconst controllerName = 'SnapInterfaceController';\n\nexport type CreateInterface = {\n type: `${typeof controllerName}:createInterface`;\n handler: SnapInterfaceController['createInterface'];\n};\n\nexport type GetInterface = {\n type: `${typeof controllerName}:getInterface`;\n handler: SnapInterfaceController['getInterface'];\n};\n\nexport type UpdateInterface = {\n type: `${typeof controllerName}:updateInterface`;\n handler: SnapInterfaceController['updateInterface'];\n};\n\nexport type DeleteInterface = {\n type: `${typeof controllerName}:deleteInterface`;\n handler: SnapInterfaceController['deleteInterface'];\n};\n\nexport type UpdateInterfaceState = {\n type: `${typeof controllerName}:updateInterfaceState`;\n handler: SnapInterfaceController['updateInterfaceState'];\n};\n\nexport type ResolveInterface = {\n type: `${typeof controllerName}:resolveInterface`;\n handler: SnapInterfaceController['resolveInterface'];\n};\n\nexport type SnapInterfaceControllerGetInterfaceStateAction = {\n type: `${typeof controllerName}:getInterfaceState`;\n handler: SnapInterfaceController['getInterfaceState'];\n};\n\nexport type SnapInterfaceControllerSetInterfaceDisplayedAction = {\n type: `${typeof controllerName}:setInterfaceDisplayed`;\n handler: SnapInterfaceController['setInterfaceDisplayed'];\n};\n\ntype AccountsControllerGetAccountByAddressAction = {\n type: `AccountsController:getAccountByAddress`;\n handler: (address: string) => InternalAccount | undefined;\n};\n\ntype AccountsControllerGetSelectedMultichainAccountAction = {\n type: `AccountsController:getSelectedMultichainAccount`;\n handler: () => InternalAccount;\n};\n\ntype AccountsControllerListMultichainAccountsAction = {\n type: `AccountsController:listMultichainAccounts`;\n handler: (chainId?: CaipChainId) => InternalAccount[];\n};\n\nexport type SnapInterfaceControllerGetStateAction = ControllerGetStateAction<\n typeof controllerName,\n SnapInterfaceControllerState\n>;\n\ntype MultichainAssetsControllerGetStateAction = ControllerGetStateAction<\n 'MultichainAssetsController',\n {\n assetsMetadata: {\n [asset: CaipAssetType]: FungibleAssetMetadata;\n };\n accountsAssets: { [account: string]: CaipAssetType[] };\n }\n>;\n\ntype PhishingControllerTestOrigin = {\n type: 'PhishingController:testOrigin';\n handler: (origin: string) => { result: boolean; type: string };\n};\n\nexport type SnapInterfaceControllerAllowedActions =\n | PhishingControllerTestOrigin\n | HasApprovalRequest\n | AcceptRequest\n | GetSnap\n | MultichainAssetsControllerGetStateAction\n | AccountsControllerGetSelectedMultichainAccountAction\n | AccountsControllerGetAccountByAddressAction\n | AccountsControllerListMultichainAccountsAction\n | HasPermission;\n\nexport type SnapInterfaceControllerActions =\n | CreateInterface\n | GetInterface\n | UpdateInterface\n | DeleteInterface\n | UpdateInterfaceState\n | ResolveInterface\n | SnapInterfaceControllerGetInterfaceStateAction\n | SnapInterfaceControllerSetInterfaceDisplayedAction\n | SnapInterfaceControllerGetStateAction;\n\nexport type SnapInterfaceControllerStateChangeEvent =\n ControllerStateChangeEvent<\n typeof controllerName,\n SnapInterfaceControllerState\n >;\n\ntype OtherNotification = { type: string; [key: string]: unknown };\n\nexport type ExpandedView = {\n title: string;\n interfaceId: string;\n footerLink?: { href: string; text: string };\n};\n\ntype NormalSnapNotificationData = { message: string; origin: string };\n\ntype ExpandedSnapNotificationData = {\n message: string;\n origin: string;\n detailedView: ExpandedView;\n};\n\ntype SnapNotification = {\n type: 'snap';\n data: NormalSnapNotificationData | ExpandedSnapNotificationData;\n readDate: string | null;\n};\n\ntype Notification = OtherNotification | SnapNotification;\n\ntype NotificationListUpdatedEvent = {\n type: 'NotificationServicesController:notificationsListUpdated';\n payload: [Notification[]];\n};\n\nexport type SnapInterfaceControllerEvents =\n | SnapInterfaceControllerStateChangeEvent\n | NotificationListUpdatedEvent;\n\nexport type SnapInterfaceControllerMessenger = Messenger<\n typeof controllerName,\n SnapInterfaceControllerActions | SnapInterfaceControllerAllowedActions,\n SnapInterfaceControllerEvents\n>;\n\nexport type StoredInterface = {\n snapId: SnapId;\n content: JSXElement;\n state: InterfaceState;\n context: InterfaceContext | null;\n contentType: ContentType | null;\n\n /**\n * Whether the interface has been displayed in the UI at least once.\n */\n displayed: boolean;\n};\n\nexport type SnapInterfaceControllerState = {\n interfaces: Record<string, StoredInterface>;\n};\n\nexport type SnapInterfaceControllerArgs = {\n messenger: SnapInterfaceControllerMessenger;\n state?: SnapInterfaceControllerState;\n};\n\n/**\n * Use this controller to manage snaps UI interfaces using RPC method hooks.\n */\nexport class SnapInterfaceController extends BaseController<\n typeof controllerName,\n SnapInterfaceControllerState,\n SnapInterfaceControllerMessenger\n> {\n constructor({ messenger, state }: SnapInterfaceControllerArgs) {\n super({\n messenger,\n metadata: {\n interfaces: {\n includeInStateLogs: true,\n persist: (interfaces: Record<string, StoredInterface>) => {\n return Object.entries(interfaces).reduce<\n Record<string, StoredInterface>\n >((persistedInterfaces, [id, snapInterface]) => {\n switch (snapInterface.contentType) {\n case ContentType.Notification:\n persistedInterfaces[id] = snapInterface;\n return persistedInterfaces;\n default:\n return persistedInterfaces;\n }\n }, {});\n },\n includeInDebugSnapshot: false,\n usedInUi: true,\n },\n },\n name: controllerName,\n state: { interfaces: {}, ...state },\n });\n\n this.messenger.subscribe(\n 'NotificationServicesController:notificationsListUpdated',\n this.#onNotificationsListUpdated.bind(this),\n );\n\n this.#registerMessageHandlers();\n }\n\n /**\n * Constructor helper for registering this controller's messaging system\n * actions.\n */\n #registerMessageHandlers() {\n this.messenger.registerActionHandler(\n `${controllerName}:createInterface`,\n this.createInterface.bind(this),\n );\n\n this.messenger.registerActionHandler(\n `${controllerName}:getInterface`,\n this.getInterface.bind(this),\n );\n\n this.messenger.registerActionHandler(\n `${controllerName}:getInterfaceState`,\n this.getInterfaceState.bind(this),\n );\n\n this.messenger.registerActionHandler(\n `${controllerName}:updateInterface`,\n this.updateInterface.bind(this),\n );\n\n this.messenger.registerActionHandler(\n `${controllerName}:deleteInterface`,\n this.deleteInterface.bind(this),\n );\n\n this.messenger.registerActionHandler(\n `${controllerName}:updateInterfaceState`,\n this.updateInterfaceState.bind(this),\n );\n\n this.messenger.registerActionHandler(\n `${controllerName}:resolveInterface`,\n this.resolveInterface.bind(this),\n );\n\n this.messenger.registerActionHandler(\n `${controllerName}:setInterfaceDisplayed`,\n this.setInterfaceDisplayed.bind(this),\n );\n }\n\n /**\n * Create an interface in the controller state with the associated data.\n *\n * @param snapId - The snap id that created the interface.\n * @param content - The interface content.\n * @param context - An optional interface context object.\n * @param contentType - The type of content.\n * @returns The newly interface id.\n */\n createInterface(\n snapId: SnapId,\n content: ComponentOrElement,\n context?: InterfaceContext,\n contentType?: ContentType,\n ) {\n const element = getJsxInterface(content);\n this.#validateContent(snapId, element);\n validateInterfaceContext(context);\n\n const id = nanoid();\n const componentState = constructState({}, element, {\n getAssetsState: this.#getAssetsState.bind(this),\n getAccountByAddress: this.#getAccountByAddress.bind(this),\n getSelectedAccount: this.#getSelectedAccount.bind(this),\n listAccounts: this.#listAccounts.bind(this),\n snapOwnsAccount: (account: InternalAccount) =>\n snapOwnsAccount(snapId, account),\n });\n\n this.update((draftState) => {\n // @ts-expect-error - TS2589: Type instantiation is excessively deep and\n // possibly infinite.\n draftState.interfaces[id] = {\n snapId,\n content: castDraft(element),\n state: componentState,\n context: context ?? null,\n contentType: contentType ?? null,\n displayed: false,\n };\n });\n\n return id;\n }\n\n /**\n * Get the data of a given interface id.\n *\n * @param snapId - The snap id requesting the interface data.\n * @param id - The interface id.\n * @returns The interface state.\n */\n getInterface(snapId: SnapId, id: string) {\n this.#validateArgs(snapId, id);\n\n return this.state.interfaces[id];\n }\n\n /**\n * Get the state of a given interface ID, if the interface has been displayed\n * at least once.\n *\n * @param snapId - The snap ID requesting the interface state.\n * @param id - The interface ID.\n * @returns The interface state.\n */\n getInterfaceState(snapId: SnapId, id: string) {\n const storedInterface = this.getInterface(snapId, id);\n if (!storedInterface.displayed) {\n throw new Error(\n `Interface with ID '${id}' has not been displayed yet. Interface state is not available until the interface has been displayed at least once.`,\n );\n }\n\n return storedInterface.state;\n }\n\n /**\n * Update the interface with the given content.\n *\n * @param snapId - The snap id requesting the update.\n * @param id - The interface id.\n * @param content - The new content.\n * @param context - An optional interface context object.\n */\n updateInterface(\n snapId: SnapId,\n id: string,\n content: ComponentOrElement,\n context?: InterfaceContext,\n ) {\n this.#validateArgs(snapId, id);\n const element = getJsxInterface(content);\n this.#validateContent(snapId, element);\n validateInterfaceContext(context);\n\n const oldState = this.state.interfaces[id].state;\n const newState = constructState(oldState, element, {\n getAssetsState: this.#getAssetsState.bind(this),\n getAccountByAddress: this.#getAccountByAddress.bind(this),\n getSelectedAccount: this.#getSelectedAccount.bind(this),\n listAccounts: this.#listAccounts.bind(this),\n snapOwnsAccount: (account: InternalAccount) =>\n snapOwnsAccount(snapId, account),\n });\n\n this.update((draftState) => {\n draftState.interfaces[id].state = newState;\n draftState.interfaces[id].content = castDraft(element);\n if (context) {\n draftState.interfaces[id].context = context;\n }\n });\n }\n\n /**\n * Delete an interface from state.\n *\n * @param id - The interface id.\n */\n deleteInterface(id: string) {\n this.update((draftState) => {\n delete draftState.interfaces[id];\n });\n }\n\n /**\n * Update the interface state.\n *\n * @param id - The interface id.\n * @param state - The new state.\n */\n updateInterfaceState(id: string, state: InterfaceState) {\n this.update((draftState) => {\n draftState.interfaces[id].state = state;\n });\n }\n\n /**\n * Resolve the promise of a given interface approval request.\n * The approval needs to have the same ID as the interface.\n *\n * @param snapId - The snap id.\n * @param id - The interface id.\n * @param value - The value to resolve the promise with.\n */\n async resolveInterface(snapId: SnapId, id: string, value: Json) {\n this.#validateArgs(snapId, id);\n this.#validateApproval(id);\n\n await this.#acceptApprovalRequest(id, value);\n\n this.deleteInterface(id);\n }\n\n /**\n * Set the interface as displayed.\n *\n * @param snapId - The snap ID requesting the update.\n * @param id - The interface ID.\n */\n setInterfaceDisplayed(snapId: SnapId, id: string) {\n this.#validateArgs(snapId, id);\n\n if (this.state.interfaces[id].displayed) {\n return;\n }\n\n this.update((draftState) => {\n draftState.interfaces[id].displayed = true;\n });\n }\n\n /**\n * Utility function to validate the args passed to the other methods.\n *\n * @param snapId - The snap id.\n * @param id - The interface id.\n */\n #validateArgs(snapId: SnapId, id: string) {\n const existingInterface = this.state.interfaces[id];\n\n assert(\n existingInterface !== undefined,\n `Interface with id '${id}' not found.`,\n );\n assert(\n existingInterface.snapId === snapId,\n `Interface not created by ${snapId}.`,\n );\n }\n\n /**\n * Utility function to validate that the approval request exists.\n *\n * @param id - The interface id.\n */\n #validateApproval(id: string) {\n assert(\n this.#hasApprovalRequest(id),\n `Approval request with id '${id}' not found.`,\n );\n }\n\n /**\n * Check an origin against the phishing list.\n *\n * @param origin - The origin to check.\n * @returns True if the origin is on the phishing list, otherwise false.\n */\n #checkPhishingList(origin: string) {\n return this.messenger.call('PhishingController:testOrigin', origin).result;\n }\n\n /**\n * Check if an approval request exists for a given interface by looking up\n * if the ApprovalController has a request with the given interface ID.\n *\n * @param id - The interface id.\n * @returns True if an approval request exists, otherwise false.\n */\n #hasApprovalRequest(id: string) {\n return this.messenger.call('ApprovalController:hasRequest', {\n id,\n });\n }\n\n /**\n * Accept an approval request for a given interface.\n *\n * @param id - The interface id.\n * @param value - The value to resolve the promise with.\n */\n async #acceptApprovalRequest(id: string, value: Json) {\n await this.messenger.call('ApprovalController:acceptRequest', id, value);\n }\n\n /**\n * Get the selected account in the client.\n *\n * @returns The selected account.\n */\n #getSelectedAccount() {\n return this.messenger.call(\n 'AccountsController:getSelectedMultichainAccount',\n );\n }\n\n /**\n * Get a list of accounts for the given chain IDs.\n *\n * @param chainIds - The chain IDs to get the accounts for.\n * @returns The list of accounts.\n */\n #listAccounts(chainIds?: CaipChainId[]) {\n const accounts = this.messenger.call(\n 'AccountsController:listMultichainAccounts',\n );\n\n if (!chainIds || chainIds.length === 0) {\n return accounts;\n }\n\n return accounts.filter((account) =>\n account.scopes.some((scope) => isMatchingChainId(scope, chainIds)),\n );\n }\n\n /**\n * Get an account by its address.\n *\n * @param address - The account address.\n * @returns The account or undefined if not found.\n */\n #getAccountByAddress(address: CaipAccountId) {\n const { address: parsedAddress } = parseCaipAccountId(address);\n\n return this.messenger.call(\n 'AccountsController:getAccountByAddress',\n parsedAddress,\n );\n }\n\n /**\n * Get the MultichainAssetsController state.\n *\n * @returns The MultichainAssetsController state.\n */\n #getAssetsState() {\n return this.messenger.call('MultichainAssetsController:getState');\n }\n\n /**\n * Get a snap by its id.\n *\n * @param id - The snap id.\n * @returns The snap.\n */\n #getSnap(id: string) {\n return this.messenger.call('SnapController:get', id);\n }\n\n #hasPermission(snapId: SnapId, permission: string) {\n return this.messenger.call(\n 'PermissionController:hasPermission',\n snapId,\n permission,\n );\n }\n\n /**\n * Utility function to validate the components of an interface.\n * Throws if something is invalid.\n *\n * @param snapId - The Snap ID.\n * @param element - The JSX element to verify.\n */\n #validateContent(snapId: SnapId, element: JSXElement) {\n // We assume the validity of this JSON to be validated by the caller.\n // E.g., in the RPC method implementation.\n const size = getJsonSizeUnsafe(element);\n assert(\n size <= MAX_UI_CONTENT_SIZE,\n `A Snap UI may not be larger than ${MAX_UI_CONTENT_SIZE / 1000000} MB.`,\n );\n\n validateJsxElements(element, {\n isOnPhishingList: this.#checkPhishingList.bind(this),\n getSnap: this.#getSnap.bind(this),\n getAccountByAddress: this.#getAccountByAddress.bind(this),\n hasPermission: this.#hasPermission.bind(this, snapId),\n });\n }\n\n #onNotificationsListUpdated(notificationsList: Notification[]) {\n const snapNotificationsWithInterface = notificationsList.filter(\n (notification) => {\n return (\n notification.type === 'snap' &&\n hasProperty((notification as SnapNotification).data, 'detailedView')\n );\n },\n );\n\n const interfaceIdSet = new Set(\n snapNotificationsWithInterface.map(\n (notification) =>\n (\n (notification as SnapNotification)\n .data as ExpandedSnapNotificationData\n ).detailedView.interfaceId,\n ),\n );\n\n this.update((state) => {\n Object.entries(state.interfaces).forEach(([id, snapInterface]) => {\n if (\n snapInterface.contentType === ContentType.Notification &&\n !interfaceIdSet.has(id)\n ) {\n delete state.interfaces[id];\n }\n });\n });\n }\n}\n"]}
@@ -169,6 +169,13 @@ class AbstractExecutionService {
169
169
  this.#messenger.publish('ExecutionService:outboundRequest', snapId);
170
170
  }
171
171
  });
172
+ // An error handler is not attached to the RPC stream until `setupSnapProvider` is called.
173
+ // We must set it up here to prevent errors from bubbling up if the stream is destroyed before then.
174
+ rpcStream.on('error', (error) => {
175
+ if (error && !error.message?.match('Premature close')) {
176
+ (0, snaps_utils_1.logError)(`Snap: "${snapId}" - RPC stream failure:`, error);
177
+ }
178
+ });
172
179
  const originalWrite = rpcStream.write.bind(rpcStream);
173
180
  // @ts-expect-error Hack to inspect the messages being written to the stream.
174
181
  rpcStream.write = (chunk, encoding, callback) => {
@@ -1 +1 @@
1
- {"version":3,"file":"AbstractExecutionService.cjs","sourceRoot":"","sources":["../../src/services/AbstractExecutionService.ts"],"names":[],"mappings":";;;;;;AAAA,+DAA2D;AAC3D,qDAAgF;AAChF,qFAA8E;AAC9E,kFAAyD;AAGzD,uDAAgF;AAMhF,2CAKyB;AACzB,mCAAgC;AAChC,qDAA2C;AAS3C,4CAAiC;AACjC,8CAAuC;AACvC,wCAAoD;AAEpD,MAAM,cAAc,GAAG,kBAAkB,CAAC;AA6C1C,MAAsB,wBAAwB;IAG5C,IAAI,GAA0B,cAAc,CAAC;IAE7C,KAAK,GAAG,IAAI,CAAC;IAEJ,KAAK,CAA+B;IAEpC,OAAO,CAA+B;IAEtC,kBAAkB,CAAoB;IAEtC,UAAU,CAA4B;IAEtC,YAAY,CAAS;IAErB,YAAY,CAAS;IAErB,mBAAmB,CAAS;IAE5B,QAAQ,CAAU;IAE3B,YAAY,EACV,iBAAiB,EACjB,SAAS,EACT,WAAW,GAAG,IAAA,sBAAc,EAAC,EAAE,EAAE,gBAAQ,CAAC,MAAM,CAAC,EACjD,WAAW,GAAG,IAAA,sBAAc,EAAC,EAAE,EAAE,gBAAQ,CAAC,MAAM,CAAC,EACjD,kBAAkB,GAAG,IAAA,sBAAc,EAAC,CAAC,EAAE,gBAAQ,CAAC,MAAM,CAAC,EACvD,OAAO,GAAG,IAAI,GACO;QACrB,IAAI,CAAC,KAAK,GAAG,IAAI,GAAG,EAAE,CAAC;QACvB,IAAI,CAAC,OAAO,GAAG,IAAI,GAAG,EAAE,CAAC;QACzB,IAAI,CAAC,kBAAkB,GAAG,iBAAiB,CAAC;QAC5C,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;QAC5B,IAAI,CAAC,YAAY,GAAG,WAAW,CAAC;QAChC,IAAI,CAAC,YAAY,GAAG,WAAW,CAAC;QAChC,IAAI,CAAC,mBAAmB,GAAG,kBAAkB,CAAC;QAC9C,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC;QAExB,IAAI,CAAC,wBAAwB,EAAE,CAAC;IAClC,CAAC;IAED;;;OAGG;IACH,wBAAwB;QACtB,IAAI,CAAC,UAAU,CAAC,qBAAqB,CACnC,GAAG,cAAc,mBAAmB,EACpC,KAAK,EAAE,MAAc,EAAE,OAAwB,EAAE,EAAE,CACjD,IAAI,CAAC,gBAAgB,CAAC,MAAM,EAAE,OAAO,CAAC,CACzC,CAAC;QAEF,IAAI,CAAC,UAAU,CAAC,qBAAqB,CACnC,GAAG,cAAc,cAAc,EAC/B,KAAK,EAAE,IAAuB,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAC1D,CAAC;QAEF,IAAI,CAAC,UAAU,CAAC,qBAAqB,CACnC,GAAG,cAAc,gBAAgB,EACjC,KAAK,EAAE,MAAc,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,CACrD,CAAC;QAEF,IAAI,CAAC,UAAU,CAAC,qBAAqB,CACnC,GAAG,cAAc,oBAAoB,EACrC,KAAK,IAAI,EAAE,CAAC,IAAI,CAAC,iBAAiB,EAAE,CACrC,CAAC;IACJ,CAAC;IAaD;;;;;;OAMG;IACI,KAAK,CAAC,aAAa,CAAC,MAAc;QACvC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACnC,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,OAAO;QACT,CAAC;QAED,IAAI,CAAC;YACH,0FAA0F;YAC1F,MAAM,MAAM,GAAG,MAAM,IAAA,mBAAW,EAC9B,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE;gBACpB,OAAO,EAAE,KAAK;gBACd,MAAM,EAAE,WAAW;gBACnB,EAAE,EAAE,IAAA,eAAM,GAAE;aACb,CAAC,EACF,IAAI,CAAC,mBAAmB,CACzB,CAAC;YAEF,IAAI,MAAM,KAAK,mBAAW,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;gBAC9C,IAAA,wBAAU,EAAC,SAAS,MAAM,mCAAmC,CAAC,CAAC;YACjE,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;QAED,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE;YAC5C,IAAI,CAAC;gBACH,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;oBACtB,MAAM,CAAC,OAAO,EAAE,CAAC;gBACnB,CAAC;YACH,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAA,sBAAQ,EAAC,+BAA+B,EAAE,KAAK,CAAC,CAAC;YACnD,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,MAAM,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;QAE7B,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC1B,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC5B,IAAA,aAAG,EAAC,SAAS,MAAM,eAAe,CAAC,CAAC;IACtC,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,QAAQ,CAAC,MAAc,EAAE,KAAY;QACzC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QAEnE,MAAM,iBAAiB,GAAG,IAAA,mDAAsB,GAAE,CAAC;QAEnD,IAAA,0BAAQ,EACN,iBAAiB,CAAC,MAAM,EACxB,OAAO,CAAC,OAAO,EACf,iBAAiB,CAAC,MAAM,EACxB,CAAC,KAAK,EAAE,EAAE;YACR,IAAI,KAAK,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,iBAAiB,CAAC,EAAE,CAAC;gBACtD,IAAA,sBAAQ,EAAC,yBAAyB,EAAE,KAAK,CAAC,CAAC;YAC7C,CAAC;QACH,CAAC,CACF,CAAC;QAEF,MAAM,SAAS,GAAG,oBAAa,CAAC,MAAM,CAAC;YACrC,UAAU,EAAE,CAAC,IAAA,gCAAc,EAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC;SAC3D,CAAC,CAAC;QAEH,MAAM,WAAW,GAAG;YAClB,EAAE,EAAE,MAAM;YACV,OAAO;YACP,SAAS;YACT,MAAM;SACP,CAAC;QACF,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;QAEpC,OAAO,WAAW,CAAC;IACrB,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,YAAY,CAChB,MAAc,EACd,KAAY;QAEZ,MAAM,MAAM,GAAG,MAAM,IAAA,mBAAW,EAAC,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,KAAK,CAAC,CAAC;QAEpE,IAAI,MAAM,KAAK,mBAAW,EAAE,CAAC;YAC3B,gHAAgH;YAChH,MAAM,IAAI,CAAC,YAAY,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;YAExC,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YACxC,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;gBACzB,wEAAwE;gBACxE,MAAM,IAAI,KAAK,CACb,qBAAqB,MAAM,wEAAwE,CACpG,CAAC;YACJ,CAAC;YACD,MAAM,IAAI,KAAK,CACb,qBAAqB,MAAM,mEAAmE,CAC/F,CAAC;QACJ,CAAC;QAED,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,CAAC;QAC7C,MAAM,GAAG,GAAG,cAAc,CAAC,SAAS,EAAE,UAAU,MAAM,GAAG,CAAC,CAAC;QAC3D,MAAM,aAAa,GAAG,GAAG,CAAC,YAAY,CAAC,+BAAiB,CAAC,OAAO,CAAC,CAAC;QAElE,4FAA4F;QAC5F,gDAAgD;QAChD,MAAM,mBAAmB,GAAG,CAC1B,OAEsD,EACtD,EAAE;YACF,IAAI,IAAA,mBAAW,EAAC,OAAO,EAAE,IAAI,CAAC,EAAE,CAAC;gBAC/B,OAAO;YACT,CAAC;YAED,IAAI,OAAO,CAAC,MAAM,KAAK,iBAAiB,EAAE,CAAC;gBACzC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,kCAAkC,EAAE,MAAM,CAAC,CAAC;YACtE,CAAC;iBAAM,IAAI,OAAO,CAAC,MAAM,KAAK,kBAAkB,EAAE,CAAC;gBACjD,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,mCAAmC,EAAE,MAAM,CAAC,CAAC;YACvE,CAAC;iBAAM,IAAI,OAAO,CAAC,MAAM,KAAK,gBAAgB,EAAE,CAAC;gBAC/C,IAAI,CAAC,UAAU,CAAC,OAAO,CACrB,iCAAiC,EACjC,MAAM,EACL,OAAO,CAAC,MAAmC,CAAC,KAAK,CACnD,CAAC;gBACF,aAAa,CAAC,cAAc,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;YAC5D,CAAC;iBAAM,CAAC;gBACN,IAAA,sBAAQ,EACN,IAAI,KAAK,CACP,oDAAoD,OAAO,CAAC,MAAM,IAAI,CACvE,CACF,CAAC;YACJ,CAAC;QACH,CAAC,CAAC;QAEF,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;QAE9C,MAAM,SAAS,GAAG,GAAG;aAClB,YAAY,CAAC,+BAAiB,CAAC,QAAQ,CAAC;aACxC,eAAe,CAAC,EAAE,CAAC,CAAC;QAEvB,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE;YAC7B,IAAI,KAAK,EAAE,IAAI,IAAI,IAAA,mBAAW,EAAC,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC;gBAClD,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,kCAAkC,EAAE,MAAM,CAAC,CAAC;YACtE,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,MAAM,aAAa,GAAG,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAEtD,6EAA6E;QAC7E,SAAS,CAAC,KAAK,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE;YAC9C,kFAAkF;YAClF,IAAI,KAAK,EAAE,IAAI,EAAE,MAAM,KAAK,uBAAuB,EAAE,CAAC;gBACpD,OAAO,IAAI,CAAC;YACd,CAAC;YAED,IAAI,KAAK,EAAE,IAAI,IAAI,IAAA,mBAAW,EAAC,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC;gBAClD,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,mCAAmC,EAAE,MAAM,CAAC,CAAC;YACvE,CAAC;YAED,OAAO,aAAa,CAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;QAClD,CAAC,CAAC;QAEF,OAAO;YACL,OAAO,EAAE;gBACP,OAAO,EAAE,aAAa;gBACtB,GAAG,EAAE,SAAS;gBACd,UAAU,EAAE,SAAS;gBACrB,GAAG;aACJ;YACD,MAAM;SACP,CAAC;IACJ,CAAC;IAYD;;;;;OAKG;IACO,aAAa,CAAC,MAAc,EAAE,MAAuB;QAC7D,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,CAAC;IAED,KAAK,CAAC,iBAAiB;QACrB,MAAM,OAAO,CAAC,GAAG,CACf,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CACzE,CAAC;IACJ,CAAC;IAED;;;;;;;;;OASG;IACH,KAAK,CAAC,WAAW,CAAC,EAChB,MAAM,EACN,UAAU,EACV,UAAU,GACQ;QAClB,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YAC3B,MAAM,IAAI,KAAK,CAAC,IAAI,MAAM,uBAAuB,CAAC,CAAC;QACrD,CAAC;QAED,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;QAEtC,MAAM,KAAK,GAAG,IAAI,aAAK,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAE3C,wEAAwE;QACxE,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QAE/C,0FAA0F;QAC1F,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClB,+CAA+C;YAC/C,MAAM,UAAU,GAAG,MAAM,IAAA,mBAAW,EAClC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,EAAE;gBACpB,OAAO,EAAE,KAAK;gBACd,MAAM,EAAE,MAAM;gBACd,EAAE,EAAE,IAAA,eAAM,GAAE;aACb,CAAC,EACF,IAAI,CAAC,YAAY,CAClB,CAAC;YAEF,IAAI,UAAU,KAAK,mBAAW,EAAE,CAAC;gBAC/B,MAAM,IAAI,KAAK,CACb,qBAAqB,MAAM,0DAA0D,CACtF,CAAC;YACJ,CAAC;QACH,CAAC;QAED,MAAM,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC;QAElC,IAAI,CAAC,kBAAkB,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;QAE3C,2DAA2D;QAC3D,4CAA4C;QAC5C,MAAM,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,EAAE,IAAI,CAAC,YAAY,GAAG,CAAC,CAAC,CAAC;QAEvE,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;QAE1C,MAAM,OAAO,GAAG;YACd,OAAO,EAAE,KAAK;YACd,MAAM,EAAE,aAAa;YACrB,MAAM,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE;YAC1C,EAAE,EAAE,IAAA,eAAM,GAAE;SACb,CAAC;QAEF,IAAA,8BAAsB,EAAC,OAAO,CAAC,CAAC;QAEhC,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;QAExC,MAAM,MAAM,GAAG,MAAM,IAAA,mBAAW,EAC9B,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,EAAE,OAAO,CAAC,EAC9B,aAAa,CACd,CAAC;QAEF,IAAI,MAAM,KAAK,mBAAW,EAAE,CAAC;YAC3B,MAAM,IAAI,KAAK,CAAC,GAAG,MAAM,mBAAmB,CAAC,CAAC;QAChD,CAAC;QAED,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;YACpB,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;QACxC,CAAC;QAED,OAAO,MAAgB,CAAC;IAC1B,CAAC;IAED,KAAK,CAAC,QAAQ,CACZ,MAAc,EACd,OAAuB;QAEvB,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACnC,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,MAAM,IAAI,KAAK,CAAC,IAAI,MAAM,6BAA6B,CAAC,CAAC;QAC3D,CAAC;QAED,IAAA,aAAG,EAAC,yBAAyB,EAAE,OAAO,CAAC,CAAC;QACxC,OAAO,MAAM,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC7C,CAAC;IAED;;;;;;OAMG;IACI,KAAK,CAAC,gBAAgB,CAC3B,MAAc,EACd,OAAwB;QAExB,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;QAE7C,OAAO,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE;YACjC,EAAE,EAAE,IAAA,eAAM,GAAE;YACZ,OAAO,EAAE,KAAK;YACd,MAAM,EAAE,SAAS;YACjB,MAAM,EAAE;gBACN,MAAM;gBACN,MAAM;gBACN,OAAO;gBACP,OAAO,EAAE,OAAyB;aACnC;SACF,CAAC,CAAC;IACL,CAAC;CACF;AAlaD,4DAkaC;AAED;;;;;;GAMG;AACH,SAAgB,cAAc,CAC5B,gBAAwB,EACxB,UAAkB;IAElB,MAAM,GAAG,GAAG,IAAI,0BAAe,EAAE,CAAC;IAClC,IAAA,0BAAQ,EAAC,gBAAgB,EAAE,GAAG,EAAE,gBAAgB,EAAE,CAAC,KAAK,EAAE,EAAE;QAC1D,IAAI,KAAK,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,iBAAiB,CAAC,EAAE,CAAC;YACtD,IAAA,sBAAQ,EAAC,IAAI,UAAU,mBAAmB,EAAE,KAAK,CAAC,CAAC;QACrD,CAAC;IACH,CAAC,CAAC,CAAC;IACH,OAAO,GAAG,CAAC;AACb,CAAC;AAXD,wCAWC","sourcesContent":["import { asV2Middleware } from '@metamask/json-rpc-engine';\nimport { JsonRpcEngineV2 as JsonRpcEngine } from '@metamask/json-rpc-engine/v2';\nimport { createStreamMiddleware } from '@metamask/json-rpc-middleware-stream';\nimport ObjectMultiplex from '@metamask/object-multiplex';\nimport type { BasePostMessageStream } from '@metamask/post-message-stream';\nimport type { SnapRpcHookArgs } from '@metamask/snaps-utils';\nimport { SNAP_STREAM_NAMES, logError, logWarning } from '@metamask/snaps-utils';\nimport type {\n Json,\n JsonRpcNotification,\n JsonRpcRequest,\n} from '@metamask/utils';\nimport {\n Duration,\n assertIsJsonRpcRequest,\n hasProperty,\n inMilliseconds,\n} from '@metamask/utils';\nimport { nanoid } from 'nanoid';\nimport { pipeline } from 'readable-stream';\nimport type { Duplex } from 'readable-stream';\n\nimport type {\n ExecutionService,\n ExecutionServiceMessenger,\n SnapErrorJson,\n SnapExecutionData,\n} from './ExecutionService';\nimport { log } from '../logging';\nimport { Timer } from '../snaps/Timer';\nimport { hasTimedOut, withTimeout } from '../utils';\n\nconst controllerName = 'ExecutionService';\n\nexport type SetupSnapProvider = (snapId: string, stream: Duplex) => void;\n\nexport type ExecutionServiceArgs = {\n setupSnapProvider: SetupSnapProvider;\n messenger: ExecutionServiceMessenger;\n initTimeout?: number;\n pingTimeout?: number;\n terminationTimeout?: number;\n usePing?: boolean;\n};\n\nexport type JobStreams = {\n command: Duplex;\n rpc: Duplex;\n connection: BasePostMessageStream;\n mux: ObjectMultiplex;\n};\n\nexport type Job<WorkerType> = {\n id: string;\n streams: JobStreams;\n rpcEngine: JsonRpcEngine<JsonRpcRequest>;\n worker: WorkerType;\n};\n\nexport type TerminateJobArgs<WorkerType> = Partial<Job<WorkerType>> &\n Pick<Job<WorkerType>, 'id'>;\n\n/** \n Statuses used for diagnostic purposes\n - created: The initial state, no initialization has started \n - initializing: Snap execution environment is initializing\n - initialized: Snap execution environment has initialized\n - executing: Snap source code is being executed\n - running: Snap executed and ready for RPC requests\n */\ntype ExecutionStatus =\n | 'created'\n | 'initializing'\n | 'initialized'\n | 'executing'\n | 'running';\n\nexport abstract class AbstractExecutionService<WorkerType>\n implements ExecutionService\n{\n name: typeof controllerName = controllerName;\n\n state = null;\n\n readonly #jobs: Map<string, Job<WorkerType>>;\n\n readonly #status: Map<string, ExecutionStatus>;\n\n readonly #setupSnapProvider: SetupSnapProvider;\n\n readonly #messenger: ExecutionServiceMessenger;\n\n readonly #initTimeout: number;\n\n readonly #pingTimeout: number;\n\n readonly #terminationTimeout: number;\n\n readonly #usePing: boolean;\n\n constructor({\n setupSnapProvider,\n messenger,\n initTimeout = inMilliseconds(60, Duration.Second),\n pingTimeout = inMilliseconds(10, Duration.Second),\n terminationTimeout = inMilliseconds(1, Duration.Second),\n usePing = true,\n }: ExecutionServiceArgs) {\n this.#jobs = new Map();\n this.#status = new Map();\n this.#setupSnapProvider = setupSnapProvider;\n this.#messenger = messenger;\n this.#initTimeout = initTimeout;\n this.#pingTimeout = pingTimeout;\n this.#terminationTimeout = terminationTimeout;\n this.#usePing = usePing;\n\n this.#registerMessageHandlers();\n }\n\n /**\n * Constructor helper for registering the controller's messaging system\n * actions.\n */\n #registerMessageHandlers(): void {\n this.#messenger.registerActionHandler(\n `${controllerName}:handleRpcRequest`,\n async (snapId: string, options: SnapRpcHookArgs) =>\n this.handleRpcRequest(snapId, options),\n );\n\n this.#messenger.registerActionHandler(\n `${controllerName}:executeSnap`,\n async (data: SnapExecutionData) => this.executeSnap(data),\n );\n\n this.#messenger.registerActionHandler(\n `${controllerName}:terminateSnap`,\n async (snapId: string) => this.terminateSnap(snapId),\n );\n\n this.#messenger.registerActionHandler(\n `${controllerName}:terminateAllSnaps`,\n async () => this.terminateAllSnaps(),\n );\n }\n\n /**\n * Performs additional necessary work during job termination. **MUST** be\n * implemented by concrete implementations. See\n * {@link AbstractExecutionService.terminate} for details.\n *\n * @param job - The object corresponding to the job to be terminated.\n */\n protected abstract terminateJob(\n job: TerminateJobArgs<WorkerType>,\n ): Promise<void>;\n\n /**\n * Terminates the Snap with the specified ID and deletes all its associated\n * data. Any subsequent messages targeting the Snap will fail with an error.\n * Throws an error if termination fails unexpectedly.\n *\n * @param snapId - The id of the Snap to be terminated.\n */\n public async terminateSnap(snapId: string): Promise<void> {\n const job = this.#jobs.get(snapId);\n if (!job) {\n return;\n }\n\n try {\n // Ping worker and tell it to run teardown, continue with termination if it takes too long\n const result = await withTimeout(\n this.#command(snapId, {\n jsonrpc: '2.0',\n method: 'terminate',\n id: nanoid(),\n }),\n this.#terminationTimeout,\n );\n\n if (result === hasTimedOut || result !== 'OK') {\n logWarning(`Snap \"${snapId}\" failed to terminate gracefully.`);\n }\n } catch {\n // Ignore\n }\n\n Object.values(job.streams).forEach((stream) => {\n try {\n if (!stream.destroyed) {\n stream.destroy();\n }\n } catch (error) {\n logError('Error while destroying stream', error);\n }\n });\n\n await this.terminateJob(job);\n\n this.#jobs.delete(snapId);\n this.#status.delete(snapId);\n log(`Snap \"${snapId}\" terminated.`);\n }\n\n /**\n * Initiates a job for a Snap.\n *\n * @param snapId - The ID of the Snap to initiate a job for.\n * @param timer - The timer to use for timeouts.\n * @returns Information regarding the created job.\n * @throws If the execution service returns an error or execution times out.\n */\n async #initJob(snapId: string, timer: Timer): Promise<Job<WorkerType>> {\n const { streams, worker } = await this.#initStreams(snapId, timer);\n\n const jsonRpcConnection = createStreamMiddleware();\n\n pipeline(\n jsonRpcConnection.stream,\n streams.command,\n jsonRpcConnection.stream,\n (error) => {\n if (error && !error.message?.match('Premature close')) {\n logError(`Command stream failure.`, error);\n }\n },\n );\n\n const rpcEngine = JsonRpcEngine.create({\n middleware: [asV2Middleware(jsonRpcConnection.middleware)],\n });\n\n const envMetadata = {\n id: snapId,\n streams,\n rpcEngine,\n worker,\n };\n this.#jobs.set(snapId, envMetadata);\n\n return envMetadata;\n }\n\n /**\n * Sets up the streams for an initiated job.\n *\n * @param snapId - The Snap ID.\n * @param timer - The timer to use for timeouts.\n * @returns The streams to communicate with the worker and the worker itself.\n * @throws If the execution service returns an error or execution times out.\n */\n async #initStreams(\n snapId: string,\n timer: Timer,\n ): Promise<{ streams: JobStreams; worker: WorkerType }> {\n const result = await withTimeout(this.initEnvStream(snapId), timer);\n\n if (result === hasTimedOut) {\n // For certain environments, such as the iframe we may have already created the worker and wish to terminate it.\n await this.terminateJob({ id: snapId });\n\n const status = this.#status.get(snapId);\n if (status === 'created') {\n // Currently this error can only be thrown by OffscreenExecutionService.\n throw new Error(\n `The executor for \"${snapId}\" couldn't start initialization. The offscreen document may not exist.`,\n );\n }\n throw new Error(\n `The executor for \"${snapId}\" failed to initialize. The iframe/webview/worker failed to load.`,\n );\n }\n\n const { worker, stream: envStream } = result;\n const mux = setupMultiplex(envStream, `Snap: \"${snapId}\"`);\n const commandStream = mux.createStream(SNAP_STREAM_NAMES.COMMAND);\n\n // Handle out-of-band errors, i.e. errors thrown from the Snap outside of the req/res cycle.\n // Also keep track of outbound request/responses\n const notificationHandler = (\n message:\n | JsonRpcRequest\n | JsonRpcNotification<Json[] | Record<string, Json>>,\n ) => {\n if (hasProperty(message, 'id')) {\n return;\n }\n\n if (message.method === 'OutboundRequest') {\n this.#messenger.publish('ExecutionService:outboundRequest', snapId);\n } else if (message.method === 'OutboundResponse') {\n this.#messenger.publish('ExecutionService:outboundResponse', snapId);\n } else if (message.method === 'UnhandledError') {\n this.#messenger.publish(\n 'ExecutionService:unhandledError',\n snapId,\n (message.params as { error: SnapErrorJson }).error,\n );\n commandStream.removeListener('data', notificationHandler);\n } else {\n logError(\n new Error(\n `Received unexpected command stream notification \"${message.method}\".`,\n ),\n );\n }\n };\n\n commandStream.on('data', notificationHandler);\n\n const rpcStream = mux\n .createStream(SNAP_STREAM_NAMES.JSON_RPC)\n .setMaxListeners(20);\n\n rpcStream.on('data', (chunk) => {\n if (chunk?.data && hasProperty(chunk?.data, 'id')) {\n this.#messenger.publish('ExecutionService:outboundRequest', snapId);\n }\n });\n\n const originalWrite = rpcStream.write.bind(rpcStream);\n\n // @ts-expect-error Hack to inspect the messages being written to the stream.\n rpcStream.write = (chunk, encoding, callback) => {\n // Ignore chain switching notifications as it doesn't matter for the SnapProvider.\n if (chunk?.data?.method === 'metamask_chainChanged') {\n return true;\n }\n\n if (chunk?.data && hasProperty(chunk?.data, 'id')) {\n this.#messenger.publish('ExecutionService:outboundResponse', snapId);\n }\n\n return originalWrite(chunk, encoding, callback);\n };\n\n return {\n streams: {\n command: commandStream,\n rpc: rpcStream,\n connection: envStream,\n mux,\n },\n worker,\n };\n }\n\n /**\n * Abstract function implemented by implementing class that spins up a new worker for a job.\n *\n * Depending on the execution environment, this may run forever if the Snap fails to start up properly, therefore any call to this function should be wrapped in a timeout.\n */\n protected abstract initEnvStream(snapId: string): Promise<{\n worker: WorkerType;\n stream: BasePostMessageStream;\n }>;\n\n /**\n * Set the execution status of the Snap.\n *\n * @param snapId - The Snap ID.\n * @param status - The current execution status.\n */\n protected setSnapStatus(snapId: string, status: ExecutionStatus) {\n this.#status.set(snapId, status);\n }\n\n async terminateAllSnaps() {\n await Promise.all(\n [...this.#jobs.keys()].map(async (snapId) => this.terminateSnap(snapId)),\n );\n }\n\n /**\n * Initializes and executes a Snap, setting up the communication channels to the Snap etc.\n *\n * @param snapData - Data needed for Snap execution.\n * @param snapData.snapId - The ID of the Snap to execute.\n * @param snapData.sourceCode - The source code of the Snap to execute.\n * @param snapData.endowments - The endowments available to the executing Snap.\n * @returns A string `OK` if execution succeeded.\n * @throws If the execution service returns an error or execution times out.\n */\n async executeSnap({\n snapId,\n sourceCode,\n endowments,\n }: SnapExecutionData): Promise<string> {\n if (this.#jobs.has(snapId)) {\n throw new Error(`\"${snapId}\" is already running.`);\n }\n\n this.setSnapStatus(snapId, 'created');\n\n const timer = new Timer(this.#initTimeout);\n\n // This may resolve even if the environment has failed to start up fully\n const job = await this.#initJob(snapId, timer);\n\n // Certain environments use ping as part of their initialization and thus can skip it here\n if (this.#usePing) {\n // Ping the worker to ensure that it started up\n const pingResult = await withTimeout(\n this.#command(job.id, {\n jsonrpc: '2.0',\n method: 'ping',\n id: nanoid(),\n }),\n this.#pingTimeout,\n );\n\n if (pingResult === hasTimedOut) {\n throw new Error(\n `The executor for \"${snapId}\" was unreachable. The executor did not respond in time.`,\n );\n }\n }\n\n const rpcStream = job.streams.rpc;\n\n this.#setupSnapProvider(snapId, rpcStream);\n\n // Use the remaining time as the timer, but ensure that the\n // Snap gets at least half the init timeout.\n const remainingTime = Math.max(timer.remaining, this.#initTimeout / 2);\n\n this.setSnapStatus(snapId, 'initialized');\n\n const request = {\n jsonrpc: '2.0',\n method: 'executeSnap',\n params: { snapId, sourceCode, endowments },\n id: nanoid(),\n };\n\n assertIsJsonRpcRequest(request);\n\n this.setSnapStatus(snapId, 'executing');\n\n const result = await withTimeout(\n this.#command(job.id, request),\n remainingTime,\n );\n\n if (result === hasTimedOut) {\n throw new Error(`${snapId} failed to start.`);\n }\n\n if (result === 'OK') {\n this.setSnapStatus(snapId, 'running');\n }\n\n return result as string;\n }\n\n async #command(\n snapId: string,\n message: JsonRpcRequest,\n ): Promise<Json | undefined> {\n const job = this.#jobs.get(snapId);\n if (!job) {\n throw new Error(`\"${snapId}\" is not currently running.`);\n }\n\n log('Parent: Sending Command', message);\n return await job.rpcEngine.handle(message);\n }\n\n /**\n * Handle RPC request.\n *\n * @param snapId - The ID of the recipient Snap.\n * @param options - Bag of options to pass to the RPC handler.\n * @returns Promise that can handle the request.\n */\n public async handleRpcRequest(\n snapId: string,\n options: SnapRpcHookArgs,\n ): Promise<unknown> {\n const { handler, request, origin } = options;\n\n return await this.#command(snapId, {\n id: nanoid(),\n jsonrpc: '2.0',\n method: 'snapRpc',\n params: {\n snapId,\n origin,\n handler,\n request: request as JsonRpcRequest,\n },\n });\n }\n}\n\n/**\n * Sets up stream multiplexing for the given stream.\n *\n * @param connectionStream - The stream to mux.\n * @param streamName - The name of the stream, for identification in errors.\n * @returns The multiplexed stream.\n */\nexport function setupMultiplex(\n connectionStream: Duplex,\n streamName: string,\n): ObjectMultiplex {\n const mux = new ObjectMultiplex();\n pipeline(connectionStream, mux, connectionStream, (error) => {\n if (error && !error.message?.match('Premature close')) {\n logError(`\"${streamName}\" stream failure.`, error);\n }\n });\n return mux;\n}\n"]}
1
+ {"version":3,"file":"AbstractExecutionService.cjs","sourceRoot":"","sources":["../../src/services/AbstractExecutionService.ts"],"names":[],"mappings":"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","sourcesContent":["import { asV2Middleware } from '@metamask/json-rpc-engine';\nimport { JsonRpcEngineV2 as JsonRpcEngine } from '@metamask/json-rpc-engine/v2';\nimport { createStreamMiddleware } from '@metamask/json-rpc-middleware-stream';\nimport ObjectMultiplex from '@metamask/object-multiplex';\nimport type { BasePostMessageStream } from '@metamask/post-message-stream';\nimport type { SnapRpcHookArgs } from '@metamask/snaps-utils';\nimport { SNAP_STREAM_NAMES, logError, logWarning } from '@metamask/snaps-utils';\nimport type {\n Json,\n JsonRpcNotification,\n JsonRpcRequest,\n} from '@metamask/utils';\nimport {\n Duration,\n assertIsJsonRpcRequest,\n hasProperty,\n inMilliseconds,\n} from '@metamask/utils';\nimport { nanoid } from 'nanoid';\nimport { pipeline } from 'readable-stream';\nimport type { Duplex } from 'readable-stream';\n\nimport type {\n ExecutionService,\n ExecutionServiceMessenger,\n SnapErrorJson,\n SnapExecutionData,\n} from './ExecutionService';\nimport { log } from '../logging';\nimport { Timer } from '../snaps/Timer';\nimport { hasTimedOut, withTimeout } from '../utils';\n\nconst controllerName = 'ExecutionService';\n\nexport type SetupSnapProvider = (snapId: string, stream: Duplex) => void;\n\nexport type ExecutionServiceArgs = {\n setupSnapProvider: SetupSnapProvider;\n messenger: ExecutionServiceMessenger;\n initTimeout?: number;\n pingTimeout?: number;\n terminationTimeout?: number;\n usePing?: boolean;\n};\n\nexport type JobStreams = {\n command: Duplex;\n rpc: Duplex;\n connection: BasePostMessageStream;\n mux: ObjectMultiplex;\n};\n\nexport type Job<WorkerType> = {\n id: string;\n streams: JobStreams;\n rpcEngine: JsonRpcEngine<JsonRpcRequest>;\n worker: WorkerType;\n};\n\nexport type TerminateJobArgs<WorkerType> = Partial<Job<WorkerType>> &\n Pick<Job<WorkerType>, 'id'>;\n\n/** \n Statuses used for diagnostic purposes\n - created: The initial state, no initialization has started \n - initializing: Snap execution environment is initializing\n - initialized: Snap execution environment has initialized\n - executing: Snap source code is being executed\n - running: Snap executed and ready for RPC requests\n */\ntype ExecutionStatus =\n | 'created'\n | 'initializing'\n | 'initialized'\n | 'executing'\n | 'running';\n\nexport abstract class AbstractExecutionService<WorkerType>\n implements ExecutionService\n{\n name: typeof controllerName = controllerName;\n\n state = null;\n\n readonly #jobs: Map<string, Job<WorkerType>>;\n\n readonly #status: Map<string, ExecutionStatus>;\n\n readonly #setupSnapProvider: SetupSnapProvider;\n\n readonly #messenger: ExecutionServiceMessenger;\n\n readonly #initTimeout: number;\n\n readonly #pingTimeout: number;\n\n readonly #terminationTimeout: number;\n\n readonly #usePing: boolean;\n\n constructor({\n setupSnapProvider,\n messenger,\n initTimeout = inMilliseconds(60, Duration.Second),\n pingTimeout = inMilliseconds(10, Duration.Second),\n terminationTimeout = inMilliseconds(1, Duration.Second),\n usePing = true,\n }: ExecutionServiceArgs) {\n this.#jobs = new Map();\n this.#status = new Map();\n this.#setupSnapProvider = setupSnapProvider;\n this.#messenger = messenger;\n this.#initTimeout = initTimeout;\n this.#pingTimeout = pingTimeout;\n this.#terminationTimeout = terminationTimeout;\n this.#usePing = usePing;\n\n this.#registerMessageHandlers();\n }\n\n /**\n * Constructor helper for registering the controller's messaging system\n * actions.\n */\n #registerMessageHandlers(): void {\n this.#messenger.registerActionHandler(\n `${controllerName}:handleRpcRequest`,\n async (snapId: string, options: SnapRpcHookArgs) =>\n this.handleRpcRequest(snapId, options),\n );\n\n this.#messenger.registerActionHandler(\n `${controllerName}:executeSnap`,\n async (data: SnapExecutionData) => this.executeSnap(data),\n );\n\n this.#messenger.registerActionHandler(\n `${controllerName}:terminateSnap`,\n async (snapId: string) => this.terminateSnap(snapId),\n );\n\n this.#messenger.registerActionHandler(\n `${controllerName}:terminateAllSnaps`,\n async () => this.terminateAllSnaps(),\n );\n }\n\n /**\n * Performs additional necessary work during job termination. **MUST** be\n * implemented by concrete implementations. See\n * {@link AbstractExecutionService.terminate} for details.\n *\n * @param job - The object corresponding to the job to be terminated.\n */\n protected abstract terminateJob(\n job: TerminateJobArgs<WorkerType>,\n ): Promise<void>;\n\n /**\n * Terminates the Snap with the specified ID and deletes all its associated\n * data. Any subsequent messages targeting the Snap will fail with an error.\n * Throws an error if termination fails unexpectedly.\n *\n * @param snapId - The id of the Snap to be terminated.\n */\n public async terminateSnap(snapId: string): Promise<void> {\n const job = this.#jobs.get(snapId);\n if (!job) {\n return;\n }\n\n try {\n // Ping worker and tell it to run teardown, continue with termination if it takes too long\n const result = await withTimeout(\n this.#command(snapId, {\n jsonrpc: '2.0',\n method: 'terminate',\n id: nanoid(),\n }),\n this.#terminationTimeout,\n );\n\n if (result === hasTimedOut || result !== 'OK') {\n logWarning(`Snap \"${snapId}\" failed to terminate gracefully.`);\n }\n } catch {\n // Ignore\n }\n\n Object.values(job.streams).forEach((stream) => {\n try {\n if (!stream.destroyed) {\n stream.destroy();\n }\n } catch (error) {\n logError('Error while destroying stream', error);\n }\n });\n\n await this.terminateJob(job);\n\n this.#jobs.delete(snapId);\n this.#status.delete(snapId);\n log(`Snap \"${snapId}\" terminated.`);\n }\n\n /**\n * Initiates a job for a Snap.\n *\n * @param snapId - The ID of the Snap to initiate a job for.\n * @param timer - The timer to use for timeouts.\n * @returns Information regarding the created job.\n * @throws If the execution service returns an error or execution times out.\n */\n async #initJob(snapId: string, timer: Timer): Promise<Job<WorkerType>> {\n const { streams, worker } = await this.#initStreams(snapId, timer);\n\n const jsonRpcConnection = createStreamMiddleware();\n\n pipeline(\n jsonRpcConnection.stream,\n streams.command,\n jsonRpcConnection.stream,\n (error) => {\n if (error && !error.message?.match('Premature close')) {\n logError(`Command stream failure.`, error);\n }\n },\n );\n\n const rpcEngine = JsonRpcEngine.create({\n middleware: [asV2Middleware(jsonRpcConnection.middleware)],\n });\n\n const envMetadata = {\n id: snapId,\n streams,\n rpcEngine,\n worker,\n };\n this.#jobs.set(snapId, envMetadata);\n\n return envMetadata;\n }\n\n /**\n * Sets up the streams for an initiated job.\n *\n * @param snapId - The Snap ID.\n * @param timer - The timer to use for timeouts.\n * @returns The streams to communicate with the worker and the worker itself.\n * @throws If the execution service returns an error or execution times out.\n */\n async #initStreams(\n snapId: string,\n timer: Timer,\n ): Promise<{ streams: JobStreams; worker: WorkerType }> {\n const result = await withTimeout(this.initEnvStream(snapId), timer);\n\n if (result === hasTimedOut) {\n // For certain environments, such as the iframe we may have already created the worker and wish to terminate it.\n await this.terminateJob({ id: snapId });\n\n const status = this.#status.get(snapId);\n if (status === 'created') {\n // Currently this error can only be thrown by OffscreenExecutionService.\n throw new Error(\n `The executor for \"${snapId}\" couldn't start initialization. The offscreen document may not exist.`,\n );\n }\n throw new Error(\n `The executor for \"${snapId}\" failed to initialize. The iframe/webview/worker failed to load.`,\n );\n }\n\n const { worker, stream: envStream } = result;\n const mux = setupMultiplex(envStream, `Snap: \"${snapId}\"`);\n const commandStream = mux.createStream(SNAP_STREAM_NAMES.COMMAND);\n\n // Handle out-of-band errors, i.e. errors thrown from the Snap outside of the req/res cycle.\n // Also keep track of outbound request/responses\n const notificationHandler = (\n message:\n | JsonRpcRequest\n | JsonRpcNotification<Json[] | Record<string, Json>>,\n ) => {\n if (hasProperty(message, 'id')) {\n return;\n }\n\n if (message.method === 'OutboundRequest') {\n this.#messenger.publish('ExecutionService:outboundRequest', snapId);\n } else if (message.method === 'OutboundResponse') {\n this.#messenger.publish('ExecutionService:outboundResponse', snapId);\n } else if (message.method === 'UnhandledError') {\n this.#messenger.publish(\n 'ExecutionService:unhandledError',\n snapId,\n (message.params as { error: SnapErrorJson }).error,\n );\n commandStream.removeListener('data', notificationHandler);\n } else {\n logError(\n new Error(\n `Received unexpected command stream notification \"${message.method}\".`,\n ),\n );\n }\n };\n\n commandStream.on('data', notificationHandler);\n\n const rpcStream = mux\n .createStream(SNAP_STREAM_NAMES.JSON_RPC)\n .setMaxListeners(20);\n\n rpcStream.on('data', (chunk) => {\n if (chunk?.data && hasProperty(chunk?.data, 'id')) {\n this.#messenger.publish('ExecutionService:outboundRequest', snapId);\n }\n });\n\n // An error handler is not attached to the RPC stream until `setupSnapProvider` is called.\n // We must set it up here to prevent errors from bubbling up if the stream is destroyed before then.\n rpcStream.on('error', (error) => {\n if (error && !error.message?.match('Premature close')) {\n logError(`Snap: \"${snapId}\" - RPC stream failure:`, error);\n }\n });\n\n const originalWrite = rpcStream.write.bind(rpcStream);\n\n // @ts-expect-error Hack to inspect the messages being written to the stream.\n rpcStream.write = (chunk, encoding, callback) => {\n // Ignore chain switching notifications as it doesn't matter for the SnapProvider.\n if (chunk?.data?.method === 'metamask_chainChanged') {\n return true;\n }\n\n if (chunk?.data && hasProperty(chunk?.data, 'id')) {\n this.#messenger.publish('ExecutionService:outboundResponse', snapId);\n }\n\n return originalWrite(chunk, encoding, callback);\n };\n\n return {\n streams: {\n command: commandStream,\n rpc: rpcStream,\n connection: envStream,\n mux,\n },\n worker,\n };\n }\n\n /**\n * Abstract function implemented by implementing class that spins up a new worker for a job.\n *\n * Depending on the execution environment, this may run forever if the Snap fails to start up properly, therefore any call to this function should be wrapped in a timeout.\n */\n protected abstract initEnvStream(snapId: string): Promise<{\n worker: WorkerType;\n stream: BasePostMessageStream;\n }>;\n\n /**\n * Set the execution status of the Snap.\n *\n * @param snapId - The Snap ID.\n * @param status - The current execution status.\n */\n protected setSnapStatus(snapId: string, status: ExecutionStatus) {\n this.#status.set(snapId, status);\n }\n\n async terminateAllSnaps() {\n await Promise.all(\n [...this.#jobs.keys()].map(async (snapId) => this.terminateSnap(snapId)),\n );\n }\n\n /**\n * Initializes and executes a Snap, setting up the communication channels to the Snap etc.\n *\n * @param snapData - Data needed for Snap execution.\n * @param snapData.snapId - The ID of the Snap to execute.\n * @param snapData.sourceCode - The source code of the Snap to execute.\n * @param snapData.endowments - The endowments available to the executing Snap.\n * @returns A string `OK` if execution succeeded.\n * @throws If the execution service returns an error or execution times out.\n */\n async executeSnap({\n snapId,\n sourceCode,\n endowments,\n }: SnapExecutionData): Promise<string> {\n if (this.#jobs.has(snapId)) {\n throw new Error(`\"${snapId}\" is already running.`);\n }\n\n this.setSnapStatus(snapId, 'created');\n\n const timer = new Timer(this.#initTimeout);\n\n // This may resolve even if the environment has failed to start up fully\n const job = await this.#initJob(snapId, timer);\n\n // Certain environments use ping as part of their initialization and thus can skip it here\n if (this.#usePing) {\n // Ping the worker to ensure that it started up\n const pingResult = await withTimeout(\n this.#command(job.id, {\n jsonrpc: '2.0',\n method: 'ping',\n id: nanoid(),\n }),\n this.#pingTimeout,\n );\n\n if (pingResult === hasTimedOut) {\n throw new Error(\n `The executor for \"${snapId}\" was unreachable. The executor did not respond in time.`,\n );\n }\n }\n\n const rpcStream = job.streams.rpc;\n\n this.#setupSnapProvider(snapId, rpcStream);\n\n // Use the remaining time as the timer, but ensure that the\n // Snap gets at least half the init timeout.\n const remainingTime = Math.max(timer.remaining, this.#initTimeout / 2);\n\n this.setSnapStatus(snapId, 'initialized');\n\n const request = {\n jsonrpc: '2.0',\n method: 'executeSnap',\n params: { snapId, sourceCode, endowments },\n id: nanoid(),\n };\n\n assertIsJsonRpcRequest(request);\n\n this.setSnapStatus(snapId, 'executing');\n\n const result = await withTimeout(\n this.#command(job.id, request),\n remainingTime,\n );\n\n if (result === hasTimedOut) {\n throw new Error(`${snapId} failed to start.`);\n }\n\n if (result === 'OK') {\n this.setSnapStatus(snapId, 'running');\n }\n\n return result as string;\n }\n\n async #command(\n snapId: string,\n message: JsonRpcRequest,\n ): Promise<Json | undefined> {\n const job = this.#jobs.get(snapId);\n if (!job) {\n throw new Error(`\"${snapId}\" is not currently running.`);\n }\n\n log('Parent: Sending Command', message);\n return await job.rpcEngine.handle(message);\n }\n\n /**\n * Handle RPC request.\n *\n * @param snapId - The ID of the recipient Snap.\n * @param options - Bag of options to pass to the RPC handler.\n * @returns Promise that can handle the request.\n */\n public async handleRpcRequest(\n snapId: string,\n options: SnapRpcHookArgs,\n ): Promise<unknown> {\n const { handler, request, origin } = options;\n\n return await this.#command(snapId, {\n id: nanoid(),\n jsonrpc: '2.0',\n method: 'snapRpc',\n params: {\n snapId,\n origin,\n handler,\n request: request as JsonRpcRequest,\n },\n });\n }\n}\n\n/**\n * Sets up stream multiplexing for the given stream.\n *\n * @param connectionStream - The stream to mux.\n * @param streamName - The name of the stream, for identification in errors.\n * @returns The multiplexed stream.\n */\nexport function setupMultiplex(\n connectionStream: Duplex,\n streamName: string,\n): ObjectMultiplex {\n const mux = new ObjectMultiplex();\n pipeline(connectionStream, mux, connectionStream, (error) => {\n if (error && !error.message?.match('Premature close')) {\n logError(`\"${streamName}\" stream failure.`, error);\n }\n });\n return mux;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"AbstractExecutionService.d.cts","sourceRoot":"","sources":["../../src/services/AbstractExecutionService.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,IAAI,aAAa,EAAE,qCAAqC;AAEhF,OAAO,eAAe,mCAAmC;AACzD,OAAO,KAAK,EAAE,qBAAqB,EAAE,sCAAsC;AAC3E,OAAO,KAAK,EAAE,eAAe,EAAE,8BAA8B;AAE7D,OAAO,KAAK,EAGV,cAAc,EACf,wBAAwB;AASzB,OAAO,KAAK,EAAE,MAAM,EAAE,wBAAwB;AAE9C,OAAO,KAAK,EACV,gBAAgB,EAChB,yBAAyB,EAEzB,iBAAiB,EAClB,+BAA2B;AAK5B,QAAA,MAAM,cAAc,qBAAqB,CAAC;AAE1C,MAAM,MAAM,iBAAiB,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;AAEzE,MAAM,MAAM,oBAAoB,GAAG;IACjC,iBAAiB,EAAE,iBAAiB,CAAC;IACrC,SAAS,EAAE,yBAAyB,CAAC;IACrC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,qBAAqB,CAAC;IAClC,GAAG,EAAE,eAAe,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,GAAG,CAAC,UAAU,IAAI;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,UAAU,CAAC;IACpB,SAAS,EAAE,aAAa,CAAC,cAAc,CAAC,CAAC;IACzC,MAAM,EAAE,UAAU,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,gBAAgB,CAAC,UAAU,IAAI,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,GACjE,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,IAAI,CAAC,CAAC;AAE9B;;;;;;;GAOG;AACH,KAAK,eAAe,GAChB,SAAS,GACT,cAAc,GACd,aAAa,GACb,WAAW,GACX,SAAS,CAAC;AAEd,8BAAsB,wBAAwB,CAAC,UAAU,CACvD,YAAW,gBAAgB;;IAE3B,IAAI,EAAE,OAAO,cAAc,CAAkB;IAE7C,KAAK,OAAQ;gBAkBD,EACV,iBAAiB,EACjB,SAAS,EACT,WAAiD,EACjD,WAAiD,EACjD,kBAAuD,EACvD,OAAc,GACf,EAAE,oBAAoB;IAwCvB;;;;;;OAMG;IACH,SAAS,CAAC,QAAQ,CAAC,YAAY,CAC7B,GAAG,EAAE,gBAAgB,CAAC,UAAU,CAAC,GAChC,OAAO,CAAC,IAAI,CAAC;IAEhB;;;;;;OAMG;IACU,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAwLzD;;;;OAIG;IACH,SAAS,CAAC,QAAQ,CAAC,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;QACxD,MAAM,EAAE,UAAU,CAAC;QACnB,MAAM,EAAE,qBAAqB,CAAC;KAC/B,CAAC;IAEF;;;;;OAKG;IACH,SAAS,CAAC,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe;IAIzD,iBAAiB;IAMvB;;;;;;;;;OASG;IACG,WAAW,CAAC,EAChB,MAAM,EACN,UAAU,EACV,UAAU,GACX,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC;IAiFtC;;;;;;OAMG;IACU,gBAAgB,CAC3B,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,OAAO,CAAC;CAepB;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,gBAAgB,EAAE,MAAM,EACxB,UAAU,EAAE,MAAM,GACjB,eAAe,CAQjB"}
1
+ {"version":3,"file":"AbstractExecutionService.d.cts","sourceRoot":"","sources":["../../src/services/AbstractExecutionService.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"AbstractExecutionService.d.mts","sourceRoot":"","sources":["../../src/services/AbstractExecutionService.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"AbstractExecutionService.d.mts","sourceRoot":"","sources":["../../src/services/AbstractExecutionService.ts"],"names":[],"mappings":"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"}
@@ -170,6 +170,13 @@ export class AbstractExecutionService {
170
170
  this.#messenger.publish('ExecutionService:outboundRequest', snapId);
171
171
  }
172
172
  });
173
+ // An error handler is not attached to the RPC stream until `setupSnapProvider` is called.
174
+ // We must set it up here to prevent errors from bubbling up if the stream is destroyed before then.
175
+ rpcStream.on('error', (error) => {
176
+ if (error && !error.message?.match('Premature close')) {
177
+ logError(`Snap: "${snapId}" - RPC stream failure:`, error);
178
+ }
179
+ });
173
180
  const originalWrite = rpcStream.write.bind(rpcStream);
174
181
  // @ts-expect-error Hack to inspect the messages being written to the stream.
175
182
  rpcStream.write = (chunk, encoding, callback) => {
@@ -1 +1 @@
1
- {"version":3,"file":"AbstractExecutionService.mjs","sourceRoot":"","sources":["../../src/services/AbstractExecutionService.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,cAAc,EAAE,kCAAkC;AAC3D,OAAO,EAAE,eAAe,IAAI,aAAa,EAAE,qCAAqC;AAChF,OAAO,EAAE,sBAAsB,EAAE,6CAA6C;AAC9E,OAAO,gBAAe,mCAAmC;;AAGzD,OAAO,EAAE,iBAAiB,EAAE,QAAQ,EAAE,UAAU,EAAE,8BAA8B;AAMhF,OAAO,EACL,QAAQ,EACR,sBAAsB,EACtB,WAAW,EACX,cAAc,EACf,wBAAwB;AACzB,OAAO,EAAE,MAAM,EAAE,eAAe;AAChC,OAAO,EAAE,QAAQ,EAAE,wBAAwB;AAS3C,OAAO,EAAE,GAAG,EAAE,uBAAmB;AACjC,OAAO,EAAE,KAAK,EAAE,2BAAuB;AACvC,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,qBAAiB;AAEpD,MAAM,cAAc,GAAG,kBAAkB,CAAC;AA6C1C,MAAM,OAAgB,wBAAwB;IAG5C,IAAI,GAA0B,cAAc,CAAC;IAE7C,KAAK,GAAG,IAAI,CAAC;IAEJ,KAAK,CAA+B;IAEpC,OAAO,CAA+B;IAEtC,kBAAkB,CAAoB;IAEtC,UAAU,CAA4B;IAEtC,YAAY,CAAS;IAErB,YAAY,CAAS;IAErB,mBAAmB,CAAS;IAE5B,QAAQ,CAAU;IAE3B,YAAY,EACV,iBAAiB,EACjB,SAAS,EACT,WAAW,GAAG,cAAc,CAAC,EAAE,EAAE,QAAQ,CAAC,MAAM,CAAC,EACjD,WAAW,GAAG,cAAc,CAAC,EAAE,EAAE,QAAQ,CAAC,MAAM,CAAC,EACjD,kBAAkB,GAAG,cAAc,CAAC,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,EACvD,OAAO,GAAG,IAAI,GACO;QACrB,IAAI,CAAC,KAAK,GAAG,IAAI,GAAG,EAAE,CAAC;QACvB,IAAI,CAAC,OAAO,GAAG,IAAI,GAAG,EAAE,CAAC;QACzB,IAAI,CAAC,kBAAkB,GAAG,iBAAiB,CAAC;QAC5C,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;QAC5B,IAAI,CAAC,YAAY,GAAG,WAAW,CAAC;QAChC,IAAI,CAAC,YAAY,GAAG,WAAW,CAAC;QAChC,IAAI,CAAC,mBAAmB,GAAG,kBAAkB,CAAC;QAC9C,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC;QAExB,IAAI,CAAC,wBAAwB,EAAE,CAAC;IAClC,CAAC;IAED;;;OAGG;IACH,wBAAwB;QACtB,IAAI,CAAC,UAAU,CAAC,qBAAqB,CACnC,GAAG,cAAc,mBAAmB,EACpC,KAAK,EAAE,MAAc,EAAE,OAAwB,EAAE,EAAE,CACjD,IAAI,CAAC,gBAAgB,CAAC,MAAM,EAAE,OAAO,CAAC,CACzC,CAAC;QAEF,IAAI,CAAC,UAAU,CAAC,qBAAqB,CACnC,GAAG,cAAc,cAAc,EAC/B,KAAK,EAAE,IAAuB,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAC1D,CAAC;QAEF,IAAI,CAAC,UAAU,CAAC,qBAAqB,CACnC,GAAG,cAAc,gBAAgB,EACjC,KAAK,EAAE,MAAc,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,CACrD,CAAC;QAEF,IAAI,CAAC,UAAU,CAAC,qBAAqB,CACnC,GAAG,cAAc,oBAAoB,EACrC,KAAK,IAAI,EAAE,CAAC,IAAI,CAAC,iBAAiB,EAAE,CACrC,CAAC;IACJ,CAAC;IAaD;;;;;;OAMG;IACI,KAAK,CAAC,aAAa,CAAC,MAAc;QACvC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACnC,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,OAAO;QACT,CAAC;QAED,IAAI,CAAC;YACH,0FAA0F;YAC1F,MAAM,MAAM,GAAG,MAAM,WAAW,CAC9B,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE;gBACpB,OAAO,EAAE,KAAK;gBACd,MAAM,EAAE,WAAW;gBACnB,EAAE,EAAE,MAAM,EAAE;aACb,CAAC,EACF,IAAI,CAAC,mBAAmB,CACzB,CAAC;YAEF,IAAI,MAAM,KAAK,WAAW,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;gBAC9C,UAAU,CAAC,SAAS,MAAM,mCAAmC,CAAC,CAAC;YACjE,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;QAED,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE;YAC5C,IAAI,CAAC;gBACH,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;oBACtB,MAAM,CAAC,OAAO,EAAE,CAAC;gBACnB,CAAC;YACH,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,QAAQ,CAAC,+BAA+B,EAAE,KAAK,CAAC,CAAC;YACnD,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,MAAM,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;QAE7B,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC1B,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC5B,GAAG,CAAC,SAAS,MAAM,eAAe,CAAC,CAAC;IACtC,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,QAAQ,CAAC,MAAc,EAAE,KAAY;QACzC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QAEnE,MAAM,iBAAiB,GAAG,sBAAsB,EAAE,CAAC;QAEnD,QAAQ,CACN,iBAAiB,CAAC,MAAM,EACxB,OAAO,CAAC,OAAO,EACf,iBAAiB,CAAC,MAAM,EACxB,CAAC,KAAK,EAAE,EAAE;YACR,IAAI,KAAK,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,iBAAiB,CAAC,EAAE,CAAC;gBACtD,QAAQ,CAAC,yBAAyB,EAAE,KAAK,CAAC,CAAC;YAC7C,CAAC;QACH,CAAC,CACF,CAAC;QAEF,MAAM,SAAS,GAAG,aAAa,CAAC,MAAM,CAAC;YACrC,UAAU,EAAE,CAAC,cAAc,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC;SAC3D,CAAC,CAAC;QAEH,MAAM,WAAW,GAAG;YAClB,EAAE,EAAE,MAAM;YACV,OAAO;YACP,SAAS;YACT,MAAM;SACP,CAAC;QACF,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;QAEpC,OAAO,WAAW,CAAC;IACrB,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,YAAY,CAChB,MAAc,EACd,KAAY;QAEZ,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,KAAK,CAAC,CAAC;QAEpE,IAAI,MAAM,KAAK,WAAW,EAAE,CAAC;YAC3B,gHAAgH;YAChH,MAAM,IAAI,CAAC,YAAY,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;YAExC,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YACxC,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;gBACzB,wEAAwE;gBACxE,MAAM,IAAI,KAAK,CACb,qBAAqB,MAAM,wEAAwE,CACpG,CAAC;YACJ,CAAC;YACD,MAAM,IAAI,KAAK,CACb,qBAAqB,MAAM,mEAAmE,CAC/F,CAAC;QACJ,CAAC;QAED,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,CAAC;QAC7C,MAAM,GAAG,GAAG,cAAc,CAAC,SAAS,EAAE,UAAU,MAAM,GAAG,CAAC,CAAC;QAC3D,MAAM,aAAa,GAAG,GAAG,CAAC,YAAY,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;QAElE,4FAA4F;QAC5F,gDAAgD;QAChD,MAAM,mBAAmB,GAAG,CAC1B,OAEsD,EACtD,EAAE;YACF,IAAI,WAAW,CAAC,OAAO,EAAE,IAAI,CAAC,EAAE,CAAC;gBAC/B,OAAO;YACT,CAAC;YAED,IAAI,OAAO,CAAC,MAAM,KAAK,iBAAiB,EAAE,CAAC;gBACzC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,kCAAkC,EAAE,MAAM,CAAC,CAAC;YACtE,CAAC;iBAAM,IAAI,OAAO,CAAC,MAAM,KAAK,kBAAkB,EAAE,CAAC;gBACjD,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,mCAAmC,EAAE,MAAM,CAAC,CAAC;YACvE,CAAC;iBAAM,IAAI,OAAO,CAAC,MAAM,KAAK,gBAAgB,EAAE,CAAC;gBAC/C,IAAI,CAAC,UAAU,CAAC,OAAO,CACrB,iCAAiC,EACjC,MAAM,EACL,OAAO,CAAC,MAAmC,CAAC,KAAK,CACnD,CAAC;gBACF,aAAa,CAAC,cAAc,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;YAC5D,CAAC;iBAAM,CAAC;gBACN,QAAQ,CACN,IAAI,KAAK,CACP,oDAAoD,OAAO,CAAC,MAAM,IAAI,CACvE,CACF,CAAC;YACJ,CAAC;QACH,CAAC,CAAC;QAEF,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;QAE9C,MAAM,SAAS,GAAG,GAAG;aAClB,YAAY,CAAC,iBAAiB,CAAC,QAAQ,CAAC;aACxC,eAAe,CAAC,EAAE,CAAC,CAAC;QAEvB,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE;YAC7B,IAAI,KAAK,EAAE,IAAI,IAAI,WAAW,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC;gBAClD,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,kCAAkC,EAAE,MAAM,CAAC,CAAC;YACtE,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,MAAM,aAAa,GAAG,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAEtD,6EAA6E;QAC7E,SAAS,CAAC,KAAK,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE;YAC9C,kFAAkF;YAClF,IAAI,KAAK,EAAE,IAAI,EAAE,MAAM,KAAK,uBAAuB,EAAE,CAAC;gBACpD,OAAO,IAAI,CAAC;YACd,CAAC;YAED,IAAI,KAAK,EAAE,IAAI,IAAI,WAAW,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC;gBAClD,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,mCAAmC,EAAE,MAAM,CAAC,CAAC;YACvE,CAAC;YAED,OAAO,aAAa,CAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;QAClD,CAAC,CAAC;QAEF,OAAO;YACL,OAAO,EAAE;gBACP,OAAO,EAAE,aAAa;gBACtB,GAAG,EAAE,SAAS;gBACd,UAAU,EAAE,SAAS;gBACrB,GAAG;aACJ;YACD,MAAM;SACP,CAAC;IACJ,CAAC;IAYD;;;;;OAKG;IACO,aAAa,CAAC,MAAc,EAAE,MAAuB;QAC7D,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,CAAC;IAED,KAAK,CAAC,iBAAiB;QACrB,MAAM,OAAO,CAAC,GAAG,CACf,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CACzE,CAAC;IACJ,CAAC;IAED;;;;;;;;;OASG;IACH,KAAK,CAAC,WAAW,CAAC,EAChB,MAAM,EACN,UAAU,EACV,UAAU,GACQ;QAClB,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YAC3B,MAAM,IAAI,KAAK,CAAC,IAAI,MAAM,uBAAuB,CAAC,CAAC;QACrD,CAAC;QAED,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;QAEtC,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAE3C,wEAAwE;QACxE,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QAE/C,0FAA0F;QAC1F,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClB,+CAA+C;YAC/C,MAAM,UAAU,GAAG,MAAM,WAAW,CAClC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,EAAE;gBACpB,OAAO,EAAE,KAAK;gBACd,MAAM,EAAE,MAAM;gBACd,EAAE,EAAE,MAAM,EAAE;aACb,CAAC,EACF,IAAI,CAAC,YAAY,CAClB,CAAC;YAEF,IAAI,UAAU,KAAK,WAAW,EAAE,CAAC;gBAC/B,MAAM,IAAI,KAAK,CACb,qBAAqB,MAAM,0DAA0D,CACtF,CAAC;YACJ,CAAC;QACH,CAAC;QAED,MAAM,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC;QAElC,IAAI,CAAC,kBAAkB,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;QAE3C,2DAA2D;QAC3D,4CAA4C;QAC5C,MAAM,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,EAAE,IAAI,CAAC,YAAY,GAAG,CAAC,CAAC,CAAC;QAEvE,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;QAE1C,MAAM,OAAO,GAAG;YACd,OAAO,EAAE,KAAK;YACd,MAAM,EAAE,aAAa;YACrB,MAAM,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE;YAC1C,EAAE,EAAE,MAAM,EAAE;SACb,CAAC;QAEF,sBAAsB,CAAC,OAAO,CAAC,CAAC;QAEhC,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;QAExC,MAAM,MAAM,GAAG,MAAM,WAAW,CAC9B,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,EAAE,OAAO,CAAC,EAC9B,aAAa,CACd,CAAC;QAEF,IAAI,MAAM,KAAK,WAAW,EAAE,CAAC;YAC3B,MAAM,IAAI,KAAK,CAAC,GAAG,MAAM,mBAAmB,CAAC,CAAC;QAChD,CAAC;QAED,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;YACpB,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;QACxC,CAAC;QAED,OAAO,MAAgB,CAAC;IAC1B,CAAC;IAED,KAAK,CAAC,QAAQ,CACZ,MAAc,EACd,OAAuB;QAEvB,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACnC,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,MAAM,IAAI,KAAK,CAAC,IAAI,MAAM,6BAA6B,CAAC,CAAC;QAC3D,CAAC;QAED,GAAG,CAAC,yBAAyB,EAAE,OAAO,CAAC,CAAC;QACxC,OAAO,MAAM,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC7C,CAAC;IAED;;;;;;OAMG;IACI,KAAK,CAAC,gBAAgB,CAC3B,MAAc,EACd,OAAwB;QAExB,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;QAE7C,OAAO,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE;YACjC,EAAE,EAAE,MAAM,EAAE;YACZ,OAAO,EAAE,KAAK;YACd,MAAM,EAAE,SAAS;YACjB,MAAM,EAAE;gBACN,MAAM;gBACN,MAAM;gBACN,OAAO;gBACP,OAAO,EAAE,OAAyB;aACnC;SACF,CAAC,CAAC;IACL,CAAC;CACF;AAED;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAC5B,gBAAwB,EACxB,UAAkB;IAElB,MAAM,GAAG,GAAG,IAAI,eAAe,EAAE,CAAC;IAClC,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE,gBAAgB,EAAE,CAAC,KAAK,EAAE,EAAE;QAC1D,IAAI,KAAK,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,iBAAiB,CAAC,EAAE,CAAC;YACtD,QAAQ,CAAC,IAAI,UAAU,mBAAmB,EAAE,KAAK,CAAC,CAAC;QACrD,CAAC;IACH,CAAC,CAAC,CAAC;IACH,OAAO,GAAG,CAAC;AACb,CAAC","sourcesContent":["import { asV2Middleware } from '@metamask/json-rpc-engine';\nimport { JsonRpcEngineV2 as JsonRpcEngine } from '@metamask/json-rpc-engine/v2';\nimport { createStreamMiddleware } from '@metamask/json-rpc-middleware-stream';\nimport ObjectMultiplex from '@metamask/object-multiplex';\nimport type { BasePostMessageStream } from '@metamask/post-message-stream';\nimport type { SnapRpcHookArgs } from '@metamask/snaps-utils';\nimport { SNAP_STREAM_NAMES, logError, logWarning } from '@metamask/snaps-utils';\nimport type {\n Json,\n JsonRpcNotification,\n JsonRpcRequest,\n} from '@metamask/utils';\nimport {\n Duration,\n assertIsJsonRpcRequest,\n hasProperty,\n inMilliseconds,\n} from '@metamask/utils';\nimport { nanoid } from 'nanoid';\nimport { pipeline } from 'readable-stream';\nimport type { Duplex } from 'readable-stream';\n\nimport type {\n ExecutionService,\n ExecutionServiceMessenger,\n SnapErrorJson,\n SnapExecutionData,\n} from './ExecutionService';\nimport { log } from '../logging';\nimport { Timer } from '../snaps/Timer';\nimport { hasTimedOut, withTimeout } from '../utils';\n\nconst controllerName = 'ExecutionService';\n\nexport type SetupSnapProvider = (snapId: string, stream: Duplex) => void;\n\nexport type ExecutionServiceArgs = {\n setupSnapProvider: SetupSnapProvider;\n messenger: ExecutionServiceMessenger;\n initTimeout?: number;\n pingTimeout?: number;\n terminationTimeout?: number;\n usePing?: boolean;\n};\n\nexport type JobStreams = {\n command: Duplex;\n rpc: Duplex;\n connection: BasePostMessageStream;\n mux: ObjectMultiplex;\n};\n\nexport type Job<WorkerType> = {\n id: string;\n streams: JobStreams;\n rpcEngine: JsonRpcEngine<JsonRpcRequest>;\n worker: WorkerType;\n};\n\nexport type TerminateJobArgs<WorkerType> = Partial<Job<WorkerType>> &\n Pick<Job<WorkerType>, 'id'>;\n\n/** \n Statuses used for diagnostic purposes\n - created: The initial state, no initialization has started \n - initializing: Snap execution environment is initializing\n - initialized: Snap execution environment has initialized\n - executing: Snap source code is being executed\n - running: Snap executed and ready for RPC requests\n */\ntype ExecutionStatus =\n | 'created'\n | 'initializing'\n | 'initialized'\n | 'executing'\n | 'running';\n\nexport abstract class AbstractExecutionService<WorkerType>\n implements ExecutionService\n{\n name: typeof controllerName = controllerName;\n\n state = null;\n\n readonly #jobs: Map<string, Job<WorkerType>>;\n\n readonly #status: Map<string, ExecutionStatus>;\n\n readonly #setupSnapProvider: SetupSnapProvider;\n\n readonly #messenger: ExecutionServiceMessenger;\n\n readonly #initTimeout: number;\n\n readonly #pingTimeout: number;\n\n readonly #terminationTimeout: number;\n\n readonly #usePing: boolean;\n\n constructor({\n setupSnapProvider,\n messenger,\n initTimeout = inMilliseconds(60, Duration.Second),\n pingTimeout = inMilliseconds(10, Duration.Second),\n terminationTimeout = inMilliseconds(1, Duration.Second),\n usePing = true,\n }: ExecutionServiceArgs) {\n this.#jobs = new Map();\n this.#status = new Map();\n this.#setupSnapProvider = setupSnapProvider;\n this.#messenger = messenger;\n this.#initTimeout = initTimeout;\n this.#pingTimeout = pingTimeout;\n this.#terminationTimeout = terminationTimeout;\n this.#usePing = usePing;\n\n this.#registerMessageHandlers();\n }\n\n /**\n * Constructor helper for registering the controller's messaging system\n * actions.\n */\n #registerMessageHandlers(): void {\n this.#messenger.registerActionHandler(\n `${controllerName}:handleRpcRequest`,\n async (snapId: string, options: SnapRpcHookArgs) =>\n this.handleRpcRequest(snapId, options),\n );\n\n this.#messenger.registerActionHandler(\n `${controllerName}:executeSnap`,\n async (data: SnapExecutionData) => this.executeSnap(data),\n );\n\n this.#messenger.registerActionHandler(\n `${controllerName}:terminateSnap`,\n async (snapId: string) => this.terminateSnap(snapId),\n );\n\n this.#messenger.registerActionHandler(\n `${controllerName}:terminateAllSnaps`,\n async () => this.terminateAllSnaps(),\n );\n }\n\n /**\n * Performs additional necessary work during job termination. **MUST** be\n * implemented by concrete implementations. See\n * {@link AbstractExecutionService.terminate} for details.\n *\n * @param job - The object corresponding to the job to be terminated.\n */\n protected abstract terminateJob(\n job: TerminateJobArgs<WorkerType>,\n ): Promise<void>;\n\n /**\n * Terminates the Snap with the specified ID and deletes all its associated\n * data. Any subsequent messages targeting the Snap will fail with an error.\n * Throws an error if termination fails unexpectedly.\n *\n * @param snapId - The id of the Snap to be terminated.\n */\n public async terminateSnap(snapId: string): Promise<void> {\n const job = this.#jobs.get(snapId);\n if (!job) {\n return;\n }\n\n try {\n // Ping worker and tell it to run teardown, continue with termination if it takes too long\n const result = await withTimeout(\n this.#command(snapId, {\n jsonrpc: '2.0',\n method: 'terminate',\n id: nanoid(),\n }),\n this.#terminationTimeout,\n );\n\n if (result === hasTimedOut || result !== 'OK') {\n logWarning(`Snap \"${snapId}\" failed to terminate gracefully.`);\n }\n } catch {\n // Ignore\n }\n\n Object.values(job.streams).forEach((stream) => {\n try {\n if (!stream.destroyed) {\n stream.destroy();\n }\n } catch (error) {\n logError('Error while destroying stream', error);\n }\n });\n\n await this.terminateJob(job);\n\n this.#jobs.delete(snapId);\n this.#status.delete(snapId);\n log(`Snap \"${snapId}\" terminated.`);\n }\n\n /**\n * Initiates a job for a Snap.\n *\n * @param snapId - The ID of the Snap to initiate a job for.\n * @param timer - The timer to use for timeouts.\n * @returns Information regarding the created job.\n * @throws If the execution service returns an error or execution times out.\n */\n async #initJob(snapId: string, timer: Timer): Promise<Job<WorkerType>> {\n const { streams, worker } = await this.#initStreams(snapId, timer);\n\n const jsonRpcConnection = createStreamMiddleware();\n\n pipeline(\n jsonRpcConnection.stream,\n streams.command,\n jsonRpcConnection.stream,\n (error) => {\n if (error && !error.message?.match('Premature close')) {\n logError(`Command stream failure.`, error);\n }\n },\n );\n\n const rpcEngine = JsonRpcEngine.create({\n middleware: [asV2Middleware(jsonRpcConnection.middleware)],\n });\n\n const envMetadata = {\n id: snapId,\n streams,\n rpcEngine,\n worker,\n };\n this.#jobs.set(snapId, envMetadata);\n\n return envMetadata;\n }\n\n /**\n * Sets up the streams for an initiated job.\n *\n * @param snapId - The Snap ID.\n * @param timer - The timer to use for timeouts.\n * @returns The streams to communicate with the worker and the worker itself.\n * @throws If the execution service returns an error or execution times out.\n */\n async #initStreams(\n snapId: string,\n timer: Timer,\n ): Promise<{ streams: JobStreams; worker: WorkerType }> {\n const result = await withTimeout(this.initEnvStream(snapId), timer);\n\n if (result === hasTimedOut) {\n // For certain environments, such as the iframe we may have already created the worker and wish to terminate it.\n await this.terminateJob({ id: snapId });\n\n const status = this.#status.get(snapId);\n if (status === 'created') {\n // Currently this error can only be thrown by OffscreenExecutionService.\n throw new Error(\n `The executor for \"${snapId}\" couldn't start initialization. The offscreen document may not exist.`,\n );\n }\n throw new Error(\n `The executor for \"${snapId}\" failed to initialize. The iframe/webview/worker failed to load.`,\n );\n }\n\n const { worker, stream: envStream } = result;\n const mux = setupMultiplex(envStream, `Snap: \"${snapId}\"`);\n const commandStream = mux.createStream(SNAP_STREAM_NAMES.COMMAND);\n\n // Handle out-of-band errors, i.e. errors thrown from the Snap outside of the req/res cycle.\n // Also keep track of outbound request/responses\n const notificationHandler = (\n message:\n | JsonRpcRequest\n | JsonRpcNotification<Json[] | Record<string, Json>>,\n ) => {\n if (hasProperty(message, 'id')) {\n return;\n }\n\n if (message.method === 'OutboundRequest') {\n this.#messenger.publish('ExecutionService:outboundRequest', snapId);\n } else if (message.method === 'OutboundResponse') {\n this.#messenger.publish('ExecutionService:outboundResponse', snapId);\n } else if (message.method === 'UnhandledError') {\n this.#messenger.publish(\n 'ExecutionService:unhandledError',\n snapId,\n (message.params as { error: SnapErrorJson }).error,\n );\n commandStream.removeListener('data', notificationHandler);\n } else {\n logError(\n new Error(\n `Received unexpected command stream notification \"${message.method}\".`,\n ),\n );\n }\n };\n\n commandStream.on('data', notificationHandler);\n\n const rpcStream = mux\n .createStream(SNAP_STREAM_NAMES.JSON_RPC)\n .setMaxListeners(20);\n\n rpcStream.on('data', (chunk) => {\n if (chunk?.data && hasProperty(chunk?.data, 'id')) {\n this.#messenger.publish('ExecutionService:outboundRequest', snapId);\n }\n });\n\n const originalWrite = rpcStream.write.bind(rpcStream);\n\n // @ts-expect-error Hack to inspect the messages being written to the stream.\n rpcStream.write = (chunk, encoding, callback) => {\n // Ignore chain switching notifications as it doesn't matter for the SnapProvider.\n if (chunk?.data?.method === 'metamask_chainChanged') {\n return true;\n }\n\n if (chunk?.data && hasProperty(chunk?.data, 'id')) {\n this.#messenger.publish('ExecutionService:outboundResponse', snapId);\n }\n\n return originalWrite(chunk, encoding, callback);\n };\n\n return {\n streams: {\n command: commandStream,\n rpc: rpcStream,\n connection: envStream,\n mux,\n },\n worker,\n };\n }\n\n /**\n * Abstract function implemented by implementing class that spins up a new worker for a job.\n *\n * Depending on the execution environment, this may run forever if the Snap fails to start up properly, therefore any call to this function should be wrapped in a timeout.\n */\n protected abstract initEnvStream(snapId: string): Promise<{\n worker: WorkerType;\n stream: BasePostMessageStream;\n }>;\n\n /**\n * Set the execution status of the Snap.\n *\n * @param snapId - The Snap ID.\n * @param status - The current execution status.\n */\n protected setSnapStatus(snapId: string, status: ExecutionStatus) {\n this.#status.set(snapId, status);\n }\n\n async terminateAllSnaps() {\n await Promise.all(\n [...this.#jobs.keys()].map(async (snapId) => this.terminateSnap(snapId)),\n );\n }\n\n /**\n * Initializes and executes a Snap, setting up the communication channels to the Snap etc.\n *\n * @param snapData - Data needed for Snap execution.\n * @param snapData.snapId - The ID of the Snap to execute.\n * @param snapData.sourceCode - The source code of the Snap to execute.\n * @param snapData.endowments - The endowments available to the executing Snap.\n * @returns A string `OK` if execution succeeded.\n * @throws If the execution service returns an error or execution times out.\n */\n async executeSnap({\n snapId,\n sourceCode,\n endowments,\n }: SnapExecutionData): Promise<string> {\n if (this.#jobs.has(snapId)) {\n throw new Error(`\"${snapId}\" is already running.`);\n }\n\n this.setSnapStatus(snapId, 'created');\n\n const timer = new Timer(this.#initTimeout);\n\n // This may resolve even if the environment has failed to start up fully\n const job = await this.#initJob(snapId, timer);\n\n // Certain environments use ping as part of their initialization and thus can skip it here\n if (this.#usePing) {\n // Ping the worker to ensure that it started up\n const pingResult = await withTimeout(\n this.#command(job.id, {\n jsonrpc: '2.0',\n method: 'ping',\n id: nanoid(),\n }),\n this.#pingTimeout,\n );\n\n if (pingResult === hasTimedOut) {\n throw new Error(\n `The executor for \"${snapId}\" was unreachable. The executor did not respond in time.`,\n );\n }\n }\n\n const rpcStream = job.streams.rpc;\n\n this.#setupSnapProvider(snapId, rpcStream);\n\n // Use the remaining time as the timer, but ensure that the\n // Snap gets at least half the init timeout.\n const remainingTime = Math.max(timer.remaining, this.#initTimeout / 2);\n\n this.setSnapStatus(snapId, 'initialized');\n\n const request = {\n jsonrpc: '2.0',\n method: 'executeSnap',\n params: { snapId, sourceCode, endowments },\n id: nanoid(),\n };\n\n assertIsJsonRpcRequest(request);\n\n this.setSnapStatus(snapId, 'executing');\n\n const result = await withTimeout(\n this.#command(job.id, request),\n remainingTime,\n );\n\n if (result === hasTimedOut) {\n throw new Error(`${snapId} failed to start.`);\n }\n\n if (result === 'OK') {\n this.setSnapStatus(snapId, 'running');\n }\n\n return result as string;\n }\n\n async #command(\n snapId: string,\n message: JsonRpcRequest,\n ): Promise<Json | undefined> {\n const job = this.#jobs.get(snapId);\n if (!job) {\n throw new Error(`\"${snapId}\" is not currently running.`);\n }\n\n log('Parent: Sending Command', message);\n return await job.rpcEngine.handle(message);\n }\n\n /**\n * Handle RPC request.\n *\n * @param snapId - The ID of the recipient Snap.\n * @param options - Bag of options to pass to the RPC handler.\n * @returns Promise that can handle the request.\n */\n public async handleRpcRequest(\n snapId: string,\n options: SnapRpcHookArgs,\n ): Promise<unknown> {\n const { handler, request, origin } = options;\n\n return await this.#command(snapId, {\n id: nanoid(),\n jsonrpc: '2.0',\n method: 'snapRpc',\n params: {\n snapId,\n origin,\n handler,\n request: request as JsonRpcRequest,\n },\n });\n }\n}\n\n/**\n * Sets up stream multiplexing for the given stream.\n *\n * @param connectionStream - The stream to mux.\n * @param streamName - The name of the stream, for identification in errors.\n * @returns The multiplexed stream.\n */\nexport function setupMultiplex(\n connectionStream: Duplex,\n streamName: string,\n): ObjectMultiplex {\n const mux = new ObjectMultiplex();\n pipeline(connectionStream, mux, connectionStream, (error) => {\n if (error && !error.message?.match('Premature close')) {\n logError(`\"${streamName}\" stream failure.`, error);\n }\n });\n return mux;\n}\n"]}
1
+ {"version":3,"file":"AbstractExecutionService.mjs","sourceRoot":"","sources":["../../src/services/AbstractExecutionService.ts"],"names":[],"mappings":"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","sourcesContent":["import { asV2Middleware } from '@metamask/json-rpc-engine';\nimport { JsonRpcEngineV2 as JsonRpcEngine } from '@metamask/json-rpc-engine/v2';\nimport { createStreamMiddleware } from '@metamask/json-rpc-middleware-stream';\nimport ObjectMultiplex from '@metamask/object-multiplex';\nimport type { BasePostMessageStream } from '@metamask/post-message-stream';\nimport type { SnapRpcHookArgs } from '@metamask/snaps-utils';\nimport { SNAP_STREAM_NAMES, logError, logWarning } from '@metamask/snaps-utils';\nimport type {\n Json,\n JsonRpcNotification,\n JsonRpcRequest,\n} from '@metamask/utils';\nimport {\n Duration,\n assertIsJsonRpcRequest,\n hasProperty,\n inMilliseconds,\n} from '@metamask/utils';\nimport { nanoid } from 'nanoid';\nimport { pipeline } from 'readable-stream';\nimport type { Duplex } from 'readable-stream';\n\nimport type {\n ExecutionService,\n ExecutionServiceMessenger,\n SnapErrorJson,\n SnapExecutionData,\n} from './ExecutionService';\nimport { log } from '../logging';\nimport { Timer } from '../snaps/Timer';\nimport { hasTimedOut, withTimeout } from '../utils';\n\nconst controllerName = 'ExecutionService';\n\nexport type SetupSnapProvider = (snapId: string, stream: Duplex) => void;\n\nexport type ExecutionServiceArgs = {\n setupSnapProvider: SetupSnapProvider;\n messenger: ExecutionServiceMessenger;\n initTimeout?: number;\n pingTimeout?: number;\n terminationTimeout?: number;\n usePing?: boolean;\n};\n\nexport type JobStreams = {\n command: Duplex;\n rpc: Duplex;\n connection: BasePostMessageStream;\n mux: ObjectMultiplex;\n};\n\nexport type Job<WorkerType> = {\n id: string;\n streams: JobStreams;\n rpcEngine: JsonRpcEngine<JsonRpcRequest>;\n worker: WorkerType;\n};\n\nexport type TerminateJobArgs<WorkerType> = Partial<Job<WorkerType>> &\n Pick<Job<WorkerType>, 'id'>;\n\n/** \n Statuses used for diagnostic purposes\n - created: The initial state, no initialization has started \n - initializing: Snap execution environment is initializing\n - initialized: Snap execution environment has initialized\n - executing: Snap source code is being executed\n - running: Snap executed and ready for RPC requests\n */\ntype ExecutionStatus =\n | 'created'\n | 'initializing'\n | 'initialized'\n | 'executing'\n | 'running';\n\nexport abstract class AbstractExecutionService<WorkerType>\n implements ExecutionService\n{\n name: typeof controllerName = controllerName;\n\n state = null;\n\n readonly #jobs: Map<string, Job<WorkerType>>;\n\n readonly #status: Map<string, ExecutionStatus>;\n\n readonly #setupSnapProvider: SetupSnapProvider;\n\n readonly #messenger: ExecutionServiceMessenger;\n\n readonly #initTimeout: number;\n\n readonly #pingTimeout: number;\n\n readonly #terminationTimeout: number;\n\n readonly #usePing: boolean;\n\n constructor({\n setupSnapProvider,\n messenger,\n initTimeout = inMilliseconds(60, Duration.Second),\n pingTimeout = inMilliseconds(10, Duration.Second),\n terminationTimeout = inMilliseconds(1, Duration.Second),\n usePing = true,\n }: ExecutionServiceArgs) {\n this.#jobs = new Map();\n this.#status = new Map();\n this.#setupSnapProvider = setupSnapProvider;\n this.#messenger = messenger;\n this.#initTimeout = initTimeout;\n this.#pingTimeout = pingTimeout;\n this.#terminationTimeout = terminationTimeout;\n this.#usePing = usePing;\n\n this.#registerMessageHandlers();\n }\n\n /**\n * Constructor helper for registering the controller's messaging system\n * actions.\n */\n #registerMessageHandlers(): void {\n this.#messenger.registerActionHandler(\n `${controllerName}:handleRpcRequest`,\n async (snapId: string, options: SnapRpcHookArgs) =>\n this.handleRpcRequest(snapId, options),\n );\n\n this.#messenger.registerActionHandler(\n `${controllerName}:executeSnap`,\n async (data: SnapExecutionData) => this.executeSnap(data),\n );\n\n this.#messenger.registerActionHandler(\n `${controllerName}:terminateSnap`,\n async (snapId: string) => this.terminateSnap(snapId),\n );\n\n this.#messenger.registerActionHandler(\n `${controllerName}:terminateAllSnaps`,\n async () => this.terminateAllSnaps(),\n );\n }\n\n /**\n * Performs additional necessary work during job termination. **MUST** be\n * implemented by concrete implementations. See\n * {@link AbstractExecutionService.terminate} for details.\n *\n * @param job - The object corresponding to the job to be terminated.\n */\n protected abstract terminateJob(\n job: TerminateJobArgs<WorkerType>,\n ): Promise<void>;\n\n /**\n * Terminates the Snap with the specified ID and deletes all its associated\n * data. Any subsequent messages targeting the Snap will fail with an error.\n * Throws an error if termination fails unexpectedly.\n *\n * @param snapId - The id of the Snap to be terminated.\n */\n public async terminateSnap(snapId: string): Promise<void> {\n const job = this.#jobs.get(snapId);\n if (!job) {\n return;\n }\n\n try {\n // Ping worker and tell it to run teardown, continue with termination if it takes too long\n const result = await withTimeout(\n this.#command(snapId, {\n jsonrpc: '2.0',\n method: 'terminate',\n id: nanoid(),\n }),\n this.#terminationTimeout,\n );\n\n if (result === hasTimedOut || result !== 'OK') {\n logWarning(`Snap \"${snapId}\" failed to terminate gracefully.`);\n }\n } catch {\n // Ignore\n }\n\n Object.values(job.streams).forEach((stream) => {\n try {\n if (!stream.destroyed) {\n stream.destroy();\n }\n } catch (error) {\n logError('Error while destroying stream', error);\n }\n });\n\n await this.terminateJob(job);\n\n this.#jobs.delete(snapId);\n this.#status.delete(snapId);\n log(`Snap \"${snapId}\" terminated.`);\n }\n\n /**\n * Initiates a job for a Snap.\n *\n * @param snapId - The ID of the Snap to initiate a job for.\n * @param timer - The timer to use for timeouts.\n * @returns Information regarding the created job.\n * @throws If the execution service returns an error or execution times out.\n */\n async #initJob(snapId: string, timer: Timer): Promise<Job<WorkerType>> {\n const { streams, worker } = await this.#initStreams(snapId, timer);\n\n const jsonRpcConnection = createStreamMiddleware();\n\n pipeline(\n jsonRpcConnection.stream,\n streams.command,\n jsonRpcConnection.stream,\n (error) => {\n if (error && !error.message?.match('Premature close')) {\n logError(`Command stream failure.`, error);\n }\n },\n );\n\n const rpcEngine = JsonRpcEngine.create({\n middleware: [asV2Middleware(jsonRpcConnection.middleware)],\n });\n\n const envMetadata = {\n id: snapId,\n streams,\n rpcEngine,\n worker,\n };\n this.#jobs.set(snapId, envMetadata);\n\n return envMetadata;\n }\n\n /**\n * Sets up the streams for an initiated job.\n *\n * @param snapId - The Snap ID.\n * @param timer - The timer to use for timeouts.\n * @returns The streams to communicate with the worker and the worker itself.\n * @throws If the execution service returns an error or execution times out.\n */\n async #initStreams(\n snapId: string,\n timer: Timer,\n ): Promise<{ streams: JobStreams; worker: WorkerType }> {\n const result = await withTimeout(this.initEnvStream(snapId), timer);\n\n if (result === hasTimedOut) {\n // For certain environments, such as the iframe we may have already created the worker and wish to terminate it.\n await this.terminateJob({ id: snapId });\n\n const status = this.#status.get(snapId);\n if (status === 'created') {\n // Currently this error can only be thrown by OffscreenExecutionService.\n throw new Error(\n `The executor for \"${snapId}\" couldn't start initialization. The offscreen document may not exist.`,\n );\n }\n throw new Error(\n `The executor for \"${snapId}\" failed to initialize. The iframe/webview/worker failed to load.`,\n );\n }\n\n const { worker, stream: envStream } = result;\n const mux = setupMultiplex(envStream, `Snap: \"${snapId}\"`);\n const commandStream = mux.createStream(SNAP_STREAM_NAMES.COMMAND);\n\n // Handle out-of-band errors, i.e. errors thrown from the Snap outside of the req/res cycle.\n // Also keep track of outbound request/responses\n const notificationHandler = (\n message:\n | JsonRpcRequest\n | JsonRpcNotification<Json[] | Record<string, Json>>,\n ) => {\n if (hasProperty(message, 'id')) {\n return;\n }\n\n if (message.method === 'OutboundRequest') {\n this.#messenger.publish('ExecutionService:outboundRequest', snapId);\n } else if (message.method === 'OutboundResponse') {\n this.#messenger.publish('ExecutionService:outboundResponse', snapId);\n } else if (message.method === 'UnhandledError') {\n this.#messenger.publish(\n 'ExecutionService:unhandledError',\n snapId,\n (message.params as { error: SnapErrorJson }).error,\n );\n commandStream.removeListener('data', notificationHandler);\n } else {\n logError(\n new Error(\n `Received unexpected command stream notification \"${message.method}\".`,\n ),\n );\n }\n };\n\n commandStream.on('data', notificationHandler);\n\n const rpcStream = mux\n .createStream(SNAP_STREAM_NAMES.JSON_RPC)\n .setMaxListeners(20);\n\n rpcStream.on('data', (chunk) => {\n if (chunk?.data && hasProperty(chunk?.data, 'id')) {\n this.#messenger.publish('ExecutionService:outboundRequest', snapId);\n }\n });\n\n // An error handler is not attached to the RPC stream until `setupSnapProvider` is called.\n // We must set it up here to prevent errors from bubbling up if the stream is destroyed before then.\n rpcStream.on('error', (error) => {\n if (error && !error.message?.match('Premature close')) {\n logError(`Snap: \"${snapId}\" - RPC stream failure:`, error);\n }\n });\n\n const originalWrite = rpcStream.write.bind(rpcStream);\n\n // @ts-expect-error Hack to inspect the messages being written to the stream.\n rpcStream.write = (chunk, encoding, callback) => {\n // Ignore chain switching notifications as it doesn't matter for the SnapProvider.\n if (chunk?.data?.method === 'metamask_chainChanged') {\n return true;\n }\n\n if (chunk?.data && hasProperty(chunk?.data, 'id')) {\n this.#messenger.publish('ExecutionService:outboundResponse', snapId);\n }\n\n return originalWrite(chunk, encoding, callback);\n };\n\n return {\n streams: {\n command: commandStream,\n rpc: rpcStream,\n connection: envStream,\n mux,\n },\n worker,\n };\n }\n\n /**\n * Abstract function implemented by implementing class that spins up a new worker for a job.\n *\n * Depending on the execution environment, this may run forever if the Snap fails to start up properly, therefore any call to this function should be wrapped in a timeout.\n */\n protected abstract initEnvStream(snapId: string): Promise<{\n worker: WorkerType;\n stream: BasePostMessageStream;\n }>;\n\n /**\n * Set the execution status of the Snap.\n *\n * @param snapId - The Snap ID.\n * @param status - The current execution status.\n */\n protected setSnapStatus(snapId: string, status: ExecutionStatus) {\n this.#status.set(snapId, status);\n }\n\n async terminateAllSnaps() {\n await Promise.all(\n [...this.#jobs.keys()].map(async (snapId) => this.terminateSnap(snapId)),\n );\n }\n\n /**\n * Initializes and executes a Snap, setting up the communication channels to the Snap etc.\n *\n * @param snapData - Data needed for Snap execution.\n * @param snapData.snapId - The ID of the Snap to execute.\n * @param snapData.sourceCode - The source code of the Snap to execute.\n * @param snapData.endowments - The endowments available to the executing Snap.\n * @returns A string `OK` if execution succeeded.\n * @throws If the execution service returns an error or execution times out.\n */\n async executeSnap({\n snapId,\n sourceCode,\n endowments,\n }: SnapExecutionData): Promise<string> {\n if (this.#jobs.has(snapId)) {\n throw new Error(`\"${snapId}\" is already running.`);\n }\n\n this.setSnapStatus(snapId, 'created');\n\n const timer = new Timer(this.#initTimeout);\n\n // This may resolve even if the environment has failed to start up fully\n const job = await this.#initJob(snapId, timer);\n\n // Certain environments use ping as part of their initialization and thus can skip it here\n if (this.#usePing) {\n // Ping the worker to ensure that it started up\n const pingResult = await withTimeout(\n this.#command(job.id, {\n jsonrpc: '2.0',\n method: 'ping',\n id: nanoid(),\n }),\n this.#pingTimeout,\n );\n\n if (pingResult === hasTimedOut) {\n throw new Error(\n `The executor for \"${snapId}\" was unreachable. The executor did not respond in time.`,\n );\n }\n }\n\n const rpcStream = job.streams.rpc;\n\n this.#setupSnapProvider(snapId, rpcStream);\n\n // Use the remaining time as the timer, but ensure that the\n // Snap gets at least half the init timeout.\n const remainingTime = Math.max(timer.remaining, this.#initTimeout / 2);\n\n this.setSnapStatus(snapId, 'initialized');\n\n const request = {\n jsonrpc: '2.0',\n method: 'executeSnap',\n params: { snapId, sourceCode, endowments },\n id: nanoid(),\n };\n\n assertIsJsonRpcRequest(request);\n\n this.setSnapStatus(snapId, 'executing');\n\n const result = await withTimeout(\n this.#command(job.id, request),\n remainingTime,\n );\n\n if (result === hasTimedOut) {\n throw new Error(`${snapId} failed to start.`);\n }\n\n if (result === 'OK') {\n this.setSnapStatus(snapId, 'running');\n }\n\n return result as string;\n }\n\n async #command(\n snapId: string,\n message: JsonRpcRequest,\n ): Promise<Json | undefined> {\n const job = this.#jobs.get(snapId);\n if (!job) {\n throw new Error(`\"${snapId}\" is not currently running.`);\n }\n\n log('Parent: Sending Command', message);\n return await job.rpcEngine.handle(message);\n }\n\n /**\n * Handle RPC request.\n *\n * @param snapId - The ID of the recipient Snap.\n * @param options - Bag of options to pass to the RPC handler.\n * @returns Promise that can handle the request.\n */\n public async handleRpcRequest(\n snapId: string,\n options: SnapRpcHookArgs,\n ): Promise<unknown> {\n const { handler, request, origin } = options;\n\n return await this.#command(snapId, {\n id: nanoid(),\n jsonrpc: '2.0',\n method: 'snapRpc',\n params: {\n snapId,\n origin,\n handler,\n request: request as JsonRpcRequest,\n },\n });\n }\n}\n\n/**\n * Sets up stream multiplexing for the given stream.\n *\n * @param connectionStream - The stream to mux.\n * @param streamName - The name of the stream, for identification in errors.\n * @returns The multiplexed stream.\n */\nexport function setupMultiplex(\n connectionStream: Duplex,\n streamName: string,\n): ObjectMultiplex {\n const mux = new ObjectMultiplex();\n pipeline(connectionStream, mux, connectionStream, (error) => {\n if (error && !error.message?.match('Premature close')) {\n logError(`\"${streamName}\" stream failure.`, error);\n }\n });\n return mux;\n}\n"]}
@@ -2009,6 +2009,13 @@ class SnapController extends base_controller_1.BaseController {
2009
2009
  }
2010
2010
  await this.#assertSnapRpcResponse(snapId, handlerType, result);
2011
2011
  const transformedResult = await this.#transformSnapRpcResponse(snapId, handlerType, transformedRequest, result);
2012
+ if (constants_1.INTERFACE_HANDLERS.includes(handlerType) &&
2013
+ transformedResult !== null) {
2014
+ (0, utils_1.assert)((0, utils_1.isObject)(transformedResult) &&
2015
+ (0, utils_1.hasProperty)(transformedResult, 'id') &&
2016
+ typeof transformedResult.id === 'string', 'Interface response must have an ID.');
2017
+ this.messenger.call('SnapInterfaceController:setInterfaceDisplayed', snapId, transformedResult.id);
2018
+ }
2012
2019
  this.#recordSnapRpcRequestFinish(snapId, transformedRequest.id, handlerType, origin, true);
2013
2020
  return transformedResult;
2014
2021
  }