@metamask/snaps-controllers 0.30.0 → 0.32.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 (44) 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/services/node/NodeProcessExecutionService.js +1 -1
  12. package/dist/services/node/NodeProcessExecutionService.js.map +1 -1
  13. package/dist/services/node/NodeThreadExecutionService.js +1 -1
  14. package/dist/services/node/NodeThreadExecutionService.js.map +1 -1
  15. package/dist/snaps/RequestQueue.js +3 -6
  16. package/dist/snaps/RequestQueue.js.map +1 -1
  17. package/dist/snaps/SnapController.d.ts +9 -11
  18. package/dist/snaps/SnapController.js +285 -188
  19. package/dist/snaps/SnapController.js.map +1 -1
  20. package/dist/snaps/endowments/cronjob.js +2 -3
  21. package/dist/snaps/endowments/cronjob.js.map +1 -1
  22. package/dist/snaps/endowments/index.js +6 -1
  23. package/dist/snaps/endowments/index.js.map +1 -1
  24. package/dist/snaps/endowments/keyring.js +3 -4
  25. package/dist/snaps/endowments/keyring.js.map +1 -1
  26. package/dist/snaps/endowments/rpc.js +2 -2
  27. package/dist/snaps/endowments/rpc.js.map +1 -1
  28. package/dist/snaps/endowments/transaction-insight.js +4 -5
  29. package/dist/snaps/endowments/transaction-insight.js.map +1 -1
  30. package/dist/snaps/location/http.js +9 -3
  31. package/dist/snaps/location/http.js.map +1 -1
  32. package/dist/snaps/location/local.js +1 -1
  33. package/dist/snaps/location/local.js.map +1 -1
  34. package/dist/snaps/location/location.js +2 -3
  35. package/dist/snaps/location/location.js.map +1 -1
  36. package/dist/snaps/location/npm.js +11 -8
  37. package/dist/snaps/location/npm.js.map +1 -1
  38. package/dist/snaps/registry/json.d.ts +33 -14
  39. package/dist/snaps/registry/json.js +119 -44
  40. package/dist/snaps/registry/json.js.map +1 -1
  41. package/package.json +33 -13
  42. package/dist/services/iframe/test/fixJSDOMPostMessageEventSource.d.ts +0 -3
  43. package/dist/services/iframe/test/fixJSDOMPostMessageEventSource.js +0 -34
  44. package/dist/services/iframe/test/fixJSDOMPostMessageEventSource.js.map +0 -1
@@ -10,20 +10,9 @@ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (
10
10
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
11
11
  return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
12
12
  };
13
- var __rest = (this && this.__rest) || function (s, e) {
14
- var t = {};
15
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
16
- t[p] = s[p];
17
- if (s != null && typeof Object.getOwnPropertySymbols === "function")
18
- for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
19
- if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
20
- t[p[i]] = s[p[i]];
21
- }
22
- return t;
23
- };
24
- var _SnapController_instances, _SnapController_closeAllConnections, _SnapController_environmentEndowmentPermissions, _SnapController_excludedPermissions, _SnapController_featureFlags, _SnapController_fetchFunction, _SnapController_idleTimeCheckInterval, _SnapController_registry, _SnapController_maxIdleTime, _SnapController_detectSnapLocation, _SnapController_rollbackSnapshots, _SnapController_timeoutForLastRequestStatus, _SnapController_statusMachine, _SnapController_initializeStateMachine, _SnapController_registerMessageHandlers, _SnapController_pollForLastRequestStatus, _SnapController_blockSnap, _SnapController_unblockSnap, _SnapController_assertIsInstallAllowed, _SnapController_stopSnapsLastRequestPastMax, _SnapController_transition, _SnapController_terminateSnap, _SnapController_removeSnapFromSubjects, _SnapController_add, _SnapController_startSnap, _SnapController_getEndowments, _SnapController_set, _SnapController_fetchSnap, _SnapController_processSnapPermissions, _SnapController_getRpcRequestHandler, _SnapController_executeWithTimeout, _SnapController_recordSnapRpcRequestStart, _SnapController_recordSnapRpcRequestFinish, _SnapController_getRollbackSnapshot, _SnapController_createRollbackSnapshot, _SnapController_rollbackSnap, _SnapController_rollbackSnaps, _SnapController_getRuntime, _SnapController_getRuntimeExpect, _SnapController_setupRuntime, _SnapController_calculatePermissionsChange, _SnapController_isValidUpdate;
13
+ var _SnapController_instances, _SnapController_closeAllConnections, _SnapController_environmentEndowmentPermissions, _SnapController_excludedPermissions, _SnapController_featureFlags, _SnapController_fetchFunction, _SnapController_idleTimeCheckInterval, _SnapController_maxIdleTime, _SnapController_detectSnapLocation, _SnapController_rollbackSnapshots, _SnapController_timeoutForLastRequestStatus, _SnapController_statusMachine, _SnapController_initializeStateMachine, _SnapController_registerMessageHandlers, _SnapController_pollForLastRequestStatus, _SnapController_blockSnap, _SnapController_unblockSnap, _SnapController_assertIsInstallAllowed, _SnapController_stopSnapsLastRequestPastMax, _SnapController_transition, _SnapController_terminateSnap, _SnapController_removeSnapFromSubjects, _SnapController_createApproval, _SnapController_updateApproval, _SnapController_add, _SnapController_startSnap, _SnapController_getEndowments, _SnapController_set, _SnapController_fetchSnap, _SnapController_processSnapPermissions, _SnapController_validateSnapPermissions, _SnapController_getRpcRequestHandler, _SnapController_executeWithTimeout, _SnapController_recordSnapRpcRequestStart, _SnapController_recordSnapRpcRequestFinish, _SnapController_getRollbackSnapshot, _SnapController_createRollbackSnapshot, _SnapController_rollbackSnap, _SnapController_rollbackSnaps, _SnapController_getRuntime, _SnapController_getRuntimeExpect, _SnapController_setupRuntime, _SnapController_calculatePermissionsChange, _SnapController_isValidUpdate;
25
14
  Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.SnapController = exports.SNAP_APPROVAL_UPDATE = exports.SNAP_APPROVAL_INSTALL = exports.controllerName = void 0;
15
+ exports.SnapController = exports.SNAP_APPROVAL_RESULT = exports.SNAP_APPROVAL_UPDATE = exports.SNAP_APPROVAL_INSTALL = exports.controllerName = void 0;
27
16
  const base_controller_1 = require("@metamask/base-controller");
28
17
  const rpc_methods_1 = require("@metamask/rpc-methods");
29
18
  const snaps_utils_1 = require("@metamask/snaps-utils");
@@ -45,6 +34,7 @@ exports.controllerName = 'SnapController';
45
34
  // TODO: Figure out how to name these
46
35
  exports.SNAP_APPROVAL_INSTALL = 'wallet_installSnap';
47
36
  exports.SNAP_APPROVAL_UPDATE = 'wallet_updateSnap';
