@metamask/snaps-controllers 0.30.0 → 0.31.0

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 (39) hide show
  1. package/dist/cronjob/CronjobController.js +11 -8
  2. package/dist/cronjob/CronjobController.js.map +1 -1
  3. package/dist/fsm.js +5 -5
  4. package/dist/fsm.js.map +1 -1
  5. package/dist/multichain/MultiChainController.js +12 -20
  6. package/dist/multichain/MultiChainController.js.map +1 -1
  7. package/dist/multichain/matching.js +3 -4
  8. package/dist/multichain/matching.js.map +1 -1
  9. package/dist/services/iframe/IframeExecutionService.js +1 -2
  10. package/dist/services/iframe/IframeExecutionService.js.map +1 -1
  11. package/dist/snaps/RequestQueue.js +3 -6
  12. package/dist/snaps/RequestQueue.js.map +1 -1
  13. package/dist/snaps/SnapController.d.ts +7 -5
  14. package/dist/snaps/SnapController.js +245 -170
  15. package/dist/snaps/SnapController.js.map +1 -1
  16. package/dist/snaps/endowments/cronjob.js +2 -3
  17. package/dist/snaps/endowments/cronjob.js.map +1 -1
  18. package/dist/snaps/endowments/index.js +6 -1
  19. package/dist/snaps/endowments/index.js.map +1 -1
  20. package/dist/snaps/endowments/keyring.js +3 -4
  21. package/dist/snaps/endowments/keyring.js.map +1 -1
  22. package/dist/snaps/endowments/rpc.js +2 -2
  23. package/dist/snaps/endowments/rpc.js.map +1 -1
  24. package/dist/snaps/endowments/transaction-insight.js +4 -5
  25. package/dist/snaps/endowments/transaction-insight.js.map +1 -1
  26. package/dist/snaps/location/http.js +9 -3
  27. package/dist/snaps/location/http.js.map +1 -1
  28. package/dist/snaps/location/local.js +1 -1
  29. package/dist/snaps/location/local.js.map +1 -1
  30. package/dist/snaps/location/location.js +2 -3
  31. package/dist/snaps/location/location.js.map +1 -1
  32. package/dist/snaps/location/npm.js +11 -8
  33. package/dist/snaps/location/npm.js.map +1 -1
  34. package/dist/snaps/registry/json.js +5 -7
  35. package/dist/snaps/registry/json.js.map +1 -1
  36. package/package.json +31 -11
  37. package/dist/services/iframe/test/fixJSDOMPostMessageEventSource.d.ts +0 -3
  38. package/dist/services/iframe/test/fixJSDOMPostMessageEventSource.js +0 -34
  39. package/dist/services/iframe/test/fixJSDOMPostMessageEventSource.js.map +0 -1
@@ -33,7 +33,10 @@ class CronjobController extends base_controller_1.BaseControllerV2 {
33
33
  jobs: { persist: true, anonymous: false },
34
34
  },
35
35
  name: controllerName,
36
- state: Object.assign({ jobs: {} }, state),
36
+ state: {
37
+ jobs: {},
38
+ ...state,
39
+ },
37
40
  });
38
41
  _CronjobController_messenger.set(this, void 0);
39
42
  _CronjobController_dailyTimer.set(this, void 0);
@@ -65,7 +68,8 @@ class CronjobController extends base_controller_1.BaseControllerV2 {
65
68
  const snaps = this.messagingSystem.call('SnapController:getAll');
66
69
  const filteredSnaps = (0, __1.getRunnableSnaps)(snaps);
67
70
  const jobs = filteredSnaps.map((snap) => this.getSnapJobs(snap.id));
68
- return (0, snaps_utils_1.flatten)(jobs).filter((job) => job !== undefined);
71
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
72
+ return jobs.flat().filter((job) => job !== undefined);
69
73
  }
70
74
  /**
71
75
  * Retrieve all Cronjob specifications for a Snap.
@@ -75,10 +79,10 @@ class CronjobController extends base_controller_1.BaseControllerV2 {
75
79
  */
