{"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;AAGhC,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;AAEjD,MAAM,yBAAyB,GAAG;IAChC,iBAAiB;IACjB,cAAc;IACd,mBAAmB;IACnB,iBAAiB;IACjB,iBAAiB;IACjB,sBAAsB;IACtB,kBAAkB;IAClB,uBAAuB;CACf,CAAC;AAwHX;;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,SAAS,CAAC,4BAA4B,CACzC,IAAI,EACJ,yBAAyB,CAC1B,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,wBAAwB,EAAE,EAAE,CAAC,CAAC;IAC3D,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  ApprovalControllerAcceptRequestAction,\n  ApprovalControllerHasRequestAction,\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 { PermissionControllerHasPermissionAction } 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 type { SnapInterfaceControllerMethodActions } from './SnapInterfaceController-method-action-types';\nimport {\n  constructState,\n  getJsxInterface,\n  isMatchingChainId,\n  validateInterfaceContext,\n} from './utils';\nimport type { SnapControllerGetSnapAction } from '../snaps';\n\nconst MAX_UI_CONTENT_SIZE = 10_000_000; // 10 mb\n\nconst controllerName = 'SnapInterfaceController';\n\nconst MESSENGER_EXPOSED_METHODS = [\n  'createInterface',\n  'getInterface',\n  'getInterfaceState',\n  'updateInterface',\n  'deleteInterface',\n  'updateInterfaceState',\n  'resolveInterface',\n  'setInterfaceDisplayed',\n] as const;\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\ntype AllowedActions =\n  | PhishingControllerTestOrigin\n  | ApprovalControllerHasRequestAction\n  | ApprovalControllerAcceptRequestAction\n  | SnapControllerGetSnapAction\n  | MultichainAssetsControllerGetStateAction\n  | AccountsControllerGetSelectedMultichainAccountAction\n  | AccountsControllerGetAccountByAddressAction\n  | AccountsControllerListMultichainAccountsAction\n  | PermissionControllerHasPermissionAction;\n\nexport type SnapInterfaceControllerActions =\n  | SnapInterfaceControllerGetStateAction\n  | SnapInterfaceControllerMethodActions;\n\nexport type SnapInterfaceControllerStateChangeEvent =\n  ControllerStateChangeEvent<\n    typeof controllerName,\n    SnapInterfaceControllerState\n  >;\n\ntype OtherNotification = { type: string; [key: string]: unknown };\n\ntype 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\ntype AllowedEvents = NotificationListUpdatedEvent;\n\nexport type SnapInterfaceControllerEvents =\n  SnapInterfaceControllerStateChangeEvent;\n\nexport type SnapInterfaceControllerMessenger = Messenger<\n  typeof controllerName,\n  SnapInterfaceControllerActions | AllowedActions,\n  SnapInterfaceControllerEvents | AllowedEvents\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.messenger.registerMethodActionHandlers(\n      this,\n      MESSENGER_EXPOSED_METHODS,\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:getSnap', 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"]}