37
+ exports.SNAP_APPROVAL_RESULT = 'wallet_installSnapResult';
48
38
  const TRUNCATED_SNAP_PROPERTIES = new Set([
49
39
  'initialPermissions',
50
40
  'id',
@@ -81,8 +71,7 @@ const name = 'SnapController';
81
71
  * - Start: Initializes the snap in its SES realm with the authorized permissions.
82
72
  */
83
73
  class SnapController extends base_controller_1.BaseControllerV2 {
84
- constructor({ closeAllConnections, messenger, state, environmentEndowmentPermissions = [], excludedPermissions = {}, idleTimeCheckInterval = (0, utils_1.inMilliseconds)(5, utils_1.Duration.Second), registry = new registry_1.JsonSnapsRegistry(), maxIdleTime = (0, utils_1.inMilliseconds)(30, utils_1.Duration.Second), maxRequestTime = (0, utils_1.inMilliseconds)(60, utils_1.Duration.Second), fetchFunction = globalThis.fetch.bind(globalThis), featureFlags = {}, detectSnapLocation: detectSnapLocationFunction = location_1.detectSnapLocation, }) {
85
- var _a, _b;
74
+ constructor({ closeAllConnections, messenger, state, environmentEndowmentPermissions = [], excludedPermissions = {}, idleTimeCheckInterval = (0, utils_1.inMilliseconds)(5, utils_1.Duration.Second), maxIdleTime = (0, utils_1.inMilliseconds)(30, utils_1.Duration.Second), maxRequestTime = (0, utils_1.inMilliseconds)(60, utils_1.Duration.Second), fetchFunction = globalThis.fetch.bind(globalThis), featureFlags = {}, detectSnapLocation: detectSnapLocationFunction = location_1.detectSnapLocation, }) {
86
75
  super({
87
76
  messenger,
88
77
  metadata: {
@@ -103,9 +92,12 @@ class SnapController extends base_controller_1.BaseControllerV2 {
103
92
  persist: (snaps) => {
104
93
  return Object.values(snaps)
105
94
  .map((snap) => {
106
- return Object.assign(Object.assign({}, snap), { sourceCode: __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRuntimeExpect).call(this, snap.id).sourceCode,
95
+ return {
96
+ ...snap,
97
+ sourceCode: __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRuntimeExpect).call(this, snap.id).sourceCode,
107
98
  // At the time state is rehydrated, no snap will be running.
108
- status: snaps_utils_1.SnapStatus.Stopped });
99
+ status: snaps_utils_1.SnapStatus.Stopped,
100
+ };
109
101
  })
110
102
  .reduce((memo, snap) => {
111
103
  memo[snap.id] = snap;
@@ -116,12 +108,18 @@ class SnapController extends base_controller_1.BaseControllerV2 {
116
108
  },
117
109
  },
118
110
  name,
119
- state: Object.assign(Object.assign({}, defaultState), Object.assign(Object.assign({}, state), { snaps: Object.values((_a = state === null || state === void 0 ? void 0 : state.snaps) !== null && _a !== void 0 ? _a : {}).reduce((memo, snap) => {
120
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
121
- const { sourceCode } = snap, rest = __rest(snap, ["sourceCode"]);
122
- memo[snap.id] = rest;
123
- return memo;
124
- }, {}) })),
111
+ state: {
112
+ ...defaultState,
113
+ ...{
114
+ ...state,
115
+ snaps: Object.values(state?.snaps ?? {}).reduce((memo, snap) => {
116
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
117
+ const { sourceCode, ...rest } = snap;
118
+ memo[snap.id] = rest;
119
+ return memo;
120
+ }, {}),
121
+ },
122
+ },
125
123
  });
126
124
  _SnapController_instances.add(this);
127
125
  _SnapController_closeAllConnections.set(this, void 0);
@@ -130,7 +128,6 @@ class SnapController extends base_controller_1.BaseControllerV2 {
130
128
  _SnapController_featureFlags.set(this, void 0);
131
129
  _SnapController_fetchFunction.set(this, void 0);
132
130
  _SnapController_idleTimeCheckInterval.set(this, void 0);
133
- _SnapController_registry.set(this, void 0);
134
131
  _SnapController_maxIdleTime.set(this, void 0);
135
132
  _SnapController_detectSnapLocation.set(this, void 0);
136
133
  _SnapController_rollbackSnapshots.set(this, void 0);
@@ -142,7 +139,6 @@ class SnapController extends base_controller_1.BaseControllerV2 {
142
139
  __classPrivateFieldSet(this, _SnapController_featureFlags, featureFlags, "f");
143
140
  __classPrivateFieldSet(this, _SnapController_fetchFunction, fetchFunction, "f");
144
141
  __classPrivateFieldSet(this, _SnapController_idleTimeCheckInterval, idleTimeCheckInterval, "f");
145
- __classPrivateFieldSet(this, _SnapController_registry, registry, "f");
146
142
  __classPrivateFieldSet(this, _SnapController_maxIdleTime, maxIdleTime, "f");
147
143
  this.maxRequestTime = maxRequestTime;
148
144
  __classPrivateFieldSet(this, _SnapController_detectSnapLocation, detectSnapLocationFunction, "f");
@@ -159,13 +155,10 @@ class SnapController extends base_controller_1.BaseControllerV2 {
159
155
  /* eslint-enable @typescript-eslint/unbound-method */
160
156
  __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_initializeStateMachine).call(this);
161
157
  __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_registerMessageHandlers).call(this);
162
- Object.values((_b = state === null || state === void 0 ? void 0 : state.snaps) !== null && _b !== void 0 ? _b : {}).forEach((snap) => {
163
- var _a, _b;
164
- return __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_setupRuntime).call(this, snap.id, {
165
- sourceCode: snap.sourceCode,
166
- state: (_b = (_a = state === null || state === void 0 ? void 0 : state.snapStates) === null || _a === void 0 ? void 0 : _a[snap.id]) !== null && _b !== void 0 ? _b : null,
167
- });
168
- });
158
+ Object.values(state?.snaps ?? {}).forEach((snap) => __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_setupRuntime).call(this, snap.id, {
159
+ sourceCode: snap.sourceCode,
160
+ state: state?.snapStates?.[snap.id] ?? null,
161
+ }));
169
162
  }
170
163
  /**
171
164
  * Checks all installed snaps against the block list and
@@ -173,7 +166,7 @@ class SnapController extends base_controller_1.BaseControllerV2 {
173
166
  * for more information.
174
167
  */
175
168
  async updateBlockedSnaps() {
176
- const blockedSnaps = await __classPrivateFieldGet(this, _SnapController_registry, "f").get(Object.values(this.state.snaps).reduce((blockListArg, snap) => {
169
+ const blockedSnaps = await this.messagingSystem.call('SnapsRegistry:get', Object.values(this.state.snaps).reduce((blockListArg, snap) => {
177
170
  blockListArg[snap.id] = {
178
171
  version: snap.version,
179
172
  checksum: snap.manifest.source.shasum,
@@ -388,7 +381,10 @@ class SnapController extends base_controller_1.BaseControllerV2 {
388
381
  addSnapError(snapError) {
389
382
  this.update((state) => {
390
383
  const id = (0, nanoid_1.nanoid)();
391
- state.snapErrors[id] = Object.assign(Object.assign({}, snapError), { internalID: id });
384
+ state.snapErrors[id] = {
385
+ ...snapError,
386
+ internalID: id,
387
+ };
392
388
  });
393
389
  }
394
390
  /**
@@ -419,7 +415,7 @@ class SnapController extends base_controller_1.BaseControllerV2 {
419
415
  */
420
416
  async getSnapState(snapId) {
421
417
  const { state } = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRuntimeExpect).call(this, snapId);
422
- return state !== null && state !== void 0 ? state : null;
418
+ return state ?? null;
423
419
  }
424
420
  /**
425
421
  * Completely clear the controller's state: delete all associated data,
@@ -517,13 +513,12 @@ class SnapController extends base_controller_1.BaseControllerV2 {
517
513
  * @returns The serialized permitted snaps for the origin.
518
514
  */
519
515
  getPermittedSnaps(origin) {
520
- var _a, _b, _c, _d, _e;
521
- const permissions = (_a = this.messagingSystem.call('PermissionController:getPermissions', origin)) !== null && _a !== void 0 ? _a : {};
522
- const snaps = (_e = (_d = (_c = (_b = permissions[rpc_methods_1.WALLET_SNAP_PERMISSION_KEY]) === null || _b === void 0 ? void 0 : _b.caveats) === null || _c === void 0 ? void 0 : _c.find((caveat) => caveat.type === snaps_utils_1.SnapCaveatType.SnapIds)) === null || _d === void 0 ? void 0 : _d.value) !== null && _e !== void 0 ? _e : {};
516
+ const permissions = this.messagingSystem.call('PermissionController:getPermissions', origin) ?? {};
517
+ const snaps = permissions[rpc_methods_1.WALLET_SNAP_PERMISSION_KEY]?.caveats?.find((caveat) => caveat.type === snaps_utils_1.SnapCaveatType.SnapIds)?.value ?? {};
523
518
  return Object.keys(snaps).reduce((permittedSnaps, snapId) => {
524
519
  const snap = this.get(snapId);
525
520
  const truncatedSnap = this.getTruncated(snapId);
526
- if (truncatedSnap && (snap === null || snap === void 0 ? void 0 : snap.status) !== snaps_utils_1.SnapStatus.Installing) {
521
+ if (truncatedSnap && snap?.status !== snaps_utils_1.SnapStatus.Installing) {
527
522
  permittedSnaps[snapId] = truncatedSnap;
528
523
  }
529
524
  return permittedSnaps;
@@ -542,22 +537,25 @@ class SnapController extends base_controller_1.BaseControllerV2 {
542
537
  async installSnaps(origin, requestedSnaps) {
543
538
  const result = {};
544
539
  const snapIds = Object.keys(requestedSnaps);
545
- // Existing snaps may need to be updated
546
- const pendingUpdates = snapIds.filter((snapId) => this.has(snapId));
547
- // Non-existing snaps will need to be installed
548
- const pendingInstalls = snapIds.filter((snapId) => !pendingUpdates.includes(snapId));
540
+ const pendingUpdates = [];
541
+ const pendingInstalls = [];
549
542
  try {
550
543
  for (const [snapId, { version: rawVersion }] of Object.entries(requestedSnaps)) {
544
+ (0, snaps_utils_1.assertIsValidSnapId)(snapId);
551
545
  const [error, version] = (0, snaps_utils_1.resolveVersionRange)(rawVersion);
552
546
  if (error) {
553
547
  throw eth_rpc_errors_1.ethErrors.rpc.invalidParams(`The "version" field must be a valid SemVer version range if specified. Received: "${rawVersion}".`);
554
548
  }
555
- const permissions = this.messagingSystem.call('PermissionController:getPermissions', origin);
556
- if (!(0, snaps_utils_1.isSnapPermitted)(permissions, snapId)) {
557
- throw eth_rpc_errors_1.ethErrors.provider.unauthorized(`Not authorized to install snap "${snapId}". Request the permission for the snap before attempting to install it.`);
558
- }
559
- const isUpdate = pendingUpdates.includes(snapId);
549
+ const location = __classPrivateFieldGet(this, _SnapController_detectSnapLocation, "f").call(this, snapId, {
550
+ versionRange: version,
551
+ fetch: __classPrivateFieldGet(this, _SnapController_fetchFunction, "f"),
552
+ allowLocal: __classPrivateFieldGet(this, _SnapController_featureFlags, "f").allowLocalSnaps,
553
+ });
554
+ // Existing snaps may need to be updated, unless they should be re-installed (e.g. local snaps)
555
+ // Everything else is treated as an install
556
+ const isUpdate = this.has(snapId) && !location.shouldAlwaysReload;
560
557
  if (isUpdate && __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_isValidUpdate).call(this, snapId, version)) {
558
+ pendingUpdates.push(snapId);
561
559
  let rollbackSnapshot = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRollbackSnapshot).call(this, snapId);
562
560
  if (rollbackSnapshot === undefined) {
563
561
  const prevSourceCode = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRuntimeExpect).call(this, snapId).sourceCode;
@@ -569,7 +567,10 @@ class SnapController extends base_controller_1.BaseControllerV2 {
569
567
  throw new Error('This snap is already being updated.');
570
568
  }
571
569
  }
572
- result[snapId] = await this.processRequestedSnap(origin, snapId, version);
570
+ else if (!isUpdate) {
571
+ pendingInstalls.push(snapId);
572
+ }
573
+ result[snapId] = await this.processRequestedSnap(origin, snapId, location, version);
573
574
  }
574
575
  snapIds.forEach((snapId) => __classPrivateFieldGet(this, _SnapController_rollbackSnapshots, "f").delete(snapId));
575
576
  }
@@ -589,16 +590,11 @@ class SnapController extends base_controller_1.BaseControllerV2 {
589
590
  *
590
591
  * @param origin - The origin requesting the snap.
591
592
  * @param snapId - The id of the snap.
593
+ * @param location - The location implementation of the snap.
592
594
  * @param versionRange - The semver range of the snap to install.
593
595
  * @returns The resulting snap object, or an error if something went wrong.
594
596
  */
595
- async processRequestedSnap(origin, snapId, versionRange) {
596
- (0, snaps_utils_1.validateSnapId)(snapId);
597
- const location = __classPrivateFieldGet(this, _SnapController_detectSnapLocation, "f").call(this, snapId, {
598
- versionRange,
599
- fetch: __classPrivateFieldGet(this, _SnapController_fetchFunction, "f"),
600
- allowLocal: __classPrivateFieldGet(this, _SnapController_featureFlags, "f").allowLocalSnaps,
601
- });
597
+ async processRequestedSnap(origin, snapId, location, versionRange) {
602
598
  const existingSnap = this.getTruncated(snapId);
603
599
  // For devX we always re-install local snaps.
604
600
  if (existingSnap && !location.shouldAlwaysReload) {
@@ -606,35 +602,55 @@ class SnapController extends base_controller_1.BaseControllerV2 {
606
602
  return existingSnap;
607
603
  }
608
604
  if (__classPrivateFieldGet(this, _SnapController_featureFlags, "f").dappsCanUpdateSnaps === true) {
609
- const updateResult = await this.updateSnap(origin, snapId, location, versionRange);
610
- if (updateResult === null) {
611
- throw eth_rpc_errors_1.ethErrors.rpc.invalidParams(`Snap "${snapId}@${existingSnap.version}" is already installed. Couldn't update to a version inside requested "${versionRange}" range.`);
612
- }
613
- return updateResult;
605
+ return await this.updateSnap(origin, snapId, location, versionRange);
614
606
  }
615
607
  throw eth_rpc_errors_1.ethErrors.rpc.invalidParams(`Version mismatch with already installed snap. ${snapId}@${existingSnap.version} doesn't satisfy requested version ${versionRange}.`);
616
608
  }
609
+ let pendingApproval = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_createApproval).call(this, {
610
+ origin,
611
+ snapId,
612
+ type: exports.SNAP_APPROVAL_INSTALL,
613
+ });
617
614
  // Existing snaps must be stopped before overwriting
618
615
  if (existingSnap && this.isRunning(snapId)) {
619
616
  await this.stopSnap(snapId, snaps_utils_1.SnapStatusEvents.Stop);
620
617
  }
618
+ // Existing snaps that should be re-installed should not maintain their existing permissions
619
+ if (existingSnap && location.shouldAlwaysReload) {
620
+ this.revokeAllSnapPermissions(snapId);
621
+ }
621
622
  try {
622
623
  const { sourceCode } = await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_add).call(this, {
623
624
  origin,
624
625
  id: snapId,
625
626
  location,
627
+ versionRange,
628
+ });
629
+ await this.authorize(snapId, pendingApproval);
630
+ pendingApproval = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_createApproval).call(this, {
631
+ origin,
632
+ snapId,
633
+ type: exports.SNAP_APPROVAL_RESULT,
626
634
  });
627
- await this.authorize(origin, snapId);
628
635
  await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_startSnap).call(this, {
629
636
  snapId,
630
637
  sourceCode,
631
638
  });
632
639
  const truncated = this.getTruncatedExpect(snapId);
640
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_updateApproval).call(this, pendingApproval.id, {
641
+ loading: false,
642
+ type: exports.SNAP_APPROVAL_INSTALL,
643
+ });
633
644
  this.messagingSystem.publish(`SnapController:snapInstalled`, truncated);
634
645
  return truncated;
635
646
  }
636
647
  catch (error) {
637
648
  (0, snaps_utils_1.logError)(`Error when adding snap.`, error);
649
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_updateApproval).call(this, pendingApproval.id, {
650
+ loading: false,
651
+ type: exports.SNAP_APPROVAL_INSTALL,
652
+ error: error instanceof Error ? error.message : error.toString(),
653
+ });
638
654
  throw error;
639
655
  }
640
656
  }
@@ -652,88 +668,108 @@ class SnapController extends base_controller_1.BaseControllerV2 {
652
668
  *
653
669
  * @param origin - The origin requesting the snap update.
654
670
  * @param snapId - The id of the Snap to be updated.
655
- * @param location - Optional location that was already used during installation flow.
671
+ * @param location - The location implementation of the snap.
656
672
  * @param newVersionRange - A semver version range in which the maximum version will be chosen.
657
673
  * @returns The snap metadata if updated, `null` otherwise.
658
674
  */
659
675
  async updateSnap(origin, snapId, location, newVersionRange = snaps_utils_1.DEFAULT_REQUESTED_SNAP_VERSION) {
660
- var _a;
661
- const snap = this.getExpect(snapId);
662
676
  if (!(0, utils_1.isValidSemVerRange)(newVersionRange)) {
663
677
  throw new Error(`Received invalid snap version range: "${newVersionRange}".`);
664
678
  }
665
- const newSnap = await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_fetchSnap).call(this, snapId, location);
666
- const newVersion = newSnap.manifest.result.version;
667
- if (!(0, utils_1.gtVersion)(newVersion, snap.version)) {
668
- (0, snaps_utils_1.logWarning)(`Tried updating snap "${snapId}" within "${newVersionRange}" version range, but newer version "${snap.version}" is already installed`);
669
- return null;
670
- }
671
- await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_assertIsInstallAllowed).call(this, snapId, {
672
- version: newVersion,
673
- checksum: newSnap.manifest.result.source.shasum,
674
- });
675
- const processedPermissions = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_processSnapPermissions).call(this, newSnap.manifest.result.initialPermissions);
676
- const { newPermissions, unusedPermissions, approvedPermissions } = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_calculatePermissionsChange).call(this, snapId, processedPermissions);
677
- const id = (0, nanoid_1.nanoid)();
678
- const _b = (await this.messagingSystem.call('ApprovalController:addRequest', {
679
+ let pendingApproval = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_createApproval).call(this, {
679
680
  origin,
680
- id,
681
+ snapId,
681
682
  type: exports.SNAP_APPROVAL_UPDATE,
682
- requestData: {
683
- // First two keys mirror installation params
684
- metadata: { id, origin: snapId, dappOrigin: origin },
683
+ });
684
+ try {
685
+ const snap = this.getExpect(snapId);
686
+ const newSnap = await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_fetchSnap).call(this, snapId, location);
687
+ const newVersion = newSnap.manifest.result.version;
688
+ if (!(0, utils_1.gtVersion)(newVersion, snap.version)) {
689
+ throw eth_rpc_errors_1.ethErrors.rpc.invalidParams(`Snap "${snapId}@${snap.version}" is already installed. Couldn't update to a version inside requested "${newVersionRange}" range.`);
690
+ }
691
+ if (!(0, utils_1.satisfiesVersionRange)(newVersion, newVersionRange)) {
692
+ throw new Error(`Version mismatch. Manifest for "${snapId}" specifies version "${newVersion}" which doesn't satisfy requested version range "${newVersionRange}".`);
693
+ }
694
+ await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_assertIsInstallAllowed).call(this, snapId, {
695
+ version: newVersion,
696
+ checksum: newSnap.manifest.result.source.shasum,
697
+ });
698
+ const processedPermissions = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_processSnapPermissions).call(this, newSnap.manifest.result.initialPermissions);
699
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_validateSnapPermissions).call(this, processedPermissions);
700
+ const { newPermissions, unusedPermissions, approvedPermissions } = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_calculatePermissionsChange).call(this, snapId, processedPermissions);
701
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_updateApproval).call(this, pendingApproval.id, {
685
702
  permissions: newPermissions,
686
- snapId,
687
703
  newVersion: newSnap.manifest.result.version,
688
704
  newPermissions,
689
705
  approvedPermissions,
690
706
  unusedPermissions,
691
- },
692
- }, true)), { permissions: approvedNewPermissions } = _b, requestData = __rest(_b, ["permissions"]);
693
- if (this.isRunning(snapId)) {
694
- await this.stopSnap(snapId, snaps_utils_1.SnapStatusEvents.Stop);
695
- }
696
- __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_transition).call(this, snapId, snaps_utils_1.SnapStatusEvents.Update);
697
- __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_set).call(this, {
698
- origin,
699
- id: snapId,
700
- manifest: newSnap.manifest,
701
- files: newSnap.files,
702
- versionRange: newVersionRange,
703
- isUpdate: true,
704
- });
705
- const unusedPermissionsKeys = Object.keys(unusedPermissions);
706
- if ((0, utils_1.isNonEmptyArray)(unusedPermissionsKeys)) {
707
- this.messagingSystem.call('PermissionController:revokePermissions', {
708
- [snapId]: unusedPermissionsKeys,
707
+ loading: false,
709
708
  });
710
- }
711
- if ((0, utils_1.isNonEmptyArray)(Object.keys(approvedNewPermissions))) {
712
- this.messagingSystem.call('PermissionController:grantPermissions', {
713
- approvedPermissions: approvedNewPermissions,
714
- subject: { origin: snapId },
715
- requestData,
709
+ const { permissions: approvedNewPermissions, ...requestData } = (await pendingApproval.promise);
710
+ pendingApproval = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_createApproval).call(this, {
711
+ origin,
712
+ snapId,
713
+ type: exports.SNAP_APPROVAL_RESULT,
716
714
  });
715
+ if (this.isRunning(snapId)) {
716
+ await this.stopSnap(snapId, snaps_utils_1.SnapStatusEvents.Stop);
717
+ }
718
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_transition).call(this, snapId, snaps_utils_1.SnapStatusEvents.Update);
719
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_set).call(this, {
720
+ origin,
721
+ id: snapId,
722
+ manifest: newSnap.manifest,
723
+ files: newSnap.files,
724
+ isUpdate: true,
725
+ });
726
+ const unusedPermissionsKeys = Object.keys(unusedPermissions);
727
+ if ((0, utils_1.isNonEmptyArray)(unusedPermissionsKeys)) {
728
+ this.messagingSystem.call('PermissionController:revokePermissions', {
729
+ [snapId]: unusedPermissionsKeys,
730
+ });
731
+ }
732
+ if ((0, utils_1.isNonEmptyArray)(Object.keys(approvedNewPermissions))) {
733
+ this.messagingSystem.call('PermissionController:grantPermissions', {
734
+ approvedPermissions: approvedNewPermissions,
735
+ subject: { origin: snapId },
736
+ requestData,
737
+ });
738
+ }
739
+ const rollbackSnapshot = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRollbackSnapshot).call(this, snapId);
740
+ if (rollbackSnapshot !== undefined) {
741
+ rollbackSnapshot.permissions.revoked = unusedPermissions;
742
+ rollbackSnapshot.permissions.granted = Object.keys(approvedNewPermissions);
743
+ rollbackSnapshot.permissions.requestData = requestData;
744
+ }
745
+ const normalizedSourcePath = (0, snaps_utils_1.normalizeRelative)(newSnap.manifest.result.source.location.npm.filePath);
746
+ const sourceCode = newSnap.files
747
+ .find((file) => file.path === normalizedSourcePath)
748
+ ?.toString();
749
+ (0, utils_1.assert)(typeof sourceCode === 'string' && sourceCode.length > 0, `Invalid source code for snap "${snapId}".`);
750
+ try {
751
+ await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_startSnap).call(this, { snapId, sourceCode });
752
+ }
753
+ catch {
754
+ throw new Error(`Snap ${snapId} crashed with updated source code.`);
755
+ }
756
+ const truncatedSnap = this.getTruncatedExpect(snapId);
757
+ this.messagingSystem.publish('SnapController:snapUpdated', truncatedSnap, snap.version);
758
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_updateApproval).call(this, pendingApproval.id, {
759
+ loading: false,
760
+ type: exports.SNAP_APPROVAL_UPDATE,
761
+ });
762
+ return truncatedSnap;
717
763
  }
718
- const rollbackSnapshot = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRollbackSnapshot).call(this, snapId);
719
- if (rollbackSnapshot !== undefined) {
720
- rollbackSnapshot.permissions.revoked = unusedPermissions;
721
- rollbackSnapshot.permissions.granted = Object.keys(approvedNewPermissions);
722
- rollbackSnapshot.permissions.requestData = requestData;
723
- }
724
- const normalizedSourcePath = (0, snaps_utils_1.normalizeRelative)(newSnap.manifest.result.source.location.npm.filePath);
725
- const sourceCode = (_a = newSnap.files
726
- .find((file) => file.path === normalizedSourcePath)) === null || _a === void 0 ? void 0 : _a.toString();
727
- (0, utils_1.assert)(typeof sourceCode === 'string' && sourceCode.length > 0, `Invalid source code for snap "${snapId}".`);
728
- try {
729
- await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_startSnap).call(this, { snapId, sourceCode });
730
- }
731
- catch (_c) {
732
- throw new Error(`Snap ${snapId} crashed with updated source code.`);
764
+ catch (error) {
765
+ (0, snaps_utils_1.logError)(`Error when updating snap,`, error);
766
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_updateApproval).call(this, pendingApproval.id, {
767
+ loading: false,
768
+ error: error instanceof Error ? error.message : error.toString(),
769
+ type: exports.SNAP_APPROVAL_UPDATE,
770
+ });
771
+ throw error;
733
772
  }