76
80
  getSnapJobs(snapId) {
77
81
  const permissions = __classPrivateFieldGet(this, _CronjobController_messenger, "f").call('PermissionController:getPermissions', snapId);
78
- const permission = permissions === null || permissions === void 0 ? void 0 : permissions[__1.SnapEndowments.Cronjob];
82
+ const permission = permissions?.[__1.SnapEndowments.Cronjob];
79
83
  const definitions = (0, cronjob_1.getCronjobCaveatJobs)(permission);
80
- return definitions === null || definitions === void 0 ? void 0 : definitions.map((definition, idx) => {
81
- return Object.assign(Object.assign({}, definition), { id: `${snapId}-${idx}`, snapId });
84
+ return definitions?.map((definition, idx) => {
85
+ return { ...definition, id: `${snapId}-${idx}`, snapId };
82
86
  });
83
87
  }
84
88
  /**
@@ -89,7 +93,7 @@ class CronjobController extends base_controller_1.BaseControllerV2 {
89
93
  */
90
94
  register(snapId) {
91
95
  const jobs = this.getSnapJobs(snapId);
92
- jobs === null || jobs === void 0 ? void 0 : jobs.forEach((job) => this.schedule(job));
96
+ jobs?.forEach((job) => this.schedule(job));
93
97
  }
94
98
  /**
95
99
  * Schedule a new job.
@@ -178,11 +182,10 @@ class CronjobController extends base_controller_1.BaseControllerV2 {
178
182
  * This is necesary for longer running jobs that execute with more than 24 hours between them.
179
183
  */
180
184
  async dailyCheckIn() {
181
- var _a;
182
185
  const jobs = this.getAllJobs();
183
186
  for (const job of jobs) {
184
187
  const parsed = (0, snaps_utils_1.parseCronExpression)(job.expression);
185
- const lastRun = (_a = this.state.jobs[job.id]) === null || _a === void 0 ? void 0 : _a.lastRun;
188
+ const lastRun = this.state.jobs[job.id]?.lastRun;
186
189
  // If a job was supposed to run while we were shut down but wasn't we run it now
187
190
  if (lastRun !== undefined &&
188
191
  parsed.hasPrev() &&
@@ -1 +1 @@
1
- {"version":3,"file":"CronjobController.js","sourceRoot":"","sources":["../../src/cronjob/CronjobController.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,+DAGmC;AAEnC,uDAQ+B;AAC/B,2CAA2D;AAE3D,0BAQY;AACZ,yDAAmE;AACnE,0CAAuC;AAiB1B,QAAA,aAAa,GAAG,IAAA,sBAAc,EAAC,EAAE,EAAE,gBAAQ,CAAC,IAAI,CAAC,CAAC;AAwB/D,MAAM,cAAc,GAAG,mBAAmB,CAAC;AAE3C;;;GAGG;AACH,MAAa,iBAAkB,SAAQ,kCAItC;IAUC,YAAY,EAAE,SAAS,EAAE,KAAK,EAAyB;QACrD,KAAK,CAAC;YACJ,SAAS;YACT,QAAQ,EAAE;gBACR,IAAI,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE;aAC1C;YACD,IAAI,EAAE,cAAc;YACpB,KAAK,kBACH,IAAI,EAAE,EAAE,IACL,KAAK,CACT;SACF,CAAC,CAAC;QApBL,+CAAuC;QAEvC,gDAAoB;QAEpB,4CAA4B;QAE5B,+BAA+B;QAC/B,6CAA8B;QAc5B,uBAAA,IAAI,6BAAW,IAAI,GAAG,EAAE,MAAA,CAAC;QACzB,uBAAA,IAAI,8BAAY,IAAI,GAAG,EAAE,MAAA,CAAC;QAC1B,uBAAA,IAAI,gCAAc,SAAS,MAAA,CAAC;QAE5B,IAAI,CAAC,yBAAyB,GAAG,IAAI,CAAC,yBAAyB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3E,IAAI,CAAC,uBAAuB,GAAG,IAAI,CAAC,uBAAuB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACvE,IAAI,CAAC,uBAAuB,GAAG,IAAI,CAAC,uBAAuB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAEvE,2BAA2B;QAC3B,sDAAsD;QACtD,IAAI,CAAC,eAAe,CAAC,SAAS,CAC5B,8BAA8B,EAC9B,IAAI,CAAC,yBAAyB,CAC/B,CAAC;QAEF,IAAI,CAAC,eAAe,CAAC,SAAS,CAC5B,4BAA4B,EAC5B,IAAI,CAAC,uBAAuB,CAC7B,CAAC;QAEF,IAAI,CAAC,eAAe,CAAC,SAAS,CAC5B,4BAA4B,EAC5B,IAAI,CAAC,uBAAuB,CAC7B,CAAC;QACF,qDAAqD;QAErD,IAAI,CAAC,YAAY,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;YAClC,IAAA,sBAAQ,EAAC,KAAK,CAAC,CAAC;QAClB,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;;OAIG;IACK,UAAU;QAChB,MAAM,KAAK,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;QACjE,MAAM,aAAa,GAAG,IAAA,oBAAgB,EAAC,KAAK,CAAC,CAAC;QAE9C,MAAM,IAAI,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;QACpE,OAAO,IAAA,qBAAO,EAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,KAAK,SAAS,CAAc,CAAC;IACvE,CAAC;IAED;;;;;OAKG;IACK,WAAW,CAAC,MAAc;QAChC,MAAM,WAAW,GAAG,uBAAA,IAAI,oCAAW,CAAC,IAAI,CACtC,qCAAqC,EACrC,MAAM,CACP,CAAC;QAEF,MAAM,UAAU,GAAG,WAAW,aAAX,WAAW,uBAAX,WAAW,CAAG,kBAAc,CAAC,OAAO,CAAC,CAAC;QACzD,MAAM,WAAW,GAAG,IAAA,8BAAoB,EAAC,UAAU,CAAC,CAAC;QAErD,OAAO,WAAW,aAAX,WAAW,uBAAX,WAAW,CAAE,GAAG,CAAC,CAAC,UAAU,EAAE,GAAG,EAAE,EAAE;YAC1C,uCAAY,UAAU,KAAE,EAAE,EAAE,GAAG,MAAM,IAAI,GAAG,EAAE,EAAE,MAAM,IAAG;QAC3D,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;;;OAKG;IACH,QAAQ,CAAC,MAAc;QACrB,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QACtC,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;IAC7C,CAAC;IAED;;;;;;;;;;OAUG;IACK,QAAQ,CAAC,GAAY;QAC3B,IAAI,uBAAA,IAAI,iCAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE;YAC5B,OAAO;SACR;QAED,MAAM,MAAM,GAAG,IAAA,iCAAmB,EAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QACnD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;QAC3B,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,EAAE,GAAG,IAAI,CAAC,OAAO,EAAE,GAAG,GAAG,CAAC,OAAO,EAAE,CAAC;QAE1C,6DAA6D;QAC7D,IAAI,EAAE,GAAG,qBAAa,EAAE;YACtB,OAAO;SACR;QAED,MAAM,KAAK,GAAG,IAAI,aAAK,CAAC,EAAE,CAAC,CAAC;QAC5B,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE;YACf,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;gBACvC,qCAAqC;gBACrC,IAAA,sBAAQ,EAAC,KAAK,CAAC,CAAC;YAClB,CAAC,CAAC,CAAC;YAEH,uBAAA,IAAI,iCAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAC5B,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QACrB,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,qBAAqB,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,iCAAiC;QACxE,uBAAA,IAAI,iCAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;QAChC,uBAAA,IAAI,kCAAS,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACxC,CAAC;IAED;;;;OAIG;IACK,KAAK,CAAC,cAAc,CAAC,GAAY;QACvC,IAAI,CAAC,qBAAqB,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;QAC/C,MAAM,uBAAA,IAAI,oCAAW,CAAC,IAAI,CAAC,8BAA8B,EAAE;YACzD,MAAM,EAAE,GAAG,CAAC,MAAM;YAClB,MAAM,EAAE,EAAE;YACV,OAAO,EAAE,yBAAW,CAAC,SAAS;YAC9B,OAAO,EAAE,GAAG,CAAC,OAAO;SACrB,CAAC,CAAC;IACL,CAAC;IAED;;;;OAIG;IACH,UAAU,CAAC,MAAc;QACvB,MAAM,IAAI,GAAG,CAAC,GAAG,uBAAA,IAAI,kCAAS,CAAC,OAAO,EAAE,CAAC,CAAC,MAAM,CAC9C,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC,EAAE,EAAE,CAAC,SAAS,KAAK,MAAM,CACzC,CAAC;QAEF,IAAI,IAAI,CAAC,MAAM,EAAE;YACf,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;gBACpB,MAAM,KAAK,GAAG,uBAAA,IAAI,iCAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBACnC,IAAI,KAAK,EAAE;oBACT,KAAK,CAAC,MAAM,EAAE,CAAC;oBACf,uBAAA,IAAI,iCAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;oBACxB,uBAAA,IAAI,kCAAS,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;iBAC1B;YACH,CAAC,CAAC,CAAC;SACJ;IACH,CAAC;IAED;;;;;OAKG;IACK,qBAAqB,CAAC,KAAa,EAAE,OAAe;QAC1D,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;YACpB,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG;gBAClB,OAAO;aACR,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,YAAY;;QAChB,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;QAE/B,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE;YACtB,MAAM,MAAM,GAAG,IAAA,iCAAmB,EAAC,GAAG,CAAC,UAAU,CAAC,CAAC;YACnD,MAAM,OAAO,GAAG,MAAA,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,0CAAE,OAAO,CAAC;YACjD,gFAAgF;YAChF,IACE,OAAO,KAAK,SAAS;gBACrB,MAAM,CAAC,OAAO,EAAE;gBAChB,MAAM,CAAC,IAAI,EAAE,CAAC,OAAO,EAAE,GAAG,OAAO,EACjC;gBACA,MAAM,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;aAChC;YAED,kEAAkE;YAClE,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;SACpB;QAED,uBAAA,IAAI,iCAAe,IAAI,aAAK,CAAC,qBAAa,CAAC,MAAA,CAAC;QAC5C,uBAAA,IAAI,qCAAY,CAAC,KAAK,CAAC,GAAG,EAAE;YAC1B,IAAI,CAAC,YAAY,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;gBAClC,qCAAqC;gBACrC,IAAA,sBAAQ,EAAC,KAAK,CAAC,CAAC;YAClB,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACH,OAAO;QACL,KAAK,CAAC,OAAO,EAAE,CAAC;QAEhB,sDAAsD;QACtD,IAAI,CAAC,eAAe,CAAC,WAAW,CAC9B,8BAA8B,EAC9B,IAAI,CAAC,yBAAyB,CAC/B,CAAC;QAEF,IAAI,CAAC,eAAe,CAAC,WAAW,CAC9B,4BAA4B,EAC5B,IAAI,CAAC,uBAAuB,CAC7B,CAAC;QAEF,IAAI,CAAC,eAAe,CAAC,WAAW,CAC9B,4BAA4B,EAC5B,IAAI,CAAC,uBAAuB,CAC7B,CAAC;QACF,qDAAqD;QAErD,uBAAA,IAAI,kCAAS,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE;YAC/B,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;QAC1B,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;;OAIG;IACK,yBAAyB,CAAC,IAAmB;QACnD,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACzB,CAAC;IAED;;;;OAIG;IACK,uBAAuB,CAAC,IAAmB;QACjD,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC3B,CAAC;IAED;;;;OAIG;IACK,uBAAuB,CAAC,IAAmB;QACjD,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACzB,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACzB,CAAC;CACF;AA1RD,8CA0RC","sourcesContent":["import {\n BaseControllerV2 as BaseController,\n RestrictedControllerMessenger,\n} from '@metamask/base-controller';\nimport { GetPermissions } from '@metamask/permission-controller';\nimport {\n HandlerType,\n SnapId,\n TruncatedSnap,\n CronjobSpecification,\n flatten,\n parseCronExpression,\n logError,\n} from '@metamask/snaps-utils';\nimport { Duration, inMilliseconds } from '@metamask/utils';\n\nimport {\n GetAllSnaps,\n getRunnableSnaps,\n HandleSnapRequest,\n SnapEndowments,\n SnapInstalled,\n SnapRemoved,\n SnapUpdated,\n} from '..';\nimport { getCronjobCaveatJobs } from '../snaps/endowments/cronjob';\nimport { Timer } from '../snaps/Timer';\n\nexport type CronjobControllerActions =\n | GetAllSnaps\n | HandleSnapRequest\n | GetPermissions;\n\nexport type CronjobControllerEvents = SnapInstalled | SnapRemoved | SnapUpdated;\n\nexport type CronjobControllerMessenger = RestrictedControllerMessenger<\n 'CronjobController',\n CronjobControllerActions,\n CronjobControllerEvents,\n CronjobControllerActions['type'],\n CronjobControllerEvents['type']\n>;\n\nexport const DAILY_TIMEOUT = inMilliseconds(24, Duration.Hour);\n\nexport type CronjobControllerArgs = {\n messenger: CronjobControllerMessenger;\n /**\n * Persisted state that will be used for rehydration.\n */\n state?: CronjobControllerState;\n};\n\nexport type Cronjob = {\n timer?: Timer;\n id: string;\n snapId: SnapId;\n} & CronjobSpecification;\n\nexport type StoredJobInformation = {\n lastRun: number;\n};\n\nexport type CronjobControllerState = {\n jobs: Record<string, StoredJobInformation>;\n};\n\nconst controllerName = 'CronjobController';\n\n/**\n * Use this controller to register and schedule periodically executed jobs\n * using RPC method hooks.\n */\nexport class CronjobController extends BaseController<\n typeof controllerName,\n CronjobControllerState,\n CronjobControllerMessenger\n> {\n #messenger: CronjobControllerMessenger;\n\n #dailyTimer!: Timer;\n\n #timers: Map<string, Timer>;\n\n // Mapping from jobId to snapId\n #snapIds: Map<string, string>;\n\n constructor({ messenger, state }: CronjobControllerArgs) {\n super({\n messenger,\n metadata: {\n jobs: { persist: true, anonymous: false },\n },\n name: controllerName,\n state: {\n jobs: {},\n ...state,\n },\n });\n this.#timers = new Map();\n this.#snapIds = new Map();\n this.#messenger = messenger;\n\n this._handleEventSnapInstalled = this._handleEventSnapInstalled.bind(this);\n this._handleEventSnapRemoved = this._handleEventSnapRemoved.bind(this);\n this._handleEventSnapUpdated = this._handleEventSnapUpdated.bind(this);\n\n // Subscribe to Snap events\n /* eslint-disable @typescript-eslint/unbound-method */\n this.messagingSystem.subscribe(\n 'SnapController:snapInstalled',\n this._handleEventSnapInstalled,\n );\n\n this.messagingSystem.subscribe(\n 'SnapController:snapRemoved',\n this._handleEventSnapRemoved,\n );\n\n this.messagingSystem.subscribe(\n 'SnapController:snapUpdated',\n this._handleEventSnapUpdated,\n );\n /* eslint-enable @typescript-eslint/unbound-method */\n\n this.dailyCheckIn().catch((error) => {\n logError(error);\n });\n }\n\n /**\n * Retrieve all cronjob specifications for all runnable snaps.\n *\n * @returns Array of Cronjob specifications.\n */\n private getAllJobs(): Cronjob[] {\n const snaps = this.messagingSystem.call('SnapController:getAll');\n const filteredSnaps = getRunnableSnaps(snaps);\n\n const jobs = filteredSnaps.map((snap) => this.getSnapJobs(snap.id));\n return flatten(jobs).filter((job) => job !== undefined) as Cronjob[];\n }\n\n /**\n * Retrieve all Cronjob specifications for a Snap.\n *\n * @param snapId - ID of a Snap.\n * @returns Array of Cronjob specifications.\n */\n private getSnapJobs(snapId: SnapId): Cronjob[] | undefined {\n const permissions = this.#messenger.call(\n 'PermissionController:getPermissions',\n snapId,\n );\n\n const permission = permissions?.[SnapEndowments.Cronjob];\n const definitions = getCronjobCaveatJobs(permission);\n\n return definitions?.map((definition, idx) => {\n return { ...definition, id: `${snapId}-${idx}`, snapId };\n });\n }\n\n /**\n * Register cron jobs for a given snap by getting specification from a permission caveats.\n * Once registered, each job will be scheduled.\n *\n * @param snapId - ID of a snap.\n */\n register(snapId: SnapId) {\n const jobs = this.getSnapJobs(snapId);\n jobs?.forEach((job) => this.schedule(job));\n }\n\n /**\n * Schedule a new job.\n * This will interpret the cron expression and tell the timer to execute the job\n * at the next suitable point in time.\n * Job last run state will be initialized afterwards.\n *\n * Note: Schedule will be skipped if the job's execution time is too far in the future and\n * will be revisited on a daily check.\n *\n * @param job - Cronjob specification.\n */\n private schedule(job: Cronjob) {\n if (this.#timers.has(job.id)) {\n return;\n }\n\n const parsed = parseCronExpression(job.expression);\n const next = parsed.next();\n const now = new Date();\n const ms = next.getTime() - now.getTime();\n\n // Don't schedule this job yet as it is too far in the future\n if (ms > DAILY_TIMEOUT) {\n return;\n }\n\n const timer = new Timer(ms);\n timer.start(() => {\n this.executeCronjob(job).catch((error) => {\n // TODO: Decide how to handle errors.\n logError(error);\n });\n\n this.#timers.delete(job.id);\n this.schedule(job);\n });\n\n this.updateJobLastRunState(job.id, 0); // 0 for init, never ran actually\n this.#timers.set(job.id, timer);\n this.#snapIds.set(job.id, job.snapId);\n }\n\n /**\n * Execute job.\n *\n * @param job - Cronjob specification.\n */\n private async executeCronjob(job: Cronjob) {\n this.updateJobLastRunState(job.id, Date.now());\n await this.#messenger.call('SnapController:handleRequest', {\n snapId: job.snapId,\n origin: '',\n handler: HandlerType.OnCronjob,\n request: job.request,\n });\n }\n\n /**\n * Unregister all jobs related to the given snapId.\n *\n * @param snapId - ID of a snap.\n */\n unregister(snapId: SnapId) {\n const jobs = [...this.#snapIds.entries()].filter(\n ([_, jobSnapId]) => jobSnapId === snapId,\n );\n\n if (jobs.length) {\n jobs.forEach(([id]) => {\n const timer = this.#timers.get(id);\n if (timer) {\n timer.cancel();\n this.#timers.delete(id);\n this.#snapIds.delete(id);\n }\n });\n }\n }\n\n /**\n * Update time of a last run for the Cronjob specified by ID.\n *\n * @param jobId - ID of a cron job.\n * @param lastRun - Unix timestamp when the job was last ran.\n */\n private updateJobLastRunState(jobId: string, lastRun: number) {\n this.update((state) => {\n state.jobs[jobId] = {\n lastRun,\n };\n });\n }\n\n /**\n * Runs every 24 hours to check if new jobs need to be scheduled.\n *\n * This is necesary for longer running jobs that execute with more than 24 hours between them.\n */\n async dailyCheckIn() {\n const jobs = this.getAllJobs();\n\n for (const job of jobs) {\n const parsed = parseCronExpression(job.expression);\n const lastRun = this.state.jobs[job.id]?.lastRun;\n // If a job was supposed to run while we were shut down but wasn't we run it now\n if (\n lastRun !== undefined &&\n parsed.hasPrev() &&\n parsed.prev().getTime() > lastRun\n ) {\n await this.executeCronjob(job);\n }\n\n // Try scheduling, will fail if an existing scheduled job is found\n this.schedule(job);\n }\n\n this.#dailyTimer = new Timer(DAILY_TIMEOUT);\n this.#dailyTimer.start(() => {\n this.dailyCheckIn().catch((error) => {\n // TODO: Decide how to handle errors.\n logError(error);\n });\n });\n }\n\n /**\n * Run controller teardown process and unsubscribe from Snap events.\n */\n destroy() {\n super.destroy();\n\n /* eslint-disable @typescript-eslint/unbound-method */\n this.messagingSystem.unsubscribe(\n 'SnapController:snapInstalled',\n this._handleEventSnapInstalled,\n );\n\n this.messagingSystem.unsubscribe(\n 'SnapController:snapRemoved',\n this._handleEventSnapRemoved,\n );\n\n this.messagingSystem.unsubscribe(\n 'SnapController:snapUpdated',\n this._handleEventSnapUpdated,\n );\n /* eslint-enable @typescript-eslint/unbound-method */\n\n this.#snapIds.forEach((snapId) => {\n this.unregister(snapId);\n });\n }\n\n /**\n * Handle cron jobs on 'snapInstalled' event.\n *\n * @param snap - Basic Snap information.\n */\n private _handleEventSnapInstalled(snap: TruncatedSnap) {\n this.register(snap.id);\n }\n\n /**\n * Handle cron jobs on 'snapRemoved' event.\n *\n * @param snap - Basic Snap information.\n */\n private _handleEventSnapRemoved(snap: TruncatedSnap) {\n this.unregister(snap.id);\n }\n\n /**\n * Handle cron jobs on 'snapUpdated' event.\n *\n * @param snap - Basic Snap information.\n */\n private _handleEventSnapUpdated(snap: TruncatedSnap) {\n this.unregister(snap.id);\n this.register(snap.id);\n }\n}\n"]}
1
+ {"version":3,"file":"CronjobController.js","sourceRoot":"","sources":["../../src/cronjob/CronjobController.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n BaseControllerV2 as BaseController,\n RestrictedControllerMessenger,\n} from '@metamask/base-controller';\nimport { GetPermissions } from '@metamask/permission-controller';\nimport {\n HandlerType,\n SnapId,\n TruncatedSnap,\n CronjobSpecification,\n parseCronExpression,\n logError,\n} from '@metamask/snaps-utils';\nimport { Duration, inMilliseconds } from '@metamask/utils';\n\nimport {\n GetAllSnaps,\n getRunnableSnaps,\n HandleSnapRequest,\n SnapEndowments,\n SnapInstalled,\n SnapRemoved,\n SnapUpdated,\n} from '..';\nimport { getCronjobCaveatJobs } from '../snaps/endowments/cronjob';\nimport { Timer } from '../snaps/Timer';\n\nexport type CronjobControllerActions =\n | GetAllSnaps\n | HandleSnapRequest\n | GetPermissions;\n\nexport type CronjobControllerEvents = SnapInstalled | SnapRemoved | SnapUpdated;\n\nexport type CronjobControllerMessenger = RestrictedControllerMessenger<\n 'CronjobController',\n CronjobControllerActions,\n CronjobControllerEvents,\n CronjobControllerActions['type'],\n CronjobControllerEvents['type']\n>;\n\nexport const DAILY_TIMEOUT = inMilliseconds(24, Duration.Hour);\n\nexport type CronjobControllerArgs = {\n messenger: CronjobControllerMessenger;\n /**\n * Persisted state that will be used for rehydration.\n */\n state?: CronjobControllerState;\n};\n\nexport type Cronjob = {\n timer?: Timer;\n id: string;\n snapId: SnapId;\n} & CronjobSpecification;\n\nexport type StoredJobInformation = {\n lastRun: number;\n};\n\nexport type CronjobControllerState = {\n jobs: Record<string, StoredJobInformation>;\n};\n\nconst controllerName = 'CronjobController';\n\n/**\n * Use this controller to register and schedule periodically executed jobs\n * using RPC method hooks.\n */\nexport class CronjobController extends BaseController<\n typeof controllerName,\n CronjobControllerState,\n CronjobControllerMessenger\n> {\n #messenger: CronjobControllerMessenger;\n\n #dailyTimer!: Timer;\n\n #timers: Map<string, Timer>;\n\n // Mapping from jobId to snapId\n #snapIds: Map<string, string>;\n\n constructor({ messenger, state }: CronjobControllerArgs) {\n super({\n messenger,\n metadata: {\n jobs: { persist: true, anonymous: false },\n },\n name: controllerName,\n state: {\n jobs: {},\n ...state,\n },\n });\n this.#timers = new Map();\n this.#snapIds = new Map();\n this.#messenger = messenger;\n\n this._handleEventSnapInstalled = this._handleEventSnapInstalled.bind(this);\n this._handleEventSnapRemoved = this._handleEventSnapRemoved.bind(this);\n this._handleEventSnapUpdated = this._handleEventSnapUpdated.bind(this);\n\n // Subscribe to Snap events\n /* eslint-disable @typescript-eslint/unbound-method */\n this.messagingSystem.subscribe(\n 'SnapController:snapInstalled',\n this._handleEventSnapInstalled,\n );\n\n this.messagingSystem.subscribe(\n 'SnapController:snapRemoved',\n this._handleEventSnapRemoved,\n );\n\n this.messagingSystem.subscribe(\n 'SnapController:snapUpdated',\n this._handleEventSnapUpdated,\n );\n /* eslint-enable @typescript-eslint/unbound-method */\n\n this.dailyCheckIn().catch((error) => {\n logError(error);\n });\n }\n\n /**\n * Retrieve all cronjob specifications for all runnable snaps.\n *\n * @returns Array of Cronjob specifications.\n */\n private getAllJobs(): Cronjob[] {\n const snaps = this.messagingSystem.call('SnapController:getAll');\n const filteredSnaps = getRunnableSnaps(snaps);\n\n const jobs = filteredSnaps.map((snap) => this.getSnapJobs(snap.id));\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion\n return jobs.flat().filter((job) => job !== undefined) as Cronjob[];\n }\n\n /**\n * Retrieve all Cronjob specifications for a Snap.\n *\n * @param snapId - ID of a Snap.\n * @returns Array of Cronjob specifications.\n */\n private getSnapJobs(snapId: SnapId): Cronjob[] | undefined {\n const permissions = this.#messenger.call(\n 'PermissionController:getPermissions',\n snapId,\n );\n\n const permission = permissions?.[SnapEndowments.Cronjob];\n const definitions = getCronjobCaveatJobs(permission);\n\n return definitions?.map((definition, idx) => {\n return { ...definition, id: `${snapId}-${idx}`, snapId };\n });\n }\n\n /**\n * Register cron jobs for a given snap by getting specification from a permission caveats.\n * Once registered, each job will be scheduled.\n *\n * @param snapId - ID of a snap.\n */\n register(snapId: SnapId) {\n const jobs = this.getSnapJobs(snapId);\n jobs?.forEach((job) => this.schedule(job));\n }\n\n /**\n * Schedule a new job.\n * This will interpret the cron expression and tell the timer to execute the job\n * at the next suitable point in time.\n * Job last run state will be initialized afterwards.\n *\n * Note: Schedule will be skipped if the job's execution time is too far in the future and\n * will be revisited on a daily check.\n *\n * @param job - Cronjob specification.\n */\n private schedule(job: Cronjob) {\n if (this.#timers.has(job.id)) {\n return;\n }\n\n const parsed = parseCronExpression(job.expression);\n const next = parsed.next();\n const now = new Date();\n const ms = next.getTime() - now.getTime();\n\n // Don't schedule this job yet as it is too far in the future\n if (ms > DAILY_TIMEOUT) {\n return;\n }\n\n const timer = new Timer(ms);\n timer.start(() => {\n this.executeCronjob(job).catch((error) => {\n // TODO: Decide how to handle errors.\n logError(error);\n });\n\n this.#timers.delete(job.id);\n this.schedule(job);\n });\n\n this.updateJobLastRunState(job.id, 0); // 0 for init, never ran actually\n this.#timers.set(job.id, timer);\n this.#snapIds.set(job.id, job.snapId);\n }\n\n /**\n * Execute job.\n *\n * @param job - Cronjob specification.\n */\n private async executeCronjob(job: Cronjob) {\n this.updateJobLastRunState(job.id, Date.now());\n await this.#messenger.call('SnapController:handleRequest', {\n snapId: job.snapId,\n origin: '',\n handler: HandlerType.OnCronjob,\n request: job.request,\n });\n }\n\n /**\n * Unregister all jobs related to the given snapId.\n *\n * @param snapId - ID of a snap.\n */\n unregister(snapId: SnapId) {\n const jobs = [...this.#snapIds.entries()].filter(\n ([_, jobSnapId]) => jobSnapId === snapId,\n );\n\n if (jobs.length) {\n jobs.forEach(([id]) => {\n const timer = this.#timers.get(id);\n if (timer) {\n timer.cancel();\n this.#timers.delete(id);\n this.#snapIds.delete(id);\n }\n });\n }\n }\n\n /**\n * Update time of a last run for the Cronjob specified by ID.\n *\n * @param jobId - ID of a cron job.\n * @param lastRun - Unix timestamp when the job was last ran.\n */\n private updateJobLastRunState(jobId: string, lastRun: number) {\n this.update((state) => {\n state.jobs[jobId] = {\n lastRun,\n };\n });\n }\n\n /**\n * Runs every 24 hours to check if new jobs need to be scheduled.\n *\n * This is necesary for longer running jobs that execute with more than 24 hours between them.\n */\n async dailyCheckIn() {\n const jobs = this.getAllJobs();\n\n for (const job of jobs) {\n const parsed = parseCronExpression(job.expression);\n const lastRun = this.state.jobs[job.id]?.lastRun;\n // If a job was supposed to run while we were shut down but wasn't we run it now\n if (\n lastRun !== undefined &&\n parsed.hasPrev() &&\n parsed.prev().getTime() > lastRun\n ) {\n await this.executeCronjob(job);\n }\n\n // Try scheduling, will fail if an existing scheduled job is found\n this.schedule(job);\n }\n\n this.#dailyTimer = new Timer(DAILY_TIMEOUT);\n this.#dailyTimer.start(() => {\n this.dailyCheckIn().catch((error) => {\n // TODO: Decide how to handle errors.\n logError(error);\n });\n });\n }\n\n /**\n * Run controller teardown process and unsubscribe from Snap events.\n */\n destroy() {\n super.destroy();\n\n /* eslint-disable @typescript-eslint/unbound-method */\n this.messagingSystem.unsubscribe(\n 'SnapController:snapInstalled',\n this._handleEventSnapInstalled,\n );\n\n this.messagingSystem.unsubscribe(\n 'SnapController:snapRemoved',\n this._handleEventSnapRemoved,\n );\n\n this.messagingSystem.unsubscribe(\n 'SnapController:snapUpdated',\n this._handleEventSnapUpdated,\n );\n /* eslint-enable @typescript-eslint/unbound-method */\n\n this.#snapIds.forEach((snapId) => {\n this.unregister(snapId);\n });\n }\n\n /**\n * Handle cron jobs on 'snapInstalled' event.\n *\n * @param snap - Basic Snap information.\n */\n private _handleEventSnapInstalled(snap: TruncatedSnap) {\n this.register(snap.id);\n }\n\n /**\n * Handle cron jobs on 'snapRemoved' event.\n *\n * @param snap - Basic Snap information.\n */\n private _handleEventSnapRemoved(snap: TruncatedSnap) {\n this.unregister(snap.id);\n }\n\n /**\n * Handle cron jobs on 'snapUpdated' event.\n *\n * @param snap - Basic Snap information.\n */\n private _handleEventSnapUpdated(snap: TruncatedSnap) {\n this.unregister(snap.id);\n this.register(snap.id);\n }\n}\n"]}
package/dist/fsm.js CHANGED
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.forceStrict = exports.validateMachine = void 0;
4
- const snaps_utils_1 = require("@metamask/snaps-utils");
5
4
  const utils_1 = require("@metamask/utils");
6
5
  const fsm_1 = require("@xstate/fsm");
7
6
  /**
@@ -13,7 +12,6 @@ const fsm_1 = require("@xstate/fsm");
13
12
  * @throws {@link AssertionError}. If the validation fails.
14
13
  */
15
14
  function validateMachine(machine) {
16
- var _a;
17
15
  (0, utils_1.assert)('_options' in machine, 'The machine is not an @xstate/fsm machine');
18
16
  const typed = machine;
19
17
  // 1.
@@ -27,17 +25,19 @@ function validateMachine(machine) {
27
25
  return [obj];
28
26
  };
29
27
  const allActions = new Set();
30
- const addActions = (actions) => (0, snaps_utils_1.flatMap)(toArray(actions), (action) => {
28
+ const addActions = (actions) => toArray(actions)
29
+ .flatMap((action) => {
31
30
  if (typeof action === 'string') {
32
31
  return [action];
33
32
  }
34
33
  (0, utils_1.assert)(typeof action === 'function');
35
34
  return [];
36
- }).forEach(allActions.add.bind(allActions));
35
+ })
36
+ .forEach(allActions.add.bind(allActions));
37
37
  for (const state of Object.values(typed.config.states)) {
38
38
  addActions(state.entry);
39
39
  addActions(state.exit);
40
- for (const transition of Object.values((_a = state.on) !== null && _a !== void 0 ? _a : {})) {
40
+ for (const transition of Object.values(state.on ?? {})) {
41
41
  addActions(transition.actions);
42
42
  }
43
43
  }
package/dist/fsm.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"fsm.js","sourceRoot":"","sources":["../src/fsm.ts"],"names":[],"mappings":";;;AAAA,uDAAgD;AAChD,2CAAyC;AACzC,qCAKqB;AAErB;;;;;;;GAOG;AACH,SAAgB,eAAe,CAI7B,OAAuD;;IACvD,IAAA,cAAM,EAAC,UAAU,IAAI,OAAO,EAAE,2CAA2C,CAAC,CAAC;IAC3E,MAAM,KAAK,GAAG,OAEb,CAAC;IAEF,KAAK;IACL,MAAM,OAAO,GAAG,CAAI,GAAY,EAAO,EAAE;QACvC,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE;YACtB,OAAO,GAAG,CAAC;SACZ;aAAM,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,IAAI,EAAE;YAC5C,OAAO,EAAE,CAAC;SACX;QACD,OAAO,CAAC,GAAG,CAAC,CAAC;IACf,CAAC,CAAC;IACF,MAAM,UAAU,GAAG,IAAI,GAAG,EAAU,CAAC;IACrC,MAAM,UAAU,GAAG,CAAC,OAAY,EAAE,EAAE,CAClC,IAAA,qBAAO,EAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE;QACnC,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE;YAC9B,OAAO,CAAC,MAAM,CAAC,CAAC;SACjB;QACD,IAAA,cAAM,EAAC,OAAO,MAAM,KAAK,UAAU,CAAC,CAAC;QACrC,OAAO,EAAE,CAAC;IACZ,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;IAE9C,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,CAC/B,KAAK,CAAC,MAAM,CAAC,MAAM,CACpB,EAAE;QACD,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACxB,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACvB,KAAK,MAAM,UAAU,IAAI,MAAM,CAAC,MAAM,CAAM,MAAA,KAAK,CAAC,EAAE,mCAAI,EAAE,CAAC,EAAE;YAC3D,UAAU,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;SAChC;KACF;IAED,UAAU,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE,CAC5B,IAAA,cAAM,EACJ,KAAK,CAAC,QAAQ,CAAC,OAAO,KAAK,SAAS,IAAI,MAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,OAAO,EACxE,WAAW,MAAM,kCAAkC,CACpD,CACF,CAAC;AACJ,CAAC;AA7CD,0CA6CC;AAED;;;;;;;;;;;;GAYG;AACH,SAAgB,WAAW,CAAC,WAAgD;IAC1E,wEAAwE;IACxE,4DAA4D;IAC5D,IAAI,eAAe,GAAG,KAAK,CAAC;IAC5B,WAAW,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,EAAE;QAC9B,IAAA,cAAM,EAAC,CAAC,eAAe,IAAI,KAAK,CAAC,OAAO,EAAE,0BAA0B,CAAC,CAAC;QACtE,eAAe,GAAG,IAAI,CAAC;IACzB,CAAC,CAAC,CAAC;IAEH,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAClD,WAAW,CAAC,IAAI,GAAG,CAAC,GAAG,IAAI,EAAE,EAAE;QAC7B,IAAA,cAAM,EACJ,WAAW,CAAC,MAAM,KAAK,uBAAiB,CAAC,OAAO,EAChD,wBAAwB,CACzB,CAAC;QACF,OAAO,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC;IACzB,CAAC,CAAC;AACJ,CAAC;AAjBD,kCAiBC","sourcesContent":["import { flatMap } from '@metamask/snaps-utils';\nimport { assert } from '@metamask/utils';\nimport {\n EventObject,\n InterpreterStatus,\n StateMachine,\n Typestate,\n} from '@xstate/fsm';\n\n/**\n * Validates the set-up of a @xstate/fsm machine.\n *\n * 1. Ensures that all named actions in the config have a provided implementation.\n *\n * @param machine - The machine to validate.\n * @throws {@link AssertionError}. If the validation fails.\n */\nexport function validateMachine<\n TContext extends object,\n TEvent extends EventObject,\n TState extends Typestate<TContext>,\n>(machine: StateMachine.Machine<TContext, TEvent, TState>) {\n assert('_options' in machine, 'The machine is not an @xstate/fsm machine');\n const typed = machine as StateMachine.Machine<TContext, TEvent, TState> & {\n _options: { actions?: StateMachine.ActionMap<TContext, TEvent> };\n };\n\n // 1.\n const toArray = <T>(obj: T | T[]): T[] => {\n if (Array.isArray(obj)) {\n return obj;\n } else if (obj === undefined || obj === null) {\n return [];\n }\n return [obj];\n };\n const allActions = new Set<string>();\n const addActions = (actions: any) =>\n flatMap(toArray(actions), (action) => {\n if (typeof action === 'string') {\n return [action];\n }\n assert(typeof action === 'function');\n return [];\n }).forEach(allActions.add.bind(allActions));\n\n for (const state of Object.values<typeof typed.config.states[string]>(\n typed.config.states,\n )) {\n addActions(state.entry);\n addActions(state.exit);\n for (const transition of Object.values<any>(state.on ?? {})) {\n addActions(transition.actions);\n }\n }\n\n allActions.forEach((action) =>\n assert(\n typed._options.actions !== undefined && action in typed._options.actions,\n `Action \"${action}\" doesn't have an implementation`,\n ),\n );\n}\n\n/**\n * Ensure that the interpreter is strict.\n * Strict means that the transition must occur.\n * The event must exist in .on {} state config and it's guard must succeed.\n *\n * The error will be thrown when an invalid `interpreter.send()` is called\n * and will be bubbled there.\n *\n * TODO(ritave): Doesn't support self transitions.\n *\n * @param interpreter - The interpreter that will be force into strict mode.\n * @throws {@link Error} Thrown when the transition is invalid.\n */\nexport function forceStrict(interpreter: StateMachine.Service<any, any, any>) {\n // As soon as a listener subscribes, it is called. It might be called in\n // an initial state which doesn't have the .changed property\n let onInitialCalled = false;\n interpreter.subscribe((state) => {\n assert(!onInitialCalled || state.changed, 'Invalid state transition');\n onInitialCalled = true;\n });\n\n const ogSend = interpreter.send.bind(interpreter);\n interpreter.send = (...args) => {\n assert(\n interpreter.status === InterpreterStatus.Running,\n 'Interpreter is stopped',\n );\n return ogSend(...args);\n };\n}\n"]}
1
+ {"version":3,"file":"fsm.js","sourceRoot":"","sources":["../src/fsm.ts"],"names":[],"mappings":";;;AAAA,2CAAyC;AACzC,qCAKqB;AAErB;;;;;;;GAOG;AACH,SAAgB,eAAe,CAI7B,OAAuD;IACvD,IAAA,cAAM,EAAC,UAAU,IAAI,OAAO,EAAE,2CAA2C,CAAC,CAAC;IAC3E,MAAM,KAAK,GAAG,OAEb,CAAC;IAEF,KAAK;IACL,MAAM,OAAO,GAAG,CAAI,GAAY,EAAO,EAAE;QACvC,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE;YACtB,OAAO,GAAG,CAAC;SACZ;aAAM,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,IAAI,EAAE;YAC5C,OAAO,EAAE,CAAC;SACX;QACD,OAAO,CAAC,GAAG,CAAC,CAAC;IACf,CAAC,CAAC;IACF,MAAM,UAAU,GAAG,IAAI,GAAG,EAAU,CAAC;IACrC,MAAM,UAAU,GAAG,CAAC,OAAY,EAAE,EAAE,CAClC,OAAO,CAAC,OAAO,CAAC;SACb,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE;QAClB,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE;YAC9B,OAAO,CAAC,MAAM,CAAC,CAAC;SACjB;QACD,IAAA,cAAM,EAAC,OAAO,MAAM,KAAK,UAAU,CAAC,CAAC;QACrC,OAAO,EAAE,CAAC;IACZ,CAAC,CAAC;SACD,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;IAE9C,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,CAC/B,KAAK,CAAC,MAAM,CAAC,MAAM,CACpB,EAAE;QACD,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACxB,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACvB,KAAK,MAAM,UAAU,IAAI,MAAM,CAAC,MAAM,CAAM,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE;YAC3D,UAAU,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;SAChC;KACF;IAED,UAAU,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE,CAC5B,IAAA,cAAM,EACJ,KAAK,CAAC,QAAQ,CAAC,OAAO,KAAK,SAAS,IAAI,MAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,OAAO,EACxE,WAAW,MAAM,kCAAkC,CACpD,CACF,CAAC;AACJ,CAAC;AA/CD,0CA+CC;AAED;;;;;;;;;;;;GAYG;AACH,SAAgB,WAAW,CAAC,WAAgD;IAC1E,wEAAwE;IACxE,4DAA4D;IAC5D,IAAI,eAAe,GAAG,KAAK,CAAC;IAC5B,WAAW,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,EAAE;QAC9B,IAAA,cAAM,EAAC,CAAC,eAAe,IAAI,KAAK,CAAC,OAAO,EAAE,0BAA0B,CAAC,CAAC;QACtE,eAAe,GAAG,IAAI,CAAC;IACzB,CAAC,CAAC,CAAC;IAEH,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAClD,WAAW,CAAC,IAAI,GAAG,CAAC,GAAG,IAAI,EAAE,EAAE;QAC7B,IAAA,cAAM,EACJ,WAAW,CAAC,MAAM,KAAK,uBAAiB,CAAC,OAAO,EAChD,wBAAwB,CACzB,CAAC;QACF,OAAO,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC;IACzB,CAAC,CAAC;AACJ,CAAC;AAjBD,kCAiBC","sourcesContent":["import { assert } from '@metamask/utils';\nimport {\n EventObject,\n InterpreterStatus,\n StateMachine,\n Typestate,\n} from '@xstate/fsm';\n\n/**\n * Validates the set-up of a @xstate/fsm machine.\n *\n * 1. Ensures that all named actions in the config have a provided implementation.\n *\n * @param machine - The machine to validate.\n * @throws {@link AssertionError}. If the validation fails.\n */\nexport function validateMachine<\n TContext extends object,\n TEvent extends EventObject,\n TState extends Typestate<TContext>,\n>(machine: StateMachine.Machine<TContext, TEvent, TState>) {\n assert('_options' in machine, 'The machine is not an @xstate/fsm machine');\n const typed = machine as StateMachine.Machine<TContext, TEvent, TState> & {\n _options: { actions?: StateMachine.ActionMap<TContext, TEvent> };\n };\n\n // 1.\n const toArray = <T>(obj: T | T[]): T[] => {\n if (Array.isArray(obj)) {\n return obj;\n } else if (obj === undefined || obj === null) {\n return [];\n }\n return [obj];\n };\n const allActions = new Set<string>();\n const addActions = (actions: any) =>\n toArray(actions)\n .flatMap((action) => {\n if (typeof action === 'string') {\n return [action];\n }\n assert(typeof action === 'function');\n return [];\n })\n .forEach(allActions.add.bind(allActions));\n\n for (const state of Object.values<typeof typed.config.states[string]>(\n typed.config.states,\n )) {\n addActions(state.entry);\n addActions(state.exit);\n for (const transition of Object.values<any>(state.on ?? {})) {\n addActions(transition.actions);\n }\n }\n\n allActions.forEach((action) =>\n assert(\n typed._options.actions !== undefined && action in typed._options.actions,\n `Action \"${action}\" doesn't have an implementation`,\n ),\n );\n}\n\n/**\n * Ensure that the interpreter is strict.\n * Strict means that the transition must occur.\n * The event must exist in .on {} state config and it's guard must succeed.\n *\n * The error will be thrown when an invalid `interpreter.send()` is called\n * and will be bubbled there.\n *\n * TODO(ritave): Doesn't support self transitions.\n *\n * @param interpreter - The interpreter that will be force into strict mode.\n * @throws {@link Error} Thrown when the transition is invalid.\n */\nexport function forceStrict(interpreter: StateMachine.Service<any, any, any>) {\n // As soon as a listener subscribes, it is called. It might be called in\n // an initial state which doesn't have the .changed property\n let onInitialCalled = false;\n interpreter.subscribe((state) => {\n assert(!onInitialCalled || state.changed, 'Invalid state transition');\n onInitialCalled = true;\n });\n\n const ogSend = interpreter.send.bind(interpreter);\n interpreter.send = (...args) => {\n assert(\n interpreter.status === InterpreterStatus.Running,\n 'Interpreter is stopped',\n );\n return ogSend(...args);\n };\n}\n"]}
@@ -90,7 +90,7 @@ class MultiChainController extends base_controller_1.BaseControllerV2 {
90
90
  const snaps = this.messagingSystem.call('SnapController:getAll');
91
91
  const filteredSnaps = (0, snaps_1.getRunnableSnaps)(snaps);
92
92
  // Get available namespaces supported by currently installed Snaps.
93
- const availableNamespaces = (0, snaps_utils_1.fromEntries)(await Promise.all(filteredSnaps.map(async (snap) => [
93
+ const availableNamespaces = Object.fromEntries(await Promise.all(filteredSnaps.map(async (snap) => [
94
94
  snap.id,
95
95
  await this.snapToNamespaces(snap),
96
96
  ])));
@@ -107,11 +107,8 @@ class MultiChainController extends base_controller_1.BaseControllerV2 {
107
107
  return acc;
108
108
  }, {});
109
109
  // If we either don't have a snap to handle a namespace or we have multiple we have conflicts
110
- const hasConflicts = Object.keys(namespaceToSnaps).some((namespace) => {
111
- var _a;
112
- return !(0, utils_1.hasProperty)(approvedNamespacesAndSnaps, namespace) ||
113
- ((_a = approvedNamespacesAndSnaps[namespace]) === null || _a === void 0 ? void 0 : _a.length) > 1;
114
- });
110
+ const hasConflicts = Object.keys(namespaceToSnaps).some((namespace) => !(0, utils_1.hasProperty)(approvedNamespacesAndSnaps, namespace) ||
111
+ approvedNamespacesAndSnaps[namespace]?.length > 1);
115
112
  // Use already approved snaps if they satisfy the requested namespaces.
116
113
  const filteredNamespacesAndSnaps = hasConflicts
117
114
  ? namespaceToSnaps
@@ -126,17 +123,13 @@ class MultiChainController extends base_controller_1.BaseControllerV2 {
126
123
  // need to show a prompt. This is handled by `resolveConflicts`.
127
124
  const resolvedAccounts = hasConflicts
128
125
  ? await this.resolveConflicts(origin, possibleAccounts)
129
- : (0, snaps_utils_1.fromEntries)(Object.entries(possibleAccounts).map(([namespace, snapAndAccounts]) => {
130
- var _a;
131
- return [
132
- namespace,
133
- (_a = snapAndAccounts[0]) !== null && _a !== void 0 ? _a : null,
134
- ];
135
- }));
126
+ : Object.fromEntries(Object.entries(possibleAccounts).map(([namespace, snapAndAccounts]) => [
127
+ namespace,
128
+ snapAndAccounts[0] ?? null,
129
+ ]));
136
130
  // Aggregate information about session namespaces.
137
131
  const providedNamespaces = Object.entries(connection.requiredNamespaces).reduce((acc, [namespaceId, namespace]) => {
138
- var _a;
139
- const accounts = (_a = resolvedAccounts[namespaceId]) === null || _a === void 0 ? void 0 : _a.accounts;
132
+ const accounts = resolvedAccounts[namespaceId]?.accounts;
140
133
  if (accounts) {
141
134
  acc[namespaceId] = {
142
135
  accounts,
@@ -180,14 +173,13 @@ class MultiChainController extends base_controller_1.BaseControllerV2 {
180
173
  * requested namespace.
181
174
  */
182
175
  async onRequest(origin, data) {
183
- var _a, _b;
184
176
  const session = this.getSession(origin);
185
177
  (0, utils_1.assert)(session, `Session for "${origin}" doesn't exist.`);
186
178
  const { namespace } = (0, snaps_utils_1.parseChainId)(data.chainId);
187
179
  const sessionNamespace = session.providedNamespaces[namespace];
188
- (0, utils_1.assert)((_a = session.providedNamespaces[namespace]) === null || _a === void 0 ? void 0 : _a.chains.includes(data.chainId), `Session for "${origin}" is not connected to "${data.chainId}" chain.`);
180
+ (0, utils_1.assert)(session.providedNamespaces[namespace]?.chains.includes(data.chainId), `Session for "${origin}" is not connected to "${data.chainId}" chain.`);
189
181
  const { method } = data.request;
190
- (0, utils_1.assert)((_b = sessionNamespace === null || sessionNamespace === void 0 ? void 0 : sessionNamespace.methods) === null || _b === void 0 ? void 0 : _b.includes(method), `Session for "${origin}" does not support ${method}`);
182
+ (0, utils_1.assert)(sessionNamespace?.methods?.includes(method), `Session for "${origin}" does not support ${method}`);
191
183
  const snapId = session.handlingSnaps[namespace];
192
184
  (0, utils_1.assert)(snapId !== undefined);
193
185
  // TODO: Get permission for origin connecting to snap, or get user approval.
@@ -259,7 +251,7 @@ class MultiChainController extends base_controller_1.BaseControllerV2 {
259
251
  */
260
252
  snapToNamespaces(snap) {
261
253
  const permissions = this.messagingSystem.call('PermissionController:getPermissions', snap.id);
262
- const keyringPermission = permissions === null || permissions === void 0 ? void 0 : permissions[snaps_1.SnapEndowments.Keyring];
254
+ const keyringPermission = permissions?.[snaps_1.SnapEndowments.Keyring];
263
255
  return (0, keyring_1.getKeyringCaveatNamespaces)(keyringPermission);
264
256
  }
265
257
  /**
@@ -277,7 +269,7 @@ class MultiChainController extends base_controller_1.BaseControllerV2 {
277
269
  */
278
270
  async namespacesToAccounts(origin, namespacesAndSnaps, requestedNamespaces) {
279
271
  const dedupedSnaps = [
280
- ...new Set((0, snaps_utils_1.flatten)(Object.values(namespacesAndSnaps))),
272
+ ...new Set(Object.values(namespacesAndSnaps).flat()),
281
273
  ];
282
274
  const allAccounts = await dedupedSnaps.reduce(async (previousPromise, snapId) => {
283
275
  const result = await this.getSnapAccounts(origin, snapId);
@@ -1 +1 @@
1
- {"version":3,"file":"MultiChainController.js","sourceRoot":"","sources":["../../src/multichain/MultiChainController.ts"],"names":[],"mappings":"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","sourcesContent":["import { AddApprovalRequest } from '@metamask/approval-controller';\nimport {\n BaseControllerV2 as BaseController,\n RestrictedControllerMessenger,\n} from '@metamask/base-controller';\nimport {\n GetPermissions,\n GrantPermissions,\n HasPermission,\n PermissionConstraint,\n} from '@metamask/permission-controller';\nimport { WALLET_SNAP_PERMISSION_KEY } from '@metamask/rpc-methods';\nimport {\n SnapKeyring,\n parseAccountId,\n AccountId,\n parseChainId,\n ChainId,\n ConnectArguments,\n isSnapPermitted,\n HandlerType,\n NamespaceId,\n RequestArguments,\n RequestNamespace,\n Session,\n TruncatedSnap,\n SnapId,\n fromEntries,\n SessionNamespace,\n flatten,\n isAccountIdArray,\n Namespaces,\n logError,\n} from '@metamask/snaps-utils';\nimport { hasProperty, assert } from '@metamask/utils';\nimport { nanoid } from 'nanoid';\n\nimport {\n GetAllSnaps,\n HandleSnapRequest,\n IncrementActiveReferences,\n DecrementActiveReferences,\n SnapEndowments,\n getRunnableSnaps,\n} from '../snaps';\nimport { getKeyringCaveatNamespaces } from '../snaps/endowments/keyring';\nimport { findMatchingKeyringSnaps } from './matching';\n\nconst controllerName = 'MultiChainController';\n\nconst defaultState: MultiChainControllerState = {\n sessions: {},\n};\n\ntype AllowedActions =\n | GetAllSnaps\n | IncrementActiveReferences\n | DecrementActiveReferences\n | HandleSnapRequest\n | GetPermissions\n | HasPermission\n | AddApprovalRequest\n | GrantPermissions;\n\ntype MultiChainControllerMessenger = RestrictedControllerMessenger<\n typeof controllerName,\n AllowedActions,\n never,\n AllowedActions['type'],\n never\n>;\n\ntype SessionData = {\n origin: string;\n requestedNamespaces: Record<NamespaceId, RequestNamespace>;\n providedNamespaces: Record<NamespaceId, RequestNamespace>;\n handlingSnaps: Record<NamespaceId, SnapId>;\n};\n\ntype MultiChainControllerState = {\n sessions: { [origin: string]: SessionData };\n};\n\ntype Notify = (\n origin: string,\n data: { method: string; params?: Record<string, unknown> },\n) => Promise<void>;\n\ntype MultiChainControllerArgs = {\n notify: Notify;\n messenger: MultiChainControllerMessenger;\n};\n\n// TODO(ritave): Support for legacy ethereum operations, not just snaps\nexport class MultiChainController extends BaseController<\n typeof controllerName,\n MultiChainControllerState,\n MultiChainControllerMessenger\n> {\n readonly #notify: Notify;\n\n /**\n * Construct a new {@link MultiChainController} instance.\n *\n * @param args - The arguments to construct the controller with.\n * @param args.messenger - The controller messenger to use.\n * @param args.notify - A function that should handle JSON-RPC notifications.\n */\n constructor({ messenger, notify }: MultiChainControllerArgs) {\n super({\n messenger,\n metadata: {\n sessions: { persist: false, anonymous: false },\n },\n name: controllerName,\n state: defaultState,\n });\n\n this.#notify = notify;\n }\n\n /**\n * Get an open session for the given origin.\n *\n * @param origin - The origin to get the session for.\n * @returns The session, if it exists, or `undefined` otherwise.\n */\n getSession(origin: string): SessionData | undefined {\n return this.state.sessions[origin];\n }\n\n /**\n * Close a session for the given origin.\n *\n * @param origin - The origin to close the session for.\n * @throws If the session does not exist.\n */\n async closeSession(origin: string): Promise<void> {\n const session = this.getSession(origin);\n assert(session, 'No session to close.');\n\n await this.#notify(origin, {\n method: 'multichainHack_metamask_disconnect',\n });\n\n this.update((state) => {\n delete state.sessions[origin];\n });\n\n await Promise.all(\n Object.values(session.handlingSnaps).map((snapId) =>\n this.messagingSystem.call(\n 'SnapController:decrementActiveReferences',\n snapId,\n ),\n ),\n );\n }\n\n /**\n * Handles a new connection from the given origin. This will create a new\n * session, and close any existing session for the origin.\n *\n * @param origin - The origin to create the session for.\n * @param connection - The connection arguments.\n * @param connection.requiredNamespaces - The namespaces that the origin\n * requires.\n * @returns The session that was created.\n */\n async onConnect(\n origin: string,\n connection: ConnectArguments,\n ): Promise<Session> {\n const existingSession = this.getSession(origin);\n if (existingSession) {\n await this.closeSession(origin);\n }\n\n const snaps = this.messagingSystem.call('SnapController:getAll');\n const filteredSnaps = getRunnableSnaps(snaps);\n\n // Get available namespaces supported by currently installed Snaps.\n const availableNamespaces = fromEntries(\n await Promise.all(\n filteredSnaps.map(async (snap) => [\n snap.id,\n await this.snapToNamespaces(snap),\n ]),\n ),\n );\n\n // The magical matching algorithm specified in SIP-2.\n const namespaceToSnaps = findMatchingKeyringSnaps(\n connection.requiredNamespaces,\n availableNamespaces,\n );\n\n const permissions = this.messagingSystem.call(\n 'PermissionController:getPermissions',\n origin,\n );\n\n assert(\n permissions !== undefined,\n `${origin} does not have any permissions.`,\n );\n\n // Find namespaces that can be satisfied with existing approved Snaps.\n const approvedNamespacesAndSnaps = Object.entries(namespaceToSnaps).reduce<\n Record<NamespaceId, SnapId[]>\n >((acc, [namespace, snapIds]) => {\n const approvedSnaps = snapIds.filter((snapId) =>\n isSnapPermitted(permissions, snapId),\n );\n\n if (approvedSnaps.length > 0) {\n acc[namespace] = approvedSnaps;\n }\n return acc;\n }, {});\n\n // If we either don't have a snap to handle a namespace or we have multiple we have conflicts\n const hasConflicts = Object.keys(namespaceToSnaps).some(\n (namespace) =>\n !hasProperty(approvedNamespacesAndSnaps, namespace) ||\n approvedNamespacesAndSnaps[namespace]?.length > 1,\n );\n\n // Use already approved snaps if they satisfy the requested namespaces.\n const filteredNamespacesAndSnaps = hasConflicts\n ? namespaceToSnaps\n : approvedNamespacesAndSnaps;\n\n // Fetch possible accounts from snaps.\n const possibleAccounts = await this.namespacesToAccounts(\n origin,\n filteredNamespacesAndSnaps,\n connection.requiredNamespaces,\n );\n\n // For now we fail here if no namespaces could be matched to a snap.\n // We don't fail if at least one namespace is matched to a snap.\n // TODO: Decide whether this is what we want\n assert(\n Object.values(possibleAccounts).some(\n (possibleAccount) => possibleAccount.length > 0,\n ),\n 'No installed snaps found for any requested namespace.',\n );\n\n // If currently installed Snaps / configuration doesn't solve request, we\n // need to show a prompt. This is handled by `resolveConflicts`.\n const resolvedAccounts = hasConflicts\n ? await this.resolveConflicts(origin, possibleAccounts)\n : fromEntries(\n Object.entries(possibleAccounts).map(\n ([namespace, snapAndAccounts]) => [\n namespace,\n snapAndAccounts[0] ?? null,\n ],\n ),\n );\n\n // Aggregate information about session namespaces.\n const providedNamespaces = Object.entries(\n connection.requiredNamespaces,\n ).reduce<Record<NamespaceId, SessionNamespace>>(\n (acc, [namespaceId, namespace]) => {\n const accounts = resolvedAccounts[namespaceId]?.accounts;\n if (accounts) {\n acc[namespaceId] = {\n accounts,\n chains: namespace.chains,\n events: namespace.events,\n methods: namespace.methods,\n };\n }\n return acc;\n },\n {},\n );\n\n // Collect information about handler Snaps for each namespace.\n const handlingSnaps = Object.entries(resolvedAccounts).reduce<\n Record<NamespaceId, SnapId>\n >((acc, [namespaceId, accountsAndSnap]) => {\n if (accountsAndSnap) {\n acc[namespaceId] = accountsAndSnap.snapId;\n }\n return acc;\n }, {});\n\n const session: SessionData = {\n origin,\n requestedNamespaces: connection.requiredNamespaces,\n providedNamespaces,\n handlingSnaps,\n };\n\n // Makes sure used Snaps aren't killed while they are serving the session.\n await Promise.all(\n Object.values(session.handlingSnaps).map((snapId) =>\n this.messagingSystem.call(\n 'SnapController:incrementActiveReferences',\n snapId,\n ),\n ),\n );\n\n this.update((state) => {\n state.sessions[origin] = session;\n });\n\n return { namespaces: providedNamespaces };\n }\n\n /**\n * Handle an incoming multichain request from the given origin. This will\n * forward the request to the appropriate Snap, and return the response.\n *\n * @param origin - The origin to handle the request for.\n * @param data - The request data.\n * @param data.chainId - The chain ID for the request.\n * @param data.request - The request arguments, i.e., the method and params.\n * @returns The response from the Snap.\n * @throws If the session does not exist, or the session does not provide the\n * requested namespace.\n */\n async onRequest(\n origin: string,\n data: { chainId: ChainId; request: RequestArguments },\n ): Promise<unknown> {\n const session = this.getSession(origin);\n assert(session, `Session for \"${origin}\" doesn't exist.`);\n\n const { namespace } = parseChainId(data.chainId);\n const sessionNamespace = session.providedNamespaces[namespace];\n assert(\n session.providedNamespaces[namespace]?.chains.includes(data.chainId),\n `Session for \"${origin}\" is not connected to \"${data.chainId}\" chain.`,\n );\n\n const { method } = data.request;\n assert(\n sessionNamespace?.methods?.includes(method),\n `Session for \"${origin}\" does not support ${method}`,\n );\n\n const snapId = session.handlingSnaps[namespace];\n assert(snapId !== undefined);\n\n // TODO: Get permission for origin connecting to snap, or get user approval.\n // In the future this is where we should prompt for this permission.\n // In this iteration, we will grant this permission in `onConnect`.\n const permissions = this.messagingSystem.call(\n 'PermissionController:getPermissions',\n origin,\n );\n\n assert(\n permissions !== undefined,\n `${origin} does not have any permissions.`,\n );\n\n assert(\n isSnapPermitted(permissions, snapId),\n `${origin} does not have permission to communicate with ${snapId}.`,\n );\n\n return this.snapRequest({\n snapId,\n origin,\n method: 'handleRequest',\n args: data,\n });\n }\n\n /**\n * Send a request to the given Snap. This calls the given method with the\n * given arguments on the keyring class in the given Snap.\n *\n * @param options - The request options.\n * @param options.snapId - The ID of the Snap to send the request to.\n * @param options.origin - The origin of the request.\n * @param options.method - The request method.\n * @param options.args - The request params.\n * @returns The response from the Snap.\n */\n private async snapRequest({\n snapId,\n origin,\n method,\n args,\n }: {\n snapId: SnapId;\n origin: string;\n method: keyof SnapKeyring;\n args?: unknown;\n }) {\n return this.messagingSystem.call('SnapController:handleRequest', {\n snapId,\n origin,\n handler: HandlerType.SnapKeyring,\n request: { method, params: args ? [args] : [] },\n });\n }\n\n /**\n * Get the accounts exposed by the Snap's keyring.\n *\n * This also verifies that the accounts returned by the snap are valid CAIP-10\n * account IDs.\n *\n * @param origin - The origin of the request.\n * @param snapId - The ID of the Snap to get the accounts from.\n * @returns The accounts, or `null` if the Snap does not have any accounts, or\n * the accounts are invalid (i.e., not valid CAIP-10 account IDs).\n */\n private async getSnapAccounts(\n origin: string,\n snapId: SnapId,\n ): Promise<AccountId[] | null> {\n try {\n const result = await this.snapRequest({\n snapId,\n origin,\n method: 'getAccounts',\n });\n\n if (isAccountIdArray(result)) {\n return result;\n }\n } catch (error) {\n // Ignore errors for now\n logError(error);\n }\n\n return null;\n }\n\n /**\n * Get the namespaces for the given Snap, as described in the Snap's manifest.\n *\n * @param snap - The Snap to get the namespaces for.\n * @returns The namespaces, or `null` if the Snap does not have any\n * namespaces.\n */\n private snapToNamespaces(snap: TruncatedSnap): Namespaces | null {\n const permissions = this.messagingSystem.call(\n 'PermissionController:getPermissions',\n snap.id,\n );\n\n const keyringPermission = permissions?.[SnapEndowments.Keyring];\n return getKeyringCaveatNamespaces(keyringPermission);\n }\n\n /**\n * Maps from an object of namespace IDs and Snap IDs, and an object of\n * namespace IDs and requested namespaces, to an object of namespace IDs and\n * resolved accounts, with the Snap ID providing the accounts.\n *\n * @param origin - The origin of the request.\n * @param namespacesAndSnaps - An object of namespace IDs and Snap IDs\n * providing the namespace.\n * @param requestedNamespaces - An object of namespace IDs and requested\n * namespaces.\n * @returns An object of namespace IDs and resolved accounts, with the Snap ID\n * providing the accounts.\n */\n private async namespacesToAccounts(\n origin: string,\n namespacesAndSnaps: Record<NamespaceId, SnapId[]>,\n requestedNamespaces: Record<NamespaceId, RequestNamespace>,\n ): Promise<Record<NamespaceId, { snapId: SnapId; accounts: AccountId[] }[]>> {\n const dedupedSnaps = [\n ...new Set(flatten(Object.values(namespacesAndSnaps))),\n ] as SnapId[];\n\n const allAccounts = await dedupedSnaps.reduce<\n Promise<Record<string, AccountId[]>>\n >(async (previousPromise, snapId) => {\n const result = await this.getSnapAccounts(origin, snapId);\n const acc = await previousPromise;\n if (result) {\n acc[snapId] = result;\n }\n return acc;\n }, Promise.resolve({}));\n\n return Object.keys(namespacesAndSnaps).reduce<\n Record<NamespaceId, { snapId: SnapId; accounts: AccountId[] }[]>\n >((acc, namespaceId) => {\n const { chains } = requestedNamespaces[namespaceId];\n\n const accountInAnyRequestedChain = (account: AccountId) => {\n const { chainId: parsedChainId } = parseAccountId(account);\n return chains.some((chainId) => chainId === parsedChainId);\n };\n\n const result = Object.entries(allAccounts)\n .map(([snapId, accounts]) => ({\n snapId,\n accounts: accounts.filter(accountInAnyRequestedChain),\n }))\n .filter(({ accounts }) => accounts.length > 0);\n\n acc[namespaceId] = result;\n\n return acc;\n }, {});\n }\n\n /**\n * If multiple Snap IDs are provided for a namespace, this method will\n * determine which Snap ID to use for the namespace, by showing the user a\n * prompt.\n *\n * @param origin - The origin of the request.\n * @param possibleAccounts - An object containing the accounts provided by\n * each Snap ID for each namespace.\n * @returns An object containing the Snap ID to use for each namespace.\n */\n private async resolveConflicts(\n origin: string,\n possibleAccounts: Record<\n NamespaceId,\n { snapId: SnapId; accounts: AccountId[] }[]\n >,\n ): Promise<\n Record<NamespaceId, { snapId: SnapId; accounts: AccountId[] } | null>\n > {\n // Get user approval for connection.\n const id = nanoid();\n const resolvedAccounts = (await this.messagingSystem.call(\n 'ApprovalController:addRequest',\n {\n origin,\n id,\n type: 'multichain_connect',\n requestData: {\n possibleAccounts,\n },\n },\n true,\n )) as Record<NamespaceId, { snapId: SnapId; accounts: AccountId[] } | null>;\n\n // TODO: In the future, use another permission here to not give full\n // permission after handshake.\n // Instead we should give origin only a read-only access to list of accounts\n // without allowing provider.request() talking to a snap before additional\n // user approval. The additional approval would be requested in `onRequest`.\n\n const approvedPermissions = Object.values(resolvedAccounts).reduce<\n Record<string, Record<string, Partial<PermissionConstraint>>>\n >(\n (acc, curr) => {\n if (curr !== null) {\n acc[WALLET_SNAP_PERMISSION_KEY][curr.snapId] = {};\n }\n return acc;\n },\n { [WALLET_SNAP_PERMISSION_KEY]: {} },\n );\n\n this.messagingSystem.call('PermissionController:grantPermissions', {\n approvedPermissions,\n subject: { origin },\n });\n\n return resolvedAccounts;\n }\n}\n"]}
1
+ {"version":3,"file":"MultiChainController.js","sourceRoot":"","sources":["../../src/multichain/MultiChainController.ts"],"names":[],"mappings":"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","sourcesContent":["import { AddApprovalRequest } from '@metamask/approval-controller';\nimport {\n BaseControllerV2 as BaseController,\n RestrictedControllerMessenger,\n} from '@metamask/base-controller';\nimport {\n GetPermissions,\n GrantPermissions,\n HasPermission,\n PermissionConstraint,\n} from '@metamask/permission-controller';\nimport { WALLET_SNAP_PERMISSION_KEY } from '@metamask/rpc-methods';\nimport {\n SnapKeyring,\n parseAccountId,\n AccountId,\n parseChainId,\n ChainId,\n ConnectArguments,\n isSnapPermitted,\n HandlerType,\n NamespaceId,\n RequestArguments,\n RequestNamespace,\n Session,\n TruncatedSnap,\n SnapId,\n SessionNamespace,\n isAccountIdArray,\n Namespaces,\n logError,\n} from '@metamask/snaps-utils';\nimport { hasProperty, assert } from '@metamask/utils';\nimport { nanoid } from 'nanoid';\n\nimport {\n GetAllSnaps,\n HandleSnapRequest,\n IncrementActiveReferences,\n DecrementActiveReferences,\n SnapEndowments,\n getRunnableSnaps,\n} from '../snaps';\nimport { getKeyringCaveatNamespaces } from '../snaps/endowments/keyring';\nimport { findMatchingKeyringSnaps } from './matching';\n\nconst controllerName = 'MultiChainController';\n\nconst defaultState: MultiChainControllerState = {\n sessions: {},\n};\n\ntype AllowedActions =\n | GetAllSnaps\n | IncrementActiveReferences\n | DecrementActiveReferences\n | HandleSnapRequest\n | GetPermissions\n | HasPermission\n | AddApprovalRequest\n | GrantPermissions;\n\ntype MultiChainControllerMessenger = RestrictedControllerMessenger<\n typeof controllerName,\n AllowedActions,\n never,\n AllowedActions['type'],\n never\n>;\n\ntype SessionData = {\n origin: string;\n requestedNamespaces: Record<NamespaceId, RequestNamespace>;\n providedNamespaces: Record<NamespaceId, RequestNamespace>;\n handlingSnaps: Record<NamespaceId, SnapId>;\n};\n\ntype MultiChainControllerState = {\n sessions: { [origin: string]: SessionData };\n};\n\ntype Notify = (\n origin: string,\n data: { method: string; params?: Record<string, unknown> },\n) => Promise<void>;\n\ntype MultiChainControllerArgs = {\n notify: Notify;\n messenger: MultiChainControllerMessenger;\n};\n\n// TODO(ritave): Support for legacy ethereum operations, not just snaps\nexport class MultiChainController extends BaseController<\n typeof controllerName,\n MultiChainControllerState,\n MultiChainControllerMessenger\n> {\n readonly #notify: Notify;\n\n /**\n * Construct a new {@link MultiChainController} instance.\n *\n * @param args - The arguments to construct the controller with.\n * @param args.messenger - The controller messenger to use.\n * @param args.notify - A function that should handle JSON-RPC notifications.\n */\n constructor({ messenger, notify }: MultiChainControllerArgs) {\n super({\n messenger,\n metadata: {\n sessions: { persist: false, anonymous: false },\n },\n name: controllerName,\n state: defaultState,\n });\n\n this.#notify = notify;\n }\n\n /**\n * Get an open session for the given origin.\n *\n * @param origin - The origin to get the session for.\n * @returns The session, if it exists, or `undefined` otherwise.\n */\n getSession(origin: string): SessionData | undefined {\n return this.state.sessions[origin];\n }\n\n /**\n * Close a session for the given origin.\n *\n * @param origin - The origin to close the session for.\n * @throws If the session does not exist.\n */\n async closeSession(origin: string): Promise<void> {\n const session = this.getSession(origin);\n assert(session, 'No session to close.');\n\n await this.#notify(origin, {\n method: 'multichainHack_metamask_disconnect',\n });\n\n this.update((state) => {\n delete state.sessions[origin];\n });\n\n await Promise.all(\n Object.values(session.handlingSnaps).map((snapId) =>\n this.messagingSystem.call(\n 'SnapController:decrementActiveReferences',\n snapId,\n ),\n ),\n );\n }\n\n /**\n * Handles a new connection from the given origin. This will create a new\n * session, and close any existing session for the origin.\n *\n * @param origin - The origin to create the session for.\n * @param connection - The connection arguments.\n * @param connection.requiredNamespaces - The namespaces that the origin\n * requires.\n * @returns The session that was created.\n */\n async onConnect(\n origin: string,\n connection: ConnectArguments,\n ): Promise<Session> {\n const existingSession = this.getSession(origin);\n if (existingSession) {\n await this.closeSession(origin);\n }\n\n const snaps = this.messagingSystem.call('SnapController:getAll');\n const filteredSnaps = getRunnableSnaps(snaps);\n\n // Get available namespaces supported by currently installed Snaps.\n const availableNamespaces = Object.fromEntries(\n await Promise.all(\n filteredSnaps.map(async (snap) => [\n snap.id,\n await this.snapToNamespaces(snap),\n ]),\n ),\n );\n\n // The magical matching algorithm specified in SIP-2.\n const namespaceToSnaps = findMatchingKeyringSnaps(\n connection.requiredNamespaces,\n availableNamespaces,\n );\n\n const permissions = this.messagingSystem.call(\n 'PermissionController:getPermissions',\n origin,\n );\n\n assert(\n permissions !== undefined,\n `${origin} does not have any permissions.`,\n );\n\n // Find namespaces that can be satisfied with existing approved Snaps.\n const approvedNamespacesAndSnaps = Object.entries(namespaceToSnaps).reduce<\n Record<NamespaceId, SnapId[]>\n >((acc, [namespace, snapIds]) => {\n const approvedSnaps = snapIds.filter((snapId) =>\n isSnapPermitted(permissions, snapId),\n );\n\n if (approvedSnaps.length > 0) {\n acc[namespace] = approvedSnaps;\n }\n return acc;\n }, {});\n\n // If we either don't have a snap to handle a namespace or we have multiple we have conflicts\n const hasConflicts = Object.keys(namespaceToSnaps).some(\n (namespace) =>\n !hasProperty(approvedNamespacesAndSnaps, namespace) ||\n approvedNamespacesAndSnaps[namespace]?.length > 1,\n );\n\n // Use already approved snaps if they satisfy the requested namespaces.\n const filteredNamespacesAndSnaps = hasConflicts\n ? namespaceToSnaps\n : approvedNamespacesAndSnaps;\n\n // Fetch possible accounts from snaps.\n const possibleAccounts = await this.namespacesToAccounts(\n origin,\n filteredNamespacesAndSnaps,\n connection.requiredNamespaces,\n );\n\n // For now we fail here if no namespaces could be matched to a snap.\n // We don't fail if at least one namespace is matched to a snap.\n // TODO: Decide whether this is what we want\n assert(\n Object.values(possibleAccounts).some(\n (possibleAccount) => possibleAccount.length > 0,\n ),\n 'No installed snaps found for any requested namespace.',\n );\n\n // If currently installed Snaps / configuration doesn't solve request, we\n // need to show a prompt. This is handled by `resolveConflicts`.\n const resolvedAccounts = hasConflicts\n ? await this.resolveConflicts(origin, possibleAccounts)\n : Object.fromEntries(\n Object.entries(possibleAccounts).map(\n ([namespace, snapAndAccounts]) => [\n namespace,\n snapAndAccounts[0] ?? null,\n ],\n ),\n );\n\n // Aggregate information about session namespaces.\n const providedNamespaces = Object.entries(\n connection.requiredNamespaces,\n ).reduce<Record<NamespaceId, SessionNamespace>>(\n (acc, [namespaceId, namespace]) => {\n const accounts = resolvedAccounts[namespaceId]?.accounts;\n if (accounts) {\n acc[namespaceId] = {\n accounts,\n chains: namespace.chains,\n events: namespace.events,\n methods: namespace.methods,\n };\n }\n return acc;\n },\n {},\n );\n\n // Collect information about handler Snaps for each namespace.\n const handlingSnaps = Object.entries(resolvedAccounts).reduce<\n Record<NamespaceId, SnapId>\n >((acc, [namespaceId, accountsAndSnap]) => {\n if (accountsAndSnap) {\n acc[namespaceId] = accountsAndSnap.snapId;\n }\n return acc;\n }, {});\n\n const session: SessionData = {\n origin,\n requestedNamespaces: connection.requiredNamespaces,\n providedNamespaces,\n handlingSnaps,\n };\n\n // Makes sure used Snaps aren't killed while they are serving the session.\n await Promise.all(\n Object.values(session.handlingSnaps).map((snapId) =>\n this.messagingSystem.call(\n 'SnapController:incrementActiveReferences',\n snapId,\n ),\n ),\n );\n\n this.update((state) => {\n state.sessions[origin] = session;\n });\n\n return { namespaces: providedNamespaces };\n }\n\n /**\n * Handle an incoming multichain request from the given origin. This will\n * forward the request to the appropriate Snap, and return the response.\n *\n * @param origin - The origin to handle the request for.\n * @param data - The request data.\n * @param data.chainId - The chain ID for the request.\n * @param data.request - The request arguments, i.e., the method and params.\n * @returns The response from the Snap.\n * @throws If the session does not exist, or the session does not provide the\n * requested namespace.\n */\n async onRequest(\n origin: string,\n data: { chainId: ChainId; request: RequestArguments },\n ): Promise<unknown> {\n const session = this.getSession(origin);\n assert(session, `Session for \"${origin}\" doesn't exist.`);\n\n const { namespace } = parseChainId(data.chainId);\n const sessionNamespace = session.providedNamespaces[namespace];\n assert(\n session.providedNamespaces[namespace]?.chains.includes(data.chainId),\n `Session for \"${origin}\" is not connected to \"${data.chainId}\" chain.`,\n );\n\n const { method } = data.request;\n assert(\n sessionNamespace?.methods?.includes(method),\n `Session for \"${origin}\" does not support ${method}`,\n );\n\n const snapId = session.handlingSnaps[namespace];\n assert(snapId !== undefined);\n\n // TODO: Get permission for origin connecting to snap, or get user approval.\n // In the future this is where we should prompt for this permission.\n // In this iteration, we will grant this permission in `onConnect`.\n const permissions = this.messagingSystem.call(\n 'PermissionController:getPermissions',\n origin,\n );\n\n assert(\n permissions !== undefined,\n `${origin} does not have any permissions.`,\n );\n\n assert(\n isSnapPermitted(permissions, snapId),\n `${origin} does not have permission to communicate with ${snapId}.`,\n );\n\n return this.snapRequest({\n snapId,\n origin,\n method: 'handleRequest',\n args: data,\n });\n }\n\n /**\n * Send a request to the given Snap. This calls the given method with the\n * given arguments on the keyring class in the given Snap.\n *\n * @param options - The request options.\n * @param options.snapId - The ID of the Snap to send the request to.\n * @param options.origin - The origin of the request.\n * @param options.method - The request method.\n * @param options.args - The request params.\n * @returns The response from the Snap.\n */\n private async snapRequest({\n snapId,\n origin,\n method,\n args,\n }: {\n snapId: SnapId;\n origin: string;\n method: keyof SnapKeyring;\n args?: unknown;\n }) {\n return this.messagingSystem.call('SnapController:handleRequest', {\n snapId,\n origin,\n handler: HandlerType.SnapKeyring,\n request: { method, params: args ? [args] : [] },\n });\n }\n\n /**\n * Get the accounts exposed by the Snap's keyring.\n *\n * This also verifies that the accounts returned by the snap are valid CAIP-10\n * account IDs.\n *\n * @param origin - The origin of the request.\n * @param snapId - The ID of the Snap to get the accounts from.\n * @returns The accounts, or `null` if the Snap does not have any accounts, or\n * the accounts are invalid (i.e., not valid CAIP-10 account IDs).\n */\n private async getSnapAccounts(\n origin: string,\n snapId: SnapId,\n ): Promise<AccountId[] | null> {\n try {\n const result = await this.snapRequest({\n snapId,\n origin,\n method: 'getAccounts',\n });\n\n if (isAccountIdArray(result)) {\n return result;\n }\n } catch (error) {\n // Ignore errors for now\n logError(error);\n }\n\n return null;\n }\n\n /**\n * Get the namespaces for the given Snap, as described in the Snap's manifest.\n *\n * @param snap - The Snap to get the namespaces for.\n * @returns The namespaces, or `null` if the Snap does not have any\n * namespaces.\n */\n private snapToNamespaces(snap: TruncatedSnap): Namespaces | null {\n const permissions = this.messagingSystem.call(\n 'PermissionController:getPermissions',\n snap.id,\n );\n\n const keyringPermission = permissions?.[SnapEndowments.Keyring];\n return getKeyringCaveatNamespaces(keyringPermission);\n }\n\n /**\n * Maps from an object of namespace IDs and Snap IDs, and an object of\n * namespace IDs and requested namespaces, to an object of namespace IDs and\n * resolved accounts, with the Snap ID providing the accounts.\n *\n * @param origin - The origin of the request.\n * @param namespacesAndSnaps - An object of namespace IDs and Snap IDs\n * providing the namespace.\n * @param requestedNamespaces - An object of namespace IDs and requested\n * namespaces.\n * @returns An object of namespace IDs and resolved accounts, with the Snap ID\n * providing the accounts.\n */\n private async namespacesToAccounts(\n origin: string,\n namespacesAndSnaps: Record<NamespaceId, SnapId[]>,\n requestedNamespaces: Record<NamespaceId, RequestNamespace>,\n ): Promise<Record<NamespaceId, { snapId: SnapId; accounts: AccountId[] }[]>> {\n const dedupedSnaps = [\n ...new Set(Object.values(namespacesAndSnaps).flat()),\n ] as SnapId[];\n\n const allAccounts = await dedupedSnaps.reduce<\n Promise<Record<string, AccountId[]>>\n >(async (previousPromise, snapId) => {\n const result = await this.getSnapAccounts(origin, snapId);\n const acc = await previousPromise;\n if (result) {\n acc[snapId] = result;\n }\n return acc;\n }, Promise.resolve({}));\n\n return Object.keys(namespacesAndSnaps).reduce<\n Record<NamespaceId, { snapId: SnapId; accounts: AccountId[] }[]>\n >((acc, namespaceId) => {\n const { chains } = requestedNamespaces[namespaceId];\n\n const accountInAnyRequestedChain = (account: AccountId) => {\n const { chainId: parsedChainId } = parseAccountId(account);\n return chains.some((chainId) => chainId === parsedChainId);\n };\n\n const result = Object.entries(allAccounts)\n .map(([snapId, accounts]) => ({\n snapId,\n accounts: accounts.filter(accountInAnyRequestedChain),\n }))\n .filter(({ accounts }) => accounts.length > 0);\n\n acc[namespaceId] = result;\n\n return acc;\n }, {});\n }\n\n /**\n * If multiple Snap IDs are provided for a namespace, this method will\n * determine which Snap ID to use for the namespace, by showing the user a\n * prompt.\n *\n * @param origin - The origin of the request.\n * @param possibleAccounts - An object containing the accounts provided by\n * each Snap ID for each namespace.\n * @returns An object containing the Snap ID to use for each namespace.\n */\n private async resolveConflicts(\n origin: string,\n possibleAccounts: Record<\n NamespaceId,\n { snapId: SnapId; accounts: AccountId[] }[]\n >,\n ): Promise<\n Record<NamespaceId, { snapId: SnapId; accounts: AccountId[] } | null>\n > {\n // Get user approval for connection.\n const id = nanoid();\n const resolvedAccounts = (await this.messagingSystem.call(\n 'ApprovalController:addRequest',\n {\n origin,\n id,\n type: 'multichain_connect',\n requestData: {\n possibleAccounts,\n },\n },\n true,\n )) as Record<NamespaceId, { snapId: SnapId; accounts: AccountId[] } | null>;\n\n // TODO: In the future, use another permission here to not give full\n // permission after handshake.\n // Instead we should give origin only a read-only access to list of accounts\n // without allowing provider.request() talking to a snap before additional\n // user approval. The additional approval would be requested in `onRequest`.\n\n const approvedPermissions = Object.values(resolvedAccounts).reduce<\n Record<string, Record<string, Partial<PermissionConstraint>>>\n >(\n (acc, curr) => {\n if (curr !== null) {\n acc[WALLET_SNAP_PERMISSION_KEY][curr.snapId] = {};\n }\n return acc;\n },\n { [WALLET_SNAP_PERMISSION_KEY]: {} },\n );\n\n this.messagingSystem.call('PermissionController:grantPermissions', {\n approvedPermissions,\n subject: { origin },\n });\n\n return resolvedAccounts;\n }\n}\n"]}
@@ -13,7 +13,7 @@ function findMatchingKeyringSnaps(requestedNamespaces, snaps) {
13
13
  return Object.entries(requestedNamespaces).reduce((acc, [requestedNamespaceId, currentRequestedNamespace]) => {
14
14
  const matchedSnaps = snapEntries
15
15
  .filter(([, namespaces]) => {
16
- const potentialMatch = namespaces === null || namespaces === void 0 ? void 0 : namespaces[requestedNamespaceId];
16
+ const potentialMatch = namespaces?.[requestedNamespaceId];
17
17
  return (potentialMatch !== undefined &&
18
18
  matchNamespace(currentRequestedNamespace, potentialMatch));
19
19
  })
@@ -33,14 +33,13 @@ exports.findMatchingKeyringSnaps = findMatchingKeyringSnaps;
33
33
  * @returns True if the potentially matching namespace is a match.
34
34
  */
35
35
  function matchNamespace(requestedNamespace, potentialMatchNamespace) {
36
- var _a, _b, _c, _d;
37
36
  if (!requestedNamespace.chains.every((requestedChain) => potentialMatchNamespace.chains.some((potentialMatchChain) => potentialMatchChain.id === requestedChain))) {
38
37
  return false;
39
38
  }
40
- if (!isSubset((_a = requestedNamespace.events) !== null && _a !== void 0 ? _a : [], (_b = potentialMatchNamespace.events) !== null && _b !== void 0 ? _b : [])) {
39
+ if (!isSubset(requestedNamespace.events ?? [], potentialMatchNamespace.events ?? [])) {
41
40
  return false;
42
41
  }
43
- if (!isSubset((_c = requestedNamespace.methods) !== null && _c !== void 0 ? _c : [], (_d = potentialMatchNamespace.methods) !== null && _d !== void 0 ? _d : [])) {
42
+ if (!isSubset(requestedNamespace.methods ?? [], potentialMatchNamespace.methods ?? [])) {
44
43
  return false;
45
44
  }
46
45
  return true;
@@ -1 +1 @@
1
- {"version":3,"file":"matching.js","sourceRoot":"","sources":["../../src/multichain/matching.ts"],"names":[],"mappings":";;;AAOA;;;;;;GAMG;AACH,SAAgB,wBAAwB,CACtC,mBAA2D,EAC3D,KAA4D;IAE5D,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAC1C,OAAO,MAAM,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC,MAAM,CAE/C,CAAC,GAAG,EAAE,CAAC,oBAAoB,EAAE,yBAAyB,CAAC,EAAE,EAAE;QAC3D,MAAM,YAAY,GAAG,WAAW;aAC7B,MAAM,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC,EAAE,EAAE;YACzB,MAAM,cAAc,GAAG,UAAU,aAAV,UAAU,uBAAV,UAAU,CAAG,oBAAoB,CAAC,CAAC;YAC1D,OAAO,CACL,cAAc,KAAK,SAAS;gBAC5B,cAAc,CAAC,yBAAyB,EAAE,cAAc,CAAC,CAC1D,CAAC;QACJ,CAAC,CAAC;aACD,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC;QAC7B,GAAG,CAAC,oBAAoB,CAAC,GAAG,YAAY,CAAC;QACzC,OAAO,GAAG,CAAC;IACb,CAAC,EAAE,EAAE,CAAC,CAAC;AACT,CAAC;AApBD,4DAoBC;AAED;;;;;;;;GAQG;AACH,SAAS,cAAc,CACrB,kBAAuE,EACvE,uBAAkC;;IAElC,IACE,CAAC,kBAAkB,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,cAAc,EAAE,EAAE,CAClD,uBAAuB,CAAC,MAAM,CAAC,IAAI,CACjC,CAAC,mBAAmB,EAAE,EAAE,CAAC,mBAAmB,CAAC,EAAE,KAAK,cAAc,CACnE,CACF,EACD;QACA,OAAO,KAAK,CAAC;KACd;IAED,IACE,CAAC,QAAQ,CACP,MAAA,kBAAkB,CAAC,MAAM,mCAAI,EAAE,EAC/B,MAAA,uBAAuB,CAAC,MAAM,mCAAI,EAAE,CACrC,EACD;QACA,OAAO,KAAK,CAAC;KACd;IAED,IACE,CAAC,QAAQ,CACP,MAAA,kBAAkB,CAAC,OAAO,mCAAI,EAAE,EAChC,MAAA,uBAAuB,CAAC,OAAO,mCAAI,EAAE,CACtC,EACD;QACA,OAAO,KAAK,CAAC;KACd;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;GAMG;AACH,SAAS,QAAQ,CAAI,eAAoB,EAAE,KAAU;IACnD,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;AAC/D,CAAC","sourcesContent":["import {\n ConnectArguments,\n NamespaceId,\n SnapId,\n Namespace,\n} from '@metamask/snaps-utils';\n\n/**\n * Finds a keyring snap for each namespace implements at a minimum the requested functionality.\n *\n * @param requestedNamespaces - The requested namespaces.\n * @param snaps - All snaps and their exposed keyring namespaces.\n * @returns A mapping between namespaces and snap ids.\n */\nexport function findMatchingKeyringSnaps(\n requestedNamespaces: ConnectArguments['requiredNamespaces'],\n snaps: Record<SnapId, Record<NamespaceId, Namespace> | null>,\n): Record<NamespaceId, SnapId[]> {\n const snapEntries = Object.entries(snaps);\n return Object.entries(requestedNamespaces).reduce<\n Record<NamespaceId, SnapId[]>\n >((acc, [requestedNamespaceId, currentRequestedNamespace]) => {\n const matchedSnaps = snapEntries\n .filter(([, namespaces]) => {\n const potentialMatch = namespaces?.[requestedNamespaceId];\n return (\n potentialMatch !== undefined &&\n matchNamespace(currentRequestedNamespace, potentialMatch)\n );\n })\n .map(([snapId]) => snapId);\n acc[requestedNamespaceId] = matchedSnaps;\n return acc;\n }, {});\n}\n\n/**\n * Determines whether a keyring namespace is a match given a potential match and the requested namespace.\n *\n * This function assumes that the namespace ID has already been matched.\n *\n * @param requestedNamespace - The requested namespace information.\n * @param potentialMatchNamespace - The potential match.\n * @returns True if the potentially matching namespace is a match.\n */\nfunction matchNamespace(\n requestedNamespace: ConnectArguments['requiredNamespaces'][NamespaceId],\n potentialMatchNamespace: Namespace,\n) {\n if (\n !requestedNamespace.chains.every((requestedChain) =>\n potentialMatchNamespace.chains.some(\n (potentialMatchChain) => potentialMatchChain.id === requestedChain,\n ),\n )\n ) {\n return false;\n }\n\n if (\n !isSubset(\n requestedNamespace.events ?? [],\n potentialMatchNamespace.events ?? [],\n )\n ) {\n return false;\n }\n\n if (\n !isSubset(\n requestedNamespace.methods ?? [],\n potentialMatchNamespace.methods ?? [],\n )\n ) {\n return false;\n }\n\n return true;\n}\n\n/**\n * Determines whether an array is a subset of another array.\n *\n * @param potentialSubset - The potential subset.\n * @param array - The other array.\n * @returns True if the first argument is a subset of second argument.\n */\nfunction isSubset<T>(potentialSubset: T[], array: T[]) {\n return potentialSubset.every((item) => array.includes(item));\n}\n"]}
1
+ {"version":3,"file":"matching.js","sourceRoot":"","sources":["../../src/multichain/matching.ts"],"names":[],"mappings":";;;AAOA;;;;;;GAMG;AACH,SAAgB,wBAAwB,CACtC,mBAA2D,EAC3D,KAA4D;IAE5D,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAC1C,OAAO,MAAM,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC,MAAM,CAE/C,CAAC,GAAG,EAAE,CAAC,oBAAoB,EAAE,yBAAyB,CAAC,EAAE,EAAE;QAC3D,MAAM,YAAY,GAAG,WAAW;aAC7B,MAAM,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC,EAAE,EAAE;YACzB,MAAM,cAAc,GAAG,UAAU,EAAE,CAAC,oBAAoB,CAAC,CAAC;YAC1D,OAAO,CACL,cAAc,KAAK,SAAS;gBAC5B,cAAc,CAAC,yBAAyB,EAAE,cAAc,CAAC,CAC1D,CAAC;QACJ,CAAC,CAAC;aACD,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC;QAC7B,GAAG,CAAC,oBAAoB,CAAC,GAAG,YAAY,CAAC;QACzC,OAAO,GAAG,CAAC;IACb,CAAC,EAAE,EAAE,CAAC,CAAC;AACT,CAAC;AApBD,4DAoBC;AAED;;;;;;;;GAQG;AACH,SAAS,cAAc,CACrB,kBAAuE,EACvE,uBAAkC;IAElC,IACE,CAAC,kBAAkB,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,cAAc,EAAE,EAAE,CAClD,uBAAuB,CAAC,MAAM,CAAC,IAAI,CACjC,CAAC,mBAAmB,EAAE,EAAE,CAAC,mBAAmB,CAAC,EAAE,KAAK,cAAc,CACnE,CACF,EACD;QACA,OAAO,KAAK,CAAC;KACd;IAED,IACE,CAAC,QAAQ,CACP,kBAAkB,CAAC,MAAM,IAAI,EAAE,EAC/B,uBAAuB,CAAC,MAAM,IAAI,EAAE,CACrC,EACD;QACA,OAAO,KAAK,CAAC;KACd;IAED,IACE,CAAC,QAAQ,CACP,kBAAkB,CAAC,OAAO,IAAI,EAAE,EAChC,uBAAuB,CAAC,OAAO,IAAI,EAAE,CACtC,EACD;QACA,OAAO,KAAK,CAAC;KACd;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;GAMG;AACH,SAAS,QAAQ,CAAI,eAAoB,EAAE,KAAU;IACnD,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;AAC/D,CAAC","sourcesContent":["import {\n ConnectArguments,\n NamespaceId,\n SnapId,\n Namespace,\n} from '@metamask/snaps-utils';\n\n/**\n * Finds a keyring snap for each namespace implements at a minimum the requested functionality.\n *\n * @param requestedNamespaces - The requested namespaces.\n * @param snaps - All snaps and their exposed keyring namespaces.\n * @returns A mapping between namespaces and snap ids.\n */\nexport function findMatchingKeyringSnaps(\n requestedNamespaces: ConnectArguments['requiredNamespaces'],\n snaps: Record<SnapId, Record<NamespaceId, Namespace> | null>,\n): Record<NamespaceId, SnapId[]> {\n const snapEntries = Object.entries(snaps);\n return Object.entries(requestedNamespaces).reduce<\n Record<NamespaceId, SnapId[]>\n >((acc, [requestedNamespaceId, currentRequestedNamespace]) => {\n const matchedSnaps = snapEntries\n .filter(([, namespaces]) => {\n const potentialMatch = namespaces?.[requestedNamespaceId];\n return (\n potentialMatch !== undefined &&\n matchNamespace(currentRequestedNamespace, potentialMatch)\n );\n })\n .map(([snapId]) => snapId);\n acc[requestedNamespaceId] = matchedSnaps;\n return acc;\n }, {});\n}\n\n/**\n * Determines whether a keyring namespace is a match given a potential match and the requested namespace.\n *\n * This function assumes that the namespace ID has already been matched.\n *\n * @param requestedNamespace - The requested namespace information.\n * @param potentialMatchNamespace - The potential match.\n * @returns True if the potentially matching namespace is a match.\n */\nfunction matchNamespace(\n requestedNamespace: ConnectArguments['requiredNamespaces'][NamespaceId],\n potentialMatchNamespace: Namespace,\n) {\n if (\n !requestedNamespace.chains.every((requestedChain) =>\n potentialMatchNamespace.chains.some(\n (potentialMatchChain) => potentialMatchChain.id === requestedChain,\n ),\n )\n ) {\n return false;\n }\n\n if (\n !isSubset(\n requestedNamespace.events ?? [],\n potentialMatchNamespace.events ?? [],\n )\n ) {\n return false;\n }\n\n if (\n !isSubset(\n requestedNamespace.methods ?? [],\n potentialMatchNamespace.methods ?? [],\n )\n ) {\n return false;\n }\n\n return true;\n}\n\n/**\n * Determines whether an array is a subset of another array.\n *\n * @param potentialSubset - The potential subset.\n * @param array - The other array.\n * @returns True if the first argument is a subset of second argument.\n */\nfunction isSubset<T>(potentialSubset: T[], array: T[]) {\n return potentialSubset.every((item) => array.includes(item));\n}\n"]}
@@ -13,8 +13,7 @@ class IframeExecutionService extends AbstractExecutionService_1.AbstractExecutio
13
13
  this.iframeUrl = iframeUrl;
14
14
  }
15
15
  terminateJob(jobWrapper) {
16
- var _a;
17
- (_a = document.getElementById(jobWrapper.id)) === null || _a === void 0 ? void 0 : _a.remove();
16
+ document.getElementById(jobWrapper.id)?.remove();
18
17
  }
19
18
  async initEnvStream(jobId) {
20
19
  const iframeWindow = await (0, snaps_utils_1.createWindow)(this.iframeUrl.toString(), jobId);
@@ -1 +1 @@
1
- {"version":3,"file":"IframeExecutionService.js","sourceRoot":"","sources":["../../../src/services/iframe/IframeExecutionService.ts"],"names":[],"mappings":";;;AAAA,uEAGuC;AACvC,uDAAqD;AAErD,0EAIqC;AAMrC,MAAa,sBAAuB,SAAQ,mDAAgC;IAG1E,YAAY,EACV,SAAS,EACT,SAAS,EACT,iBAAiB,GACqB;QACtC,KAAK,CAAC;YACJ,SAAS;YACT,iBAAiB;SAClB,CAAC,CAAC;QACH,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;IAC7B,CAAC;IAES,YAAY,CAAC,UAAuB;;QAC5C,MAAA,QAAQ,CAAC,cAAc,CAAC,UAAU,CAAC,EAAE,CAAC,0CAAE,MAAM,EAAE,CAAC;IACnD,CAAC;IAES,KAAK,CAAC,aAAa,CAAC,KAAa;QAIzC,MAAM,YAAY,GAAG,MAAM,IAAA,0BAAY,EAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,KAAK,CAAC,CAAC;QAE1E,MAAM,MAAM,GAAG,IAAI,6CAAuB,CAAC;YACzC,IAAI,EAAE,QAAQ;YACd,MAAM,EAAE,OAAO;YACf,YAAY,EAAE,YAAY;YAC1B,YAAY,EAAE,GAAG;SAClB,CAAC,CAAC;QAEH,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,CAAC;IAC1C,CAAC;CACF;AAlCD,wDAkCC","sourcesContent":["import {\n WindowPostMessageStream,\n BasePostMessageStream,\n} from '@metamask/post-message-stream';\nimport { createWindow } from '@metamask/snaps-utils';\n\nimport {\n Job,\n AbstractExecutionService,\n ExecutionServiceArgs,\n} from '../AbstractExecutionService';\n\ntype IframeExecutionEnvironmentServiceArgs = {\n iframeUrl: URL;\n} & ExecutionServiceArgs;\n\nexport class IframeExecutionService extends AbstractExecutionService<Window> {\n public iframeUrl: URL;\n\n constructor({\n iframeUrl,\n messenger,\n setupSnapProvider,\n }: IframeExecutionEnvironmentServiceArgs) {\n super({\n messenger,\n setupSnapProvider,\n });\n this.iframeUrl = iframeUrl;\n }\n\n protected terminateJob(jobWrapper: Job<Window>): void {\n document.getElementById(jobWrapper.id)?.remove();\n }\n\n protected async initEnvStream(jobId: string): Promise<{\n worker: Window;\n stream: BasePostMessageStream;\n }> {\n const iframeWindow = await createWindow(this.iframeUrl.toString(), jobId);\n\n const stream = new WindowPostMessageStream({\n name: 'parent',\n target: 'child',\n targetWindow: iframeWindow,\n targetOrigin: '*',\n });\n\n return { worker: iframeWindow, stream };\n }\n}\n"]}
1
+ {"version":3,"file":"IframeExecutionService.js","sourceRoot":"","sources":["../../../src/services/iframe/IframeExecutionService.ts"],"names":[],"mappings":";;;AAAA,uEAGuC;AACvC,uDAAqD;AAErD,0EAIqC;AAMrC,MAAa,sBAAuB,SAAQ,mDAAgC;IAG1E,YAAY,EACV,SAAS,EACT,SAAS,EACT,iBAAiB,GACqB;QACtC,KAAK,CAAC;YACJ,SAAS;YACT,iBAAiB;SAClB,CAAC,CAAC;QACH,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;IAC7B,CAAC;IAES,YAAY,CAAC,UAAuB;QAC5C,QAAQ,CAAC,cAAc,CAAC,UAAU,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC;IACnD,CAAC;IAES,KAAK,CAAC,aAAa,CAAC,KAAa;QAIzC,MAAM,YAAY,GAAG,MAAM,IAAA,0BAAY,EAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,KAAK,CAAC,CAAC;QAE1E,MAAM,MAAM,GAAG,IAAI,6CAAuB,CAAC;YACzC,IAAI,EAAE,QAAQ;YACd,MAAM,EAAE,OAAO;YACf,YAAY,EAAE,YAAY;YAC1B,YAAY,EAAE,GAAG;SAClB,CAAC,CAAC;QAEH,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,CAAC;IAC1C,CAAC;CACF;AAlCD,wDAkCC","sourcesContent":["import {\n WindowPostMessageStream,\n BasePostMessageStream,\n} from '@metamask/post-message-stream';\nimport { createWindow } from '@metamask/snaps-utils';\n\nimport {\n Job,\n AbstractExecutionService,\n ExecutionServiceArgs,\n} from '../AbstractExecutionService';\n\ntype IframeExecutionEnvironmentServiceArgs = {\n iframeUrl: URL;\n} & ExecutionServiceArgs;\n\nexport class IframeExecutionService extends AbstractExecutionService<Window> {\n public iframeUrl: URL;\n\n constructor({\n iframeUrl,\n messenger,\n setupSnapProvider,\n }: IframeExecutionEnvironmentServiceArgs) {\n super({\n messenger,\n setupSnapProvider,\n });\n this.iframeUrl = iframeUrl;\n }\n\n protected terminateJob(jobWrapper: Job<Window>): void {\n document.getElementById(jobWrapper.id)?.remove();\n }\n\n protected async initEnvStream(jobId: string): Promise<{\n worker: Window;\n stream: BasePostMessageStream;\n }> {\n const iframeWindow = await createWindow(this.iframeUrl.toString(), jobId);\n\n const stream = new WindowPostMessageStream({\n name: 'parent',\n target: 'child',\n targetWindow: iframeWindow,\n targetOrigin: '*',\n });\n\n return { worker: iframeWindow, stream };\n }\n}\n"]}
@@ -12,8 +12,7 @@ class RequestQueue {
12
12
  * @param origin - A string identifying the origin.
13
13
  */
14
14
  increment(origin) {
15
- var _a;
16
- const currentCount = (_a = this.queueSizes.get(origin)) !== null && _a !== void 0 ? _a : 0;
15
+ const currentCount = this.queueSizes.get(origin) ?? 0;
17
16
  if (currentCount >= this.maxQueueSize) {
18
17
  throw new Error('Maximum number of requests reached. Try again later.');
19
18
  }
@@ -25,8 +24,7 @@ class RequestQueue {
25
24
  * @param origin - A string identifying the origin.
26
25
  */
27
26
  decrement(origin) {
28
- var _a;
29
- const currentCount = (_a = this.queueSizes.get(origin)) !== null && _a !== void 0 ? _a : 0;
27
+ const currentCount = this.queueSizes.get(origin) ?? 0;
30
28
  if (currentCount === 0) {
31
29
  throw new Error(`Cannot decrement, ${origin} does not have any outstanding requests.`);
32
30
  }
@@ -39,8 +37,7 @@ class RequestQueue {
39
37
  * @returns The queue count for the origin.
40
38
  */
41
39
  get(origin) {
42
- var _a;
43
- return (_a = this.queueSizes.get(origin)) !== null && _a !== void 0 ? _a : 0;
40
+ return this.queueSizes.get(origin) ?? 0;
44
41
  }
45
42
  }
46
43
  exports.RequestQueue = RequestQueue;
@@ -1 +1 @@
1
- {"version":3,"file":"RequestQueue.js","sourceRoot":"","sources":["../../src/snaps/RequestQueue.ts"],"names":[],"mappings":";;;AAAA,MAAa,YAAY;IAKvB,YAAY,YAAoB;QAC9B,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;QACjC,IAAI,CAAC,UAAU,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC9C,CAAC;IAED;;;;OAIG;IACI,SAAS,CAAC,MAAc;;QAC7B,MAAM,YAAY,GAAG,MAAA,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,mCAAI,CAAC,CAAC;QACtD,IAAI,YAAY,IAAI,IAAI,CAAC,YAAY,EAAE;YACrC,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAC;SACzE;QACD,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,MAAM,EAAE,YAAY,GAAG,CAAC,CAAC,CAAC;IAChD,CAAC;IAED;;;;OAIG;IACI,SAAS,CAAC,MAAc;;QAC7B,MAAM,YAAY,GAAG,MAAA,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,mCAAI,CAAC,CAAC;QACtD,IAAI,YAAY,KAAK,CAAC,EAAE;YACtB,MAAM,IAAI,KAAK,CACb,qBAAqB,MAAM,0CAA0C,CACtE,CAAC;SACH;QACD,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,MAAM,EAAE,YAAY,GAAG,CAAC,CAAC,CAAC;IAChD,CAAC;IAED;;;;;OAKG;IACI,GAAG,CAAC,MAAc;;QACvB,OAAO,MAAA,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,mCAAI,CAAC,CAAC;IAC1C,CAAC;CACF;AA/CD,oCA+CC","sourcesContent":["export class RequestQueue {\n public readonly maxQueueSize: number;\n\n private readonly queueSizes: Map<string, number>;\n\n constructor(maxQueueSize: number) {\n this.maxQueueSize = maxQueueSize;\n this.queueSizes = new Map<string, number>();\n }\n\n /**\n * Increments the queue count for a particular origin.\n *\n * @param origin - A string identifying the origin.\n */\n public increment(origin: string) {\n const currentCount = this.queueSizes.get(origin) ?? 0;\n if (currentCount >= this.maxQueueSize) {\n throw new Error('Maximum number of requests reached. Try again later.');\n }\n this.queueSizes.set(origin, currentCount + 1);\n }\n\n /**\n * Decrements the queue count for a particular origin.\n *\n * @param origin - A string identifying the origin.\n */\n public decrement(origin: string) {\n const currentCount = this.queueSizes.get(origin) ?? 0;\n if (currentCount === 0) {\n throw new Error(\n `Cannot decrement, ${origin} does not have any outstanding requests.`,\n );\n }\n this.queueSizes.set(origin, currentCount - 1);\n }\n\n /**\n * Gets the queue count for a particular origin.\n *\n * @param origin - A string identifying the origin.\n * @returns The queue count for the origin.\n */\n public get(origin: string): number {\n return this.queueSizes.get(origin) ?? 0;\n }\n}\n"]}
1
+ {"version":3,"file":"RequestQueue.js","sourceRoot":"","sources":["../../src/snaps/RequestQueue.ts"],"names":[],"mappings":";;;AAAA,MAAa,YAAY;IAKvB,YAAY,YAAoB;QAC9B,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;QACjC,IAAI,CAAC,UAAU,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC9C,CAAC;IAED;;;;OAIG;IACI,SAAS,CAAC,MAAc;QAC7B,MAAM,YAAY,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACtD,IAAI,YAAY,IAAI,IAAI,CAAC,YAAY,EAAE;YACrC,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAC;SACzE;QACD,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,MAAM,EAAE,YAAY,GAAG,CAAC,CAAC,CAAC;IAChD,CAAC;IAED;;;;OAIG;IACI,SAAS,CAAC,MAAc;QAC7B,MAAM,YAAY,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACtD,IAAI,YAAY,KAAK,CAAC,EAAE;YACtB,MAAM,IAAI,KAAK,CACb,qBAAqB,MAAM,0CAA0C,CACtE,CAAC;SACH;QACD,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,MAAM,EAAE,YAAY,GAAG,CAAC,CAAC,CAAC;IAChD,CAAC;IAED;;;;;OAKG;IACI,GAAG,CAAC,MAAc;QACvB,OAAO,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC1C,CAAC;CACF;AA/CD,oCA+CC","sourcesContent":["export class RequestQueue {\n public readonly maxQueueSize: number;\n\n private readonly queueSizes: Map<string, number>;\n\n constructor(maxQueueSize: number) {\n this.maxQueueSize = maxQueueSize;\n this.queueSizes = new Map<string, number>();\n }\n\n /**\n * Increments the queue count for a particular origin.\n *\n * @param origin - A string identifying the origin.\n */\n public increment(origin: string) {\n const currentCount = this.queueSizes.get(origin) ?? 0;\n if (currentCount >= this.maxQueueSize) {\n throw new Error('Maximum number of requests reached. Try again later.');\n }\n this.queueSizes.set(origin, currentCount + 1);\n }\n\n /**\n * Decrements the queue count for a particular origin.\n *\n * @param origin - A string identifying the origin.\n */\n public decrement(origin: string) {\n const currentCount = this.queueSizes.get(origin) ?? 0;\n if (currentCount === 0) {\n throw new Error(\n `Cannot decrement, ${origin} does not have any outstanding requests.`,\n );\n }\n this.queueSizes.set(origin, currentCount - 1);\n }\n\n /**\n * Gets the queue count for a particular origin.\n *\n * @param origin - A string identifying the origin.\n * @returns The queue count for the origin.\n */\n public get(origin: string): number {\n return this.queueSizes.get(origin) ?? 0;\n }\n}\n"]}
@@ -1,4 +1,4 @@
1
- import { AddApprovalRequest } from '@metamask/approval-controller';
1
+ import { AddApprovalRequest, UpdateRequestState } from '@metamask/approval-controller';
2
2
  import { BaseControllerV2 as BaseController, RestrictedControllerMessenger } from '@metamask/base-controller';
3
3
  import { GetEndowments, GetPermissions, GetSubjects, GrantPermissions, HasPermission, HasPermissions, RevokeAllPermissions, RevokePermissionForAllSubjects, RevokePermissions, UpdateCaveat } from '@metamask/permission-controller';
4
4
  import { BlockReason } from '@metamask/snaps-registry';
@@ -14,6 +14,7 @@ import { Timer } from './Timer';
14
14
  export declare const controllerName = "SnapController";
15
15
  export declare const SNAP_APPROVAL_INSTALL = "wallet_installSnap";
16
16
  export declare const SNAP_APPROVAL_UPDATE = "wallet_updateSnap";
17
+ export declare const SNAP_APPROVAL_RESULT = "wallet_installSnapResult";
17
18
  export declare type PendingRequest = {
18
19
  requestId: unknown;
19
20
  timer: Timer;
@@ -238,7 +239,7 @@ export declare type SnapTerminated = {
238
239
  payload: [snap: TruncatedSnap];
239
240
  };
240
241
  export declare type SnapControllerEvents = SnapAdded | SnapBlocked | SnapInstalled | SnapRemoved | SnapStateChange | SnapUnblocked | SnapUpdated | SnapRolledback | SnapTerminated;
241
- export declare type AllowedActions = GetEndowments | GetPermissions | GetSubjects | GetSubjectMetadata | HasPermission | HasPermissions | RevokePermissions | RevokeAllPermissions | RevokePermissionForAllSubjects | GrantPermissions | AddApprovalRequest | HandleRpcRequestAction | ExecuteSnapAction | TerminateAllSnapsAction | TerminateSnapAction | UpdateCaveat;
242
+ export declare type AllowedActions = GetEndowments | GetPermissions | GetSubjects | GetSubjectMetadata | HasPermission | HasPermissions | RevokePermissions | RevokeAllPermissions | RevokePermissionForAllSubjects | GrantPermissions | AddApprovalRequest | HandleRpcRequestAction | ExecuteSnapAction | TerminateAllSnapsAction | TerminateSnapAction | UpdateCaveat | UpdateRequestState;
242
243
  export declare type AllowedEvents = ExecutionServiceEvents;
243
244
  declare type SnapControllerMessenger = RestrictedControllerMessenger<typeof controllerName, SnapControllerActions | AllowedActions, SnapControllerEvents | AllowedEvents, AllowedActions['type'], AllowedEvents['type']>;
244
245
  declare type FeatureFlags = {
@@ -517,6 +518,7 @@ export declare class SnapController extends BaseController<string, SnapControlle
517
518
  *
518
519
  * @param origin - The origin requesting the snap.
519
520
  * @param snapId - The id of the snap.
521
+ * @param location - The location implementation of the snap.
520
522
  * @param versionRange - The semver range of the snap to install.
521
523
  * @returns The resulting snap object, or an error if something went wrong.
522
524
  */
@@ -535,11 +537,11 @@ export declare class SnapController extends BaseController<string, SnapControlle
535
537
  *
536
538
  * @param origin - The origin requesting the snap update.
537
539
  * @param snapId - The id of the Snap to be updated.
538
- * @param location - Optional location that was already used during installation flow.
540
+ * @param location - The location implementation of the snap.
539
541
  * @param newVersionRange - A semver version range in which the maximum version will be chosen.
540
542
  * @returns The snap metadata if updated, `null` otherwise.
541
543
  */
542
- updateSnap(origin: string, snapId: ValidatedSnapId, location: SnapLocation, newVersionRange?: string): Promise<TruncatedSnap | null>;
544
+ updateSnap(origin: string, snapId: ValidatedSnapId, location: SnapLocation, newVersionRange?: string): Promise<TruncatedSnap>;
543
545
  /**
544
546
  * Get metadata for the given snap ID.
545
547
  *
@@ -554,8 +556,8 @@ export declare class SnapController extends BaseController<string, SnapControlle
554
556
  *
555
557
  * This function is not hash private yet because of tests.
556
558
  *
557
- * @param origin - The origin of the install request.
558
559
  * @param snapId - The id of the Snap.
560
+ * @param pendingApproval - Pending approval to update.
559
561
  * @returns The snap's approvedPermissions.
560
562
  */
561
563
  private authorize;