734
- const truncatedSnap = this.getTruncatedExpect(snapId);
735
- this.messagingSystem.publish('SnapController:snapUpdated', truncatedSnap, snap.version);
736
- return truncatedSnap;
737
773
  }
738
774
  /**
739
775
  * Get metadata for the given snap ID.
@@ -743,7 +779,7 @@ class SnapController extends base_controller_1.BaseControllerV2 {
743
779
  * verified.
744
780
  */
745
781
  async getRegistryMetadata(snapId) {
746
- return await __classPrivateFieldGet(this, _SnapController_registry, "f").getMetadata(snapId);
782
+ return await this.messagingSystem.call('SnapsRegistry:getMetadata', snapId);
747
783
  }
748
784
  /**
749
785
  * Initiates a request for the given snap's initial permissions.
@@ -751,36 +787,23 @@ class SnapController extends base_controller_1.BaseControllerV2 {
751
787
  *
752
788
  * This function is not hash private yet because of tests.
753
789
  *
754
- * @param origin - The origin of the install request.
755
790
  * @param snapId - The id of the Snap.
791
+ * @param pendingApproval - Pending approval to update.
756
792
  * @returns The snap's approvedPermissions.
757
793
  */
758
- async authorize(origin, snapId) {
794
+ async authorize(snapId, pendingApproval) {
759
795
  (0, logging_1.log)(`Authorizing snap: ${snapId}`);
760
796
  const snapsState = this.state.snaps;
761
797
  const snap = snapsState[snapId];
762
798
  const { initialPermissions } = snap;
763
799
  try {
764
800
  const processedPermissions = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_processSnapPermissions).call(this, initialPermissions);
765
- const excludedPermissionErrors = Object.keys(processedPermissions).reduce((errors, permission) => {
766
- if ((0, utils_1.hasProperty)(__classPrivateFieldGet(this, _SnapController_excludedPermissions, "f"), permission)) {
767
- errors.push(__classPrivateFieldGet(this, _SnapController_excludedPermissions, "f")[permission]);
768
- }
769
- return errors;
770
- }, []);
771
- (0, utils_1.assert)(excludedPermissionErrors.length === 0, `One or more permissions are not allowed:\n${excludedPermissionErrors.join('\n')}`);
772
- const id = (0, nanoid_1.nanoid)();
773
- const _a = (await this.messagingSystem.call('ApprovalController:addRequest', {
774
- origin,
775
- id,
776
- type: exports.SNAP_APPROVAL_INSTALL,
777
- requestData: {
778
- // Mirror previous installation metadata
779
- metadata: { id, origin: snapId, dappOrigin: origin },
780
- permissions: processedPermissions,
781
- snapId,
782
- },
783
- }, true)), { permissions: approvedPermissions } = _a, requestData = __rest(_a, ["permissions"]);
801
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_validateSnapPermissions).call(this, processedPermissions);
802
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_updateApproval).call(this, pendingApproval.id, {
803
+ loading: false,
804
+ permissions: processedPermissions,
805
+ });
806
+ const { permissions: approvedPermissions, ...requestData } = (await pendingApproval.promise);
784
807
  if ((0, utils_1.isNonEmptyArray)(Object.keys(approvedPermissions))) {
785
808
  this.messagingSystem.call('PermissionController:grantPermissions', {
786
809
  approvedPermissions,
@@ -823,9 +846,9 @@ class SnapController extends base_controller_1.BaseControllerV2 {
823
846
  }
824
847
  if (permissionName === endowments_1.SnapEndowments.Rpc) {
825
848
  const subject = this.messagingSystem.call('SubjectMetadataController:getSubjectMetadata', origin);
826
- const isSnap = (subject === null || subject === void 0 ? void 0 : subject.subjectType) === subject_metadata_controller_1.SubjectType.Snap;
849
+ const isSnap = subject?.subjectType === subject_metadata_controller_1.SubjectType.Snap;
827
850
  const permissions = this.messagingSystem.call('PermissionController:getPermissions', snapId);
828
- const rpcPermission = permissions === null || permissions === void 0 ? void 0 : permissions[endowments_1.SnapEndowments.Rpc];
851
+ const rpcPermission = permissions?.[endowments_1.SnapEndowments.Rpc];
829
852
  (0, utils_1.assert)(rpcPermission);
830
853
  const origins = (0, rpc_1.getRpcCaveatOrigins)(rpcPermission);
831
854
  (0, utils_1.assert)(origins);
@@ -841,7 +864,7 @@ class SnapController extends base_controller_1.BaseControllerV2 {
841
864
  }
842
865
  }
843
866
  exports.SnapController = SnapController;
844
- _SnapController_closeAllConnections = new WeakMap(), _SnapController_environmentEndowmentPermissions = new WeakMap(), _SnapController_excludedPermissions = new WeakMap(), _SnapController_featureFlags = new WeakMap(), _SnapController_fetchFunction = new WeakMap(), _SnapController_idleTimeCheckInterval = new WeakMap(), _SnapController_registry = new WeakMap(), _SnapController_maxIdleTime = new WeakMap(), _SnapController_detectSnapLocation = new WeakMap(), _SnapController_rollbackSnapshots = new WeakMap(), _SnapController_timeoutForLastRequestStatus = new WeakMap(), _SnapController_statusMachine = new WeakMap(), _SnapController_instances = new WeakSet(), _SnapController_initializeStateMachine = function _SnapController_initializeStateMachine() {
867
+ _SnapController_closeAllConnections = new WeakMap(), _SnapController_environmentEndowmentPermissions = new WeakMap(), _SnapController_excludedPermissions = new WeakMap(), _SnapController_featureFlags = new WeakMap(), _SnapController_fetchFunction = new WeakMap(), _SnapController_idleTimeCheckInterval = new WeakMap(), _SnapController_maxIdleTime = new WeakMap(), _SnapController_detectSnapLocation = new WeakMap(), _SnapController_rollbackSnapshots = new WeakMap(), _SnapController_timeoutForLastRequestStatus = new WeakMap(), _SnapController_statusMachine = new WeakMap(), _SnapController_instances = new WeakSet(), _SnapController_initializeStateMachine = function _SnapController_initializeStateMachine() {
845
868
  const disableGuard = ({ snapId }) => {
846
869
  return this.getExpect(snapId).enabled;
847
870
  };
@@ -862,6 +885,7 @@ _SnapController_closeAllConnections = new WeakMap(), _SnapController_environment
862
885
  target: snaps_utils_1.SnapStatus.Running,
863
886
  cond: disableGuard,
864
887
  },
888
+ [snaps_utils_1.SnapStatusEvents.Stop]: snaps_utils_1.SnapStatus.Stopped,
865
889
  },
866
890
  },
867
891
  [snaps_utils_1.SnapStatus.Running]: {
@@ -951,13 +975,12 @@ async function _SnapController_blockSnap(snapId, blockedSnapInfo) {
951
975
  });
952
976
  this.messagingSystem.publish(`${exports.controllerName}:snapUnblocked`, snapId);
953
977
  }, _SnapController_assertIsInstallAllowed = async function _SnapController_assertIsInstallAllowed(snapId, snapInfo) {
954
- var _a, _b;
955
- const results = await __classPrivateFieldGet(this, _SnapController_registry, "f").get({
978
+ const results = await this.messagingSystem.call('SnapsRegistry:get', {
956
979
  [snapId]: snapInfo,
957
980
  });
958
981
  const result = results[snapId];
959
982
  if (result.status === registry_1.SnapsRegistryStatus.Blocked) {
960
- throw new Error(`Cannot install version "${snapInfo.version}" of snap "${snapId}": The version is blocked. ${(_b = (_a = result.reason) === null || _a === void 0 ? void 0 : _a.explanation) !== null && _b !== void 0 ? _b : ''}`);
983
+ throw new Error(`Cannot install version "${snapInfo.version}" of snap "${snapId}": The version is blocked. ${result.reason?.explanation ?? ''}`);
961
984
  }
962
985
  else if (__classPrivateFieldGet(this, _SnapController_featureFlags, "f").requireAllowlist &&
963
986
  result.status !== registry_1.SnapsRegistryStatus.Verified) {
@@ -989,14 +1012,16 @@ async function _SnapController_terminateSnap(snapId) {
989
1012
  await this.messagingSystem.call('ExecutionService:terminateSnap', snapId);
990
1013
  this.messagingSystem.publish('SnapController:snapTerminated', this.getTruncatedExpect(snapId));
991
1014
  }, _SnapController_removeSnapFromSubjects = function _SnapController_removeSnapFromSubjects(snapId) {
992
- var _a, _b, _c, _d;
993
1015
  const subjects = this.messagingSystem.call('PermissionController:getSubjectNames');
994
1016
  for (const subject of subjects) {
995
1017
  const subjectPermissions = this.messagingSystem.call('PermissionController:getPermissions', subject);
996
- const snapIdsCaveat = ((_c = (_b = (_a = subjectPermissions === null || subjectPermissions === void 0 ? void 0 : subjectPermissions[rpc_methods_1.WALLET_SNAP_PERMISSION_KEY]) === null || _a === void 0 ? void 0 : _a.caveats) === null || _b === void 0 ? void 0 : _b.find((caveat) => caveat.type === snaps_utils_1.SnapCaveatType.SnapIds)) !== null && _c !== void 0 ? _c : {});
997
- const caveatHasSnap = Boolean((_d = snapIdsCaveat.value) === null || _d === void 0 ? void 0 : _d[snapId]);
1018
+ const snapIdsCaveat = (subjectPermissions?.[rpc_methods_1.WALLET_SNAP_PERMISSION_KEY]?.caveats?.find((caveat) => caveat.type === snaps_utils_1.SnapCaveatType.SnapIds) ??
1019
+ {});
1020
+ const caveatHasSnap = Boolean(snapIdsCaveat.value?.[snapId]);
998
1021
  if (caveatHasSnap) {
999
- const newCaveatValue = Object.assign({}, snapIdsCaveat.value);
1022
+ const newCaveatValue = {
1023
+ ...snapIdsCaveat.value,
1024
+ };
1000
1025
  delete newCaveatValue[snapId];
1001
1026
  if (Object.keys(newCaveatValue).length > 0) {
1002
1027
  this.messagingSystem.call('PermissionController:updateCaveat', subject, rpc_methods_1.WALLET_SNAP_PERMISSION_KEY, snaps_utils_1.SnapCaveatType.SnapIds, newCaveatValue);
@@ -1008,6 +1033,32 @@ async function _SnapController_terminateSnap(snapId) {
1008
1033
  }
1009
1034
  }
1010
1035
  }
1036
+ }, _SnapController_createApproval = function _SnapController_createApproval({ origin, snapId, type, }) {
1037
+ const id = (0, nanoid_1.nanoid)();
1038
+ const promise = this.messagingSystem.call('ApprovalController:addRequest', {
1039
+ origin,
1040
+ id,
1041
+ type,
1042
+ requestData: {
1043
+ // Mirror previous installation metadata
1044
+ metadata: { id, origin: snapId, dappOrigin: origin },
1045
+ snapId,
1046
+ },
1047
+ requestState: {
1048
+ loading: true,
1049
+ },
1050
+ }, true);
1051
+ return { id, promise };
1052
+ }, _SnapController_updateApproval = function _SnapController_updateApproval(id, requestState) {
1053
+ try {
1054
+ this.messagingSystem.call('ApprovalController:updateRequestState', {
1055
+ id,
1056
+ requestState,
1057
+ });
1058
+ }
1059
+ catch {
1060
+ // Do nothing
1061
+ }
1011
1062
  }, _SnapController_add =
1012
1063
  /**
1013
1064
  * Returns a promise representing the complete installation of the requested snap.
@@ -1019,8 +1070,7 @@ async function _SnapController_terminateSnap(snapId) {
1019
1070
  * @returns The resulting snap object.
1020
1071
  */
1021
1072
  async function _SnapController_add(args) {
1022
- const { id: snapId, location } = args;
1023
- (0, snaps_utils_1.validateSnapId)(snapId);
1073
+ const { id: snapId, location, versionRange } = args;
1024
1074
  __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_setupRuntime).call(this, snapId, { sourceCode: null, state: null });
1025
1075
  const runtime = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRuntimeExpect).call(this, snapId);
1026
1076
  if (!runtime.installPromise) {
@@ -1029,11 +1079,20 @@ async function _SnapController_add(args) {
1029
1079
  // to null in the authorize() method.
1030
1080
  runtime.installPromise = (async () => {
1031
1081
  const fetchedSnap = await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_fetchSnap).call(this, snapId, location);
1082
+ const manifest = fetchedSnap.manifest.result;
1083
+ (0, snaps_utils_1.assertIsSnapManifest)(manifest);
1084
+ if (!(0, utils_1.satisfiesVersionRange)(manifest.version, versionRange)) {
1085
+ throw new Error(`Version mismatch. Manifest for "${snapId}" specifies version "${manifest.version}" which doesn't satisfy requested version range "${versionRange}".`);
1086
+ }
1032
1087
  await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_assertIsInstallAllowed).call(this, snapId, {
1033
- version: fetchedSnap.manifest.result.version,
1034
- checksum: fetchedSnap.manifest.result.source.shasum,
1088
+ version: manifest.version,
1089
+ checksum: manifest.source.shasum,
1090
+ });
1091
+ return __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_set).call(this, {
1092
+ ...args,
1093
+ ...fetchedSnap,
1094
+ id: snapId,
1035
1095
  });
1036
- return __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_set).call(this, Object.assign(Object.assign(Object.assign({}, args), fetchedSnap), { id: snapId }));
1037
1096
  })();
1038
1097
  }
1039
1098
  try {
@@ -1051,7 +1110,10 @@ async function _SnapController_add(args) {
1051
1110
  throw new Error(`Snap "${snapId}" is already started.`);
1052
1111
  }
1053
1112
  try {
1054
- const result = await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_executeWithTimeout).call(this, snapId, this.messagingSystem.call('ExecutionService:executeSnap', Object.assign(Object.assign({}, snapData), { endowments: await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getEndowments).call(this, snapId) })));
1113
+ const result = await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_executeWithTimeout).call(this, snapId, this.messagingSystem.call('ExecutionService:executeSnap', {
1114
+ ...snapData,
1115
+ endowments: await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getEndowments).call(this, snapId),
1116
+ }));
1055
1117
  __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_transition).call(this, snapId, snaps_utils_1.SnapStatusEvents.Start);
1056
1118
  return result;
1057
1119
  }
@@ -1098,25 +1160,22 @@ async function _SnapController_getEndowments(snapId) {
1098
1160
  }
1099
1161
  return dedupedEndowments;
1100
1162
  }, _SnapController_set = function _SnapController_set(args) {
1101
- var _a, _b;
1102
- const { id: snapId, origin, manifest, files, versionRange = snaps_utils_1.DEFAULT_REQUESTED_SNAP_VERSION, isUpdate = false, } = args;
1163
+ const { id: snapId, origin, manifest, files, isUpdate = false } = args;
1103
1164
  (0, snaps_utils_1.assertIsSnapManifest)(manifest.result);
1104
1165
  const { version } = manifest.result;
1105
- if (!(0, utils_1.satisfiesVersionRange)(version, versionRange)) {
1106
- throw new Error(`Version mismatch. Manifest for "${snapId}" specifies version "${version}" which doesn't satisfy requested version range "${versionRange}"`);
1107
- }
1108
1166
  const normalizedSourcePath = (0, snaps_utils_1.normalizeRelative)(manifest.result.source.location.npm.filePath);
1109
1167
  const { iconPath } = manifest.result.source.location.npm;
1110
1168
  const normalizedIconPath = iconPath && (0, snaps_utils_1.normalizeRelative)(iconPath);
1111
- const sourceCode = (_a = files
1112
- .find((file) => file.path === normalizedSourcePath)) === null || _a === void 0 ? void 0 : _a.toString();
1169
+ const sourceCode = files
1170
+ .find((file) => file.path === normalizedSourcePath)
1171
+ ?.toString();
1113
1172
  const svgIcon = normalizedIconPath
1114
1173
  ? files.find((file) => file.path === normalizedIconPath)
1115
1174
  : undefined;
1116
1175
  (0, utils_1.assert)(typeof sourceCode === 'string' && sourceCode.length > 0, `Invalid source code for snap "${snapId}".`);
1117
1176
  const snapsState = this.state.snaps;
1118
1177
  const existingSnap = snapsState[snapId];
1119
- const previousVersionHistory = (_b = existingSnap === null || existingSnap === void 0 ? void 0 : existingSnap.versionHistory) !== null && _b !== void 0 ? _b : [];
1178
+ const previousVersionHistory = existingSnap?.versionHistory ?? [];
1120
1179
  const versionHistory = [
1121
1180
  ...previousVersionHistory,
1122
1181
  {
@@ -1125,11 +1184,20 @@ async function _SnapController_getEndowments(snapId) {
1125
1184
  origin,
1126
1185
  },
1127
1186
  ];
1128
- const snap = Object.assign(Object.assign({}, existingSnap), {
1187
+ const snap = {
1188
+ // Restore relevant snap state if it exists
1189
+ ...existingSnap,
1129
1190
  // Note that the snap will be unblocked and enabled, regardless of its
1130
1191
  // previous state.
1131
- blocked: false, enabled: true, id: snapId, initialPermissions: manifest.result.initialPermissions, manifest: manifest.result, status: __classPrivateFieldGet(this, _SnapController_statusMachine, "f").config.initial, version,
1132
- versionHistory });
1192
+ blocked: false,
1193
+ enabled: true,
1194
+ id: snapId,
1195
+ initialPermissions: manifest.result.initialPermissions,
1196
+ manifest: manifest.result,
1197
+ status: __classPrivateFieldGet(this, _SnapController_statusMachine, "f").config.initial,
1198
+ version,
1199
+ versionHistory,
1200
+ };
1133
1201
  // If the snap was blocked, it isn't any longer
1134
1202
  delete snap.blockInformation;
1135
1203
  // store the snap back in state
@@ -1146,8 +1214,8 @@ async function _SnapController_getEndowments(snapId) {
1146
1214
  }
1147
1215
  const runtime = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRuntimeExpect).call(this, snapId);
1148
1216
  runtime.sourceCode = sourceCode;
1149
- this.messagingSystem.publish(`SnapController:snapAdded`, snap, svgIcon === null || svgIcon === void 0 ? void 0 : svgIcon.toString());
1150
- return Object.assign(Object.assign({}, snap), { sourceCode });
1217
+ this.messagingSystem.publish(`SnapController:snapAdded`, snap, svgIcon?.toString());
1218
+ return { ...snap, sourceCode };
1151
1219
  }, _SnapController_fetchSnap =
1152
1220
  /**
1153
1221
  * Fetches the manifest and source code of a snap.
@@ -1178,7 +1246,7 @@ async function _SnapController_fetchSnap(snapId, location) {
1178
1246
  throw new Error(`Failed to fetch Snap "${snapId}": ${message}.`);
1179
1247
  }
1180
1248
  }, _SnapController_processSnapPermissions = function _SnapController_processSnapPermissions(initialPermissions) {
1181
- return (0, snaps_utils_1.fromEntries)(Object.entries(initialPermissions).map(([initialPermission, value]) => {
1249
+ return Object.fromEntries(Object.entries(initialPermissions).map(([initialPermission, value]) => {
1182
1250
  if ((0, utils_1.hasProperty)(rpc_methods_1.caveatMappers, initialPermission)) {
1183
1251
  return [initialPermission, rpc_methods_1.caveatMappers[initialPermission](value)];
1184
1252
  }
@@ -1194,6 +1262,17 @@ async function _SnapController_fetchSnap(snapId, location) {
1194
1262
  value,
1195
1263
  ];
1196
1264
  }));
1265
+ }, _SnapController_validateSnapPermissions = function _SnapController_validateSnapPermissions(processedPermissions) {
1266
+ const permissionKeys = Object.keys(processedPermissions);
1267
+ const handlerPermissions = Object.values(endowments_1.handlerEndowments);
1268
+ (0, utils_1.assert)(permissionKeys.some((key) => handlerPermissions.includes(key)), `A snap must request at least one of the following permissions: ${handlerPermissions.join(', ')}.`);
1269
+ const excludedPermissionErrors = permissionKeys.reduce((errors, permission) => {
1270
+ if ((0, utils_1.hasProperty)(__classPrivateFieldGet(this, _SnapController_excludedPermissions, "f"), permission)) {
1271
+ errors.push(__classPrivateFieldGet(this, _SnapController_excludedPermissions, "f")[permission]);
1272
+ }
1273
+ return errors;
1274
+ }, []);
1275
+ (0, utils_1.assert)(excludedPermissionErrors.length === 0, `One or more permissions are not allowed:\n${excludedPermissionErrors.join('\n')}`);
1197
1276
  }, _SnapController_getRpcRequestHandler = function _SnapController_getRpcRequestHandler(snapId) {
1198
1277
  const runtime = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRuntimeExpect).call(this, snapId);
1199
1278
  const existingHandler = runtime.rpcHandler;
@@ -1234,7 +1313,7 @@ async function _SnapController_fetchSnap(snapId, location) {
1234
1313
  }
1235
1314
  let _request = request;
1236
1315
  if (!(0, utils_1.hasProperty)(request, 'jsonrpc')) {
1237
- _request = Object.assign(Object.assign({}, request), { jsonrpc: '2.0' });
1316
+ _request = { ...request, jsonrpc: '2.0' };
1238
1317
  }
1239
1318
  else if (request.jsonrpc !== '2.0') {
1240
1319
  throw eth_rpc_errors_1.ethErrors.rpc.invalidRequest({
@@ -1276,7 +1355,7 @@ async function _SnapController_executeWithTimeout(snapId, promise, timer) {
1276
1355
  (0, snaps_utils_1.logWarning)(`${endowments_1.SnapEndowments.LongRunning} will soon be deprecated. For more information please see https://github.com/MetaMask/snaps-monorepo/issues/945.`);
1277
1356
  return promise;
1278
1357
  }
1279
- const result = await (0, utils_2.withTimeout)(promise, timer !== null && timer !== void 0 ? timer : this.maxRequestTime);
1358
+ const result = await (0, utils_2.withTimeout)(promise, timer ?? this.maxRequestTime);
1280
1359
  if (result === utils_2.hasTimedOut) {
1281
1360
  throw new Error('The request timed out.');
1282
1361
  }
@@ -1317,16 +1396,26 @@ async function _SnapController_executeWithTimeout(snapId, promise, timer) {
1317
1396
  * @throws {@link Error}. If a snapshot does not exist.
1318
1397
  */
1319
1398
  async function _SnapController_rollbackSnap(snapId) {
1320
- var _a;
1321
1399
  const rollbackSnapshot = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRollbackSnapshot).call(this, snapId);
1322
1400
  if (!rollbackSnapshot) {
1323
1401
  throw new Error('A snapshot does not exist for this snap.');
1324
1402
  }
1325
1403
  await this.stopSnap(snapId, snaps_utils_1.SnapStatusEvents.Stop);
1404
+ // Always set to stopped even if it wasn't running initially
1405
+ if (this.get(snapId)?.status !== snaps_utils_1.SnapStatus.Stopped) {
1406
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_transition).call(this, snapId, snaps_utils_1.SnapStatusEvents.Stop);
1407
+ }
1326
1408
  const { statePatches, sourceCode, permissions } = rollbackSnapshot;
1327
- if (statePatches === null || statePatches === void 0 ? void 0 : statePatches.length) {
1409
+ if (statePatches?.length) {
1328
1410
  this.applyPatches(statePatches);
1329
1411
  }
1412
+ // Reset snap status, as we may have been in another state when we stored state patches
1413
+ // But now we are 100% in a stopped state
1414
+ if (this.get(snapId)?.status !== snaps_utils_1.SnapStatus.Stopped) {
1415
+ this.update((state) => {
1416
+ state.snaps[snapId].status = snaps_utils_1.SnapStatus.Stopped;
1417
+ });
1418
+ }
1330
1419
  if (sourceCode) {
1331
1420
  const runtime = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRuntimeExpect).call(this, snapId);
1332
1421
  runtime.sourceCode = sourceCode;
@@ -1338,7 +1427,7 @@ async function _SnapController_rollbackSnap(snapId) {
1338
1427
  requestData: permissions.requestData,
1339
1428
  });
1340
1429
  }
1341
- if ((_a = permissions.granted) === null || _a === void 0 ? void 0 : _a.length) {
1430
+ if (permissions.granted?.length) {
1342
1431
  this.messagingSystem.call('PermissionController:revokePermissions', {
1343
1432
  [snapId]: permissions.granted,
1344
1433
  });
@@ -1364,7 +1453,6 @@ async function _SnapController_rollbackSnaps(snapIds) {
1364
1453
  (0, utils_1.assert)(runtime !== undefined, new Error(`Snap "${snapId}" runtime data not found`));
1365
1454
  return runtime;
1366
1455
  }, _SnapController_setupRuntime = function _SnapController_setupRuntime(snapId, data) {
1367
- var _a;
1368
1456
  if (this.snapsRuntimeData.has(snapId)) {
1369
1457
  return;
1370
1458
  }
@@ -1372,13 +1460,22 @@ async function _SnapController_rollbackSnaps(snapIds) {
1372
1460
  const interpreter = (0, fsm_1.interpret)(__classPrivateFieldGet(this, _SnapController_statusMachine, "f"));
1373
1461
  interpreter.start({
1374
1462
  context: { snapId },
1375
- value: (_a = snap === null || snap === void 0 ? void 0 : snap.status) !== null && _a !== void 0 ? _a : __classPrivateFieldGet(this, _SnapController_statusMachine, "f").config.initial,
1463
+ value: snap?.status ??
1464
+ __classPrivateFieldGet(this, _SnapController_statusMachine, "f").config.initial,
1376
1465
  });
1377
1466
  (0, fsm_2.forceStrict)(interpreter);
1378
- this.snapsRuntimeData.set(snapId, Object.assign({ lastRequest: null, rpcHandler: null, installPromise: null, activeReferences: 0, pendingInboundRequests: [], pendingOutboundRequests: 0, interpreter }, data));
1467
+ this.snapsRuntimeData.set(snapId, {
1468
+ lastRequest: null,
1469
+ rpcHandler: null,
1470
+ installPromise: null,
1471
+ activeReferences: 0,
1472
+ pendingInboundRequests: [],
1473
+ pendingOutboundRequests: 0,
1474
+ interpreter,
1475
+ ...data,
1476
+ });
1379
1477
  }, _SnapController_calculatePermissionsChange = function _SnapController_calculatePermissionsChange(snapId, desiredPermissionsSet) {
1380
- var _a;
1381
- const oldPermissions = (_a = this.messagingSystem.call('PermissionController:getPermissions', snapId)) !== null && _a !== void 0 ? _a : {};
1478
+ const oldPermissions = this.messagingSystem.call('PermissionController:getPermissions', snapId) ?? {};
1382
1479
  const newPermissions = (0, utils_2.setDiff)(desiredPermissionsSet, oldPermissions);
1383
1480
  // TODO(ritave): The assumption that these are unused only holds so long as we do not
1384
1481
  // permit dynamic permission requests.